使用Rust在树莓派上编写操作系统 - 00 - 前言
在我们开始之前
以下文本是文档的1:1副本,会出现在每章教程所对应的内核主要源文件的顶部。文档描述了相应源文件的结构,并尝试解释该实现背后的原理。请阅读该文档,以熟悉将会在教程中遇到的内容,文档将帮助你更好地浏览代码,并理解每一章教程之间的递进关系。
另请注意,以下文本将引用第一批教程中尚不存在的源文件(如**/memory.rs
)或函数。随着教程的推进,它们将逐渐被添加。
玩得开心!
代码组织和架构
代码分为不同的模块,每个模块代表一个典型的内核
子系统。子系统的顶层模块文件直接位于src
文件夹中。例如,src/memory.rs
将包含与所有内存管理相关的代码。
处理器架构代码的可见性
内核
的某些子系统基于特定目标处理器架构的底层代码。对于每个支持的处理器架构,在src/_arch
中都存在一个子文件夹,例如,src/_arch/aarch64
。
这些架构文件夹反映了在src
中布置的子系统模块。例如,属于内核
MMU子系统(src/memory/mmu.rs
)的架构相关代码将位于src/_arch/aarch64/memory/mmu.rs
。里面的文件将以模块的方式,通过path
属性加载到src/memory/mmu.rs
中。通常使用以arch_
为前缀,后接通用模块名称,作为导入的模块名称。
例如,这是src/memory/mmu.rs
的前几行:
1 |
|
大多数情况下,arch_module
中的项会被父模块以公开方式重导出。如此,针对各架构的模块就可以对外提供其各项实现,而调用者不必关心哪个架构已经被条件编译了。
BSP代码
BSP
即Board Support Package。BSP
代码位于src/bsp.rs
下,包含目标板特定的变量和函数,例如目标板内存的映射、目标板特有设备的驱动程序实例之类的东西。
就像处理器架构代码那样,BSP
代码的模块结构,类似内核
子系统代码的模块结构,只不过这次没有重导出。这意味着必须从bsp
命名空间开始调用其提供的任何内容,例如bsp::driver::driver_manager()
。
内核接口
arch
和bsp
都包含条件编译的代码,具体取决于编译内核的目标环境和板卡。例如,树莓派3
和树莓派4
的中断控制器
的硬件是不同的,但我们希望其余的内核
能够很方便地与两者中的任何一个一起使用。
为了在arch
、bsp
和通用内核代码
之间提供一个清晰的抽象,我们在只要有可能且有意义的地方封装了interface
trait。这些trait定义在各子系统模块中,以强制我们实现面向接口编程,而是不面向实现编程。例如,将有一个通用IRQ处理接口,由两个版本树莓派各自的中断控制器驱动
实现,并且只将该接口导出到内核
的其余部分。
1 | +-------------------+ |
总结
对于逻辑内核
子系统,相应的代码可以分发在不同的物理位置。以内存子系统为例:
src/memory.rs
与src/memory/**/*
:- 通用代码并不知道目标处理器架构和
BSP
特征。- 示例:一个用于将内存块归零的函数。
- 由
arch
或BSP
中的代码实现的内存子系统的接口。- 示例:定义
MMU
函数原型的MMU
接口。
- 示例:定义
- 通用代码并不知道目标处理器架构和
src/bsp/__board_name__/memory.rs
与src/bsp/__board_name__/memory/**/*
:- 特定
BSP
的代码。 - 示例:特定板子的内存映射(DRAM和MMIO设备的物理地址)。
- 特定
src/_arch/__arch_name__/memory.rs
与src/_arch/__arch_name__/memory/**/*
:- 特定处理器架构的代码。
- 示例:实现
__arch_name__
处理器架构的MMU
接口。
从名称空间的角度来看,内存子系统代码位于:
crate::memory::*
crate::bsp::memory::*
启动流程
- 内核的入口点是函数
cpu::boot::arch_boot::_start()
。
- 其实现位于
src/_arch/__arch_name__/cpu/boot.rs
。
下面不是翻译,是我写的使用方法。
下载
直接克隆本教程即可使用:
1 | gh repo clone rust-embedded/rust-raspberrypi-OS-tutorials |
rust-analyzer
我的macOS貌似通过macports
装了另一个版本的Rust,要知道通过rustup
安装的Rust位于~/.cargo/bin/
中,而port
安装的Rust位于/opt/local/bin/
中。这导致vscode启动时,默认加载的环境变量可能会使用port
的版本,而该版本并不是rustup
安装的,可能缺少rust-src
等组件,这会导致rust-analyzer工作异常。尝试解决:
😕 直接
export
环境变量后启动vscode,能解决问题,但是每次启动很麻烦,并不优雅。😕 修改shell的各个
profile
环境变量,但是这会牵连出很多未知问题,因为可能影响到其他程序的环境变量搜索顺序。(附:shell各profile的加载顺序介绍12)😕 尝试修改rust项目中配置文件的
rustc
设置,发现对rust-analyzer并不起作用。😕 直接卸载
port
版本,简单有效,但是由于忘记了port
版本是不是我有意识安装的,所以并不知道会不会对其他程序产生影响。🎉 修改vscode的rust-analyzer插件的
extraEnv
配置,该配置位于:扩展: rust-analyzer -> 扩展设置 -> 搜索extraEnv
-> 在 setting.json 中编辑,打开编辑器,向extraEnv
字典新增两行路径:setting.json 1
2
3
4"rust-analyzer.server.extraEnv": {
"RUSTC": "/Users/zealot/.cargo/bin/rustc",
"RUSTDOC": "/Users/zealot/.cargo/bin/rustdoc"
}重新加载即可。
运行
自第一章起,每一章都是一个可执行的小系统,比如进入01_WAIT_FOREVER
目录,即可执行make qemu
来编译,并在虚拟环境中运行内核。