C04 · 第 6 章 · 内存与文件

文件系统与崩溃恢复

介绍文件、目录与块索引,计算直接和间接索引的容量,分析多块更新、重做日志及同步接口,并检查不同中断位置的恢复结果。

预备知识虚拟内存与分页

本章目标

  1. 区分文件名称、打开引用和存储块的生命周期。
  2. 计算文件块号、间接索引和最大数据容量。
  3. 检查日志提交前后不同断电位置的恢复结果。
  4. 按照同步接口区分数据与目录项的持久性。
页面阅读位置0%
章节未开始
本册完成进度0/6 章 · 0%
本页目录

文件、目录与存储块

文件向应用提供按偏移访问的字节序列,文件系统把这些字节映射到存储设备上的块。目录保存名称与文件对象之间的对应关系,文件元数据记录大小、权限和块位置等信息。路径解析从某个目录开始,逐项查找名称;相同文件可以具有多个名称,打开后的描述符又可以在名称变化后继续引用文件对象。

Unix 风格文件系统通常以 inode 表示文件元数据,目录项引用 inode 编号。硬链接增加同一文件的目录引用,符号链接则保存另一条路径。删除一个目录项会减少名称引用;若文件仍然被进程打开,文件对象可能继续存在,直到名称引用和打开引用都不再需要它。这些规则解释了文件名、打开描述符和实际存储空间之间的不同生命周期。

文件块映射

给定块大小 BB 字节和文件字节偏移 xx,逻辑块号为 ⌊x/B⌋\lfloor x/B\rfloor,块内偏移为 x mod Bx\bmod B。文件系统用索引结构把逻辑块号映射到设备块位置,再访问对应的块内字节。稀疏文件中的某些逻辑块可以没有实际分配的数据块。

块大小影响索引规模、输入输出粒度和内部碎片。小文件最后一块通常没有填满,分配空间可能大于文件长度;缓存和延迟分配又使应用观察到的写入时刻与设备分配时刻不同。容量计算应明确计量的是文件逻辑长度、数据块占用,还是包含索引和日志在内的总占用。

直接索引与间接索引

直接索引把少量数据块地址直接放入文件元数据,访问小文件时查找路径较短。间接索引通过额外索引块保存更多数据块地址,扩大文件容量;多级间接索引进一步增加覆盖范围,也增加查找层级。现代文件系统还可能用连续块区间或树结构记录映射,具体格式随实现而变化。

一级间接索引的容量

设一个教学文件系统使用四 KiB 数据块,块地址占四字节,inode 含十二个直接地址和一个一级间接地址。一个间接块能容纳 4096/4=10244096/4=1024 个地址,因此数据块最多为 12+1024=103612+1024=1036 个,最大逻辑长度为

1036×4096=4243456 bytes.1036\times4096=4243456\ \mathrm{bytes}.

该值只计算数据容量,达到上限时还要额外占用一个间接索引块。对于文件偏移五万字节,我们计算 50000=12×4096+84850000=12\times4096+848,逻辑块号为十二,正好进入间接块的第零项,块内偏移为八百四十八字节。最后一个直接块号为十一,边界检查有助于发现索引差一的错误。

读取该位置可能需要先取 inode、再取间接块、最后取数据块,但缓存命中会减少实际设备访问次数。因此索引层级给出的是依赖关系,不能直接等同于每次访问的设备请求数量。顺序读取还可能通过预读合并后续请求,随机访问则更依赖索引与数据缓存的命中情况。

多块更新与崩溃状态

创建文件可能同时修改空闲空间位图、inode 和目录项。假设先把目录项写入设备,随后系统断电,新的目录名称可能指向尚未初始化的 inode;若只更新位图却丢失文件引用,则已分配空间可能无人引用。单个写入成功不保证一组相关修改整体完成。

崩溃一致性要求恢复后的文件系统满足元数据结构约束,例如每个已引用数据块都有一致的分配记录。应用持久性则关心已经向用户确认的业务数据是否仍然存在。这两类目标相关但有区别:一个结构一致的文件系统仍可能丢失最近尚未同步的数据,应用也可能把多次文件操作组合成更大范围的业务事务。

设备和控制器可能缓冲或重新排序写入,因而数据稳定保存的顺序可能不同于程序发出请求的顺序。持久化协议需要明确刷新、写屏障或其他设备保证的含义,并处理写入错误。以下日志例子采用完整块写入及指定持久化顺序成立的教学假设,实际系统还需处理部分写入、校验和与设备故障。

重做日志与提交

重做日志先保存更新后的块内容及目标位置,在这些日志内容持久化后写入提交标记。提交标记确认一组更新已经足够完整,恢复程序随后把它们复制到正式位置。正式位置全部更新完成后,日志空间才可以回收。若在复制过程中再次崩溃,恢复程序重复写入相同块内容,应得到相同结果。

目录与位图的恢复顺序

某次文件创建需要把位图块改为新值甲,把目录块改为新值乙。日志先记录两块新内容和各自目标位置,等待两块日志内容稳定保存,再写入并持久化提交标记,最后依次更新正式位图和目录。我们分三种断电时刻检查恢复结果。

若提交标记尚未持久化,恢复程序不采用这组未提交日志,正式位置仍保持旧状态。若标记已经持久化但正式位置尚未修改,恢复程序复制甲、乙两块,完成整个更新。若正式位图已修改而目录尚未修改,恢复程序再次复制甲并补上乙,两块最终仍达到同一新状态。重复写入完整的同一块值具有幂等性,这是重复恢复能够安全进行的依据。

日志必须确认记录完整性,否则仅有一个残留的提交标记可能使恢复程序读取旧数据。实际实现可采用长度、事务序号、校验信息和严格的回收顺序。只记录元数据的日志减少写入量,但用户数据的写入顺序需要单独设计,避免新元数据指向尚未准备好的数据块。OSTEP 的 FSCK and Journaling 章节详细比较这些更新顺序及其故障窗口。

写入返回与持久化接口

普通写入返回成功通常说明内核已经接收相应数据,数据可能仍位于内存缓存。应用需要持久化保证时,应使用运行系统规定的同步接口并检查返回值。在 Linux 中,fsync 同步指定文件的数据和相关元数据;新建或重命名后,目录项的持久化还可能需要对目录执行同步,具体依据文件系统与接口承诺。

例如,保存配置文件可以先在同一文件系统创建临时文件,写完并同步其内容,再进行名称替换,并按系统要求同步所在目录。这样设计时仍需处理磁盘写满、同步失败和目录权限问题。名称替换的原子可见性与掉电后的持久性是不同属性,应用必须明确向使用者确认保存成功的时刻。

Linux man-pages 的 fsync 页面明确说明文件同步不会自动保证包含它的目录项已经写入设备。调用接口时,错误检查必须延续到最后一次必要的同步,不能在前一次普通写入成功后提前报告完成。对网络文件系统、可移动介质或具有特殊缓存策略的设备,还需要查看相应实现的保证范围。

恢复测试与错误范围

恢复测试在更新过程的不同位置模拟中断,重启后检查结构与业务条件。对于小型日志协议,我们可以枚举提交前、提交后、部分安装和日志回收后的状态,核对恢复结果属于完整旧状态或完整新状态。应用层还应检查重复处理是否造成重复扣款、重复消息或覆盖新数据,因为块级一致性无法自动定义业务语义。

文件系统检查工具可以根据元数据关系寻找丢失引用、重复分配和计数不一致,但恢复出的结构未必完整保留原有用户意图。备份与日志解决的故障范围也不同:日志通常帮助完成正在进行的更新,独立备份则用于设备丢失、错误删除或历史数据恢复。安排恢复策略时,应按所需故障范围确定保存位置和恢复步骤。

文件系统练习

练习

块大小四 KiB,一个普通非稀疏文件长六千字节。求最少数据块数和最后一块未使用的空间,说明计量范围。

查看解答

需要向上取整 6000/4096,分配两个数据块,占 8192 字节。最后一块内部未用空间为 8192−6000=21928192-6000=2192 字节;该结果未计索引、日志或其他元数据。

练习

块大小四 KiB,地址占八字节,inode 有八个直接地址和一个一级间接地址。求最大数据块数与数据长度。

查看解答

一个索引块容纳 4096/8=5124096/8=512 个地址。加上八个直接地址,总共覆盖五百二十个数据块,最大数据长度为 520×4096=2129920520\times 4096=2129920 字节。

练习

重做日志在所有新块内容持久化之前先写入提交标记,会出现什么恢复风险?说明需要的顺序。

查看解答

恢复程序可能看见有效提交标记,却读取到尚未持久化或不完整的日志内容。应先保证全部日志内容稳定保存,再持久化提交标记,并按协议验证记录完整性。

练习

Linux 程序写入并同步了新文件内容,随后完成重命名。若需要保证掉电后名称更新也保留,还应检查哪一类持久化操作?

查看解答

文件内容同步与目录项同步具有不同范围。新文件名称或重命名结果要按 Linux 文件系统约定同步所在目录,并检查同步错误,才能满足相应的崩溃持久性要求。

存储恢复参考资料

Operating Systems: Three Easy Pieces

Remzi H. Arpaci-Dusseau, Andrea C. Arpaci-Dusseau · 2023 · Arpaci-Dusseau Books

打开官方来源

OSTEP 的 Files and Directories、File System Implementation 与 FSCK and Journaling 章节介绍文件接口、索引结构和多块更新。阅读时可逐步列出每项操作修改的数据块,再检查每一个可能的中断位置会留下怎样的持久状态。

xv6: a simple, Unix-like teaching operating system

Russ Cox, Frans Kaashoek, Robert Morris · 2025 · Massachusetts Institute of Technology

打开官方来源

MIT xv6 教材第 10 章给出缓冲缓存、日志、inode 与目录层的实现。其教学日志将修改块先写入日志,提交后再安装到正式位置;这为追踪提交点、恢复重放与日志回收提供了一份完整源码。

Linux man-pages 的文件同步接口说明区分数据、元数据和目录持久化范围,并列出错误返回。应用应按照实际目标系统的接口保证安排保存步骤,检查每次同步的结果。