Skip to content

fs:扩展大文件索引,并增加符号链接 ​

版本:fs,提交 a95dec46d4443c579f47e793e7d731cce2d34388。先读文件系统。官网文字有一处仍写 util,但实际命令和实验分支是 fs,以命令、代码与本页固定提交为准。

双级索引的地址计算 ​

磁盘 inode 大小不变,因此改成 11 个直接地址、1 个单级间接地址、1 个双级间接地址。kernel/fs.h 的 dinode 与 kernel/file.h 的内存 inode 必须拥有相同数量的 addrs 项。相应修改 NDIRECT/MAXFILE 后,重建 fs.img,让 mkfs 与内核对磁盘布局形成一致理解。

新布局中逻辑块号 0–10 是直接块,11–266 是单级间接块,267 起进入双级区域。设请求 bn=1000,则 q=1000−11−256=733,外层索引为 2,内层索引为 221。这个例子应先在纸上算对,再写除法和取模。

flowchart TD
  I[inode addrs] --> D[11 个直接数据块]
  I --> S[单级索引块]
  S --> SD[256 个数据块]
  I --> T[双级索引块]
  T --> T0[第 0 个下级索引]
  T --> T1[第 1 个下级索引]
  T --> TN[其余下级索引]
  T0 --> DATA[每个下级索引最多 256 个数据块]
查看流程图文本
flowchart TD
  I[inode addrs] --> D[11 个直接数据块]
  I --> S[单级索引块]
  S --> SD[256 个数据块]
  I --> T[双级索引块]
  T --> T0[第 0 个下级索引]
  T --> T1[第 1 个下级索引]
  T --> TN[其余下级索引]
  T0 --> DATA[每个下级索引最多 256 个数据块]

bmap 与 itrunc 必须成对修改 ​

bmap() 先判断逻辑范围,再按需分配不存在的索引块与数据块。每次修改索引 buffer 都使用与原代码一致的 log_write;每次 bread 都有 brelse。不要一开始为每个文件预分配全部 256 个下级索引,稀疏使用的大文件会浪费大量空间。

itrunc() 是逆操作。读取双级块,逐个扫描非零下级索引,再释放其中非零数据块;释放下级索引块;最后释放双级块。之后清空 inode 地址与长度,并按原事务协议更新。释放顺序让所有还要读取的索引在访问结束前仍存活。

边界测试比随机大文件更能定位错误:写入第 10/11、266/267、最后一个合法逻辑块;再删除并重建。若 bigfile 成功但第二次运行失败,常见原因是 itrunc 漏回收;若进入双级区第一块就坏,检查减偏移顺序;若重启后所有目录异常,检查磁盘 inode 布局或旧镜像。

符号链接保存的是路径 ​

新建 T_SYMLINK 类型、O_NOFOLLOW 标志与 symlink 系统调用。symlink(target,path) 创建链接 inode,并将目标字符串保存为它的内容;目标此时不必存在。需要对字符串长度、写入失败与新 inode 清理定义清楚的路径。

在 sys_open() 中遇到符号链接且没有 O_NOFOLLOW 时,读取目标路径,再查找目标;如果仍是链接则继续,设置有限跟随深度以拒绝循环。每轮都要正确释放旧 inode 锁和引用,再取得新的对象,避免同时持有不必要的多把 inode 锁。

link/unlink 不跟随链接,操作链接自身。不要把“所有 namei 自动跟随”作为快捷方式,否则会改变实验明确要求的其他接口语义。符号链接到目录、完整 POSIX 相对路径规则等没有被本实验全部覆盖,应把实现范围写清楚。

事务与失败路径审查 ​

文件系统操作不只是主路径的算法。对新建 inode、分配数据块、读取链接内容、循环上限等每个失败点,列出当前持有的 inode、buffer、file 引用和事务状态。退出前需要恢复一致性并结束 begin_op/end_op 配对,而不能以一次 panic 代替正常错误返回。

特别检查“成功创建 inode 后写目标路径失败”:若只返回 -1,目录可能留下部分构造对象。应采用与原 create/open 路径一致的清理方式,并说明失败后磁盘上允许存在什么状态。实验测试未覆盖的路径也要保持不变量。

验收 ​

运行 bigfile,目标是写入 65803 数据块;随后运行 symlinktest、usertests -q、make grade。大文件测试较慢,应等待真实完成,不要因为几十秒没有新输出就判为死锁。保留镜像观察可使用 make qemu-fs;改变布局后则应重新生成镜像。

自测:为什么双级入口增加了 65536 个块,却总容量不是原容量加 65536?

为保持 inode 大小不变,牺牲了一个直接地址。因此容量从 12+256 变成 11+256+65536,净增 65535 个数据块。

来源:2025 fs 官方任务,代码入口 fs.c:bmap/itrunc、sysfile.c:sys_open、fs.h/file.h/stat.h/fcntl.h。