# rust_os **Repository Path**: aisleeppppp/rust_os ## Basic Information - **Project Name**: rust_os - **Description**: 用rust实现的一个os,主要是自己学习os的项目 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-07-11 - **Last Updated**: 2023-07-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ch1目标: 1.首先,我们使用qemu模拟一台risc-v的计算机,我们将在这台模拟出来的计算机中执行编写的os程序。指令如下: 1.1编写一条内核指令 1.2编写链接脚本调整os程序的内存布局,因为我们需要让内核程序的第一个指令位于0x80200000处 1.3获取os.bin rust-objcopy --strip-all target/riscv64gc-unknown-none-elf/release/os -O binary target/riscv64gc-unknown-none-elf/release/os.bin 1.4执行指令:qemu-system-riscv64 \ -machine virt \ -nographic \ -bios ../bootloader/rustsbi-qemu.bin \ -device loader,file=target/riscv64gc-unknown-none-elf/release/os.bin,addr=0x80200000 \ -s -S 2.因为我们编写的是os程序,所以不能依赖别的操作系统,在rust中默认是需要依赖库的,而依赖库是需要操作系统支持的,所以我们需要剔除依赖库编写rust代码 --2.1 os程序的目标平台是riscv64gc-unknown-none-elf --2.2 使用rust的core库。Rust 有一个对 Rust 语言标准库–std 裁剪过后的 Rust 语言核心库 core。 core库是不需要任何操作系统支持的,它的功能也比较受限,但是也包含了 Rust 语言相当一部分的核心机制,可以满足我们的大部分功能需求。 --2.3 需要实现panic!宏,不然编译不通过。 #[panic_handler] 是一种编译指导属性,用于标记核心库core中的 panic! 宏要对接的函数(该函数实现对致命错误的具体处理)。 该编译指导属性所标记的函数需要具有 fn(&PanicInfo) -> ! 函数签名,函数可通过 PanicInfo 数据结构获取致命错误的相关信息。 这样Rust编译器就可以把核心库core中的 panic! 宏定义与 #[panic_handler] 指向的panic函数实现合并在一起,使得no_std程序具有类似std库的应对致命错误的功能。 --2.4 编译时报错 error: requires `start` lang_item 我们回忆一下,之前提到语言标准库和三方库作为应用程序的执行环境,需要负责在执行应用程序之前进行一些初始化工作, 然后才跳转到应用程序的入口点(也就是跳转到我们编写的 main 函数)开始执行。事实上 start 语义项代表了标准库 std 在执行应用程序之前需要进行的一些初始化工作。 由于我们禁用了标准库,编译器也就找不到这项功能的实现了。最简单的解决方案就是压根不让编译器使用这项功能。 我们在 main.rs 的开头加入设置 #![no_main] 告诉编译器我们没有一般意义上的 main 函数,并将原来的 main 函数删除。在失去了 main 函数的情况下,编译器也就不需要完成所谓的初始化工作了。 3.因为我们实现的操作系统必定是需要支持函数调用这个基本功能的,所以我们要支持这个能力。 3.1分配栈帧 3.2设置sp 我们设置成功sp之后,编译器会自动帮我们完成函数调用的逻辑(会在os代码函数调用的代码附近插入一些特定的汇编代码) 3.3清零.bss段(这里把栈帧放到了.bss段中,当然并不一定要放在.bss段中) 4.基于 RustSBI 提供的服务完成在屏幕上打印 Hello world! 和关机操作。 执行流程: Qemu把包含app,OS的image镜像, rustsbi加载到内存中(目标1)。将必要的文件载入到 Qemu 物理内存之后, Qemu CPU 的程序计数器(PC, Program Counter)会被初始化为 0x1000 , 因此 Qemu 实际执行的第一条指令位于物理地址 0x1000 ,接下来它将执行寥寥数条指令并跳转到物理地址 0x80000000,所以我们需要将rustsbi放到0x80000000 然后rustsbi会调用我们写的os程序,rustsbi约定os程序在0x80200000处,所以我们要将os放在这里。 总的来说,我们要关注两个位置:0x80000000(存放bootloader --rustsbi),0x80200000(存放os程序) RustSBI(bootloader)完成基本的硬件初始化后,跳转到OS起始位置,(要做一些约定) OS首先进行app执行前的初始化工作,即建立栈空间和清零bss段,然后跳转到app去执行。 app在执行过程中,会通过函数调用的方式得到OS提供的OS服务,如输出字符串等,避免了app与硬件直接交互的繁琐过程。(目标3) 整体的代码逻辑是:通过linker.ld设置我们自己需要的程序内存布局,.text.entry是内核的一个指令所在的地方(因为我们设置了BASE_ADDRESS = 0x80200000;所以内核的第一个指令所在地为0x80200000;) 我们在0x80200000处放的第一个指令是la sp, boot_stack_top,call rust_main,所以这个时候我们在最开始就已经将sp赋值为boot_statck_top ,然后之后,就有了函数调用的能力。接着执行call rust_main 所有权就会移到了rust代码中(main.rs中的rust_main function) console.rs是我们实现的一些宏,lang_items.rs是我们自己实现的一个简单的panic。sbi.rs是我们调用rustsbi实现的一些函数。