引言

本章导读

大多数程序员的职业生涯都从 Hello, world! 开始。

printf("Hello world!\n");
cout << "Hello world!\n";
print("Hello world!")
System.out.println("Hello world!");
echo "Hello world!"
println!("Hello world!");
console.log("Hello world!");

然而,要用几行代码向世界问好,并不像表面上那么简单。 Hello, world! 程序能够编译运行,靠的是以 编译器 为主的开发环境和以 操作系统 为主的执行环境。

在本章中,我们将抽丝剥茧,一步步让 Hello, world! 程序脱离其依赖的执行环境,编写一个能打印 Hello, world! 的 OS。这趟旅途将让我们对应用程序及其执行环境有更深入的理解。

注意

实验指导书存在的目的是帮助读者理解框架代码。

为便于测试,完成编程实验时,请以框架代码为基础,不必跟着文档从零开始编写内核。

本章我们将从操作系统最简单但也最重要的 println 入手,实现一个裸机上的 println 和带色彩的 LOG (如 info、warn 和 error 等功能),这对程序等开发和调试至关重要。作为操作系统实验“正文”的第一章,本章的所有代码已经帮大家写好,没有需要亲自编写代码的部分。但它作为第一章又是尤为重要的一章:使用 Rust 进行实验的同学要熟悉如何脱离标准库、如何编译到裸机平台;使用 C 进行实验的同学要熟悉整个框架是如何编译的。简而言之,本章是带着大家熟悉选择的语言及对应框架,帮助大家顺利进行之后的实验。

系统调用

在实验开始之前,大家要熟悉一下系统调用 (syscall) 的概念。相信大家在汇编的课程中一定接触过这个名词。我们 OS 课程中的 syscall 的意义也是一样的,它是操作系统提供给软件的一系列接口,使得软件能够使用系统的功能。syscall 本质上属于一种异常/中断,它在 riscv 的汇编指令中以 ecall 的形式出现。

本章的 println 在 console 中打印字符,也需要调用到 syscall。syscall 的种类有很多,操作系统通过区分 syscall 的 id 来判断是哪一个syscall。

实践体验

首先我们要让脱离了标准库的程序能输出 (即支持 println!),这对程序的开发和调试至关重要。我们先在用户态下实现该功能,在 此处 获取相关代码。之后我们把程序移植到内核态,构建在裸机上支持输出的最小运行时环境。

获取本章代码:

$ git clone https://github.com/LearningOS/rCore-Tutorial-2021Autumn.git
$ cd rCore-Tutorial-2021Autumn
$ git checkout ch1

运行本章代码,并设置日志级别为 TRACE

$ cd os
$ make run LOG=TRACE

预期输出:

../_images/color-demo-rs.png

除了 Hello, world! 之外还有一些额外的信息,最后关机。

注解

RustSBI 是啥?

TODO: 添加 RustSBI 的相关章节

本章代码树

.
├── bootloader (内核依赖的运行在 M 特权级的 SBI 实现,本项目中我们使用 RustSBI)
│  └── rustsbi-qemu.bin
├── Dockerfile
├── LICENSE
├── Makefile
├── os
│  ├── Cargo.toml (cargo 项目配置文件)
│  ├── Makefile
│  └── src
│     ├── console.rs (将打印字符的 SBI 接口进一步封装实现更加强大的格式化输出)
│     ├── entry.asm (设置内核执行环境的的一段汇编代码)
│     ├── lang_items.rs (需要我们提供给 Rust 编译器的一些语义项,目前包含内核 panic 时的处理逻辑)
│     ├── linker.ld (控制内核内存布局的链接脚本以使内核运行在 qemu 虚拟机上)
│     ├── logging.rs (为本项目实现了日志功能)
│     ├── main.rs (内核主函数)
│     └── sbi.rs (封装底层 SBI 实现提供的 SBI 接口)
├── README.md
└── rust-toolchain (整个项目的工具链版本)

===============================================================================
Language            Files        Lines         Code     Comments       Blanks
===============================================================================
Assembly                1           12           11            0            1
Dockerfile              1           40           31            5            4
Makefile                2           57           40            4           13
Markdown                1            1            0            1            0
Rust                    5          186          155           10           21
TOML                    1           10            7            1            2
===============================================================================
Total                  11          306          244           21           41
===============================================================================