← 返回 操作系统

操作系统

第七章

第七章的主题是管道、重定向和更完整的 Unix 风格 I/O。ch6 已经让进程可以通过文件系统保存数据,ch7 则进一步解决“进程之间如何边运行边传数据”的问题。管道 pipe 可以理解成内核中的一段环形缓冲区,一端写入、一端读出,让数据在进程之间流动。

这一章最关键的抽象变化是 fd 不再只代表普通文件。ch7 把 fd_table 的元素抽象成 Fd 枚举,可以同时表示普通文件、管道读端、管道写端、标准输入输出,以及本实验扩展中的图形输出 fd。这样同一个 read/write 接口就能根据 fd 背后的真实对象分发到不同实现。

pipe 的直觉是 writer -> kernel ring buffer -> reader。用户程序调用 pipe 后得到两个 fd:pipe_fd[0] 是读端,pipe_fd[1] 是写端。fork 后子进程继承父进程的 fd_table,因此父子进程可以关闭自己不用的一端,形成单向通信。这也是管道经常和 fork 配合使用的原因。

ring buffer 本质上就是循环队列,维护 head 和 tail 两个位置。写入时 tail 前进,读取时 head 前进,走到数组末尾后通过取模回到开头。缓冲区空或满时,教学系统里可能返回 -2,用户库再 sched_yield 后重试,避免进程空转占满 CPU。

重定向的本质不是修改程序代码,而是修改 fd_table。比如 shell 处理 cat file > out 时,会在子进程 exec 前把 fd=1 从控制台改成目标文件。目标程序仍然正常 write(1, ...),但 stdout 背后的对象已经变成 out 文件。exec 替换程序代码和地址空间,但保留 fd_table,这正是重定向能生效的原因。

ch7 还涉及命令行参数传递。exec 时,内核不仅要加载新的 ELF,还要读取用户传入的 path 和 args,把参数字符串放到用户栈上,设置 argc/argv,然后让新程序从入口开始执行。这对应 C 语言里的 int main(int argc, char **argv)。

本仓库的组件化实现和 rCore Guide 文件名不完全一致:Fd 枚举和统一 read/write 逻辑集中在 ch7/src/fs.rs,系统调用实现主要在 ch7/src/main.rs,Process 和 fd_table 在 ch7/src/process.rs,PipeReader/PipeWriter/UserBuffer 来自 easy-fs 相关实现。读代码时更适合按功能链路找,而不是死找 Guide 目录名。

这次 ch7 扩展实现了用户态 Pacman:用户程序维护地图、豆子、分数、生命值和幽灵追踪,通过 read(stdin) 读取键盘,通过 write(fd=3) 提交 PacmanFrame;内核侧 graphics.rs 负责 VirtIO-GPU 绘制地图、豆子、Pacman 和幽灵,keyboard.rs 负责把 VirtIO-keyboard 事件转换成 WASD。

Pacman 本身不一定依赖 pipe,但它很好地展示了 ch7 的统一 fd 思想:stdin fd=0 可以接键盘输入,graphics fd=3 可以接图形输出,普通文件和管道也都通过 read/write 进入同一套 I/O 抽象。默认 cargo run 启动 Pacman 图形窗口,测试模式 CHAPTER=-7 则运行 ch7b_usertest 并使用 headless QEMU。

一句话总结 ch7:它让 fd 从“文件编号”升级为进程访问各种 I/O 对象的统一入口;管道负责进程间流式通信,重定向负责改变 fd 背后的对象,Pacman 扩展则说明设备也可以被包装进同一套模型。

GitHub 仓库:flyyansii/Tg-rCore-Tutorial-2026S

ch7 提交:adee827 finish ch7 pacman demo and notes

ch7 文档目录:doc/ch7

源码入口:tg-rcore-tutorial-ch7

用户态 Pacman:ch7_pacman.rs