把磁盘块设计成文件、目录、fd 和崩溃后仍能恢复的一致状态。
| 用户要什么 | 磁盘提供什么 | 被迫设计 |
|---|---|---|
| 按路径找到文件 | 固定大小 block | 目录项:name → inode |
| 知道文件大小和数据位置 | 块编号 | inode 记录元数据和数据块 |
| 连续读写 | 离散块读写 | fd 保存偏移和打开状态 |
| 删除不破坏正在使用的文件 | 块可被重新分配 | 引用计数和打开文件表 |
| 断电后不乱 | 写入可能停在任意一步 | 写入顺序和 journal |
| 对象 | 字段 | 负责什么 |
|---|---|---|
SuperBlock | block_size、inode_start、data_start、free_map | 描述整个文件系统布局 |
Inode | id、type、size、blocks、link_count、mode | 文件身份、大小、权限和数据块位置 |
DirectoryEntry | name、inode_id | 路径名到文件身份的映射 |
OpenFile | inode、offset、flags、ref_count | 一次打开文件的读写状态 |
FileDescriptor | fd、open_file | 进程用小编号引用内核文件对象 |
JournalRecord | tx_id、old_meta、new_meta、committed | 记录元数据变更是否完成 |
| 调用 | 内核路径 | 关键检查 |
|---|---|---|
open("/a.txt") | 解析目录 → 找 inode → 创建 OpenFile → 分配 fd | 路径存在、权限允许、fd 未超限 |
read(fd, buf, len) | fd → OpenFile → inode → block → copy_to_user | fd 可读、用户缓冲区可写 |
write(fd, buf, len) | copy_from_user → 分配块 → 更新 inode → 写数据 | fd 可写、空间足够、写入顺序正确 |
close(fd) | 移除进程 fd → OpenFile ref-- → 必要时释放 | 引用计数不能提前归零 |
| 步骤 | 设计动作 | 为什么 |
|---|---|---|
| 删除目录项 | 移除 name → inode 映射 | 新 open 找不到这个名字 |
| 减少 link_count | inode 不一定马上释放 | 可能还有别的硬链接 |
| 检查 open 引用 | open_file ref_count 归零后才释放 | 已打开 fd 仍有一致语义 |
| 释放块 | 最后回收 inode 和 data blocks | 避免悬空引用 |
| 断电位置 | 可能后果 | 第一版保护 |
|---|---|---|
| 分配了数据块,inode 未更新 | 空间泄漏 | journal 记录分配意图,恢复时回滚或补完 |
| inode 更新了,目录项没写 | 孤儿文件 | 元数据事务提交后再可见 |
| 目录项写了,inode 不完整 | 路径指向坏 inode | 目录项最后提交 |
| 恢复过程中再次断电 | 二次破坏 | 恢复流程必须幂等 |
| 实验 | 通过标准 | 证明了什么 |
|---|---|---|
| 路径实验 | 创建、打开、读取同一路径得到同一文件 | 目录项和 inode 映射成立 |
| fd 偏移实验 | 连续 read 自动推进 offset | OpenFile 状态成立 |
| 删除实验 | 删除后新 open 失败,旧 fd 仍可按规则读完 | 引用计数成立 |
| 权限实验 | 无权限写入返回错误 | 权限检查接入系统调用 |
| 断电实验 | create/write 中途断电后,重启无坏目录项 | 元数据恢复成立 |