整体架构和启动过程

项目文件夹

root

.
├── README.md
├── status.md   支持状态文档
├── docs        各种文档,包含课程报告等
├── kernel      内核主项目目录
├── user        用户程序开发环境
├── crate       从内核中独立出去的库,包含内存管理、进程管理等
└── riscv-pk    BBL for RV32

kernel

.
├── src           代码目录
│   ├── arch      平台相关部分代码
│   │   ├── riscv32
│   │   └── x86_64
│   ├── xxx.rs    平台无关部分代码
│   └── ...
├── target        生成文件目录
├── Cargo.lock    cargo依赖锁定文件
├── Cargo.toml    cargo项目配置文件
├── build.rs      cargo build 前会执行的build脚本
├── Makefile
├── riscv32-blog_os.json    riscv32的target配置文件
└── x86_64-blog_os.json     x86_64的target配置文件

crate

.
├── bbl            bbl接口库
├── bit-allocator  线段树实现的0-N整数分配器,用来快速分配物理页
├── memory         内存管理模块
├── process        进程管理模块
├── riscv          riscv底层支持模块(Fork: rust-embedded/riscv)
└── sync           基于std的同步互斥测试程序

user

.
├── Cargo.lock
├── Cargo.toml
├── Makefile
├── riscv32-ucore.json    用于用户程序的target配置文件
├── x86_64-ucore.json     ……
├── src
│   └── bin
│       └── hello.rs      用户程序
├── ucore-ulib            RustOS的用户程序库
# 以上用于编写Rust用户程序

# 以下是现有二进制用户程序打包的磁盘镜像,未来可能被替代
├── ucore32.img           ucore x86-32用户程序 SFS磁盘镜像
├── user-riscv.img        ucore rv32用户程序 SFS磁盘镜像
└── xv6_64.img            xv6-x86_64用户程序 ……

启动过程概览

整体上和原版ucore一致,只是实现细节有所不同。

Boot

首先由Bootloader为Kernel配置好运行环境,设置好栈寄存器,跳转到Rust代码。

  • riscv32:arch/riscv32/mod.rs::rust_main()

  • x86_64:arch/x86_64/mod.rs::_start()

平台初始化

以x86_64为例,上面的函数依次执行以下初始化工作:

  1. Log模块初始化:设置全局Logger,此后即可使用debug!等宏输出不同等级的调试信息,它们会在终端中以彩色显示。

  2. 中断初始化:设置中断向量表,此后中断处理函数即可工作。

  3. 内存初始化:根据bootloader传来的内存信息……

    1. 初始化物理帧分配器,此后页表即可工作

    2. 为内核构造新的页表并启用(已废弃,转移到bootloader)

    3. 初始化堆分配器:此后即可在堆上分配内存,并使用core中如Vec、BTreeSet等强力工具。

  4. GDT和TSS初始化:后面用户程序要用到

  5. 设备初始化:PIC/APIC,时钟,串口,键盘

RISCV下则相对简单:

  1. Log模块初始化

  2. 中断初始化

  3. 内存初始化:……

  4. 时钟初始化

执行完毕后,跳转到kmain函数。

线程初始化

初始化线程管理器,并添加当前线程和idle线程。

进入Kernel Shell

它作为一个内核线程运行。首先加载用户程序磁盘数据,x86_64下通过IDE磁盘读取,riscv32下将磁盘文件硬连接到kernel中,通过内存读取。然后使用SFS库解析磁盘内容,根据用户输入的程序名,将相应用户程序数据读取到内存。使用ELF库解析用户程序内容,设置好页表映射,创建新的线程。

执行线程切换

Kernel Shell在为用户程序创建好新的线程后,会主动wait它,触发调度,切换到此线程执行。根据其初始内核栈帧的设置,上下文切换后它将进入用户态执行。

当用户程序运行一段时间后,可能由于系统调用、时钟中断、发生异常等原因,进入中断处理函数,并发生线程切换。

当用户程序主动或被动地退出后,它所占用的资源会被回收,Kernel Shell被唤醒,上述过程循环往复。

Last updated