ext4 通用信息¶
Ext4 是 ext3 文件系统的进阶版本,它引入了可扩展性和可靠性增强功能,以支持大型文件系统(64 位),从而顺应不断增长的磁盘容量和最先进的功能需求。
邮件列表: linux-ext4@vger.kernel.org 网站: http://ext4.wiki.kernel.org
快速使用说明¶
注意:有关 ext4 入门的更多详细信息,可以在 ext4 wiki 网站上找到,网址为: http://ext4.wiki.kernel.org/index.php/Ext4_Howto
e2fsprogs 的最新版本可以在以下位置找到
https://linuxkernel.org.cn/pub/linux/kernel/people/tytso/e2fsprogs/
或者
http://sourceforge.net/project/showfiles.php?group_id=2406
或者从以下地址获取最新的 git 仓库
使用 ext4 文件系统类型创建一个新的文件系统
# mke2fs -t ext4 /dev/hda1
或者将现有的 ext3 文件系统配置为支持区段(extents)
# tune2fs -O extents /dev/hda1
如果文件系统是用 128 字节的 inode 创建的,可以通过以下方式将其转换为使用 256 字节以获得更高的效率
# tune2fs -I 256 /dev/hda1
挂载
# mount -t ext4 /dev/hda1 /wherever
在与其他文件系统进行性能比较时,尝试多种工作负载始终很重要;通常,工作负载参数的微小变化可能会完全改变各个文件系统表现优劣的排名。在与 ext3 进行比较时,请注意 ext4 默认启用写屏障(write barriers),而 ext3 默认不启用写屏障。因此,为了进行公平比较,在 ext3 和 ext4 文件系统中使用 ‘-o barriers=[0|1]’ 挂载选项明确指定是否启用屏障会很有用。在调整 ext3 以获得最佳基准测试数据时,尝试更改数据日志记录模式通常是值得的;对于某些工作负载,‘-o data=writeback’ 可能会更快。(但请注意,在非正常关机的情况下,以 data=writeback 挂载运行可能会将陈旧的数据暴露在最近写入的文件中,在某些情况下这可能是一个安全隐患。)为元数据密集型工作负载配置带有大日志的文件系统也可能会有所帮助。
功能¶
当前可用功能¶
能够使用大于 16TB 的文件系统(e2fsprogs 暂不支持)
区段(extent)格式减少了元数据开销(内存、访问所需的 IO、事务)
面对由于魔数(magics)导致的磁盘损坏时,区段格式更具鲁棒性,
树中具有内部冗余
改进的文件分配(多块分配)
取消由 i_links_count[1] 强加的 32000 个子目录限制
用于 mtime、atime、ctime 和创建时间的纳秒级时间戳
磁盘上的 inode 版本字段(NFSv4、Lustre)
通过 uninit_bg 特性减少了 e2fsck 时间
用于增强鲁棒性和性能的日志校验和
持久化文件预分配(例如用于流媒体、数据库)
通过 flex_bg 特性能将位图和 inode 表打包到更大的虚拟组中
大文件支持
使用通过 flex_bg 的大型虚拟块组进行 inode 分配
延迟分配
大块(最大至页面大小)支持
JBD2 和 ext4 中高效的新有序模式(避免使用缓冲区头部来强制排序)
不区分大小写的文件名查找
基于文件的加密支持(fscrypt)
基于文件的完整性校验支持(fsverity)
[1] 块大小为 1k 的文件系统可能会受到目录哈希树最大深度为二的限制。
不区分大小写的文件名查找¶
不区分大小写的文件名查找功能是以每个目录为基础进行支持的,允许用户在同一个文件系统中混合使用不区分大小写和区分大小写的目录。通过切换空目录的 +F inode 属性来启用它。只有当我们知道文本是如何编码成字节序列时,不区分大小写的字符串匹配操作才有定义。因此,为了启用不区分大小写的目录,文件系统必须具备 casefold 特性,该特性存储了所使用的文件系统范围的编码模型。默认情况下,采用的字符集是 Unicode 的最新版本(撰写本文时为 12.1.0),以 UTF-8 形式编码。比较算法是通过将字符串规范化为 Unicode 定义的规范分解形式(Canonical decomposition form),然后进行逐字节比较来实现的。
大小写不敏感性在磁盘上是保留名称的(name-preserving),这意味着用户空间提供的文件名与实际写入磁盘的内容逐字节匹配。因此,内核使用的 Unicode 规范化格式是一种内部表示,不会暴露给用户空间或磁盘,但有一个重要的例外:在具有 DX 特性的大型不区分大小写目录上使用的磁盘哈希。在 DX 目录上,必须使用文件名的 casefold 版本计算哈希,这意味着所使用的规范化格式实际上会影响目录条目的存储位置。
当我们将文件名从视为不透明的字节序列转变为视为编码字符串时,我们需要处理当程序尝试使用无效名称创建文件时会发生什么。内核中的 Unicode 子系统将这种情况下该做什么的决定留给了文件系统,文件系统通过启用/禁用严格模式来选择其首选行为。当 Ext4 遇到这些字符串之一且文件系统不需要严格模式时,它会退回到将整个字符串视为不透明字节序列的处理方式,这仍然允许用户对该文件进行操作,但不区分大小写的查找将无法工作。
选项¶
挂载 ext4 文件系统时,接受以下选项:(*) == 默认值
- ro
以只读方式挂载文件系统。请注意,即使在“只读”挂载时,ext4 也会重放日志(从而写入分区)。可以使用挂载选项 “ro,noload” 来防止对文件系统的写入。
- journal_checksum
启用日志事务的校验和。这将允许 e2fsck 和内核中的恢复代码检测日志中的损坏。这是一个兼容的更改,旧内核会忽略它。
- journal_async_commit
提交块可以在不等待描述符块的情况下写入磁盘。如果启用,旧内核将无法挂载该设备。这将在内部启用 ‘journal_checksum’。
- journal_path=path, journal_dev=devnum
当外部日志设备的主/次设备号发生变化时,这些选项允许用户指定新的日志位置。日志设备可以通过编码在 devnum 中的新主/次设备号或通过设备的路径来识别。
- norecovery, noload
挂载时不加载日志。请注意,如果文件系统未干净卸载,跳过日志重放将导致文件系统包含不一致性,从而引发各种问题。
- data=journal
所有数据在写入主文件系统之前都会提交到日志中。启用此模式将禁用延迟分配和 O_DIRECT 支持。
- data=ordered (*)
在其元数据提交到日志之前,所有数据都会被强制直接写入主文件系统。
- data=writeback
不保留数据顺序,数据在其元数据提交到日志之后可能会被写入主文件系统。
- commit=nrsec (*)
此设置将运行事务的最大生存时间限制为 ‘nrsec’ 秒。默认值为 5 秒。这意味着如果断电,您最多会丢失最近 5 秒的元数据更改(不过由于有日志记录,您的文件系统不会损坏)。此默认值(或任何较低的值)会损害性能,但对数据安全有利。将其设置为 0 的效果与保留默认值(5 秒)相同。将其设置为非常大的值将提高性能。请注意,由于延迟分配,甚至更旧的数据也可能会在断电时丢失,因为这些数据的回写(writeback)仅在 /proc/sys/vm/dirty_expire_centisecs 中设置的时间之后才开始。
- barrier=<0|1(*)>, barrier(*), nobarrier
这会启用/禁用 jbd 代码中写屏障的使用。barrier=0 禁用,barrier=1 启用。这还要求 IO 栈能够支持屏障,如果 jbd 在屏障写入时出错,它会再次禁用并发出警告。写屏障强制执行正确的日志提交磁盘顺序,使易失性磁盘写缓存可以安全使用,但会牺牲一些性能。如果您的磁盘通过某种方式配备了电池备份,禁用屏障可以安全地提高性能。挂载选项 “barrier” 和 “nobarrier” 也可以用来启用或禁用屏障,以便与其它 ext4 挂载选项保持一致。
- inode_readahead_blks=n
此调整参数控制 ext4 的 inode 表预读算法将预读到缓冲区缓存中的 inode 表块的最大数量。默认值为 32 个块。
- bsddf (*)
使 ‘df’ 的行为类似于 BSD。
- minixdf
使 ‘df’ 的行为类似于 Minix。
- debug
额外的调试信息会被发送到 syslog。
- abort
出于调试目的,模拟调用
ext4_abort()的效果。这通常在重新挂载已经挂载的文件系统时使用。- errors=remount-ro
发生错误时以只读方式重新挂载文件系统。
- errors=continue
发生文件系统错误时继续运行。
- errors=panic
如果发生错误,则引发内核崩溃(Panic)并停止机器。(这些挂载选项会覆盖超级块中指定的错误行为,该行为可以使用 tune2fs 进行配置)
- data_err=ignore(*)
如果文件数据缓冲区中发生错误,仅打印错误消息。
- data_err=abort
如果文件数据缓冲区中发生错误,则中止日志。
- grpid | bsdgroups
新对象具有其父对象的组 ID。
- nogrpid (*) | sysvgroups
新对象具有其创建者的组 ID。
- resgid=n
可以使用保留块的组 ID。
- resuid=n
可以使用保留块的用户 ID。
- sb=
在此位置使用备用超级块。
- quota, noquota, grpquota, usrquota
文件系统会忽略这些选项。它们仅被配额工具(quota tools)用于识别应开启配额的卷。有关更多详细信息,请参阅 quota-tools 软件包中的文档(http://sourceforge.net/projects/linuxquota)。
- jqfmt=<quota type>, usrjquota=<file>, grpjquota=<file>
这些选项向文件系统告知有关配额的详细信息,以便在日志重放期间能够正确更新配额信息。它们替换了上述配额选项。有关更多详细信息,请参阅 quota-tools 软件包中的文档(http://sourceforge.net/projects/linuxquota)。
- stripe=n
mballoc 将尝试用于分配大小和对齐的文件系统块数。对于 RAID5/6 系统,这应该是数据盘数量 * 文件系统块中的 RAID 块大小(chunk size)。
- delalloc (*)
延迟块分配,直到 ext4 写出相关块之前。这允许 ext4 更高效地做出更好的分配决策。
- nodelalloc
禁用延迟分配。当数据从用户空间复制到页面缓存(page cache)时分配块,无论是通过 write(2) 系统调用,还是当首次写入以前未分配的 mmap 映射页面时。
- max_batch_time=usec
ext4 应等待其他文件系统操作与同步写操作一起批处理的最长时间。由于同步写操作将强制提交然后等待 I/O 完成,这成本不高,并且可以带来巨大的吞吐量提升,因此我们等待一小段时间,看看是否有其他事务可以搭同步写的便车。所使用的算法旨在通过测量完成提交事务所需的时间(平均值)来自动调整磁盘速度。将此时间称为“提交时间”。如果事务运行的时间少于提交时间,ext4 将尝试休眠提交时间,以查看其他操作是否会加入该事务。提交时间受 max_batch_time 限制,默认为 15000us (15ms)。通过将 max_batch_time 设置为 0,可以完全关闭此优化。
- min_batch_time=usec
此参数将提交时间(如上所述)设置为至少 min_batch_time。其默认值为零微秒。增加此参数可能会提高非常快的磁盘上多线程同步工作负载的吞吐量,代价是增加延迟。
- journal_ioprio=prio
在提交操作期间由 kjournald2 提交的 I/O 操作应使用的 I/O 优先级(从 0 到 7,其中 0 是最高优先级)。默认为 3,这比默认的 I/O 优先级略高。
- auto_da_alloc(*), noauto_da_alloc
许多有缺陷的应用程序在通过诸如 fd = open(“foo.new”)/write(fd,..)/close(fd)/ rename(“foo.new”, “foo”) 或更糟糕的 fd = open(“foo”, O_TRUNC)/write(fd,..)/close(fd) 等模式替换现有文件时,不使用
fsync()。如果启用了 auto_da_alloc,ext4 将检测通过重命名替换(replace-via-rename)和通过截断替换(replace-via-truncate)的模式,并强制分配任何延迟分配的块,以便在下一次日志提交时(在默认的 data=ordered 模式下),新文件的数据块在rename()操作提交之前被强制写入磁盘。这提供了与 ext3 大致相同的保证级别,并避免了在延迟分配块被强制写入磁盘之前系统崩溃时可能发生的“零长度”问题。- noinit_itable
不在后台初始化任何未初始化的 inode 表块。安装光盘可能会使用此功能,以便安装过程尽可能快地完成;然后,inode 表初始化过程将被推迟到下次卸载文件系统时。
- init_itable=n
惰性 itable 初始化代码将等待将前一个块组的 inode 表清零所花费的毫秒数的 n 倍时间。这在初始化文件系统的 inode 表时最大程度地减少了对系统性能的影响。
- discard, nodiscard(*)
控制当释放块时,ext4 是否应向底层块设备发出 discard/TRIM 命令。这对于 SSD 设备和稀疏/精简配置的 LUN 非常有用,但在完成充分测试之前默认处于关闭状态。
- nouid32
禁用 32 位 UID 和 GID。这是为了与仅存储和期望 16 位值的旧内核进行互操作。
- block_validity(*), noblock_validity
这些选项启用或禁用内核中用于在内部数据结构中跟踪文件系统元数据块的功能。这允许多块分配器和其他例程注意到导致分配与文件系统元数据块重叠的块的错误或损坏的分配位图。
- dioread_lock, dioread_nolock
控制 ext4 是否应使用 DIO 读取锁定。如果指定了 dioread_nolock 选项,ext4 将在缓冲区写入之前分配未初始化的区段,并在 IO 完成后将区段转换为已初始化。这种方法允许 ext4 代码避免使用 inode 互斥锁,从而提高了高速存储上的可扩展性。但是,这不适用于数据日志记录(data journaling),并且 dioread_nolock 选项将被忽略并伴随内核警告。请注意,dioread_nolock 代码路径仅用于基于区段的文件。由于此选项包含的限制,它默认处于关闭状态(即 dioread_lock)。
- max_dir_size_kb=n
这限制了目录的大小,因此任何试图将其扩展超过指定的千字节限制的操作都将导致 ENOSPC 错误。这在内存受限的环境中非常有用,在这些环境中,非常大的目录会导致严重的性能问题甚至引发 OOM(Out Of Memory)killer。(例如,如果只有 512mb 可用内存,一个 176mb 的目录可能会严重拖累系统。)
- i_version
启用 64 位 inode 版本支持。此选项默认关闭。
- dax
使用直接访问(无页面缓存)。请参阅 Direct Access for files。请注意,此选项与 data=journal 不兼容。
- inlinecrypt
如果可能,使用 blk-crypto 框架而不是文件系统层加密来加密/解密加密文件的内容。这允许使用内联加密(inline encryption)硬件。磁盘格式不受影响。有关更多详细信息,请参阅 Inline Encryption。
数据模式¶
有 3 种不同的数据模式
回写(writeback)模式
在 data=writeback 模式下,ext4 完全不记录数据日志。此模式提供的日志记录级别类似于 XFS 和 JFS 在其默认模式下的级别 —— 元数据日志记录。崩溃+恢复可能会导致在崩溃前不久写入的文件中出现不正确的数据。此模式通常会提供最佳的 ext4 性能。
有序(ordered)模式
在 data=ordered 模式下,ext4 官方只记录元数据日志,但它会在逻辑上将与数据更改相关的元数据信息与数据块组合成一个称为事务的单元。当需要将新元数据写入磁盘时,会先写入相关的数据块。通常,此模式的性能比 writeback 略慢,但比 journal 模式显著快。
日志(journal)模式
data=journal 模式提供完整的数据和元数据日志记录。所有新数据首先写入日志,然后写入其最终位置。如果发生崩溃,可以重放日志,使数据和元数据都处于一致状态。除了需要同时从磁盘读取和写入数据的情况外,此模式是最慢的,在那种情况下它的表现优于所有其他模式。启用此模式将禁用延迟分配和 O_DIRECT 支持。
/proc 条目¶
有关已挂载 ext4 文件系统的信息可以在 /proc/fs/ext4 中找到。每个已挂载的文件系统都会在 /proc/fs/ext4 中根据其设备名称拥有一个目录(即 /proc/fs/ext4/hdc 或 /proc/fs/ext4/dm-0)。每个设备目录中的文件如下表所示。
/proc/fs/ext4/<devname> 中的文件
- mb_groups
多块分配器空闲块伙伴缓存的详细信息
/sys 条目¶
有关已挂载 ext4 文件系统的信息可以在 /sys/fs/ext4 中找到。每个已挂载的文件系统都会在 /sys/fs/ext4 中根据其设备名称拥有一个目录(即 /sys/fs/ext4/hdc 或 /sys/fs/ext4/dm-0)。每个设备目录中的文件如下表所示。
/sys/fs/ext4/<devname> 中的文件
(另请参阅 ABI file testing/sysfs-fs-ext4)
- delayed_allocation_blocks
此文件是只读的,显示页面缓存中处于脏状态但尚未在文件系统中分配位置的块数。
- inode_goal
调整参数,用于控制(如果不为零)inode 分配器优先于所有其他分配启发式算法使用的目标 inode。这仅用于调试目的,在生产系统上应为 0。
- inode_readahead_blks
调整参数,控制 ext4 的 inode 表预读算法将预读到缓冲区缓存中的 inode 表块的最大数量。
- lifetime_write_kbytes
此文件是只读的,显示自创建以来写入此文件的数据的千字节数。
- max_writeback_mb_bump
回写代码在转到另一个 inode 之前将尝试写出的最大兆字节数。
- mb_group_prealloc
如果未在 ext4 超级块中设置条带大小,多块分配器会将分配请求向上舍入为此调整参数的倍数
- mb_max_to_scan
多块分配器为寻找最佳区段将搜索的最大区段数。
- mb_min_to_scan
多块分配器为寻找最佳区段将搜索的最小区段数。
- mb_order2_req
调整参数,控制使用伙伴缓存的请求的最小大小(以 2 的幂表示)。
- mb_stats
控制多块分配器是否应收集统计信息,这些信息在卸载期间显示。1 表示收集统计信息,0 表示不收集统计信息。
- mb_stream_req
块数少于此可调整参数的文件将从块组特定的预分配池中分配块,以便将小文件紧密打包在一起。每个大文件都将从其自己的唯一预分配池中分配块。
- session_write_kbytes
此文件是只读的,显示自挂载以来写入此文件系统的千字节数据量。
- reserved_clusters
这是一个读写(RW)文件,包含文件系统中保留的簇数,这些簇将在特定情况下使用,以避免高昂的清零开销、意外的 ENOSPC 或可能的数据丢失。默认值为 2% 或 4096 个簇(以较小者为准),并且可以更改,但绝不能超过文件系统中的簇数。如果在挂载文件系统时保留空间的空间不足,挂载操作_不会_失败。
Ioctls¶
Ext4 实现了各种 ioctl,应用程序可以使用这些 ioctl 来访问 ext4 特定的功能。下表显示了这些 ioctl 的不完全列表。此列表包括真正的 ext4 特定 ioctl(EXT4_IOC_*)以及最初可能是 ext4 特定但现在也被某些其他文件系统支持的 ioctl(FS_IOC_*)。
Ext4 ioctl 表
- FS_IOC_GETFLAGS
获取与 inode 关联的附加属性。ioctl 参数是一个整数位域,其位值在 ext4.h 中描述。
- FS_IOC_SETFLAGS
设置与 inode 关联的附加属性。ioctl 参数是一个整数位域,其位值在 ext4.h 中描述。
- EXT4_IOC_GETVERSION, EXT4_IOC_GETVERSION_OLD
获取为每个 inode 存储的 inode i_generation 编号。i_generation 编号通常仅在创建新 inode 时更改,它对网络文件系统特别有用。此 ioctl 的 ‘_OLD’ 版本是 FS_IOC_GETVERSION 的别名。
- EXT4_IOC_SETVERSION, EXT4_IOC_SETVERSION_OLD
设置为每个 inode 存储的 inode i_generation 编号。此 ioctl 的 ‘_OLD’ 版本是 FS_IOC_GETVERSION 的别名。
- EXT4_IOC_GROUP_EXTEND
此 ioctl 与 resize 挂载选项具有相同的目的。它允许将文件系统调整大小到最后一个现有块组的末尾,进一步调整大小必须通过在线或离线的 resize2fs 完成。该参数指向表示文件系统新块数的无符号长整型数字。
- EXT4_IOC_MOVE_EXT
将块区段从 orig_fd(此 ioctl 指向的文件描述符)移动到 donor_fd(传递给此 ioctl 的参数的 move_extent 结构中指定的文件描述符)。然后,交换 orig_fd 和 donor_fd 之间的 inode 元数据。这对在线碎片整理特别有用,因为分配器有机会更好地分配移动的块,理想情况下分配到一个连续的区段中。
- EXT4_IOC_GROUP_ADD
向现有或新的组描述符块添加新的组描述符。新组描述符由 ext4_new_group_input 结构描述,该结构作为参数传递给此 ioctl。这与 EXT4_IOC_GROUP_EXTEND 结合使用时特别有用,后者允许在线将文件系统调整大小到最后一个现有块组的末尾。这两个 ioctl 结合使用在用户空间在线调整大小工具(例如 resize2fs)中。
- EXT4_IOC_MIGRATE
此 ioctl 对文件系统本身进行操作。它通过遍历原始 inode 的间接块映射并将连续的块范围转换为临时 inode 的 ext4 区段,将 ext3 间接块映射的 inode 转换为(迁移)ext4 区段映射的 inode。然后,交换 inode。从 ext3 文件系统迁移到 ext4 文件系统时,此 ioctl 可能会有帮助,但建议创建全新的 ext4 文件系统并从备份中复制数据。请注意,文件系统必须支持区段此 ioctl 才能工作。
- EXT4_IOC_ALLOC_DA_BLKS
强制分配所有延迟分配的块,以保持应用程序期望的 ext3 行为。请注意,这也将开始触发数据块的写入,但此行为将来可能会更改,因为它不是必需的,并且这样做只是为了简单起见。
- EXT4_IOC_RESIZE_FS
将文件系统调整大小为新的大小。调整大小后的文件系统的块数通过 64 位整数参数传入。内核会分配位图和 inode 表,因此用户空间工具只需传递新的块数。
- EXT4_IOC_SWAP_BOOT
将指定 inode 的 i_blocks 及相关属性(如 i_blocks、i_size、i_flags 等)与 inode EXT4_BOOT_LOADER_INO(#5)进行交换。这通常用于将引导加载程序(boot loader)存储在文件系统的安全部分中,普通用户无法意外更改它。前一个引导加载程序的数据块将与给定的 inode 关联。
参考资料¶
- 内核源码: <file:fs/ext4/>