> For the complete documentation index, see [llms.txt](https://freebsd-journal-cn.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://freebsd-journal-cn.bsdcn.org/20160304-jiao-xue/a-brief-history-of-the-bsd-fast-filesystem.md).

# BSD 快速文件系统简史

* 原文：[A Brief History of the BSD Fast Filesystem](https://freebsdfoundation.org/wp-content/uploads/2016/04/A-Brief-History-of-the-BSD-Fast-Filesystem.pdf)
* 作者：**Marshall Kirk McKusick**

本文系统梳理了 1979 年至今的文件系统与存储发展，重点聚焦 BSD 快速文件系统（Fast Filesystem）。文章描述了早期通过增大磁盘块尺寸、感知磁盘几何结构并据此优化旋转布局所做的性能改进工作。20 世纪 80 年代后期，磁盘几何结构被抽象化，硬件也具备了缓存与并发处理多请求的能力，文件系统性能优化不再追踪几何结构，转而通过文件连续布局追求最大性能。小文件性能则通过日志和软更新等技术优化。到 20 世纪 90 年代末，文件系统不得不重新设计以应对不断增长的磁盘容量。快照的加入使得备份更快速、更频繁。多处理支持也被引入，以充分利用多核处理器中日益普遍的所有 CPU。互联网环境日益严苛，要求访问控制列表和强制访问控制提供更强的数据保护。最终，元数据优化的引入将我们带到现在，并指向未来可能的方向。

## 1979：早期文件系统工作

加州大学伯克利分校最早对 UNIX 文件系统开展的工作，试图同时改进文件系统的可靠性与吞吐量。开发者通过对关键文件系统信息的修改进行分阶段处理来提升可靠性，这样在崩溃后，这些修改既可以完成，也可以由程序干净地修复 \[15]。将文件系统块尺寸加倍后，4.0 BSD 文件系统相比 3 BSD 文件系统性能提升超过两倍。这种加倍让每次磁盘传输访问的数据块数量翻倍，并消除了许多文件对间接块的需求。

3 BSD 文件系统的性能提升有力地表明，增大块尺寸是改进吞吐量的有效方法。虽然吞吐量翻倍，3 BSD 文件系统仍然只用到最大磁盘吞吐量的约 4%。主要问题在于，随着文件的创建和删除，空闲链表上的块顺序很快变得混乱。最终，空闲链表顺序完全随机化，导致文件的块在磁盘上随机分配。这种随机性迫使每次块访问前都要做一次寻道。3 BSD 文件系统刚建立时传输速率可达每秒 175 KB，但空闲链表的混乱使该速率在数周中等强度使用后衰减到平均每秒 30 KB。除了重建系统，没有任何办法恢复 3 BSD 文件系统的性能。

## 1982：快速文件系统诞生

当前 BSD 文件系统的第一个版本写于 1982 年，并随 4.2 BSD 广泛发布 \[14]。该版本至今仍在 Solaris 和 Darwin 等系统中使用。要使用大块而不造成显著浪费，就必须更高效地存储小文件。为提升空间利用率，文件系统允许把单个文件系统块划分为一个或多个片段。片段大小在文件系统创建时指定；每个文件系统块可选地划分为两片、四片或八片，每片都可独立寻址。片段大小的下限受磁盘扇区大小限制，通常为 512 字节。20 世纪 80 年代早期磁盘空间昂贵且容量有限，文件系统最初以默认 4096 字节块尺寸部署，以便小文件能存入单个 512 字节扇区。

## 1986：放弃磁盘几何结构计算

BSD 文件系统组织将磁盘分区划分为一个或多个区域，每个区域称为一个柱面组。历史上，柱面组由磁盘上一个或多个连续柱面组成。虽然文件系统仍使用相同的数据结构来描述柱面组，但其实际定义已发生变化。文件系统最初设计时，可以准确获知磁盘几何结构，包括柱面和磁道边界，并能精确计算每个扇区的旋转位置。到 1986 年，磁盘开始隐藏这些信息，提供虚假的每磁道块数、每柱面磁道数和每磁盘柱面数。事实上，在现代 RAID 阵列中，呈现给文件系统的“磁盘”可能实际由 RAID 阵列中的若干磁盘组合而成。虽然已有研究试图找出磁盘的真实几何结构 \[5, 10, 25]，但有效利用这类信息的复杂度很高。现代磁盘外圈每磁道的扇区数比内圈更多，使得任意扇区旋转位置的计算变得复杂。因此 1986 年，所有旋转布局代码被弃用，改为使用数值相近的块号来布局文件（顺序被视为最优），相信这样能带来最佳性能。虽然柱面组结构被保留下来，它仅作为管理逻辑上相近块组的便利手段。

## 1987：文件系统堆叠

早期的 vnode 接口只是底层文件系统的面向对象接口。到 1987 年，对新文件系统特性的需求不断增长。如何在不必修改现有且稳定的文件系统代码的前提下提供这些特性，变得日益迫切。一种方法是为多个文件系统互相堆叠提供机制 \[24]。堆叠思想在 4.4 BSD 中得到完善和实现 \[7]。vnode 栈的底部通常是基于磁盘的文件系统，而其上使用的层通常对参数进行转换，再把参数传递给更下层。

堆叠使用 `mount` 命令创建新层。`mount` 命令将新层推入 vnode 栈；`unmount` 命令移除一层。与挂载文件系统类似，vnode 栈对系统上所有进程可见。`mount` 命令识别栈中的底层，创建新层，并将该层附加到文件系统命名空间。新层可以附加到与旧层相同的位置（覆盖旧层），也可以附加到树中的其他位置（让两层同时可见）。

当栈中的某个 vnode 发生文件访问（例如 `open`、`read`、`stat` 或 `close`）时，该 vnode 有几种选择：

* 执行所请求的操作并返回结果。
* 不加修改地把操作传递给栈中下一层 vnode。当操作从下层 vnode 返回时，可以修改结果或直接返回。
* 修改请求附带的操作数，再传递给下一层 vnode。当操作从下层 vnode 返回时，可以修改结果或直接返回。如果某个操作被传递到栈底而没有任何层对其采取行动，接口将返回“操作不支持”错误。

最简单的文件系统层是 nullfs。它对参数不做任何转换，只是把收到的所有请求透传，并把得到的结果原样返回。虽然直接堆叠在现有 vnode 之上时它没什么用处，但 nullfs 可以通过将根源于源 vnode 的文件系统挂载到文件系统树中的其他位置，提供回环文件系统。nullfs 的代码也是设计师构建自己文件系统层的优秀起点。可构建的示例包括压缩层或加密层。

union 文件系统是中间文件系统层的另一个例子。与 nullfs 一样，它不存储数据，只提供命名空间转换。它松散地借鉴了 3-D 文件系统 \[9]、Translucent 文件系统 \[8] 和 Automounter \[20] 的工作。union 文件系统取一个已有文件系统，并透明地将其叠加到另一个文件系统之上。与大多数其他文件系统不同，union 挂载不会覆盖文件系统所挂载到的目录。相反，它展示两个目录的逻辑合并，并允许同时访问两棵目录树 \[19]。

## 1988：提升块尺寸

到 1988 年，磁盘容量已增长到足以将默认块尺寸提升为 8196 字节块、1024 字节片段。虽然这意味着小文件至少占用两个磁盘扇区，但块尺寸加倍带来的吞吐量近乎翻倍，对于实测多出 1.4% 的浪费空间而言，似乎是合理的折衷。

## 1990：动态块重分配

20 世纪 80 年代大部分时间里，文件的最优布局是隔一块写一块。通过在每个已分配块之间留出空隙，磁盘有时间在上一操作完成后调度下一次读或写。20 世纪 80 年代后期随着磁盘缓存和并发处理多重请求（标签队列）能力的出现，将文件连续布局到磁盘上变得更有利。

操作系统无法预知文件首次以写方式打开时最终会有多大。如果它假定所有文件都很大，并试图把它们放入最大的可用连续空间，很快就会只剩下零散的小块连续空间。反过来，如果它假定所有文件都很小，并试图把它们放入碎片空间，那么长大后文件的开头部分布局就会很差。

为避免这些问题，文件系统在 1990 年改为做动态块重分配。文件系统最初把文件块放入小块空闲空间，但随着文件增长，会把它们移到更大的空闲空间。使用这种技术，小文件使用小块空闲空间，大文件则在大型空闲空间中连续布局。该算法不会增加 I/O 负载，因为缓冲缓存通常会把文件内容保留足够长时间，到文件数据第一次刷写到磁盘时最终块分配已确定。

该算法的效果是，即使使用多年后空闲空间仍基本不碎片化。哈佛大学的一项研究发现，使用了三年的文件系统吞吐量仅下降 15%，而禁用动态重分配的相同文件系统则下降 40% \[27]。

## 1996：软更新

在文件系统中，元数据（如目录、inode 和空闲块映射）为原始存储容量赋予结构。元数据提供指针和描述，把多个磁盘扇区链接成文件并标识这些文件。要用于持久存储，文件系统必须能在不可预知的系统崩溃（如电力中断和操作系统故障）面前维持元数据完整性。由于这类崩溃通常导致易失主存中的所有信息丢失，非易失存储（即磁盘）中的信息必须始终足够一致，才能确定性地重建一个连贯的文件系统状态。具体而言，文件系统的磁盘映像不得有指向未初始化空间的悬空指针，不得有因多个指针引起的资源归属不明，也不得有未被引用的活跃资源。维持这些不变量通常需要对小型磁盘元数据对象的更新进行排序（或原子分组）。

传统上，文件系统使用同步写来正确排序稳定存储的变更。例如，创建文件涉及先分配并初始化一个新 inode，再填写一个新的目录项指向它。采用同步写方式时，文件系统强制创建文件的应用等待初始化磁盘 inode 的磁盘写操作完成。结果，文件创建和删除这类文件系统操作以磁盘速度进行，而非处理器/内存速度 \[16, 18, 26]。由于磁盘访问时间相对其他计算机组件的速度要长得多，同步写降低了系统性能。

元数据更新问题也可以用其他机制解决。例如，可以使用 NVRAM 技术（如不间断电源或 Flash RAM）来消除对磁盘状态一致性的依赖 \[17, 33]。文件系统操作只要把待写块复制到稳定存储中即可继续，更新可以以任何顺序、在任何方便的时候传播到磁盘。如果系统失败，待完成的磁盘操作可以在系统重启时从稳定存储中完成。

另一种方法是把每组相关更新作为原子操作，通过某种形式的预写日志 \[3, 6] 或影子分页 \[2, 28] 实现。这些方法在磁盘状态之外，使用独立磁盘或稳定存储上的文件系统更新日志。文件系统操作只要把待执行操作写入日志即可继续。如果系统失败，未完成的文件系统操作可以在系统重启时从日志中完成。与同步写方式相比，许多现代文件系统成功使用预写日志来改进性能。

一种名为软更新的替代方法在研究原型中进行了评估 \[4]。在成功评估后，1996 年为 BSD 编写了软更新的生产版本。采用软更新时，文件系统对元数据变更使用延迟写（即写回缓存），跟踪更新之间的依赖关系，并在写回时强制执行这些依赖。由于大多数元数据块包含多个指针，仅在块级别记录依赖时常常出现循环依赖。因此，软更新按每个指针跟踪依赖，允许块以任何顺序写入。在块写入前，元数据块中仍依赖的更新被回滚，写入后再前滚。这样，依赖循环问题就被消除了。采用软更新，应用总是看到元数据块的最新副本，而磁盘总是看到与其他内容一致的副本。

## 1999：快照

1999 年，文件系统加入了快照功能。文件系统快照是文件系统在某一时刻的冻结映像。快照支持若干重要特性：能够在一天中的多个时刻为文件系统提供备份，以及对活动的文件系统进行可靠 dump 的能力。快照可在任何时刻拍摄。如果在白天每隔几小时拍一次，用户就可以找回几小时前写下、随后误删或被覆盖的文件。快照比 dump 磁带使用起来方便得多，创建频率也可以高得多。

为让用户能通过传统文件系统接口访问快照，系统管理员使用 mount 命令将冻结文件系统映像的副本放置到命名空间中任何方便的位置。

文件系统快照可用之后，就可以安全地 dump 活动文件系统了。当 dump 发现自己被要求 dump 一个已挂载的文件系统时，只需对文件系统拍一张快照，然后对快照而非活动文件系统运行即可。dump 完成后，释放快照。

## 2001：再次提升块尺寸

到 2001 年，磁盘容量已增长到足以将默认块尺寸提升为 16,384 字节块、2,048 字节片段。虽然这意味着小文件至少占用四个磁盘扇区，但块尺寸加倍带来的吞吐量近乎翻倍，对于实测多出 2.9% 的浪费空间而言，似乎是合理的折衷。

## 2002：后台 fsck

传统上，系统非正常关机后，文件系统检查程序 fsck 必须遍历文件系统中所有 inode，以确定哪些 inode 和块正在使用，并校正位图。这一检查过程极其缓慢，可能让大型服务器的重启延迟一小时甚至更久。软更新保证所有文件系统资源（包括 inode 和块位图）的一致性。采用软更新时，文件系统中可能出现的唯一不一致（排除软件错误和介质故障）是某些未被引用的块可能未出现在位图中，以及某些 inode 的链接计数可能过高需要降低。因此，崩溃后直接开始使用文件系统而不先运行 fsck 是完全安全的。但每次崩溃后可能丢失一些文件系统空间。因此，让 fsck 能在活动文件系统上后台运行，以查找并恢复丢失的块、调整链接计数过高的 inode，是有价值的。

加入快照后，这一任务变得简单，只需对标准 fsck 做少量修改。在后台清理模式下运行时，fsck 首先对要检查的文件系统拍一张快照。然后 fsck 对快照文件系统映像运行，做与正常操作相同的计算。唯一的其它改动在运行结束时，它想写出更新后的位图。此时，修改后的 fsck 取它发现快照拍摄时正在使用的块集合，并从快照拍摄时标记为正在使用的块集合中移除——差集就是丢失的块集合。它还构造需要调整链接计数的 inode 列表。然后 fsck 使用一个新的系统调用通知文件系统所识别的丢失块，以便文件系统将其放回位图。它还给出需要调整链接计数的 inode 集合；那些链接计数降为零的 inode 被截断为零长度并释放。fsck 完成后，释放其快照。后台 fsck 实现的完整细节可参见 McKusick \[12, 13]。

## 2003：多 TB 支持

原始 BSD 快速文件系统及其衍生系统使用 32 位指针引用文件在磁盘上使用的块。在 20 世纪 80 年代早期设计时，最大的磁盘是 330MB。当时有争论是否值得为每个块指针浪费 32 位，而不是使用它所替代的文件系统的 24 位块指针。幸好未来主义观点占了上风，设计使用了 32 位块指针。

在部署以来的 20 年中，存储系统已增长到容纳超过 1TB 数据。根据块尺寸配置，原始文件系统的 32 位块指针在 1 到 4TB 范围内耗尽空间。虽然可以使用一些权宜之计来扩展原始文件系统支持的最大存储系统，但到 2002 年，唯一长期的解决方案是使用 64 位块指针。因此，我们决定构建一个使用 64 位块指针的新文件系统。

我们考虑了多种替代方案：对现有文件系统做增量修改，或导入其它现有文件系统如 XFS \[29] 或 ReiserFS \[21]。我们还考虑从头写一个新文件系统，以便利用最近的文件系统研究和经验。我们选择扩展现有文件系统，因为这种方法允许我们重用其大部分现有代码库。这一决定的好处是，64 位块文件系统快速开发并部署，迅速变得稳定可靠，同一代码库可以同时支持 32 位块和 64 位块文件系统格式。超过 90% 的代码库是共享的，因此错误修复和特性或性能增强通常适用于两种文件系统格式。

## 2004：访问控制列表

在文件系统更新为使用 64 位块指针的同时，还添加了对扩展属性的支持。扩展属性是与 inode 关联的辅助数据存储，可用于存储与文件内容分开的辅助数据。这个想法类似于 Apple 文件系统中使用的数据分支概念 \[1]。通过将扩展属性集成到 inode 本身，可以提供与文件内容本身相同的完整性保证。具体而言，fsync 系统调用的成功完成确保文件数据、扩展属性以及指向文件名的所有名称和路径都在稳定存储中。

扩展属性首先用于支持访问控制列表（通常称为 ACL）。ACL 用更具体的允许访问文件的用户列表替换文件的组权限。ACL 还包括授予每个用户的权限列表。这些权限包括传统的读、写和执行权限，以及重命名或删除文件的权利等其它属性 \[22]。

早期的 ACL 实现使用每个文件系统一个辅助文件，按 inode 号索引，并有小的固定大小区域存储 ACL 权限。小尺寸是为了保持辅助文件大小合理，因为它必须为文件系统中每个可能的 inode 留出空间。这种实现有两个问题。每个 inode 存储 ACL 信息的空间固定大小意味着无法给长用户列表授权。第二个问题是难以原子地提交文件 ACL 列表的更改，因为更新要求同时写入文件 inode 和 ACL 文件才能生效 \[30]。

辅助文件实现 ACL 的两个问题都通过将 ACL 信息直接存储在 inode 的扩展属性数据区来解决。由于扩展属性数据区的大尺寸（最小 8KB，通常 32KB），可以轻松存储长列表的 ACL 信息。用于存储扩展属性信息的空间与具有扩展属性的 inode 数量和它们使用的 ACL 列表大小成正比。信息的原子更新要容易得多，因为写入 inode 将在一次磁盘操作中更新 inode 属性和它引用的数据集，包括扩展属性。

虽然可以在文件系统上每次 fsync 系统调用时更新旧的辅助文件，但这样做的代价过高。这里，内核知道 inode 的扩展属性数据块是否脏，并可以在 inode 的 fsync 调用期间只写该数据块。

## 2005：强制访问控制

扩展属性的第二个用途是数据标记。数据标记为内核强制的强制访问控制（MAC）框架提供权限。内核的 MAC 框架允许动态引入的系统安全模块修改系统安全功能。该框架可用于支持各种新安全服务，包括传统的标记强制访问控制模型。框架提供一系列入口点，由支持各种内核服务的代码调用，特别是在访问控制点和对象创建方面。然后框架调用安全模块，让它们有机会在这些 MAC 入口点修改安全行为。因此，文件系统不规定标记如何使用或执行。它只是存储与 inode 关联的标记，并在安全模块需要查询它们以进行权限检查时产生这些标记 \[31, 32]。

## 2006：对称多处理

20 世纪 90 年代后期，FreeBSD 项目开始了将内核转换为支持对称多处理的漫长艰苦任务。第一步是在整个内核周围添加一个巨型锁，以确保一次只有一个处理器能在内核中运行。每个内核子系统通过重写使其能被多个处理器同时执行而从巨型锁下移出。vnode 接口于 2004 年从巨型锁下移出。磁盘子系统于 2005 年成为多处理器安全。最终，2006 年，快速文件系统经过大修以支持对称多处理，完成了从系统调用到硬件的无巨型锁路径。

## 2009：日志软更新

虽然软更新避免了崩溃后运行 fsck 的需要，但软更新仍可能导致块和 inode 丢失；它们不被文件系统使用，但仍在文件系统的位图中被标记为使用中。仍需定期运行 fsck 来回收丢失的空间。因此，出现了用日志补充软更新的想法，日志跟踪资源的释放，以便崩溃后可以重放日志来恢复丢失的资源。

具体而言，日志包含恢复已释放但其在系统故障前未能写入磁盘的块和 inode 资源所需的信息。崩溃后，老牌 fsck 程序的变体遍历日志以识别并释放丢失的资源。只有在日志和文件系统之间检测到不一致时，才需要运行全文件系统 fsck。日志很小：通常 16MB 就够了，与文件系统大小无关。虽然日志处理需要在重启前完成，但处理时间通常只需几秒，最坏情况下一分钟。使用软更新日志不需要构建新文件系统。向现有 FreeBSD 快速文件系统添加或删除软更新日志使用 tunefs 程序完成。

## 2011：又一次提升块尺寸

到 2011 年，磁盘容量已增长到足以将默认块尺寸提升为 32,768 字节块、4,096 字节片段。这一提升也由磁盘技术向 4K 扇区转变推动。随着扇区尺寸增大，小文件再次至少占用一个磁盘扇区。因此文件系统再次将吞吐量翻倍，且没有额外的磁盘空间浪费。

## 2013：优化的元数据布局

为加速文件的随机访问以及 fsck 对元数据的检查，文件系统将每个柱面组中前 4% 的数据块保留给元数据使用 \[11]。策略例程优先将元数据放在元数据区，其它内容放在元数据区之后的块中。元数据区的大小不需要精确计算，因为它只是策略例程放置元数据的提示。如果元数据区满了，元数据可以放在常规块区；如果常规块区满了，常规块也可以放在元数据区。这一决定按柱面组逐个进行，因此一些柱面组可能溢出其元数据区而其它则不溢出。策略是将所有元数据放在与其 inode 相同的柱面组中。将元数据分散到各柱面组通常会降低文件系统性能。

元数据放置策略的一个例外是文件的第一个间接块。策略是将第一个（单级）间接块与文件数据内联放置（例如，它尝试连续布局前 12 个直接块，紧接着是间接块，紧接着是间接块引用的数据块）。将第一个间接块与数据内联而非放在元数据区，是为了避免读取它时的两次额外寻道。这两次额外寻道会明显减慢对只使用间接块引用的前几个块的文件的访问。

只有第二级和第三级间接块及其引用的间接块分配在元数据区。将这部分元数据近乎连续地分配在引用它们的 inode 附近，明显改善了对文件的随机访问时间，也加快了 fsck 的运行时间。此外，当读取第二级间接块时，磁盘的磁道缓存通常已填满了文件的大部分元数据，因此通常也能加快文件的顺序读取速度。

除了将间接块放在元数据区，将存放目录内容的块也放在那里也有帮助。将目录内容放在元数据区加快了目录树遍历，因为数据距离读取目录 inode 的位置只有一次短寻道，而且可能已经因为同柱面组的其它目录读取而在磁盘的磁道缓存中。

## 未来方向

文件系统有许多已提出或被请求的更改。第一类包括无需更改文件系统磁盘格式即可进行的改进：

* 在闪存等需要先批量擦除块才能重用的设备上运行时，文件系统会在不再使用某个块时通知底层设备。设备通过 TRIM 请求通知，通常在文件删除或截断时释放块时进行。每释放一个块都会通过 GEOM 层向设备发送一个 TRIM 命令，并在完成时返回相应确认。通常，一个文件由许多连续块组成。将这些连续块收集在一起并为整个大块发送单个 TRIM 请求会高效得多。
* 目前，启用了日志软更新的文件系统无法拍摄快照。这一限制是因为 fsck 中执行日志回滚的代码在释放块时不知道如何处理快照文件。具体而言，当释放一个块时，需要检查每个快照以查看被释放的块是否是快照想认领的。只有没有任何快照想要该块时，才能将其释放到空闲链表。fsck 中的日志回滚总是将块释放到空闲链表。因此，如果文件系统包含任何需要认领已释放块之一的快照，它将被损坏。要么需要将检查是否有快照需要释放块的内核代码集成到 fsck 中，要么在 fsck 以日志回滚模式运行时删除所有现有快照。
* 为叠瓦式磁记录（SMR）磁盘驱动器提供支持。这些驱动器主要由必须连续写入的大型区域（通常几 MB）组成（类似于闪存块）。磁盘的一小部分能够写入普通的碎片块。处理这些驱动器的策略是将位图分为连续和碎片区域以匹配磁盘上的区域。软更新代码然后将增强为将软更新收集成可连续写入的块批次。
* 当前文件系统有规定允许单个文件使用更大的块尺寸。这一功能从未实现，但对较大文件会非常有益。

对文件系统的其它所需更改需要新的磁盘格式，通常称为 UFS3：

* 其中最重要的更改是将目录格式的文件号从 32 位增加到 64 位。随着磁盘容量增加，每个文件系统 40 亿文件的限制日益成为问题。进行此更改的最大障碍是 FreeBSD 文件系统接口目前只支持 32 位文件号。自从具有原生 64 位文件号的 ZFS 文件系统被引入系统以来，就一直讨论更改此接口。希望接口更改能及时在 FreeBSD 11 发行版中实现。
* 当前文件系统格式的另一个限制是它使用 16 位字段记录指向文件的链接数，因此限制一个文件最多有 65,535 个目录项引用它。更改磁盘格式时，此字段应增加到 64 位以解决未来多年的问题。

作为 UFS3 一部分修改磁盘格式时，可能有用的是添加一些类似 ZFS 的特性：

* 添加校验和以提高文件系统的健壮性和数据完整性。最容易实现的是将校验和放在文件的每个块中。这种方法无法检测写入错误位置的块。采用 ZFS 将每个校验和与块指针一起存储的方法可以避免此问题，但需要对现有文件系统代码做更具影响力的更改。
* 另一个提高健壮性和数据完整性的有用更改是提供文件系统元数据的冗余。至少，文件系统应提供 inode 和间接块的冗余副本。如果可行，文件系统还应提供目录数据块的多个副本。如果实现足够灵活，它可以像 ZFS 那样为用户数据块提供可选冗余。

另一种开始出现在市场上的技术是键/值磁盘，如 Seagate 最近发布的那些。这些磁盘提供最大 1MB 的对象，使用 64 位键标识。文件系统可以改造为使用这些磁盘，方法是使用 1MB 的块尺寸，这将大幅减少需要维护的元数据信息量。块号将替换为 64 位对象键，文件内容存储为对象值。文件的最后片段可以存储在较小的对象中，从而允许磁盘管理磁盘碎片问题。

***

**Marshall Kirk McKusick** 是 4.2BSD 快速文件系统的实现者，曾长期任职于加州大学伯克利分校计算机系统研究组（CSRG）。他撰写的《The Design and Implementation of the 4.4BSD Operating System》是 BSD 领域的经典著作。他是 FreeBSD 基金会的董事，长期致力于文件系统、操作系统和计算机系统架构的研究。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://freebsd-journal-cn.bsdcn.org/20160304-jiao-xue/a-brief-history-of-the-bsd-fast-filesystem.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
