logo
0
0
WeChat Login
prepare for tg-rcore-tutorial

第一章:应用程序与基本执行环境

本章实现了一个最简单的 RISC-V S 态裸机程序(tg-ch1),展示操作系统的最小执行环境。程序在 QEMU 模拟的 RISC-V 64 硬件上运行,不依赖 OpenSBI 或 RustSBI,通过 -bios none 模式直接启动,打印 Hello, world! 后关机。

通过本章的学习和实践,你将理解:

  • 应用程序的执行环境是什么,为什么 Hello, world! 并不简单
  • 如何让 Rust 程序脱离标准库,在裸机上运行
  • RISC-V 的启动流程和特权级机制
  • SBI 的作用以及操作系统如何与硬件交互

项目结构

ch1/ ├── .cargo/ │ └── config.toml # Cargo 配置:指定交叉编译目标和 QEMU runner ├── build.rs # 构建脚本:自动生成链接脚本 ├── Cargo.toml # 项目配置与依赖 ├── README.md # 本文档 └── src/ └── main.rs # 程序源码:入口、主函数、panic 处理

源码阅读导航索引

返回根文档导航总表

建议把本章源码阅读聚焦在一个文件:src/main.rs

阅读顺序位置重点问题
1_start为什么裸机入口要手动设栈,且不能依赖标准运行时?
2rust_main最小执行环境中,console_putcharshutdown 如何构成完整闭环?
3panic_handler#![no_std] 下发生异常时,系统如何收口与退出?

配套建议:阅读 tg-sbi/src/lib.rs 中的 SBI 调用封装,理解 console_putchar/shutdown 的底层调用路径。

DoD 验收标准(本章完成判据)

  • 能在 ch1 目录执行 cargo run,看到 Hello, world! 并正常关机退出
  • 能解释 #![no_std]#![no_main] 在裸机实验中的必要性
  • 能从 src/main.rs 说明 _start -> rust_main -> panic_handler 的控制流
  • 能说明 tg-sbi 在本章承担的最小职责(输出字符与关机)

概念-源码-测试三联表

核心概念源码入口自测方式(命令/现象)
裸机入口与手动设栈ch1/src/main.rs_startcargo run 可启动且无运行时依赖报错
SBI 最小服务调用ch1/src/main.rsrust_maintg-sbi/src/lib.rs看到串口输出后正常关机
无标准库异常处理ch1/src/main.rspanic_handler人为触发 panic 时可打印信息并异常关机

遇到构建/运行异常可先查看根文档的“高频错误速查表”。

一、环境准备

1.1 安装 Rust 工具链

本项目使用 Rust 语言编写,需要通过 rustup 安装 Rust 工具链。

Linux / macOS / WSL:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh source "$HOME/.cargo/env"

Windows:

https://rustup.rs 下载并运行 rustup-init.exe

验证安装:

rustc --version # 应显示 rustc 1.xx.x cargo --version # 应显示 cargo 1.xx.x

1.2 添加 RISC-V 64 编译目标

由于 tg-ch1 是面向 RISC-V 64 裸机平台的程序,需要添加对应的编译目标:

rustup target add riscv64gc-unknown-none-elf

这个目标三元组的含义是:

  • riscv64gc:RISC-V 64 位,支持 G(通用)和 C(压缩)指令集扩展
  • unknown:没有特定的 CPU 厂商
  • none:没有操作系统
  • elf:生成 ELF 格式的可执行文件,无标准运行时库

1.3 安装 QEMU 模拟器

tg-ch1 在 QEMU 模拟的 RISC-V 64 虚拟机上运行,需要安装 qemu-system-riscv64(建议版本 >= 7.0)。

Ubuntu / Debian:

sudo apt update sudo apt install qemu-system-misc

macOS(Homebrew):

brew install qemu

验证安装:

qemu-system-riscv64 --version

1.4 获取源代码

方式一 只获取本实验

cargo clone tg-ch1 cd tg-ch1

获取所有8个实验和所依赖的tg-* crates. 方式二

git clone https://github.com/rcore-os/tg-rcore-tutorial.git cd tg-rcore-tutorial/ch1

二、编译与运行

2.1 编译

tg-ch1ch1 目录下执行:

cargo build

这条命令实际上执行的是交叉编译——编译器在你的主机(如 x86_64)上运行,但生成的可执行文件是针对 riscv64gc-unknown-none-elf 平台的。这个目标平台由 .cargo/config.toml 中的配置自动指定:

[build] target = "riscv64gc-unknown-none-elf"

编译过程中,build.rs 构建脚本会自动检测目标架构,为 RISC-V 64 生成链接脚本(linker.ld),控制程序的内存布局。

编译成功后,可执行文件位于 target/riscv64gc-unknown-none-elf/debug/tg-ch1

2.2 运行

cargo run

cargo run 在编译成功后会自动调用 .cargo/config.toml 中配置的 runner 来执行程序。实际执行的命令等价于:

qemu-system-riscv64 \ -machine virt \ -nographic \ -bios none \ -kernel target/riscv64gc-unknown-none-elf/debug/tg-ch1

QEMU 参数说明:

参数说明
-machine virt使用 QEMU 的 virt 虚拟平台,这是一个通用的 RISC-V 虚拟机
-nographic无图形界面,所有输出通过串口重定向到终端
-bios none不加载任何 BIOS/SBI 固件,tg-ch1 自带 M-mode 启动代码
-kernel <文件>将 ELF 可执行文件加载到内存中作为内核启动

2.3 预期输出

Hello, world!

输出一行 Hello, world! 后,QEMU 自动退出。这是因为程序通过 SBI 调用执行了关机操作。


三、操作系统核心概念

以下内容帮助你理解 tg-ch1 代码背后的操作系统原理。

3.1 应用程序执行环境

大多数程序员的职业生涯都从 Hello, world! 开始。然而,要在屏幕上打印一行字,并不像表面上那么简单。

在日常开发中,我们编写的应用程序运行在一个多层次的执行环境栈之上:

┌─────────────────────────┐ │ 应用程序 │ ← 你写的代码 ├─────────────────────────┤ │ 标准库 (std / libc) │ ← println! 等函数的实现 ├─────────────────────────┤ │ 操作系统内核 │ ← 系统调用:write, exit 等 ├─────────────────────────┤ │ 硬件抽象层 (SBI/BIOS) │ ← 固件,为内核提供基础服务 ├─────────────────────────┤ │ 硬件 (CPU/内存) │ ← 物理硬件 └─────────────────────────┘

每一层为上一层提供服务,层与层之间通过明确定义的接口交互:

  • 应用程序通过系统调用(如 ecall)请求操作系统服务
  • 操作系统通过 SBI 调用请求固件服务
  • 固件直接操作硬件

当我们在 Linux 上执行 println!("Hello, world!") 时,实际经历了:println! → Rust 标准库 → libc 的 write() → Linux 内核 sys_write 系统调用 → 串口/终端驱动 → 硬件显示。

tg-ch1 做了什么? 它跳过了标准库和操作系统内核,直接在裸机上通过 SBI 接口输出字符。这就是"最小执行环境"的含义。

3.2 移除标准库依赖

要让程序在裸机上运行,首先需要摆脱对操作系统的依赖。Rust 标准库 std 依赖操作系统提供的系统调用(如文件 I/O、内存分配、线程等),在没有操作系统的裸机上无法使用。

tg-ch1 在 src/main.rs 的开头使用了两个关键的属性标记:

#![no_std] —— 不使用标准库

告诉 Rust 编译器不链接标准库 std,改用核心库 core。核心库 core 是 Rust 语言的子集实现,不依赖任何操作系统功能,包含了基本类型、迭代器、Option/Result 等核心机制。

#![no_main] —— 不使用标准入口

标准的 main() 函数入口需要运行时环境(如 C runtime)进行初始化。在裸机环境中没有这些支持,所以我们告诉编译器不使用标准入口,自己定义程序的入口点 _start

#[panic_handler] —— 自定义 panic 处理

标准库提供了 panic 时打印错误信息并终止程序的功能。使用 #![no_std] 后,需要自己实现 panic 处理函数。tg-ch1 中的实现是直接调用 SBI 关机:

#[panic_handler] fn panic(_info: &core::panic::PanicInfo) -> ! { shutdown(true) // 以异常状态关机 }

什么是交叉编译?

编译器运行在主机平台(如 x86_64-unknown-linux-gnu)上,但生成的可执行文件需要在目标平台(riscv64gc-unknown-none-elf)上运行,这种情况称为交叉编译(Cross Compile)。.cargo/config.toml 中的 target = "riscv64gc-unknown-none-elf" 配置使 cargo 自动进行交叉编译。

3.3 裸机启动流程

理解程序如何在裸机上启动,是操作系统学习的重要一步。

tg-ch1 采用 nobios 模式-bios none),不依赖外部 SBI 固件,而是在 tg-sbi 库中自带了一个最小的 M-mode 启动代码。启动流程如下:

QEMU 加电 │ ▼ PC = 0x1000(QEMU 内置引导代码) │ ▼ 跳转到 0x80000000(M-mode 入口,tg-sbi 的 _m_start) │ ── 在 M-mode 下初始化硬件环境 │ ── 设置中断委托、PMP 等 ▼ 跳转到 0x80200000(S-mode 入口,tg-ch1 的 _start) │ ── 设置栈指针 sp ▼ 跳转到 rust_main() │ ── 打印 "Hello, world!" │ ── 调用 SBI shutdown 关机 ▼ QEMU 退出

关键地址:

  • 0x80000000:M-mode 代码的起始地址,由链接脚本中的 M_BASE_ADDRESS 指定
  • 0x80200000:S-mode 代码的起始地址,由链接脚本中的 S_BASE_ADDRESS 指定,这是 _start 函数所在的位置

链接脚本的作用

链接脚本控制程序各段在内存中的布局。build.rs 在编译时自动生成链接脚本,将程序组织为:

地址空间布局: 0x80000000 ┌────────────────────┐ │ .text.m_entry │ M-mode 入口代码(tg-sbi) │ .text.m_trap │ M-mode 中断处理 │ .bss.m_stack │ M-mode 栈空间 │ .bss.m_data │ M-mode 数据 │ ... │ 0x80200000 ├────────────────────┤ │ .text │ S-mode 代码段(含 .text.entry) │ .rodata │ 只读数据段 │ .data │ 可读写数据段 │ .bss │ 未初始化数据段(含栈) └────────────────────┘

栈空间初始化

在裸机环境中,没有操作系统帮我们设置栈。_start 是一个裸函数#[unsafe(naked)]),它不会生成函数序言(prologue)和尾声(epilogue),可以在没有栈的情况下执行。它做的第一件事就是设置栈指针 sp,然后跳转到 Rust 函数 rust_main

#[unsafe(naked)] #[unsafe(no_mangle)] #[unsafe(link_section = ".text.entry")] unsafe extern "C" fn _start() -> ! { const STACK_SIZE: usize = 4096; #[unsafe(link_section = ".bss.uninit")] static mut STACK: [u8; STACK_SIZE] = [0u8; STACK_SIZE]; core::arch::naked_asm!( "la sp, {stack} + {stack_size}", // 将 sp 设置为栈顶地址 "j {main}", // 跳转到 rust_main stack_size = const STACK_SIZE, stack = sym STACK, main = sym rust_main, ) }

注意:Rust edition 2024 要求 no_manglelink_section 等 unsafe 属性必须用 unsafe(...) 包装,这与 edition 2021 的写法不同。

栈大小为 4096 字节(4 KiB),放置在 .bss.uninit 段中。la sp, STACK + 4096sp 设置为栈顶地址(栈从高地址向低地址增长)。

3.4 SBI 与特权级

RISC-V 特权级

RISC-V 定义了三个特权级(Privilege Level),从高到低:

特权级缩写说明
Machine ModeM-mode最高特权级,直接访问所有硬件资源
Supervisor ModeS-mode操作系统内核运行的特权级
User ModeU-mode应用程序运行的特权级

不同特权级之间通过 ecall(Environment Call)指令切换:

  • 应用程序(U-mode)执行 ecall → 陷入操作系统(S-mode):这是系统调用
  • 操作系统(S-mode)执行 ecall → 陷入固件(M-mode):这是 SBI 调用

虽然都是 ecall 指令,但因为所在特权级不同,产生的效果也不同。

SBI(Supervisor Binary Interface)

SBI 是 RISC-V 的标准规范,定义了 S-mode 软件(操作系统)向 M-mode 固件请求服务的接口。可以把 SBI 理解为"操作系统的操作系统"——它为操作系统提供最基本的硬件抽象服务。

tg-ch1 通过 use tg_sbi::{console_putchar, shutdown} 引入了两个 SBI 服务:

函数说明
console_putchar(c)向控制台输出一个字符(通过串口)
shutdown(fail)关闭虚拟机(fail=false 正常关机,fail=true 异常关机)

rust_main 的实现非常简洁——逐字符输出 "Hello, world!\n",然后关机:

extern "C" fn rust_main() -> ! { for c in b"Hello, world!\n" { console_putchar(*c); } shutdown(false) // false 表示正常关机 }

nobios 模式的特殊之处

传统方案(如 rCore-Tutorial 旧版)使用外部 SBI 固件(如 RustSBI),需要将 SBI 固件和内核分别加载。tg-ch1 采用 tg-sbinobios 特性,将 M-mode 启动代码直接编译进同一个 ELF 文件中,因此可以用 -bios none -kernel 的方式一步加载,简化了启动流程。


四、代码解读

4.1 .cargo/config.toml —— 交叉编译与运行配置

[build] target = "riscv64gc-unknown-none-elf" [target.riscv64gc-unknown-none-elf] runner = [ "qemu-system-riscv64", "-machine", "virt", "-nographic", "-bios", "none", "-kernel", ]
  • [build] target:设置默认编译目标为 RISC-V 64 裸机平台,每次 cargo build 自动交叉编译
  • [target...] runner:设置运行器为 QEMU,cargo run 时自动在 QEMU 中执行编译产物

4.2 Cargo.toml —— 项目配置

[package] name = "tg-ch1" edition = "2024" # ... [profile.dev] panic = "abort" [profile.release] panic = "abort" [dependencies] tg-sbi = { version = "0.1.0-preview.1", features = ["nobios"] }

关键配置:

  • edition = "2024":使用 Rust 2024 edition,要求 unsafe 属性使用 unsafe(...) 包装
  • panic = "abort":panic 时直接终止,不进行栈展开(unwinding),减少裸机程序的复杂度
  • tg-sbi 依赖启用了 nobios 特性,使其内建 M-mode 启动代码

4.3 build.rs —— 构建脚本

fn main() { use std::{env, fs, path::PathBuf}; if env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default() == "riscv64" { let ld = PathBuf::from(env::var_os("OUT_DIR").unwrap()).join("linker.ld"); fs::write(&ld, LINKER_SCRIPT).unwrap(); println!("cargo:rustc-link-arg=-T{}", ld.display()); } }

构建脚本在编译之前自动执行:

  1. 检测目标架构是否为 riscv64
  2. 如果是,将内嵌的链接脚本写入 OUT_DIR/linker.ld
  3. 通过 cargo:rustc-link-arg 指示链接器使用该脚本

链接脚本定义了两个关键地址:

  • M_BASE_ADDRESS = 0x80000000:M-mode 代码起始地址
  • S_BASE_ADDRESS = 0x80200000:S-mode 代码起始地址(_start 所在位置)

4.4 src/main.rs —— 程序源码

整个程序由五部分组成:

模块文档与属性标记(第 1-19 行):

  • 模块级文档注释(//!)概述本章关键概念
  • #![no_std]:不使用标准库
  • #![no_main]:不使用标准入口
  • cfg_attr:在 RISC-V 64 上启用严格警告,其他架构允许死代码(用于 cargo publish --dry-run 在主机上通过编译)

SBI 引入(第 21-23 行):

  • use tg_sbi::{console_putchar, shutdown} 明确引入所需的两个 SBI 函数

入口函数 _start(第 34-53 行):

  • 仅在 riscv64 架构下编译(#[cfg(target_arch = "riscv64")]
  • 裸函数,放置在 .text.entry 段,链接脚本将其安排在 0x80200000
  • edition 2024 要求使用 #[unsafe(no_mangle)]#[unsafe(link_section = "...")] 语法
  • 分配 4 KiB 栈空间,设置 sp 后跳转到 rust_main

主函数 rust_main(第 59-64 行):

  • 逐字节调用 console_putchar 输出 "Hello, world!\n"
  • 调用 shutdown(false) 正常关机

panic 处理(第 69-72 行):

  • 发生 panic 时调用 shutdown(true) 以异常方式关机

非 RISC-V 占位模块 stub(第 78-95 行):

  • 提供 main__libc_start_main 等符号,使得在非 RISC-V 平台上也能通过编译(用于 cargo publish --dry-run 验证)

五、本章小结

通过本章的学习和实践,你完成了从普通应用程序到裸机程序的蜕变过程:

  1. 理解了执行环境:应用程序依赖多层执行环境(标准库 → 操作系统 → 硬件),Hello, world! 的背后并不简单
  2. 摆脱了标准库:通过 #![no_std]#![no_main],让 Rust 程序不再依赖操作系统
  3. 掌握了裸机启动流程:从 QEMU 加电到 M-mode 初始化,再到 S-mode 的 _start 入口
  4. 认识了 RISC-V 特权级和 SBI:M-mode / S-mode / U-mode 的层次关系,以及 ecall 指令如何跨越特权级

这是操作系统内核开发的第一步——在后续章节中,我们将在这个最小执行环境的基础上,逐步添加批处理、多道程序、内存管理、进程调度等操作系统核心功能。

六、思考题

  1. 为什么 _start 函数必须是裸函数(#[naked])? 如果不是裸函数会发生什么问题?提示:思考函数序言(prologue)需要什么前提条件。

  2. ecall 指令在不同特权级中的效果有何不同? 为什么应用程序和操作系统都使用 ecall,却能产生不同的行为?

  3. 如果把链接脚本中的 S_BASE_ADDRESS0x80200000 改为其他值(如 0x80100000),程序还能正常运行吗? 需要做哪些相应的修改?

参考资料

Dependencies

依赖说明
tg-sbiSBI 调用封装库,支持 nobios 模式,内建 M-mode 启动代码

附录:rCore-Tutorial 组件分析表

表 1:tg-ch1 ~ tg-ch8 操作系统内核总体情况描述表

操作系统内核所涉及核心知识点主要完成功能所依赖的组件
tg-ch1应用程序执行环境
裸机编程(Bare-metal)
SBI(Supervisor Binary Interface)
RISC-V 特权级(M/S-mode)
链接脚本(Linker Script)
内存布局(Memory Layout)
Panic 处理
最小 S-mode 裸机程序
QEMU 直接启动(无 OpenSBI)
打印 "Hello, world!" 并关机
演示最基本的 OS 执行环境
tg-sbi
tg-ch2批处理系统(Batch Processing)
特权级切换(U-mode ↔ S-mode)
Trap 处理(ecall / 异常)
上下文保存与恢复
系统调用(write / exit)
用户态 / 内核态
sret 返回指令
批处理操作系统
顺序加载运行多个用户程序
特权级切换和 Trap 处理框架
实现 write / exit 系统调用
tg-sbi
tg-linker
tg-console
tg-kernel-context
tg-syscall
tg-ch3多道程序(Multiprogramming)
任务控制块(TCB)
协作式调度(yield)
抢占式调度(Preemptive)
时钟中断(Clock Interrupt)
时间片轮转(Time Slice)
任务切换(Task Switch)
任务状态(Ready/Running/Finished)
clock_gettime 系统调用
多道程序与分时多任务
多程序同时驻留内存
协作式 + 抢占式调度
时钟中断与时间管理
tg-sbi
tg-linker
tg-console
tg-kernel-context
tg-syscall
tg-ch4虚拟内存(Virtual Memory)
Sv39 三级页表(Page Table)
地址空间隔离(Address Space)
页表项(PTE)与标志位
地址转换(VA → PA)
异界传送门(MultislotPortal)
ELF 加载与解析
堆管理(sbrk)
恒等映射(Identity Mapping)
内存保护(Memory Protection)
satp CSR
引入 Sv39 虚拟内存
每个用户进程独立地址空间
跨地址空间上下文切换
进程隔离和内存保护
tg-sbi
tg-linker
tg-console
tg-kernel-context
tg-kernel-alloc
tg-kernel-vm
tg-syscall
tg-ch5进程(Process)
进程控制块(PCB)
进程标识符(PID)
fork(地址空间深拷贝)
exec(程序替换)
waitpid(等待子进程)
进程树 / 父子关系
初始进程(initproc)
Shell 交互式命令行
进程生命周期(Ready/Running/Zombie)
步幅调度(Stride Scheduling)
引入进程管理
fork / exec / waitpid 系统调用
动态创建、替换、等待进程
Shell 交互式命令行
tg-sbi
tg-linker
tg-console
tg-kernel-context
tg-kernel-alloc
tg-kernel-vm
tg-syscall
tg-task-manage
tg-ch6文件系统(File System)
easy-fs 五层架构
SuperBlock / Inode / 位图
DiskInode(直接+间接索引)
目录项(DirEntry)
文件描述符表(fd_table)
文件句柄(FileHandle)
VirtIO 块设备驱动
MMIO(Memory-Mapped I/O)
块缓存(Block Cache)
硬链接(Hard Link)
open / close / read / write 系统调用
引入文件系统与 I/O
用户程序存储在磁盘镜像(fs.img)
VirtIO 块设备驱动
easy-fs 文件系统实现
文件打开 / 关闭 / 读写
tg-sbi
tg-linker
tg-console
tg-kernel-context
tg-kernel-alloc
tg-kernel-vm
tg-syscall
tg-task-manage
tg-easy-fs
tg-ch7进程间通信(IPC)
管道(Pipe)
环形缓冲区(Ring Buffer)
统一文件描述符(Fd 枚举)
信号(Signal)
信号集(SignalSet)
信号屏蔽字(Signal Mask)
信号处理函数(Signal Handler)
kill / sigaction / sigprocmask / sigreturn
命令行参数(argc / argv)
I/O 重定向(dup)
进程间通信-管道
异步事件通知(信号)
统一文件描述符抽象
信号发送 / 注册 / 屏蔽 / 返回
tg-sbi
tg-linker
tg-console
tg-kernel-context
tg-kernel-alloc
tg-kernel-vm
tg-syscall
tg-task-manage
tg-easy-fs
tg-signal
tg-signal-impl
tg-ch8同步互斥(Sync&Mutex)
线程(Thread)/ 线程标识符(TID)
进程-线程分离
竞态条件(Race Condition)
临界区(Critical Section)
互斥(Mutual Exclusion)
互斥锁(Mutex:自旋锁 vs 阻塞锁)
信号量(Semaphore:P/V 操作)
条件变量(Condvar)
管程(Monitor:Mesa 语义)
线程阻塞与唤醒(wait queue)
死锁(Deadlock)/ 死锁四条件
银行家算法(Banker's Algorithm)
双层管理器(PThreadManager)
进程-线程分离
同一进程内多线程并发
互斥锁(MutexBlocking)
信号量(Semaphore)
条件变量(Condvar)
线程阻塞与唤醒机制
死锁检测(练习)
tg-sbi
tg-linker
tg-console
tg-kernel-context
tg-kernel-alloc
tg-kernel-vm
tg-syscall
tg-task-manage
tg-easy-fs
tg-signal
tg-signal-impl
tg-sync

表 2:tg-ch1 ~ tg-ch8 操作系统内核所依赖组件总体情况描述表

功能组件所涉及核心知识点主要完成功能所依赖的组件
tg-sbiSBI(Supervisor Binary Interface)
console_putchar / console_getchar
系统关机(shutdown)
RISC-V 特权级(M/S-mode)
ecall 指令
S→M 模式的 SBI 调用封装
字符输出 / 字符读取
系统关机
支持 nobios 直接操作 UART
tg-console控制台 I/O
格式化输出(print! / println!)
日志系统(Log Level)
自旋锁保护的全局控制台
可定制 print! / println! 宏
log::Log 日志实现
Console trait 抽象底层输出
tg-kernel-context上下文(Context)
Trap 帧(Trap Frame)
寄存器保存与恢复
特权级切换
stvec / sepc / scause CSR
LocalContext(本地上下文)
ForeignContext(跨地址空间上下文)
异界传送门(MultislotPortal)
用户/内核态切换上下文管理
LocalContext 结构
ForeignContext(含 satp)
MultislotPortal 跨地址空间执行
tg-kernel-alloc内核堆分配器
伙伴系统(Buddy Allocation)
动态内存管理
#[global_allocator]
基于伙伴算法的 GlobalAlloc
堆初始化(init)
物理内存转移(transfer)
tg-kernel-vm虚拟内存管理
页表(Page Table)
Sv39 分页(三级页表)
虚拟地址(VAddr)/ 物理地址(PAddr)
虚拟页号(VPN)/ 物理页号(PPN)
页表项(PTE)/ 页表标志位(VmFlags)
地址空间(AddressSpace)
PageManager trait
地址翻译(translate)
Sv39 页表管理
AddressSpace 地址空间抽象
虚实地址转换
页面映射(map / map_extern)
页表项操作
tg-syscall系统调用(System Call)
系统调用号(SyscallId)
系统调用分发(handle)
系统调用结果(Done / Unsupported)
Caller 抽象
IO / Process / Scheduling / Clock /
Signal / Thread / SyncMutex trait 接口
系统调用 ID 与参数定义
trait 接口供内核实现
init_io / init_process / init_scheduling /
init_clock / init_signal /
init_thread / init_sync_mutex
支持 kernel / user feature
tg-signal-defs
tg-task-manage任务管理(Task Management)
调度(Scheduling)
进程管理器(PManager, proc feature)
双层管理器(PThreadManager, thread feature)
ProcId / ThreadId
就绪队列(Ready Queue)
Manage trait / Schedule trait
进程等待(wait / waitpid)
线程等待(waittid)
阻塞与唤醒(blocked / re_enque)
Manage 和 Schedule trait 抽象
proc feature:单层进程管理器(PManager)
thread feature:双层管理器(PThreadManager)
进程树 / 父子关系
线程阻塞 / 唤醒
tg-easy-fs文件系统(File System)
SuperBlock / Inode / 位图(Bitmap)
DiskInode(直接+间接索引)
块缓存(Block Cache)
BlockDevice trait
文件句柄(FileHandle)
打开标志(OpenFlags)
管道(Pipe)/ 环形缓冲区
用户缓冲区(UserBuffer)
FSManager trait
easy-fs 五层架构实现
文件创建 / 读写 / 目录操作
块缓存管理
管道环形缓冲区实现
FSManager trait 抽象
tg-signal-defs信号编号(SignalNo)
SIGKILL / SIGINT / SIGUSR1 等
信号动作(SignalAction)
信号集(SignalSet)
最大信号数(MAX_SIG)
信号编号枚举定义
信号动作结构定义
信号集类型定义
为 tg-signal 和 tg-syscall 提供共用类型
tg-signal信号处理(Signal Handling)
Signal trait 接口
add_signal / handle_signals
get_action_ref / set_action
update_mask / sig_return / from_fork
SignalResult(Handled / ProcessKilled)
Signal trait 接口定义
信号添加 / 处理 / 动作设置
屏蔽字更新 / 信号返回
fork 继承
tg-kernel-context
tg-signal-defs
tg-signal-implSignalImpl 结构
已接收信号位图(received)
信号屏蔽字(mask)
信号处理中状态(handling)
信号动作表(actions)
信号处理函数调用
上下文保存与恢复
Signal trait 的参考实现
信号接收位图管理
屏蔽字逻辑
处理状态和动作表
tg-kernel-context
tg-signal
tg-sync互斥锁(Mutex trait: lock / unlock)
阻塞互斥锁(MutexBlocking)
信号量(Semaphore: up / down)
条件变量(Condvar: signal / wait_with_mutex)
等待队列(VecDeque<ThreadId>)
UPIntrFreeCell
MutexBlocking 阻塞互斥锁
Semaphore 信号量
Condvar 条件变量
通过 ThreadId 与调度器交互
tg-task-manage
tg-user用户态程序(User-space App)
用户库(User Library)
系统调用封装(syscall wrapper)
用户堆分配器
用户态 print! / println!
用户测试程序运行时库
系统调用封装
用户堆分配器
各章节测试用例(ch2~ch8)
tg-console
tg-syscall
tg-checker测试验证
输出模式匹配
正则表达式(Regex)
测试用例判定
rCore-Tutorial CLI 测试输出检查工具
验证内核输出匹配预期模式
支持 --ch N 和 --exercise 模式
tg-linker链接脚本(Linker Script)
内核内存布局(KernelLayout)
.text / .rodata / .data / .bss / .boot 段
入口点(boot0! 宏)
BSS 段清零
形成内核空间布局的链接脚本模板
用于 build.rs 工具构建 linker.ld
内核布局定位(KernelLayout::locate)
入口宏(boot0!)
段信息迭代

License

Licensed under GNU GENERAL PUBLIC LICENSE, Version 3.0.