在之前关于 Linux 文件系统的文章中,我写了Linux 文件系统简介以及一些更高级别的概念,例如一切皆文件。我想更详细地介绍 EXT 文件系统的具体细节,但首先,让我们回答这个问题:“什么是文件系统?” 文件系统包含以下所有内容:
- 数据存储:任何文件系统的首要功能是成为存储和检索数据的结构化场所。
- 命名空间:一种命名和组织方法,提供命名和构建数据的规则。
- 安全模型:一种定义访问权限的方案。
- API:用于操作文件系统对象(如目录和文件)的系统函数调用。
- 实现:实现上述功能的软件。
本文重点关注列表中的第一项,并探讨元数据结构,这些结构为 EXT 文件系统中的数据存储提供逻辑框架。
EXT 文件系统历史
虽然 EXT 文件系统是为 Linux 编写的,但它起源于 Minix 操作系统和 Minix 文件系统,它们比 Linux 早大约五年,于 1987 年首次发布。如果我们回顾 EXT 文件系统家族从 Minix 根基的历史和技术演变,那么理解 EXT4 文件系统就会容易得多。
Minix
在编写原始 Linux 内核时,Linus Torvalds 需要一个文件系统,但当时他不想编写一个。因此,他简单地包含了Minix 文件系统,该文件系统由Andrew S. Tanenbaum编写,是 Tanenbaum 的 Minix 操作系统的一部分。Minix是一个为教育目的而编写的类 Unix 操作系统。它的代码是免费提供的,并且获得了适当的许可,允许 Torvalds 将其包含在他的第一个 Linux 版本中。
Minix 具有以下结构,其中大多数结构位于生成文件系统的分区中
- 安装它的硬盘驱动器的第一个扇区中的引导扇区。引导块包括一个非常小的引导记录和一个分区表。
- 每个分区中的第一个块是超级块,其中包含定义其他文件系统结构并将其定位在分配给该分区的物理磁盘上的元数据。
- 一个 inode 位图块,用于确定哪些 inode 已使用,哪些是空闲的。
- inode,在磁盘上有自己的空间。每个 inode 包含有关一个文件的信息,包括数据块的位置,即属于该文件的区域。
- 一个 区域位图,用于跟踪已用和空闲的数据区域。
- 一个 数据区域,其中实际存储数据。
对于两种类型的位图,一位代表一个特定的数据区域或一个特定的 inode。如果该位为零,则区域或 inode 是空闲且可用的,但如果该位为一,则数据区域或 inode 正在使用中。
什么是 inode?inode 是 index-node 的缩写,是磁盘上的 256 字节块,用于存储有关文件的数据。这包括文件的大小;文件用户和组所有者的用户 ID;文件模式(即,访问权限);以及三个时间戳,用于指定上次访问、上次修改文件以及上次修改 inode 中的数据的时间和日期。
inode 还包含指向文件数据在硬盘驱动器上的位置的数据。在 Minix 和 EXT1-3 文件系统中,这是一个数据区域或块列表。Minix 文件系统 inode 支持九个数据块,七个直接块和两个间接块。如果您想了解更多信息,这里有一个 PDF 文件,其中详细描述了Minix 文件系统结构,以及维基百科上对inode 指针结构的快速概述。
EXT
最初的EXT 文件系统 (Extended) 由Rémy Card编写,并于 1992 年与 Linux 一起发布,以克服 Minix 文件系统的一些大小限制。主要结构更改是文件系统的元数据,它基于 Unix 文件系统 (UFS),也称为 Berkeley Fast File System (FFS)。我发现关于 EXT 文件系统的已发布信息非常少,而且无法验证,显然是因为它存在重大问题,并且很快就被 EXT2 文件系统取代。
EXT2
EXT2 文件系统非常成功。它在 Linux 发行版中使用了多年,并且是我在 1997 年左右开始使用 Red Hat Linux 5.0 时遇到的第一个文件系统。EXT2 文件系统具有与 EXT 文件系统基本相同的元数据结构,但是 EXT2 更具前瞻性,因为在元数据结构之间留出了大量磁盘空间供将来使用。
与 Minix 一样,EXT2 在安装它的硬盘驱动器的第一个扇区中也有一个引导扇区,其中包括一个非常小的引导记录和一个分区表。然后在引导扇区之后有一些保留空间,该空间跨越引导记录和硬盘驱动器上通常位于下一个柱面边界上的第一个分区之间的空间。GRUB2—以及可能的 GRUB1—使用此空间来存储其部分引导代码。
每个 EXT2 分区中的空间都划分为柱面组,以便更精细地管理数据空间。根据我的经验,组大小通常约为 8MB。下图 1 显示了柱面组的基本结构。柱面中的数据分配单元是块,通常大小为 4K。
图 1:EXT 文件系统中柱面组的结构
柱面组中的第一个块是超级块,其中包含定义其他文件系统结构并将其定位在物理磁盘上的元数据。分区中的一些其他组将具有备份超级块,但并非全部。损坏的超级块可以使用磁盘实用程序(如 dd)将备份超级块的内容复制到主超级块来替换。这种情况并不经常发生,但很多年前,我遇到过一个损坏的超级块,并且我能够使用其中一个备份超级块恢复其内容。幸运的是,我很有远见,并使用 dumpe2fs 命令转储了系统上分区的描述符信息。
以下是 dumpe2fs 命令的部分输出。它显示了超级块中包含的元数据,以及有关文件系统中前两个柱面组中每个组的数据。
# dumpe2fs /dev/sda1
Filesystem volume name: boot
Last mounted on: /boot
Filesystem UUID: 79fc5ed8-5bbc-4dfe-8359-b7b36be6eed3
Filesystem magic number: 0xEF53
Filesystem revision #: 1 (dynamic)
Filesystem features: has_journal ext_attr resize_inode dir_index filetype needs_recovery extent 64bit flex_bg sparse_super large_file huge_file dir nlink extra_isize
Filesystem flags: signed_directory_hash
Default mount options: user_xattr acl
Filesystem state: clean
Errors behavior: Continue
Filesystem OS type: Linux
Inode count: 122160
Block count: 488192
Reserved block count: 24409
Free blocks: 376512
Free inodes: 121690
First block: 0
Block size: 4096
Fragment size: 4096
Group descriptor size: 64
Reserved GDT blocks: 238
Blocks per group: 32768
Fragments per group: 32768
Inodes per group: 8144
Inode blocks per group: 509
Flex block group size: 16
Filesystem created: Tue Feb 7 09:33:34 2017
Last mount time: Sat Apr 29 21:42:01 2017
Last write time: Sat Apr 29 21:42:01 2017
Mount count: 25
Maximum mount count: -1
Last checked: Tue Feb 7 09:33:34 2017
Check interval: 0 (<none>)
Lifetime writes: 594 MB
Reserved blocks uid: 0 (user root)
Reserved blocks gid: 0 (group root)
First inode: 11
Inode size: 256
Required extra isize: 32
Desired extra isize: 32
Journal inode: 8
Default directory hash: half_md4
Directory Hash Seed: c780bac9-d4bf-4f35-b695-0fe35e8d2d60
Journal backup: inode blocks
Journal features: journal_64bit
Journal size: 32M
Journal length: 8192
Journal sequence: 0x00000213
Journal start: 0
Group 0: (Blocks 0-32767)
Primary superblock at 0, Group descriptors at 1-1
Reserved GDT blocks at 2-239
Block bitmap at 240 (+240)
Inode bitmap at 255 (+255)
Inode table at 270-778 (+270)
24839 free blocks, 7676 free inodes, 16 directories
Free blocks: 7929-32767
Free inodes: 440, 470-8144
Group 1: (Blocks 32768-65535)
Backup superblock at 32768, Group descriptors at 32769-32769
Reserved GDT blocks at 32770-33007
Block bitmap at 241 (bg #0 + 241)
Inode bitmap at 256 (bg #0 + 256)
Inode table at 779-1287 (bg #0 + 779)
8668 free blocks, 8142 free inodes, 2 directories
Free blocks: 33008-33283, 33332-33791, 33974-33975, 34023-34092, 34094-34104, 34526-34687, 34706-34723, 34817-35374, 35421-35844, 35935-36355, 36357-36863, 38912-39935, 39940-40570, 42620-42623, 42655, 42674-42687, 42721-42751, 42798-42815, 42847, 42875-42879, 42918-42943, 42975, 43000-43007, 43519, 43559-44031, 44042-44543, 44545-45055, 45116-45567, 45601-45631, 45658-45663, 45689-45695, 45736-45759, 45802-45823, 45857-45887, 45919, 45950-45951, 45972-45983, 46014-46015, 46057-46079, 46112-46591, 46921-47103, 49152-49395, 50027-50355, 52237-52255, 52285-52287, 52323-52351, 52383, 52450-52479, 52518-52543, 52584-52607, 52652-52671, 52734-52735, 52743-53247
Free inodes: 8147-16288
Group 2: (Blocks 65536-98303)
Block bitmap at 242 (bg #0 + 242)
Inode bitmap at 257 (bg #0 + 257)
Inode table at 1288-1796 (bg #0 + 1288)
6326 free blocks, 8144 free inodes, 0 directories
Free blocks: 67042-67583, 72201-72994, 80185-80349, 81191-81919, 90112-94207
Free inodes: 16289-24432
Group 3: (Blocks 98304-131071)
<snip>
每个柱面组都有自己的 inode 位图,用于确定该组中哪些 inode 已使用,哪些是空闲的。inode 在每个组中都有自己的空间。每个 inode 包含有关一个文件的信息,包括属于该文件的数据块的位置。块位图跟踪文件系统中已用和空闲的数据块。请注意,上面显示的输出中有很多关于文件系统的数据。在非常大的文件系统上,组数据可以运行到数百页的长度。组元数据包括组中所有空闲数据块的列表。
EXT 文件系统实现了数据分配策略,以确保文件碎片最小化。减少碎片提高了文件系统性能。这些策略在下面的 EXT4 部分中进行了描述。
我在某些情况下遇到的 EXT2 文件系统的最大问题是,崩溃后可能需要数小时才能恢复,因为 fsck(文件系统检查)程序需要很长时间才能找到并纠正文件系统中的任何不一致之处。有一次,我的计算机之一在崩溃后重新启动时,花了超过 28 个小时才完全恢复磁盘—当时磁盘的大小以数百兆字节为单位。
EXT3
EXT3 文件系统的唯一目标是克服 fsck 程序完全恢复因在文件更新操作期间发生的不当关机而损坏的磁盘结构所需的大量时间。EXT 文件系统唯一新增的功能是日志,它预先记录将对文件系统执行的更改。磁盘结构的其余部分与 EXT2 中的结构相同。
与以前的版本直接将数据写入磁盘的数据区域不同,EXT3 中的日志将文件数据及其元数据写入磁盘上的指定区域。一旦数据安全地存储在硬盘驱动器上,就可以将其合并或附加到目标文件,几乎没有丢失数据的机会。当此数据提交到磁盘的数据区域时,日志会更新,以便在系统故障导致日志中的所有数据都未提交之前,文件系统将保持一致状态。在下次启动时,将检查文件系统是否存在不一致之处,然后将日志中剩余的数据提交到磁盘的数据区域,以完成对目标文件的更新。
日志记录确实会降低数据写入性能,但是日志有三个选项可用,允许用户在性能与数据完整性和安全性之间进行选择。我个人偏好安全性,因为我的环境不需要大量的磁盘写入活动。
日志记录功能将故障后检查硬盘驱动器是否存在不一致之处所需的时间从数小时(甚至数天)减少到最多几分钟。多年来,我遇到过许多导致系统崩溃的问题。细节可以再写一篇文章,但足以说明大多数是人为造成的,例如踢掉电源插头。幸运的是,EXT 日志文件系统已将启动恢复时间缩短至两到三分钟。此外,自从我开始使用带有日志记录的 EXT3 以来,我从未遇到过数据丢失的问题。
EXT3 的日志记录功能可以关闭,然后它将作为 EXT2 文件系统运行。日志本身仍然存在,但为空且未使用。只需使用 mount 命令重新挂载分区,并使用 type 参数指定 EXT2 即可。您可能可以从命令行执行此操作,具体取决于您正在使用的文件系统,但是您可以更改 /etc/fstab 文件中的类型说明符,然后重新启动。我强烈建议不要将 EXT3 文件系统作为 EXT2 挂载,因为这会增加数据丢失和延长恢复时间的可能性。
可以使用以下命令通过添加日志将现有的 EXT2 文件系统升级到 EXT3。
tune2fs -j /dev/sda1
其中 /dev/sda1 是驱动器和分区标识符。请务必更改 /etc/fstab 中的文件类型说明符并重新挂载分区或重新启动系统,以使更改生效。
EXT4
EXT4 文件系统主要提高了性能、可靠性和容量。为了提高可靠性,添加了元数据和日志校验和。为了满足各种关键任务的要求,文件系统时间戳得到了改进,增加了低至纳秒的时间间隔。在时间戳字段中添加两个高位位将2038 年问题推迟到 2446 年—至少对于 EXT4 文件系统而言是这样。
在 EXT4 中,数据分配从固定块更改为区段。区段由其在硬盘驱动器上的起始和结束位置描述。这使得可以在单个 inode 指针条目中描述非常长的、物理上连续的文件,这可以显着减少描述较大文件中所有数据位置所需的指针数量。EXT4 中还实施了其他分配策略,以进一步减少碎片。
EXT4 通过将新创建的文件分散在磁盘上,而不是像许多早期 PC 文件系统那样将它们集中在磁盘开头的某个位置来减少碎片。文件分配算法尝试将文件尽可能均匀地分布在柱面组之间,并且在必要时进行碎片整理时,使不连续的文件区段尽可能靠近同一文件中的其他区段,以尽可能减少磁头寻道和旋转延迟。创建新文件或扩展现有文件时,会使用其他策略来预先分配额外的磁盘空间。这有助于确保扩展文件不会自动导致其碎片化。新文件永远不会在现有文件之后立即分配,这也防止了现有文件的碎片化。
除了磁盘上数据的实际位置之外,EXT4 还使用功能性策略,例如延迟分配,以允许文件系统在为其分配空间之前收集所有正在写入磁盘的数据。这可以提高数据空间连续的可能性。
较旧的 EXT 文件系统(例如 EXT2 和 EXT3)可以作为 EXT4 挂载,以获得一些小的性能提升。不幸的是,这需要关闭 EXT4 的一些重要新功能,因此我不建议这样做。
自 Fedora 14 以来,EXT4 一直是 Fedora 的默认文件系统。可以使用 Fedora 文档中描述的过程将 EXT3 文件系统升级到 EXT4,但是由于残留的 EXT3 元数据结构,其性能仍然会受到影响。从 EXT3 升级到 EXT4 的最佳方法是备份目标文件系统分区上的所有数据,使用 mkfs 命令将空的 EXT4 文件系统写入分区,然后从备份还原所有数据。
Inode
如前所述,inode 是 EXT 文件系统中元数据的关键组件。图 2 显示了 inode 和存储在硬盘驱动器上的数据之间的关系。此图是单个文件的目录和 inode,在这种情况下,该文件可能高度碎片化。EXT 文件系统积极工作以减少碎片,因此您不太可能看到具有如此多间接数据块或区段的文件。实际上,正如您将在下面看到的,EXT 文件系统中的碎片非常低,因此大多数 inode 将仅使用一个或两个直接数据指针,而不会使用任何间接指针。
图 2:inode 存储有关每个文件的信息,并使 EXT 文件系统能够定位属于它的所有数据。
inode 不包含文件名。对文件的访问是通过目录条目进行的,目录条目本身就是文件名,并包含指向 inode 的指针。该指针的值是 inode 编号。文件系统中的每个 inode 都有一个唯一的 ID 编号,但是同一计算机(甚至同一硬盘驱动器)上其他文件系统中的 inode 可以具有相同的 inode 编号。这对链接有影响,并且此讨论超出了本文的范围。
inode 包含有关文件的元数据,包括其类型和权限以及其大小。inode 还包含用于 15 个指针的空间,这些指针描述了柱面组数据部分中数据块或区段的位置和长度。十二个指针提供对数据区段的直接访问,并且应该足以处理大多数文件。但是,对于具有大量碎片的文件,有必要在间接节点的形式中具有一些额外的功能。从技术上讲,这些实际上不是 inode,因此为了方便起见,我在此处使用术语“节点”。
间接节点是文件系统中的普通数据块,仅用于描述数据,而不用于存储元数据,因此可以支持超过 15 个条目。例如,4K 的块大小可以支持 512 个 4 字节的间接节点,从而允许单个文件具有 12(直接)+ 512(间接)= 524 个区段。还支持双重和三重间接节点支持,但我们大多数人不太可能遇到需要那么多区段的文件。
数据碎片
对于许多较旧的 PC 文件系统,例如 FAT(及其所有变体)和 NTFS,碎片一直是导致磁盘性能下降的重大问题。碎片整理本身已成为一个行业,各种品牌的碎片整理软件从非常有效到仅略微有效不等。
Linux 的扩展文件系统使用数据分配策略,有助于最大限度地减少硬盘驱动器上文件的碎片,并减少碎片发生时的影响。您可以使用 EXT 文件系统上的 fsck 命令检查文件系统的总碎片。以下示例检查了我的主工作站的主目录,该目录的碎片仅为 1.5%。请务必使用 -n 参数,因为它会阻止 fsck 对扫描的文件系统执行任何操作。
fsck -fn /dev/mapper/vg_01-home
我曾经进行过一些理论计算,以确定磁盘碎片整理是否可能导致任何明显的性能提升。虽然我确实做了一些假设,但我使用的数据磁盘性能数据来自新的 300GB Western Digital 硬盘驱动器,其磁道到磁道寻道时间为 2.0 毫秒。此示例中的文件数量是我在进行计算当天文件系统中存在的实际数量。我确实假设每天会访问相当大量的碎片文件(20%)。
文件总数 | 271,794 |
碎片百分比 | 5.00% |
不连续性 | 13,590 |
每天访问的碎片文件百分比 | 20%(假设) |
额外的寻道次数 | 2,718 |
平均寻道时间 | 10.90 毫秒 |
每天额外的总寻道时间 | 29.63 秒 |
0.49 分钟 | |
磁道到磁道寻道时间 | 2.00 毫秒 |
每天额外的总寻道时间 | 5.44 秒 |
0.091 分钟 |
表 1:碎片对磁盘性能的理论影响
我已经对每天额外的总寻道时间进行了两次计算,一次基于磁道到磁道寻道时间,这对于大多数文件来说是更可能的情况,因为 EXT 文件分配策略,另一次基于平均寻道时间,我认为这将构成一个相当糟糕的情况。
从表 1 中可以看出,碎片对现代 EXT 文件系统的影响,即使是性能适中的硬盘驱动器,对于绝大多数应用程序来说也是微乎其微且可以忽略不计的。您可以将环境中的数字插入到您自己的类似电子表格中,以查看您可能期望的性能影响。这种类型的计算最有可能无法代表实际性能,但它可以提供一些关于碎片及其对系统的理论影响的见解。
我的大多数分区大约有 1.5% 或 1.6% 的碎片;我确实有一个分区有 3.3% 的碎片,但这是一个大型的 128GB 文件系统,其中包含少于 100 个非常大的 ISO 映像文件;多年来,我不得不多次扩展分区,因为它变得太满了。
这并不是说某些应用程序环境不需要更可靠的甚至更少的碎片。EXT 文件系统可以通过知识渊博的管理员进行精心调整,他们可以调整参数以补偿特定的工作负载类型。这可以在创建文件系统时完成,也可以稍后使用 tune2fs 命令完成。应测试、仔细记录和分析每次调整更改的结果,以确保目标环境的最佳性能。在最坏的情况下,如果性能无法提高到所需的水平,则可以使用其他文件系统类型,这些类型可能更适合特定的工作负载。并且请记住,在单个主机系统上混合文件系统类型以匹配每个文件系统上的负载是很常见的。
由于大多数 EXT 文件系统上的碎片量很少,因此无需进行碎片整理。无论如何,EXT 文件系统没有安全的碎片整理工具。有一些工具允许您检查单个文件的碎片或文件系统中剩余可用空间的碎片。有一个工具 e4defrag,它可以根据剩余的可用空间尽可能多地整理文件、目录或文件系统的碎片。顾名思义,它仅适用于 EXT4 文件系统中的文件,并且确实有一些限制。
如果需要对 EXT 文件系统执行完整的碎片整理,则只有一种方法可以可靠地工作。您必须将要整理碎片的文件系统中的所有文件移动到另一个位置,确保在安全复制到另一个位置后删除它们。如果可能,您可以增加文件系统的大小,以帮助减少未来的碎片。然后将文件复制回目标文件系统。即使这样也不能保证所有文件都将被完全碎片整理。
结论
20 多年来,EXT 文件系统一直是许多 Linux 发行版的默认文件系统。它们提供稳定性、大容量、可靠性和性能,同时需要最少的维护。我尝试过其他文件系统,但总是回到 EXT。我使用 Linux 的每个地方都使用 EXT 文件系统,并发现它们适用于其上使用的所有主流负载。毫无疑问,除非有令人信服的理由使用其他文件系统,否则 EXT4 文件系统应该用于大多数 Linux 系统。
9 条评论