大多数现代 Linux 发行版默认使用 ext4 文件系统,就像以前的 Linux 发行版默认使用 ext3、ext2 以及——如果追溯到更久以前——ext 一样。
如果您是 Linux 新手——或者文件系统新手——您可能会想知道 ext4 相比 ext3 有什么优势。您可能还会想知道 ext4 是否仍在积极开发中,考虑到关于 btrfs、xfs 和 zfs 等替代文件系统的新闻铺天盖地。
我们无法在一篇文章中涵盖关于文件系统的所有内容,但我们将尝试向您介绍 Linux 默认文件系统的历史、现状以及未来的发展方向。
我在准备这份概述时,大量参考了维基百科上关于各种 ext 文件系统的文章、kernel.org 上关于 ext4 的 wiki 条目以及我自己的经验。
ext 的简史
MINIX 文件系统
在 ext 出现之前,有 MINIX 文件系统。如果您不熟悉 Linux 历史,MINIX 是一个非常小的类 Unix 操作系统,用于 IBM PC/AT 微型计算机。Andrew Tannenbaum 开发它是为了教学目的,并在 1987 年以印刷形式发布了其源代码!

opensource.com
虽然您可以细读 MINIX 的源代码,但它实际上并不是自由和开源软件 (FOSS)。Tannebaum 书籍的出版商要求 69 美元的许可费才能运行 MINIX,这笔费用包含在书的成本中。尽管如此,这在当时来说非常便宜,MINIX 的采用迅速普及,很快就超出了 Tannenbaum 最初仅将其用于教授操作系统编码的意图。在 1990 年代,您可以在全球各地的大学中找到蓬勃发展的 MINIX 安装——一位年轻的 Linus Torvalds 使用 MINIX 开发了最初的 Linux 内核,该内核于 1991 年首次发布,并于 1992 年 12 月在 GPL 下发布。
但是等等,这是一篇文件系统文章,对吧?是的,MINIX 有自己的文件系统,早期版本的 Linux 也依赖于它。与 MINIX 一样,它可以毫不客气地被描述为“玩具”级别的例子——MINIX 文件系统只能处理最多 14 个字符的文件名,并且只能寻址 64MB 的存储空间。在 1991 年,典型的硬盘驱动器已经有 40-140MB 的大小。Linux 显然需要一个更好的文件系统!
ext
当 Linus 埋头苦干于新兴的 Linux 内核时,Rémy Card 致力于第一个 ext 文件系统。ext 于 1992 年首次实现——仅在 Linux 本身首次发布一年后!——解决了 MINIX 文件系统最糟糕的问题。
1992 年的 ext 使用了 Linux 内核中新的虚拟文件系统 (VFS) 抽象层。与之前的 MINIX 文件系统不同,ext 可以寻址高达 2GB 的存储空间并处理 255 个字符的文件名。
但 ext 的统治并没有持续多久,这主要是由于其原始的时间戳(每个文件只有一个时间戳,而不是我们今天熟悉的 inode 创建、文件访问和文件修改的三个独立时间戳)。仅仅一年后,ext2 就取代了它。
ext2
Rémy 显然很快意识到了 ext 的局限性,因为他在一年后设计了 ext2 作为其替代品。虽然 ext 仍然扎根于“玩具”操作系统,但 ext2 从一开始就被设计为商业级文件系统,其原则与 BSD 的 Berkeley Fast File System 相同。
Ext2 提供了千兆字节的最大文件大小和太字节的文件系统大小,使其在 1990 年代稳居大型文件系统之列。它很快被广泛采用,不仅在 Linux 内核中,最终也在 MINIX 中,而且还通过第三方模块使其可用于 MacOS 和 Windows。
但仍有一些问题需要解决:ext2 文件系统,就像 1990 年代的大多数文件系统一样,如果在向磁盘写入数据时系统崩溃或断电,很容易发生灾难性损坏。随着时间的推移,它们还会因碎片(单个文件存储在多个位置,物理上分散在旋转磁盘周围)而遭受严重的性能损失。
尽管存在这些问题,但 ext2 今天仍然在一些孤立的情况下使用——最常见的是作为便携式 USB 闪存驱动器的格式。
ext3
1998 年,在 ext2 采用六年之后,Stephen Tweedie 宣布他正在致力于显着改进它。这成为了 ext3,它在 2001 年 11 月随内核版本 2.4.15 被纳入主线 Linux。

opensource.com
Ext2 在很大程度上为 Linux 发行版做得很好,但是——像 FAT、FAT32、HFS 和当时的其它文件系统一样——它在断电期间很容易发生灾难性损坏。如果您在向文件系统写入数据时断电,文件系统可能会处于所谓的不一致状态——在这种状态下,有些事情只完成了一半,有些事情只取消了一半。这可能会导致丢失或损坏与正在保存的文件无关的大量文件,甚至导致整个文件系统无法挂载。
Ext3 和 1990 年代末期的其他文件系统(例如 Microsoft 的 NTFS)使用日志来解决这个问题。日志是磁盘上用于存储事务的特殊分配;如果事务完成写入磁盘,则其在日志中的数据将提交到文件系统本身。如果系统在操作提交之前崩溃,则重新启动的系统会将其识别为未完成的事务并将其回滚,就好像它从未发生过一样。这意味着正在处理的文件可能仍然会丢失,但文件系统本身保持一致,所有其他数据都是安全的。Linux 内核实现的 ext3 中提供了三个级别的日志:journal、ordered 和 writeback。
- Journal 是风险最低的模式,在提交到文件系统之前,将数据和元数据都写入日志。这确保了正在写入的文件以及整个文件系统的一致性,但可能会显着降低性能。
- Ordered 是大多数 Linux 发行版中的默认模式;ordered 模式将元数据写入日志,但将数据直接提交到文件系统。顾名思义,这里的操作顺序是严格的:首先,元数据提交到日志;其次,数据写入文件系统,然后才将日志中关联的元数据刷新到文件系统本身。这确保了,在发生崩溃的情况下,与未完成写入关联的元数据仍在日志中,文件系统可以在回滚日志时清理这些未完成的写入。在 ordered 模式下,崩溃可能会导致在崩溃期间主动写入的文件或文件损坏,但文件系统本身——以及未主动写入的文件——保证安全。
- Writeback 是第三种——也是最不安全的——日志模式。在 writeback 模式下,与 ordered 模式一样,元数据会被记录日志,但数据不会。与 ordered 模式不同,元数据和数据都可以以任何对最佳性能有意义的顺序写入。这可以显着提高性能,但安全性要差得多。虽然 writeback 模式仍然保证文件系统本身的安全性,但在崩溃期间或崩溃之前写入的文件很容易丢失或损坏。
与之前的 ext2 一样,ext3 使用 16 位内部寻址。这意味着在 4K 的块大小下,它可以处理的最大文件大小为 2 TiB,最大文件系统大小为 16 TiB。
ext4
Theodore Ts'o(当时是 ext3 的主要开发人员)在 2006 年宣布了 ext4,它在两年后被添加到主线 Linux 中,内核版本为 2.6.28。Ts'o 将 ext4 描述为一种权宜之计技术,它显着扩展了 ext3,但仍然依赖于旧技术。他预计它最终将被真正的下一代文件系统取代。

opensource.com
Ext4 在功能上与 ext3 非常相似,但带来了大型文件系统支持、改进的抗碎片能力、更高的性能和改进的时间戳。
Ext4 与 ext3
Ext3 和 ext4 有一些非常具体的差异,我将在这里重点介绍。
向后兼容性
Ext4 专门设计为尽可能向后兼容 ext3。这不仅允许将 ext3 文件系统就地升级到 ext4;它还允许 ext4 驱动程序在 ext3 模式下自动挂载 ext3 文件系统,从而无需单独维护两个代码库。
大型文件系统
Ext3 文件系统使用 32 位寻址,将其限制为 2 TiB 文件和 16 TiB 文件系统(假设块大小为 4 KiB;某些 ext3 文件系统使用较小的块大小,因此限制更大)。
Ext4 使用 48 位内部寻址,理论上可以在高达 1,000,000 TiB (1 EiB) 的文件系统上分配高达 16 TiB 的文件。早期版本的 ext4 仍然受到一些用户空间实用程序限制,最大文件系统为 16 TiB,但截至 2011 年,e2fsprogs 已直接支持创建 >16TiB 的 ext4 文件系统。例如,Red Hat Enterprise Linux 在合同上仅支持高达 50 TiB 的 ext4 文件系统,并建议 ext4 卷不大于 100 TiB。
分配改进
Ext4 在写入磁盘之前分配存储块的方式方面引入了许多改进,这可以显着提高读取和写入性能。
区段
区段是连续物理块的范围(假设 4 KiB 块大小,最大 128 MiB),可以一次性保留和寻址。使用区段减少了给定文件所需的 inode 数量,并显着减少了碎片,并在写入大型文件时提高了性能。
多块分配
Ext3 为每个新分配的块调用一次其块分配器。当多个写入器同时打开时,这很容易导致严重的碎片。但是,ext4 使用延迟分配,这使其可以合并写入,并在如何为尚未提交的写入分配块方面做出更好的决策。
持久预分配
当为文件预分配磁盘空间时,大多数文件系统必须在创建时将零写入该文件的块。Ext4 允许使用 fallocate()
代替,这保证了空间可用性(并尝试找到连续空间),而无需先写入它。这显着提高了流媒体和数据库应用程序的写入和未来读取写入数据的性能。
延迟分配
这是一个棘手且有争议的功能。延迟分配允许 ext4 等待分配它将写入数据的实际块,直到它准备好将该数据提交到磁盘。(相比之下,ext3 会立即分配块,即使数据仍在流入写入缓存中。)
随着数据在缓存中累积而延迟分配块,这允许文件系统在如何分配这些块方面做出更明智的选择,从而减少碎片(写入以及稍后的读取)并显着提高性能。不幸的是,它增加了在程序员希望确保数据已完全刷新到磁盘时,未专门编写为调用 fsync()
的程序中数据丢失的可能性。
假设一个程序完全重写一个文件
fd=open("file" ,O_TRUNC); write(fd, data); close(fd);
对于旧文件系统,close(fd);
足以保证 file
的内容将被刷新到磁盘。即使从严格意义上讲,写入不是事务性的,如果在文件关闭后发生崩溃,丢失数据的风险也很小。
如果写入不成功(由于程序中的错误、磁盘上的错误、断电等),则文件的原始版本和较新版本都可能丢失或损坏。如果其他进程在文件正在写入时访问该文件,它们将看到损坏的版本。如果其他进程打开了该文件并且不希望其内容发生更改——例如,映射到多个正在运行的程序中的共享库——它们可能会崩溃。
为了避免这些问题,一些程序员避免完全使用 O_TRUNC
。相反,他们可能会写入新文件,关闭它,然后将其重命名为旧文件
fd=open("newfile"); write(fd, data); close(fd); rename("newfile", "file");
在没有延迟分配的文件系统下,这足以避免上述潜在的损坏和崩溃问题:由于 rename()
是原子操作,因此不会被崩溃中断;并且正在运行的程序将继续引用旧的、现在已取消链接的 file
版本,只要它们具有指向它的打开文件句柄。但是,由于 ext4 的延迟分配可能会导致写入延迟和重新排序,因此 rename("newfile","file")
可能会在 newfile
的内容实际写入磁盘之前执行,这再次引发了并行进程获取错误版本 file
的问题。
为了缓解这种情况,Linux 内核(自版本 2.6.30 起)尝试检测这些常见的代码情况,并强制立即分配有问题的文件的空间。这减少了,但不能阻止,数据丢失的可能性——并且它对新文件没有任何帮助。如果您是开发人员,请注意:保证数据立即写入磁盘的唯一方法是适当地调用 fsync()
。
无限子目录
Ext3 限制为总共 32,000 个子目录;ext4 允许无限数量。从内核 2.6.23 开始,ext4 使用 HTree 索引来缓解大量子目录造成的性能损失。
日志校验和
Ext3 不对其日志进行校验和,这对于具有自身缓存(在内核直接控制之外)的磁盘或控制器设备提出了问题。如果具有自身缓存的控制器或磁盘以乱序的方式进行写入,则可能会破坏 ext3 的日志事务顺序,从而可能损坏在崩溃期间(或在崩溃前一段时间内)写入的文件。
理论上,通过使用写入屏障可以解决此问题——挂载文件系统时,您可以在挂载选项中设置 barrier=1
,然后设备将一直到硬件层都遵守 fsync()
调用。实际上,已经发现存储设备和控制器经常不遵守写入屏障——提高性能(和基准测试,在基准测试中,它们与竞争对手进行比较),但开启了本应避免的数据损坏的可能性。
对日志进行校验和允许文件系统在崩溃后的首次挂载时意识到其某些条目无效或顺序错误。这样可以避免回滚部分或乱序日志条目的错误,并进一步损坏文件系统——即使存储设备撒谎并且不遵守屏障。
快速文件系统检查
在 ext3 下,当调用 fsck
时,需要检查整个文件系统——包括已删除和空文件。相比之下,ext4 将未分配的块和 inode 表的节标记为未分配,允许 fsck
完全跳过它们。这大大减少了在大多数文件系统上运行 fsck
的时间,并且自内核 2.6.24 以来已实现。
改进的时间戳
Ext3 提供的时间戳精度为一秒。虽然对于大多数用途来说足够了,但关键任务应用程序通常需要更严格的时间控制。Ext4 通过提供纳秒级的时间戳,使其自身可用于这些企业、科学和关键任务应用程序。
Ext3 文件系统也没有提供足够的位来存储 2038 年 1 月 18 日之后的日期。Ext4 在此处添加了额外的两位,将 Unix 纪元 又延长了 408 年。如果您在公元 2446 年阅读本文,您可能已经迁移到更好的文件系统——但如果您仍然在测量自 UTC 1970 年 1 月 1 日 00:00 以来的时间,我会 posthumously 非常非常高兴。
在线碎片整理
Ext2 和 ext3 都不直接支持在线碎片整理——即在挂载时整理文件系统。Ext2 有一个包含的实用程序 e2defrag,它的名称暗示了它的作用——但它需要在文件系统未挂载时离线运行。(显然,对于根文件系统来说,这尤其成问题。)Ext3 的情况更糟——尽管 ext3 不太可能像 ext2 那样遭受严重的碎片,但针对 ext3 文件系统运行 e2defrag 可能会导致灾难性损坏和数据丢失。
虽然 ext3 最初被认为是“不受碎片影响的”,但采用大规模并行写入进程到同一文件的进程(例如,BitTorrent)清楚地表明情况并非完全如此。一些用户空间黑客和解决方法,例如 Shake,以一种或另一种方式解决了这个问题——但它们比真正的、文件系统感知的、内核级别的碎片整理过程更慢,并且在各种方面都不令人满意。
Ext4 通过 e4defrag 解决了这个问题,这是一个在线的、内核模式的、文件系统感知的、块和区段级别的碎片整理实用程序。
正在进行的 ext4 开发
正如 Monty Python 瘟疫受害者曾经说过的那样,Ext4 “还没有完全死掉!” 尽管 其主要开发人员认为它 只是通往真正 下一代文件系统 的权宜之计,但没有一个可能的候选者(由于技术或许可问题)可以在一段时间内准备好部署为根文件系统。
未来版本的 ext4 仍在开发一些关键功能,包括元数据校验和、一流的配额支持和大型分配块。
元数据校验和
由于 ext4 具有冗余超级块,因此对其中的元数据进行校验和处理,为文件系统提供了一种自行判断主超级块是否损坏以及是否需要使用备用超级块的方法。无需校验和也可以从损坏的超级块中恢复——但用户首先需要意识到它已损坏,然后尝试手动挂载文件系统并使用备用超级块。由于在某些情况下,使用损坏的主超级块以读写方式挂载文件系统可能会造成进一步的损坏,因此即使对于经验丰富的用户来说,这也不是一个充分的解决方案!
与 btrfs 或 zfs 等下一代文件系统提供的极其强大的每块校验和相比,ext4 的元数据校验和是一个相当弱的功能。但它总比没有好得多。
虽然这听起来像是不需要动脑筋的事情——是的,校验和所有东西!——但在事后将校验和添加到文件系统中存在一些重大挑战;有关详细信息,请参阅 设计文档。
一流的配额支持
等等,配额?!我们从 ext2 时代就有了这些!是的,但它们一直是一个事后才想到的东西,而且一直有点糟糕。可能不值得在这里深入探讨其中的复杂细节,但 设计文档 阐述了配额将如何从用户空间移动到内核,并以更正确和更高性能的方式强制执行。
大型分配块
随着时间的推移,那些讨厌的存储系统变得越来越大。随着一些固态硬盘已经使用 8K 硬件块大小,ext4 当前对 4K 块的限制变得越来越具有限制性。更大的存储块可以显着减少碎片并提高性能,但代价是增加了“空闲”空间(当您只需要块的一部分来存储文件或文件的最后一部分时,剩余的空间)。
您可以在 设计文档 中查看详细信息。
ext4 的实际限制
Ext4 是一种健壮、稳定的文件系统,它可能是 2018 年大多数人应该用作根文件系统的文件系统。但它无法处理所有事情。让我们简要地谈谈您不应期望从 ext4 获得的一些东西——现在或将来可能都不会获得。
尽管 ext4 可以寻址高达 1 EiB(相当于 1,000,000 TiB)的数据,但您真的,真的不应该尝试这样做。除了仅仅能够记住更多块的地址之外,还存在规模问题,并且 ext4 现在(并且可能永远不会)在超过 50-100 TiB 数据的情况下很好地扩展。
Ext4 也没有做足够的工作来保证数据的完整性。尽管日志在 ext3 时代是一个巨大的进步,但它并没有涵盖许多常见的数据损坏原因。如果数据在已经存储在磁盘上时被 损坏 ——由于硬件故障、宇宙射线的影响(是的,真的)或数据随时间的简单退化——ext4 无法检测或修复此类损坏。
在最后两项的基础上,ext4 只是一个纯粹的文件系统,而不是存储卷管理器。这意味着即使您有多个磁盘——因此具有奇偶校验或冗余,理论上可以从中恢复损坏的数据——ext4 也无法知道这一点或利用它来发挥您的优势。虽然理论上可以在不丢失自动损坏检测和修复功能的情况下,在离散层中分离文件系统和存储卷管理系统,但这并不是当前存储系统的设计方式,并且会对新设计提出重大挑战。
备用文件系统
在我们开始之前,先提醒一句:对于任何未内置到并直接支持作为您的发行版主线内核一部分的备用文件系统,请非常小心!
即使文件系统是安全的,如果内核升级期间出现问题,将其用作根文件系统也可能非常可怕。如果您对从备用介质启动并在 chroot 中手动且耐心地摆弄内核模块、grub 配置和 DKMS 的想法极其感到自在... 请不要在对您重要的系统上使用超出保留范围的根文件系统。
可能有充分的理由使用您的发行版不直接支持的文件系统——但如果您这样做,我强烈建议您在系统启动并可用之后再挂载它。(例如,您可能有一个 ext4 根文件系统,但将您的大部分数据存储在 zfs 或 btrfs 池中。)
XFS
XFS 大概是 Linux 下非 ext 文件系统的主流。它是一个 64 位、日志文件系统,自 2001 年以来已内置到 Linux 内核中,并为大型文件系统和高并发性(即,大量进程同时写入文件系统)提供高性能。
从 RHEL 7 开始,XFS 成为 Red Hat Enterprise Linux 的默认文件系统。对于家庭或小型企业用户来说,它仍然有一些缺点——最值得注意的是,调整现有 XFS 文件系统的大小非常麻烦,以至于通常创建另一个文件系统并将数据复制过来更有意义。
虽然 XFS 稳定且性能良好,但在它和 ext4 之间没有足够的具体最终用途差异来建议在任何非默认使用它的地方(例如,RHEL7)使用它,除非它解决了您在使用 ext4 时遇到的特定问题,例如 >50 TiB 容量的文件系统。
XFS 在任何方面都不是 ZFS、btrfs 甚至 WAFL(专有 SAN 文件系统)意义上的“下一代”文件系统。像 ext4 一样,它最有可能被认为是通往 更好事物 的权宜之计。
ZFS
ZFS 由 Sun Microsystems 开发,并以 zettabyte 命名——相当于 1 万亿千兆字节——因为它理论上可以寻址如此大的存储系统。
ZFS 是真正的下一代文件系统,提供卷管理(在单个文件系统中寻址多个单独存储设备的能力)、块级加密校验和(允许以极高的准确率检测数据损坏)、自动损坏修复(在冗余或奇偶校验存储可用的情况下)、快速 异步增量复制、内联压缩等等。还有更多。
从 Linux 用户的角度来看,ZFS 最大的问题是许可。ZFS 获得了 CDDL 许可,这是一种半许可许可,与 GPL 冲突。关于在 Linux 内核中使用 ZFS 的含义存在很多争议,意见从“它违反了 GPL”到“它违反了 CDDL”再到“它完全没问题,只是尚未在法庭上进行测试”。最值得注意的是,自 2016 年以来,Canonical 已将其默认内核中的 ZFS 代码内联,但迄今为止尚未受到法律挑战。
目前,即使我自己也是一个非常狂热的 ZFS 用户,我也不建议将 ZFS 用作根 Linux 文件系统。如果您想在 Linux 上利用 ZFS 的优势,请在 ext4 上设置一个小根文件系统,然后将 ZFS 放在您剩余的存储空间上,并将数据、应用程序以及您喜欢的任何内容放在上面——但将根保留在 ext4 上,直到您的发行版明确
btrfs
Btrfs——B-Tree 文件系统的缩写,通常发音为“butter”——由 Chris Mason 在 2007 年在 Oracle 任职期间宣布。Btrfs 的目标与 ZFS 大致相同,提供多设备管理、每块校验和、异步复制、内联压缩和 更多。
截至 2018 年,btrfs 作为标准的单磁盘文件系统相当稳定且可用,但不应依赖它作为卷管理器。在许多常见用例中,与 ext4、XFS 或 ZFS 相比,它存在显着的性能问题,并且其下一代功能——复制、多磁盘拓扑和快照管理——可能非常不稳定,结果从灾难性地降低性能到实际数据丢失不等。
btrfs 的当前状态存在争议;SUSE Enterprise Linux 在 2015 年将其采用为默认文件系统,而 Red Hat 宣布将不再支持从 2017 年的 RHEL 7.4 开始的 btrfs。可能值得注意的是,生产中受支持的 btrfs 部署将其用作单磁盘文件系统,而不是像 ZFS 那样的多磁盘卷管理器——即使是 Synology,在其存储设备上使用 btrfs,但将其分层在传统 Linux 内核 RAID (mdraid) 之上以管理磁盘。
16 条评论