← 返回 操作系统

操作系统

第六章

第六章的主题是文件系统。ch5 以后,进程已经能 fork、exec、wait、exit,但它们仍然缺少长期保存数据的能力;ch6 把 open/read/write/close/linkat/unlinkat/fstat 这些文件接口接入内核,让用户程序可以通过统一的文件抽象访问控制台、普通磁盘文件、目录项、块设备上的 easy-fs 对象,以及扩展出来的特殊图形 fd。

文件描述符 fd 可以理解成当前进程打开文件表里的下标,而不是全局文件编号。用户程序拿到的是整数 fd,内核再从当前 Process 的 fd_table 找到 Arc<Mutex<OSInode>>,进一步连到 Inode、easy-fs、块设备和 fs.img。不同进程里的 fd=3 可以指向完全不同的对象,这也是进程隔离和文件抽象结合起来的地方。

stdin/stdout 也能看成文件,是 Unix“一切皆文件”思想的教学版本。write(1, buf) 可以输出到控制台,write(fd_file, buf) 可以写入磁盘文件,而本章扩展中 write(3, frame) 可以把图形帧交给 VirtIO-GPU 渲染。接口统一,背后的对象和设备不同。

UserBuffer 仍然是 ch6 的重点。用户程序传给内核的 buf/path/stat 指针都是用户虚拟地址,内核不能直接当作内核地址使用,必须通过当前进程 AddressSpace 做页表翻译和权限检查。read/write/open/fstat 等系统调用都要先找到当前进程,再根据 fd 或 path 找对象,最后把用户地址安全地转成内核可访问地址。

easy-fs 是教学用文件系统,核心结构包括 super block、inode bitmap、data bitmap、inode area 和 data area。路径从文件名开始,经目录项 DirEntry 找到 inode 编号,再进入 DiskInode,通过 direct/indirect 数据块定位真实内容,最后由 block cache 和 VirtIO block driver 同步到 QEMU 的 fs.img。

linkat 和 unlinkat 的难点在于它们操作的是目录项和 inode 链接关系,而不是简单复制或删除文件内容。linkat 增加一个新的名字指向同一个 inode,unlinkat 删除一个目录项并减少 nlink;只有当引用关系满足条件时,文件内容才可能真正释放。fstat 则把文件类型、inode 编号、链接数等元信息写回用户传入的 Stat 结构。

本章练习实现点包括普通文件的 open/read/write/close,linkat/unlinkat,fstat,继续保留 spawn 与 mmap/munmap,以及让文件系统链路和进程、地址空间、安全用户缓冲区协作起来。它把 ch4 的地址翻译、ch5 的进程表和 ch6 的 fd_table/easy-fs 串成了一条完整系统调用链。

ch6 扩展做了 breakout 打砖块游戏。用户态 ch6_breakout 维护挡板、小球、砖块、分数、生命、关卡和保存状态;每帧读取键盘、更新物理碰撞、构造 BreakoutFrame,再通过 write(fd=3) 提交给内核图形输出。按 S 保存时写入 breakout.sav,按 R 恢复时读取并校验 SaveData,这让“文件系统能保存世界状态”变得很直观。

breakout 也要求内核同时支持 VirtIO block、VirtIO-GPU 和 VirtIO-keyboard。原本 ch6 只需要块设备,扩展后还要映射 GPU 和键盘的 MMIO 区间,否则访问设备地址会出错。图形仍然使用特殊 fd=3,键盘仍然走 read(0, buf),没有键时返回 -2,让游戏循环不会卡死。

一句话总结 ch6:它让进程不再只是运行在内存里的临时状态,而是能通过 fd_table、OSInode、easy-fs、block cache、virtio-blk 和 fs.img 把数据长期保存下来;breakout 的保存/恢复则把这个抽象落到了一个可以玩、可以存档、可以恢复的实验里。

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

ch6 提交:4d07ce1 finish ch6 fs exercise and breakout notes

ch6 文档目录:doc/ch6

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

用户态 breakout:ch6_breakout.rs