现代 Linux 内核中的文件系统考古与演进:从 2026 LSFMM 峰会看缓冲区头的去留

📅 2026/7/7 19:45:21 👁️ 阅读次数 📝 编程学习
现代 Linux 内核中的文件系统考古与演进:从 2026 LSFMM 峰会看缓冲区头的去留

在计算机操作系统的演进长河中,一些诞生于“远古时代”的设计往往会因为极强的兼容性或特定的技术历史原因,在内核深处驻留数十年之久。在 2026 年 Linux 存储、文件系统、内存管理与 BPF 峰会(LSFMM+BPF)的一个简短的文件系统专场会议上,内核专家 Jan Kara 带来了一场关于缓冲区头(Buffer Heads)清理进展的报告。

这场报告不仅关乎内核底层数据结构的瘦身,更串联起了阿米加文件系统(AFFS)、UDF、VFAT 等一系列见证了存储技术发展史的经典概念。

一、 什么是缓冲区头(Buffer Heads)?

Jan Kara 在会议开始时坦言,缓冲区头已经是“古董级的东西”了,“基本上从 Linux 诞生的第一天起(1991年)”就是内核的一部分。

历史与曾经解决的问题

在 UNIX 时代延续下来的传统中,磁盘访问是以块(Block)(通常为 512 字节或 4KB)为单位的。当时内存管理还没有现在这么复杂的“页面(Page)/ 简页(Folio)”概念。buffer_head结构体就是内核为了架起“内存”与“磁盘块”之间桥梁而设计的。每一个buffer_head描述了一个逻辑磁盘块在内存中的映射状态:比如这个块是不是脏的?有没有被锁住?对应磁盘的哪个扇区?

现在的优势与劣势

  • 优势:对于文件系统的元数据(Metadata)(如 inode 表、位图、目录结构),它们通常非常小,不需要占用一整页内存。buffer_head允许内核以极其细致的“块”粒度来管理和同步这些碎片化的数据状态。

  • 劣势:随着现代磁盘容量暴增,如果大文件读写还用buffer_head一个块一个块地追踪,会在内存中产生海量的结构体,极大浪费 CPU 和内存。此外,现代 Linux 内核的内存管理和 I/O 引擎(如iomap)已经全面转向以Folio(复合页面)为核心,buffer_head夹在中间显得臃肿且过时。

2026 峰会最新进展

内核文件系统开发者正试图将缓冲区头从文件系统的数据路径中移除,但许多文件系统的元数据路径中仍在使用它们。总的来说,缓冲区头在短期内不会消失,因为这些文件系统需要对单个块的状态进行精细化追踪。

Kara 一直在推进的工作之一,是构建一个通用基础设施,用于追踪属于给定 inode 的所有元数据块,以便在调用fsync()时将它们刷写到磁盘。他指出,ext4、ext2、UDF、VFAT 等几个文件系统都在使用该基础设施。他将元数据缓冲区头的追踪功能从通用的inode结构中剥离出来,放入了只有需要该功能的文件系统才会使用的私有inode部分。这样一来,不需要该追踪功能的文件系统,其inode结构体体积可以缩小40 字节。该工作已被 Christian Brauner 合并至7.1 内核

二、 峰会聚焦:三大经典文件系统的清理与适配

在这次缓冲区头的清理与重构工作中,三个不同时代、不同应用场景的文件系统成为了典型案例。

1. 阿米加文件系统 (AFFS)

  • 历史与原由:AFFS 是 1980 年代末至 1990 年代专门为Amiga 个人电脑(由 Commodore 公司推出,曾以强大的多媒体能力风靡一时)设计的文件系统。早期的文件系统在每个数据块里都塞入了元数据指针,导致文件断断续续,速度极慢。AFFS 重构了结构,将数据连续存储,大幅提升了软盘和早期硬盘的读写性能。

  • 现状与峰会适配:在今天,它唯一的价值就是考古与兼容,允许现代 Linux 系统读取和挂载那些古董级 Amiga 设备的磁盘镜像。Kara 在近期对 AFFS 的 Linux 实现进行了一次小规模的清理。该文件系统此前大费周章地去追踪元数据缓冲区头,但在执行fsync()时却从未真正使用过这些信息。鉴于维护者告诉他 AFFS 的性能并不是什么大问题,他直接移除了相关追踪代码,而不是将 AFFS 切换到使用新的通用基础设施。

2. UDF (Universal Disk Format - 通用光盘格式)

  • 历史与原由:UDF 是光学存储技术协会(OSTA)在 1990 年代中期开发的文件系统,旨在取代老旧的 ISO 9660 格式。它是为了DVD、蓝光光盘(Blu-ray)以及可擦写光盘(CD-RW)而生的。它引入了分包写入(Packet Writing)技术,让光盘能像 U 盘一样随时修改和追加文件,并且很早就支持大于 4GB 的单文件。

  • 现状与峰会适配:UDF 具有极强的跨平台兼容性,在超大容量光盘(如 100GB 蓝光档案光盘)和部分跨平台大容量 U 盘中仍在使用。在本次重构中,UDF 属于“需要精细化追踪元数据”的阵营,因此适配并采用了 Kara 开发的新通用缓冲区头追踪基础设施。

3. VFAT (Virtual File Allocation Table)

  • 历史与原由:VFAT 是微软在Windows 95时代对经典 FAT16/FAT32 文件系统做出的重大扩展。在 VFAT 出现之前,FAT 文件系统受到严格的8.3 文件名限制(文件名最多 8 个字符,扩展名最多 3 个字符)。VFAT 通过在目录项中巧妙地“拼凑”额外的隐藏条目,实现了对长文件名(最高 255 字符)和 Unicode 的支持,同时保持了向后兼容。

  • 现状与峰会适配:VFAT (FAT32) 拥有绝对的工业级兼容通用性,全球几乎所有计算设备(如单片机、数码相机、3D 打印机、SD 卡)都支持它。但它有着 4GB 单文件大小限制,且不支持日志化,断电易丢数据。在内核演进中,VFAT 同样属于保留元数据缓冲区头追踪的传统文件系统,并在 7.1 内核中一同适配了新的基础设施。

三、 深度重构:计划中的修复与技术挑战

尽管取得了一定进展,Jan Kara 在峰会上指出,目前的缓冲区头追踪和锁机制依然存在严重的缺陷与隐患,这也是接下来一年的攻坚方向。

1. 锁竞争与安全性修复(Race Condition)

目前在追踪元数据缓冲区头时存在一个竞态条件,可能会导致 inode 和所有元数据无法被写入后端存储。这意味着,如果发生fsync()后紧接着发生崩溃,本应刷写到磁盘的元数据可能会丢失。目前 ext4 已经对该问题进行了临时修复(workaround),但使用新基础设施的其他所有文件系统(如 ext2、UDF、VFAT)仍然容易受到该漏洞的影响。Kara 目前正在开发一个通用修复方案,这需要扩展用于追踪元数据缓冲区头的结构体。

2. 锁机制重构:引入 RCU

他还计划重构缓冲区头的锁机制。他指出,当缓冲区头挂载到 folio 上时,有两个锁对其进行保护:

  1. 一个是其所挂载 folio 的folio 锁folio_lock());

  2. 另一个是映射的私有锁address_space结构体中的i_private_lock)。

后者被用于那些无法获取 folio 锁(因为 folio 锁可能会导致休眠)的场景。但他希望停止使用该私有锁,因为它大大增加了锁机制的复杂性;他希望改用读-复制更新(RCU)机制。他希望在未来一年或更短的时间内完成这项工作。

四、 互动讨论:乱麻般的data=journal与 exFAT 的未来

在会议的讨论环节,其他内核大牛的提问将焦点引向了更复杂的重构工作。

1. 清理data=journal模式下的“ limbo 状态”

Christoph Hellwig 提出了一个“略微相关”的问题:在data=journal(数据日志)模式下,ext4 可能会创建与映射脱离的脏缓冲区头,这“需要魔改级别的处理”。他想知道 Kara 是否有解开这个乱麻的思想。

Kara 回应称,该问题在其他模式下也可能发生,但在data=journal模式下更为常见。当 VFS(虚拟文件系统)想要回收一个块或 folio,但由于文件系统仍在对该数据进行日志记录而拒绝回收时,就会发生这种情况,从而将数据推入了一种“诡异的中间地带(limbo state)”。

针对这一问题,他已经有了一些解决思路。目前有两条路径可以对日志化的缓冲区头进行回写:

  • 标准路径:最终到达块设备层,运行良好;

  • 日志路径:它只是简单地写入由缓冲区头追踪的块,而不改变包含它们的 folio 的状态,正是这一点引发了问题。

解决方案:

修复方法是让日志路径利用标准的回写机制来写入 folio,“而不是从底层偷走缓冲区头”。不过 Kara 也坦言,这“对日志机制来说是一个不小的重写工程”,这番话引发了会场内一些心领神会的笑声。他表示可以为任何想要挑战这一问题的人提供指导和部分代码。

2. exFAT 作为现代迁移模板

Hellwig 随后提到,他一直在与 Namjae Jeon 合作,将exFAT 文件系统的数据路径转换为使用 iomap。由于 exFAT 是一个“典型的简单文件系统”,这项工作可以作为一个很好的模板,供其他文件系统参考并以类似方式迁移到 iomap,“因为这是一个近期完成的、不含任何魔改行为的通用文件系统迁移案例”。

该工作最初计划赶在 7.1 的合并窗口完成,但遇到了一些问题,预计将在7.2 版本中亮相。随着老旧文件系统逐步向iomap靠拢,未来 Linux 内核对于缓冲区头的依赖将会进一步降低。

随着所有议题讨论完毕,这场关乎 Linux 文件系统承前启后的技术专场会议圆满结束。