本文由编者复现 rust 操作系统 第一章内容的过程整理.
本文价值在于读者大体感知原教程第一章的主线内容,不被各种小插曲模糊目标,耗费耐心无法进行实验;也是读者做过同样的实验下,可以对比验证实验过程,或产生新的理解与感受,或巩固实验操作要点;
感谢提供这么好的操作系统实践教程资料,教程内容足够丰富有趣,实验足够小巧,我想如果认真进行复现操作,操作系统入门这个层面必然融会贯通,得心应手.
目标
rust 语言编写操作系统,执行打印函数,输出 hello world
原理
开发环境
核心思想
应用程序执行环境
代码结构
/.cargeo/config设置我们的默认编译目标(默认会使用 riscv64gc 作为目标平台而不是原先的默认 x86_64-unknown-linux-gnu),同时调整编译选项,设置链接脚本以及强制打开fp选项,这样才会避免fp相关指令被编译器优化掉 (rust-objdump工具反汇编内核或者应用程序可执行文件,并找到某个函数的入口)/bootloader/rustsbi-qemu.bin对应的 RustSBI 文件/src/entry.asm分配启动栈空间,并在控制权被转交给 Rust 入口之前将栈指针sp设置为栈顶的位置/src/linker.ld链接脚本,调整链接器的行为,使得最终生成的可执行文件的内存布局符合Qemu的预期,即内核第一条指令的地址应该位于 0x80200000(.bss.stack段最终会被汇集到.bss段中)/src/main.rs操作系统入口/src/console.rs重新定义输出,调用sbi.rs中的打印文本接口/src/lang_items.rs处理致命错误/src/sbi.rs调用 sbiMakefile一些方便构建、运行、编译项目的脚本rust-toolchain.toml它代表整个项目采用的 Rust 工具链版本。请务必保持其与原版仓库对应分支一致Cargo.toml引入包 sbi-rt, 内核和 RustSBI 链接到一起
/target/riscv64gc-xxxx/cloneOS.bin编译生成的os镜像文件
核心流程
- 环境准备
- 构建内核镜像
- 执行输出 hello world
Windows 下操作步骤
注意: 主操作流程的每一步,实际都可能需要花费半个小时甚至几个小时完成,因为需要时间理解每步的含义,或者需要验证步骤是否正确,或者需要时间解决步骤中奇奇怪怪 bug 等等,详细流程请移步文头引入的原始教程文档地址
环境准备
- 安装 Rust
- 安装 MEMU
- 安装 IDE(编者使用 RustRover)
- 下载 RustSBI 二进制文件 (编者直接到教程的项目下面拷贝,理论上自己去下载一个也可以)
构建内核镜像
- 准备好系统代码
- 直接通过编译代码
➜ cargo build
- 丢弃内核可执行文件中的元数据得到内核镜像
➜ rust-objcopy --strip-all target/riscv64gc-unknown-none-elf/release/cloneOS -O binary target/riscv64gc-unknown-none-elf/release/cloneOS.bin
注解: 上面得到的内核可执行文件完全符合我们对于内存布局的要求,但是我们不能将其直接提交给 Qemu , 因为它除了实际会被用到的代码和数据段之外还有一些多余的元数据, 这些元数据无法被 Qemu 在加载文件时利用,且会使代码和数据段被加载到错误的位置, 所以需要丢弃内核可执行文件中的元数据得到内核镜像
执行输出hello world
- 在
os目录下通过以下命令启动 Qemu 并加载 RustSBI 和内核镜像
➜ qemu-system-riscv64 -machine virt -nographic -bios ./bootloader/rustsbi-qemu.bin -device loader,file=target/riscv64gc-unknown-none-elf/release/cloneOS.bin,addr=0x80200000 -s -S
- 观察到控制台输出了 hello world
附录
编译器环境相关命令
Rust 编译器默认配置信息
$ rustc --version --verbose
Rust 编译器支持哪些基于 RISC-V 的目标平台
$ rustc --print target-list | grep riscv
Ubuntu 监视程序系统调用请求的工具 strace 来重新执行一下,看程序执行过程中的大量动态信息
$ strace ./donothing
rustc 编译器缺省生成RISC-V 64的目标代码
$ rustup target add riscv64gc-unknown-none-elf
分析目前的程序
文件格式
$ file target/riscv64gc-unknown-none-elf/debug/os
文件头信息
$ rust-readobj -h target/riscv64gc-unknown-none-elf/debug/os
反汇编导出汇编程序
$ rust-objdump -S target/riscv64gc-unknown-none-elf/debug/os
stat 工具来比较内核可执行文件和内核镜像的大小
➜ stat target/riscv64gc-unknown-none-elf/release/cloneOS
File: target/riscv64gc-unknown-none-elf/release/cloneOS
Size: 5272 Blocks: 8 IO Block: 65536 regular file
Device: ea9d99a5h/3936197029d Inode: 562949954437586 Links: 2
Access: (0644/-rw-r--r--) Uid: (2753560/W9041345) Gid: (1049089/ UNKNOWN)
Access: 2024-01-29 14:39:19.351366200 +0800
Modify: 2024-01-29 11:43:37.799889600 +0800
Change: 2024-01-29 11:43:37.834887200 +0800
Birth: 2024-01-29 11:43:37.798906300 +0800
➜ stat target/riscv64gc-unknown-none-elf/release/cloneOS.bin
File: target/riscv64gc-unknown-none-elf/release/cloneOS.bin
Size: 4 Blocks: 1 IO Block: 65536 regular file
Device: ea9d99a5h/3936197029d Inode: 1125899907859077 Links: 1
Access: (0644/-rw-r--r--) Uid: (2753560/W9041345) Gid: (1049089/ UNKNOWN)
Access: 2024-01-29 14:47:10.963529900 +0800
Modify: 2024-01-29 14:46:57.317414900 +0800
Change: 2024-01-29 14:46:57.317414900 +0800
Birth: 2024-01-29 14:46:57.315408400 +0800
概念
RustSBI 是什么
SBI 是 RISC-V 的一种底层规范,RustSBI 是它的一种实现。 操作系统内核与 RustSBI 的关系有点像应用与操作系统内核的关系,后者向前者提供一定的服务。只是SBI提供的服务很少, 比如关机,显示字符串,读入字符串等。