本章讲了如何用Rust写一个简易的操作系统(FledgeOS),除了Rust的知识外,可以了解操作系统主要的组成部分,加深对操作系统的理解
- 首选需要掌握和了解一些工具或者技术,QEMU是一种虚拟化技术,用于搭建一个虚拟机来让你的程序运行在上面;
bootimage
库可以帮助减少底层代码的开发; - 一开始需要安装几个库,
cargo-binutils
可以让cargo直接操作可执行文件,bootimage
让cargo可以直接构建一个初始化镜像(boot image),镜像可以直接在硬件上初始化;rustup toolchain install nightly & rustup default nightly
使用rust的一些尚未稳定的特性和功能;rustup component add rust-src
下载rust语言的源码,让rust能为新的操作系统编译一个编译器;rustup component add llvm-tools-preview
安装LLVM编译器的扩展,是rust编译器的一部分; - 使用书中对应的代码编译成功后可以看到一个QEMU的黑色框,说明成功编译,在这个过程中
bootimage
库帮忙做了很多事情,a. 为目标操作系统创建了一系列机器可读(machine-readable)的定义和术语,例如目标CUP架构;b. 编译Rust core代码在目标OS上运行;c. 用刚编译的Rust语言来编译OS内核;d. 编译一个引导程序(bootloader)来读取刚编译的OS内核;e. 在虚拟环境QEMU中执行引导程序,引导程序运行OS内核; - 项目结构中出现了
.cargo/config.toml
,里面提供了额外的配置参数,这里的作用是让编译器自己来编译std::core
的部分,而不是依赖已经预先安装好的版本;cargo.toml
里出现的run-command = ["qemu-system-x86_64", "-drive", "format=raw,file={}"]
表示在cargo run
的时候运行一个QEMU session, - 代码列表11.4里出现了很多新的属性(attributes),Page373页有详细介绍;主程序的代码永不返回,设置了返回值是
!
;当程序挂掉的时候相当于整个电脑崩溃了,这里用到的是LLVM的abort()
方法;在新的OS里为了避免动态分配内存选择了禁用标准库#![no_std]
;Rust的编译器有一部分是LLVM提供的,叫做intrinsic functions,为了使用这部分功能需要在开头加上#![core_intrinsics]
属性,同时由于LLVM跟Rust的非耦合关系,其提供的API可能会变化,所以要显式地引入Rust nightly的编译器;Rust在引入外部crate的时候为了避免有些变量的名称冲突会引入name_mangling,但在这里需要禁用这个功能,通过#![no_mangle]
属性;引入外部C代码要加上关键字extern "C"
;禁用main函数,使用#![no_main]
属性,因为main函数的参数是由编译器_start()
提供,所以这里不能有main函数; - VGA兼容的模式下,往屏幕写数据的逻辑是,屏幕在硬件里被划分成一个80x25的网格,每一格由2个字节来表示,包括背景色、字符色等等,具体可见Page375,这个网格被叫做
frame buffer
,通过计算知道buffer大小是4000字节,本项目设置0xb8000为buffer的起始位置,通过.offset(1)
来定位要写的buffer的位置,.write_volatie(0x30)
来往对应的byte写入数据; - FledgeOS如何启动,不是通过main()函数,而是需要软件能直接跟CPU通信,这个软件是BootLoader,由linker将某个符号(_start)链接到初始代码(entry point)处,CPU读取初始代码的指令开始执行;
x86_64
库能直接写指令跟CPU沟通,例如hlt()
能让CPU休眠,减少CPU的使用率和电量;#![feature(lang_items)]
注释表示当前文件提供的是语言条款(language item),语言条款是Rust实现的除了编译器以外的库,因为一开始标注了#[no_std]
不能使用Rust标准库,所以为了新OS必须实现一些必要的库和函数;- 后续这个OS主要讲了如何在QEMU虚拟环境如何处理输入输出和panic handling,与其他OS开发无异,详细可以看对应的章节;
- 区分信号(signals)和中断(interrupts),信号是软件层级的一种抽象,与OS相关;中断是硬件层级的抽象,主要是跟CPU有关;可见下图
- 中断(interrupt)会强制让应用的控制流改变,信号(signal)在某些情况下可以忽略(ignored),一旦收到中断,CPU要跳到对应的中断处理代码,中断处理代码由BIOS和OS在初始化进程(bootup process)预先定义;
- Rust实现软中断可以通过
std::asm
库带上#![feature(asm)
属性,asm!("int 42")
宏可以插入中断,硬件中断最常见的是键盘中断,原理如下图12.3所示