知识全景图/ 软件工程与系统/ 如何设计系统/ 如何设计操作系统/ 文件系统

如何设计文件系统

把磁盘块设计成文件、目录、fd 和崩溃后仍能恢复的一致状态。

定位:这页只讲 ToyOS 第一版文件系统。重点不是“文件系统有哪些类型”,而是磁盘只认识块,用户却需要路径、文件名、权限、读写位置和崩溃恢复。
一、坏场景:磁盘只认识块,用户要的是文件
失败:如果每个程序都自己管理磁盘块,文件名、空间分配、权限、删除、崩溃恢复都会各写各的。很快就会出现两个程序写同一块、删除后仍被引用、断电后元数据互相矛盾。
用户要什么磁盘提供什么被迫设计
按路径找到文件固定大小 block目录项:name → inode
知道文件大小和数据位置块编号inode 记录元数据和数据块
连续读写离散块读写fd 保存偏移和打开状态
删除不破坏正在使用的文件块可被重新分配引用计数和打开文件表
断电后不乱写入可能停在任意一步写入顺序和 journal
二、最小对象
对象字段负责什么
SuperBlockblock_size、inode_start、data_start、free_map描述整个文件系统布局
Inodeid、type、size、blocks、link_count、mode文件身份、大小、权限和数据块位置
DirectoryEntryname、inode_id路径名到文件身份的映射
OpenFileinode、offset、flags、ref_count一次打开文件的读写状态
FileDescriptorfd、open_file进程用小编号引用内核文件对象
JournalRecordtx_id、old_meta、new_meta、committed记录元数据变更是否完成
三、一次 open / read / write 怎样走
调用内核路径关键检查
open("/a.txt")解析目录 → 找 inode → 创建 OpenFile → 分配 fd路径存在、权限允许、fd 未超限
read(fd, buf, len)fd → OpenFile → inode → block → copy_to_userfd 可读、用户缓冲区可写
write(fd, buf, len)copy_from_user → 分配块 → 更新 inode → 写数据fd 可写、空间足够、写入顺序正确
close(fd)移除进程 fd → OpenFile ref-- → 必要时释放引用计数不能提前归零
四、删除不是简单释放块
坏场景:A 打开文件后继续读取,B 删除这个文件名。如果删除立刻释放数据块,A 的 fd 可能读到别人新写入的数据。
步骤设计动作为什么
删除目录项移除 name → inode 映射新 open 找不到这个名字
减少 link_countinode 不一定马上释放可能还有别的硬链接
检查 open 引用open_file ref_count 归零后才释放已打开 fd 仍有一致语义
释放块最后回收 inode 和 data blocks避免悬空引用
五、崩溃恢复先只保护元数据
断电位置可能后果第一版保护
分配了数据块,inode 未更新空间泄漏journal 记录分配意图,恢复时回滚或补完
inode 更新了,目录项没写孤儿文件元数据事务提交后再可见
目录项写了,inode 不完整路径指向坏 inode目录项最后提交
恢复过程中再次断电二次破坏恢复流程必须幂等
create_file(path): begin_tx() inode = alloc_inode() blocks = alloc_blocks() write_inode(inode) write_dir_entry(path, inode.id) commit_tx()
六、验收实验
实验通过标准证明了什么
路径实验创建、打开、读取同一路径得到同一文件目录项和 inode 映射成立
fd 偏移实验连续 read 自动推进 offsetOpenFile 状态成立
删除实验删除后新 open 失败,旧 fd 仍可按规则读完引用计数成立
权限实验无权限写入返回错误权限检查接入系统调用
断电实验create/write 中途断电后,重启无坏目录项元数据恢复成立
七、回到操作系统主线
下一页:设备驱动与中断
文件系统需要块设备,终端和网络也需要设备事件。继续看 如何设计设备驱动与中断
回总页
一句话:文件系统不是“把文件存到磁盘”,而是把块设备改造成有名字、有身份、有权限、有引用关系、断电后还能恢复的资源账本。