本章在第五章"进程管理"的基础上,引入了 文件系统 支持。用户程序不再嵌入内核镜像,而是存放在 磁盘镜像(fs.img)中,内核通过 VirtIO 块设备驱动 和 easy-fs 文件系统 按名称加载和执行程序。同时,进程拥有了文件描述符表,可以通过 open/close/read/write 等标准接口操作文件。
通过本章的学习和实践,你将理解:
前置知识:建议先完成第一章至第五章的学习,理解裸机启动、Trap 处理、系统调用、多任务调度、虚拟内存和进程管理。
ch6/ ├── .cargo/ │ └── config.toml # Cargo 配置:交叉编译目标和 QEMU runner(含块设备参数) ├── .gitignore # Git 忽略规则 ├── build.rs # 构建脚本:编译用户程序,打包 easy-fs 磁盘镜像 ├── Cargo.toml # 项目配置与依赖 ├── LICENSE # GPL v3 许可证 ├── README.md # 本文档 ├── rust-toolchain.toml # Rust 工具链配置 ├── test.sh # 自动测试脚本 └── src/ ├── main.rs # 内核主体:初始化、调度循环、系统调用实现 ├── fs.rs # 文件系统管理:easy-fs 封装 ├── process.rs # 进程结构:含文件描述符表 ├── processor.rs # 处理器管理:进程管理器 └── virtio_block.rs # VirtIO 块设备驱动
本章建议按“块设备 -> 文件系统 -> fd_table -> 文件 syscall”逐层阅读。
| 阅读顺序 | 文件 | 重点问题 |
|---|---|---|
| 1 | src/virtio_block.rs | VirtIO 驱动如何把块设备能力暴露给 easy-fs? |
| 2 | src/fs.rs | FS、open、read_all 如何构成“按文件加载程序”的路径? |
| 3 | src/process.rs | 进程的 fd_table 如何初始化并在 fork 时继承? |
| 4 | src/main.rs 的 impls::IO | open/read/write/close 如何经由 fd 映射到具体文件对象? |
| 5 | src/main.rs 的 kernel_space | MMIO 映射为何是块设备可用的前提? |
配套建议:结合 tg-easy-fs 的 layout/efs/vfs 注释阅读,能更完整理解“磁盘块 -> 文件语义”的抽象过程。
open/read/write/close 经 fd_table 到具体文件对象的路径./test.sh base(练习时补充 ./test.sh exercise)| 核心概念 | 源码入口 | 自测方式(命令/现象) |
|---|---|---|
| 块设备驱动接入 | ch6/src/virtio_block.rs | 启动日志出现 VirtIO MMIO 映射,文件系统可读写 |
| 文件系统封装 | ch6/src/fs.rs 的 FS/open/read_all | initproc 能从文件系统成功加载 |
| 进程 fd 表 | ch6/src/process.rs 的 fd_table | fork 后子进程可继承并使用已打开 fd |
| 文件 syscall 实现 | ch6/src/main.rs 的 impls::IO | open/read/write/close 行为与期望一致 |
遇到构建/运行异常可先查看根文档的“高频错误速查表”。
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
验证:
rustc --version # 要求 >= 1.85.0(支持 edition 2024)
cargo --version
rustup target add riscv64gc-unknown-none-elf
Ubuntu / Debian:
sudo apt update sudo apt install qemu-system-misc
macOS(Homebrew):
brew install qemu
验证:
qemu-system-riscv64 --version # 建议 >= 7.0
cargo install cargo-clone cargo install cargo-binutils rustup component add llvm-tools
方式一:只获取本实验
cargo clone tg-ch6
cd tg-ch6
方式二:获取所有实验
git clone https://github.com/rcore-os/tg-rcore-tutorial.git
cd tg-rcore-tutorial/ch6
cargo build
编译过程与前几章类似,但 build.rs 有重要变化:
tg-user 用户程序fs.img 中环境变量说明:
TG_USER_DIR:指定本地 tg-user 源码路径TG_USER_VERSION:指定 tg-user 版本(默认0.2.0-preview.1)TG_SKIP_USER_APPS:跳过用户程序编译LOG:设置日志级别
基础模式:
cargo run
练习模式:
cargo run --features exercise
实际执行的 QEMU 命令等价于:
qemu-system-riscv64 \
-machine virt \
-nographic \
-bios none \
-drive file=target/riscv64gc-unknown-none-elf/debug/fs.img,if=none,format=raw,id=x0 \
-device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 \
-kernel target/riscv64gc-unknown-none-elf/debug/tg-ch6
注意与第五章不同:QEMU 命令中多了 -drive 和 -device 参数,用于挂载 fs.img 磁盘镜像作为 VirtIO 块设备。
[tg-ch6 ...] Hello, world! [ INFO] .text ---> 0x80200000..0x8023xxxx [ INFO] .rodata ---> 0x8023xxxx..0x8024xxxx [ INFO] .data ---> 0x8024xxxx..0x81exxxxx [ INFO] .boot ---> 0x81exxxxx..0x81exxxxx [ INFO] (heap) ---> 0x81exxxxx..0x83200000 [ INFO] MMIO range -> 0x10001000..0x10002000 Rust user shell >> ch5b_forktest_simple ... Shell: Process 2 exited with code 0 >>
与第五章不同,你会看到:
./test.sh # 运行全部测试(基础 + 练习)
./test.sh base # 仅运行基础测试
./test.sh exercise # 仅运行练习测试
在前几章中,用户程序直接嵌入内核镜像(通过 APP_ASM 或 APPS 表)。这存在明显的局限性:
| 问题 | 说明 |
|---|---|
| 耦合性 | 程序与内核绑定,修改用户程序需要重新编译内核 |
| 灵活性 | 无法在运行时动态创建、修改、删除文件 |
| 持久性 | 数据仅存在于内存中,关机后丢失 |
| 标准化 | 没有统一的文件操作接口(open/read/write/close) |
文件系统 通过在磁盘上组织数据,解决了这些问题:
easy-fs 是一个简化的类 UNIX inode 文件系统,采用五层架构:
┌─────────────────────────────────┐ │ 第 5 层:Inode(虚拟文件系统) │ 文件/目录操作接口 │ find / create / read / write │ ├─────────────────────────────────┤ │ 第 4 层:磁盘管理器 │ 文件系统全局管理 │ EasyFileSystem │ inode/数据块分配 ├─────────────────────────────────┤ │ 第 3 层:磁盘数据结构 │ SuperBlock / DiskInode │ Bitmap / DirEntry │ DiskInode 索引结构 ├─────────────────────────────────┤ │ 第 2 层:块缓存 │ BlockCache + CacheManager │ 缓存磁盘块到内存 │ 自动回写脏块 ├─────────────────────────────────┤ │ 第 1 层:块设备接口 │ BlockDevice trait │ read_block / write_block │ 由 VirtIO 驱动实现 └─────────────────────────────────┘
easy-fs 将磁盘划分为五个区域:
+------------+--------------+------------+-------------+-----------+ | SuperBlock | Inode Bitmap | Inode Area | Data Bitmap | Data Area | +------------+--------------+------------+-------------+-----------+ 1 块 若干块 若干块 若干块 若干块
| 区域 | 作用 |
|---|---|
| SuperBlock | 文件系统元信息(魔数、总块数、各区域大小) |
| Inode Bitmap | inode 分配位图,每 bit 对应一个 inode |
| Inode Area | 存储 DiskInode(文件元数据:大小、类型、数据块索引) |
| Data Bitmap | 数据块分配位图 |
| Data Area | 存储文件实际数据和目录项 |
DiskInode 索引结构(支持大文件):
DiskInode(128 字节) ├── 28 个直接索引块(每块 512 字节 → 14 KiB) ├── 1 个一级间接索引(128 个块 → 64 KiB) └── 1 个二级间接索引(128 × 128 个块 → 8 MiB) 最大文件大小 ≈ 8 MiB + 64 KiB + 14 KiB
目录项(DirEntry,32 字节):
┌──────────────────┬──────────┐ │ 文件名(28 字节) │ inode 号 │ │ 含 '\0' 终止符 │ 4 字节 │ └──────────────────┴──────────┘ 每个 512 字节磁盘块可存储 16 个目录项
为了减少磁盘 I/O,easy-fs 使用块缓存(BlockCache):
程序读写请求 │ ▼ 查找块缓存 ──命中──→ 直接读写内存缓冲区 │ 未命中 │ ▼ 从磁盘读取块到缓存 ──→ 读写内存缓冲区 │ 缓存满时 │ ▼ 淘汰最早的缓存块(若脏则回写磁盘)
块缓存的关键设计:
VirtIO 是 QEMU 使用的虚拟化 I/O 标准。在 tg-ch6 中:
QEMU 宿主机 ┌──────────────────────────────────┐ │ fs.img ──→ VirtIO 后端 │ │ ▲ │ │ │ MMIO(0x10001000) │ │ ▼ │ │ VirtIO 前端(Guest 内核驱动) │ └──────────────────────────────────┘
内核需要:
0x10001000Hal trait(DMA 内存分配、地址转换)BlockDevice trait,将 read_block/write_block 转发给 VirtIO 驱动本章为进程引入了文件描述符表(fd_table):
pub struct Process {
pub fd_table: Vec<Option<Mutex<FileHandle>>>,
// ... 其他字段
}
预留的标准文件描述符:
| fd | 名称 | 说明 |
|---|---|---|
| 0 | stdin | 标准输入(SBI console_getchar) |
| 1 | stdout | 标准输出(SBI console_putchar) |
| 2 | stderr | 标准错误(同 stdout) |
| 3+ | 普通文件 | 通过 open 系统调用分配 |
文件操作流程:
用户程序 内核 │ │ │── open("test.txt") ───→ │ 1. 地址翻译读取文件名 │ │ 2. easy-fs 查找/创建文件 │ │ 3. 分配 fd,插入 fd_table │←── 返回 fd = 3 ────────│ │ │ │── write(3, buf, len) ──→│ 1. 查找 fd_table[3] │ │ 2. 地址翻译获取用户缓冲区 │ │ 3. 通过 FileHandle 写入文件 │←── 返回写入字节数 ──────│ │ │ │── close(3) ────────────→│ 1. fd_table[3] = None │←── 返回 0 ─────────────│
| syscall ID | 名称 | 功能 |
|---|---|---|
| 56 | open | 打开文件(新增) |
| 57 | close | 关闭文件描述符(新增) |
| 63 | read | 读取文件或标准输入(扩展:支持文件 fd) |
| 64 | write | 写入文件或标准输出(扩展:支持文件 fd) |
| 93 | exit | 退出进程 |
| 124 | sched_yield | 让出 CPU |
| 113 | clock_gettime | 获取时间 |
| 172 | getpid | 获取 PID |
| 214 | sbrk | 调整堆 |
| 220 | fork | 创建子进程(扩展:复制 fd_table) |
| 221 | exec | 替换程序(变化:从文件系统加载) |
| 260 | wait | 等待子进程 |
| 37 | linkat | 创建硬链接(练习题) |
| 35 | unlinkat | 删除链接(练习题) |
| 80 | fstat | 获取文件状态(练习题) |
对比第五章和第六章的程序加载方式:
第五章:程序嵌入内核 ──────────────────── build.rs 编译用户程序 → 生成 APP_ASM → 嵌入内核镜像 exec 时:APPS.get(name) → 内存中的 ELF 数据 第六章:程序存储在文件系统 ──────────────────────── build.rs 编译用户程序 → 打包到 fs.img → QEMU 挂载为块设备 exec 时:FS.open(name) → read_all() → 从磁盘读取 ELF 数据
启动流程(与第五章类似,新增 MMIO 映射):
0x10001000IO 系统调用的变化:
write/read:先检查是否为标准 I/O fd,否则通过 fd_table 查找文件句柄读写open:从用户空间读取文件路径字符串 → easy-fs 打开文件 → 分配 fdclose:将 fd_table 对应项设为 Noneexec:从 FS.open(name) + read_all() 加载 ELF,而非 APPS.get(name)FS:全局文件系统实例,通过 BLOCK_DEVICE(VirtIO)打开 easy-fs
FileSystem 实现 FSManager trait:
open():支持 CREATE(创建)、TRUNC(清空)等标志find():在根目录中查找文件readdir():列出所有文件名read_all():辅助函数,读取整个文件内容与第五章相比新增 fd_table 字段:
from_elf():初始化时预留 fd 0/1/2fork():深拷贝父进程的 fd_table(子进程继承已打开文件)exec():保留 fd_table 不变BLOCK_DEVICE:全局块设备实例VirtIOBlock:封装 virtio-drivers 库的 VirtIOBlkVirtioHal:DMA 内存分配和地址转换(恒等映射下很简单)| 依赖 | 说明 |
|---|---|
virtio-drivers | VirtIO 设备驱动库(本章新增) |
tg-easy-fs | easy-fs 文件系统实现(本章新增) |
xmas-elf | ELF 文件解析 |
riscv | RISC-V CSR 寄存器访问 |
spin | 自旋锁(Lazy、Mutex) |
tg-sbi | SBI 调用封装 |
tg-linker | 链接脚本和内核布局 |
tg-console | 控制台输出和日志 |
tg-kernel-context | 用户上下文及异界传送门 |
tg-kernel-alloc | 内核堆分配器 |
tg-kernel-vm | 虚拟内存管理 |
tg-syscall | 系统调用定义与分发 |
tg-task-manage | 进程管理框架 |
硬链接要求两个不同的目录项指向同一个文件,在我们的文件系统中也就是两个不同名称目录项指向同一个磁盘块。
本节要求实现三个系统调用 linkat、unlinkat、fstat。
fn linkat(&self, _caller: Caller, _olddirfd: i32, oldpath: usize,
_newdirfd: i32, newpath: usize, _flags: u32) -> isize
fn unlinkat(&self, _caller: Caller, _dirfd: i32, path: usize, _flags: u32) -> isize
fn fstat(&self, _caller: Caller, fd: usize, st: usize) -> isize
#[repr(C)]
pub struct Stat {
pub dev: u64, // 磁盘驱动器号(写死为 0)
pub ino: u64, // inode 编号
pub mode: StatMode, // 文件类型(FILE 或 DIR)
pub nlink: u32, // 硬链接数量(初始为 1)
pad: [u64; 7],
}
bitflags! {
pub struct StatMode: u32 {
const NULL = 0;
const DIR = 0o040000; // 目录
const FILE = 0o100000; // 普通文件
}
}
linkat 和 unlinkat 的文件路径读取可参考 src/main.rs 中 open 系统调用的实现fstat 的 Stat 结构体写入可参考 clock_gettime 对 TimeSpec 的写入方式(地址翻译后写入)tg-easy-fs 到本地并修改以支持硬链接:
cd tg-ch6
cargo clone tg-easy-fs
然后修改 Cargo.toml:
[dependencies]
tg-easy-fs = { path = "./tg-easy-fs" }
目录结构:
tg-ch6/ ├── Cargo.toml(需要修改依赖配置) ├── src/(需要修改) │ ├── main.rs │ ├── fs.rs │ ├── process.rs │ ├── processor.rs │ └── virtio_block.rs ├── tg-easy-fs/(需拉取到本地并修改以支持硬链接) │ └── src/ └── tg-user/(自动拉取,无需修改)
运行和测试:
cargo run --features exercise # 运行练习测例
./test.sh exercise # 测试练习测例
然后在终端中输入 ch6_usertest 运行所有练习测例。
前向兼容:你的内核必须前向兼容,需要能通过前一章的所有测例。
通过本章的学习和实践,你完成了操作系统中的重要基础设施——文件系统:
在后续章节中,我们将在文件系统的基础上引入进程间通信(管道等)机制。
为什么需要块缓存? 如果每次读写都直接访问磁盘,性能会怎样?块缓存的淘汰策略(FIFO vs LRU)对性能有什么影响?
DiskInode 的索引设计? 为什么 easy-fs 的 DiskInode 使用直接 + 一级间接 + 二级间接的三级索引结构?如果只有直接索引,最大文件大小是多少?
文件描述符表的继承? fork 时子进程复制了父进程的 fd_table。如果父进程打开了一个文件然后 fork,父子进程写入同一个文件会发生什么?
硬链接 vs 软链接? 硬链接和软链接有什么区别?为什么硬链接不能跨文件系统?删除一个硬链接后,文件何时真正被删除?
exec 后的 fd_table? 本实现中 exec 不清除 fd_table。这意味着什么?UNIX 系统中 exec 如何处理文件描述符(提示:close-on-exec 标志)?
| 操作系统内核 | 所涉及核心知识点 | 主要完成功能 | 所依赖的组件 |
|---|---|---|---|
| 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 |
| 功能组件 | 所涉及核心知识点 | 主要完成功能 | 所依赖的组件 |
|---|---|---|---|
| tg-sbi | SBI(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-impl | SignalImpl 结构 已接收信号位图(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!) 段信息迭代 | 无 |
Licensed under GNU GENERAL PUBLIC LICENSE, Version 3.0.