MINIX系统的文件系统
1987年发布(印刷版),教学目的
最多能处理 14 个字符的文件名,并且只能处理 64MB 的存储空间
1992年发布
可以处理高达 2 GB 存储空间并处理 255 个字符的文件名
使用在 Linux 内核中的新虚拟文件系统(VFS)抽象层
缺点: 每个文件仅有一个时间戳,而没有今天所熟悉的 inode中的最近文件访问时间和最新文件修改时间的时间戳。
1993年发布
提供了 GB 级别的最大文件大小和 TB 级别的文件系统大小
利用第三方模块可以使其应用于 MacOS 和 Windows
缺点: 在断电时容易发生灾难性的破坏。如果在将数据写入文件系统时候发生断电,则可能会将其留在所谓 不一致 的状态 -- 事情只完成一半而另一半未完成。这可能导致大量文件丢失或损坏,这些文件与正在保存的文件无关甚至导致整个文件系统无法卸载。随着时间的推移,由于碎片(单个文件存储在多个位置,物理上其分散在旋转的磁盘上),也会遭受了严重的性能损失。
1998年发布
对于data block为4K 大小的 ext3 在最大规格为 16 TiB 的文件系统中可以处理的最大文件大小为 2 TiB。
使用 日志 来解决“断电导致文件损坏或数据不一致”的问题。日志是磁盘上的一种特殊的分配区域,其写入被存储在事务中;如果该事务完成磁盘写入,则日志中的数据将提交给文件系统自身。如果系统在该操作提交前崩溃,则重新启动的系统识别其为未完成的事务而将其进行回滚,就像从未发生过一样。这意味着正在处理的文件可能依然会丢失,但文件系统 本身 保持一致,且其它所有数据都是安全的。
在使用 ext3 文件系统的 Linux 内核中实现了三个级别的日志记录方式: 日志(journal)、 顺序(ordered)和 回写(writeback)。
日志(journal): 最低风险模式,在将数据和元数据提交给文件系统之前将其写入日志。这可以保证正在写入的文件与整个文件系统的一致性,但其显著降低了性能。
顺序(ordered): 大多数 Linux 发行版默认模式;只将元数据写入日志,而直接将数据提交到文件系统。顾名思义,这里的操作顺序是固定的:首先,元数据提交到日志;其次,数据写入文件系统,然后才将日志中关联的元数据更新到文件系统。这确保了在发生崩溃时,那些与未完整写入相关联的元数据仍在日志中,且文件系统可以在回滚日志时清理那些不完整的写入事务。在顺序模式下,系统崩溃可能导致在崩溃期间文件的错误被主动写入,但文件系统它本身 —— 以及未被主动写入的文件 —— 确保是安全的。
回写(write back): 最不安全的日志模式。和顺序模式一样,元数据会被记录到日志,但数据不会。与顺序模式不同的是,元数据和数据都可以以任何有利于获得最佳性能的顺序写入。这可以显著提高性能,但安全性低很多。尽管回写模式仍然保证文件系统本身的安全性,但在崩溃或崩溃之前写入的文件很容易丢失或损坏。
2006年发布,2008年在2.6.28内核版本中被加入主线
兼容 ext3: 允许 ext3 文件系统原地升级到 ext4;也允许 ext4 驱动程序以 ext3 模式自动挂载 ext3 文件系统
大文件系统: ext4 使用 48 位的内部寻址,理论上可以在文件系统上分配高达 16 TiB 大小的文件,其中文件系统最高可达 1000000 TiB(1 EiB)。但是,红帽Linux 官方仅支持最高 50 TiB 的 ext4 文件系统,并建议 ext4 卷不超过 100 TiB。
区段(extent): 是一系列连续的物理块(block),可以一次性保留和寻址。使用区段可以减少 inode 数量,并显著减少碎片并提高写入大文件时的性能。
多块分配: ext3 为每一个新分配的块调用一次块分配器。当多个写入同时打开分配器时,很容易导致严重的碎片。而ext4 使用延迟分配,这允许它合并写入并更好地决定如何为尚未提交的写入分配块。
持久的预分配: 在为文件预分配磁盘空间时,大部分文件系统必须在创建时将零写入该文件的块中。ext4 允许替代使用 fallocate(),它保证了空间的可用性(并试图为它找到连续的空间),而不需要先写入零。这显著提高了写入和将来读取流和数据库应用程序的写入数据的性能。
延迟分配:这是一个有争议的功能。它要求显式调用fsync()才能将数据刷到磁盘上,而调用close(fd)并不能保证做到这一点。
无限制的子目录: ext3 仅限于 32000 个子目录;ext4 允许无限数量的子目录。从 2.6.23 内核版本开始,ext4 使用 HTree 索引来减少大量子目录的性能损失。
日志校验
快速文件系统检查: 在 ext3 下,在 fsck 被调用时会检查整个文件系统 ,包括已删除或空文件。而ext4 标记了 inode 表未分配的块和扇区,从而允许 fsck 完全跳过它们。 它实现于内核 2.6.24.
ext3 提供秒级的时间戳; ext4 提供纳秒级的时间戳。 ext3 文件系统没有提供足够的位来存储 2038 年 1 月 18 日以后的日期;ext4 增加了两个位,将 UNIX纪元扩展到了公元 2446 年。
在线碎片整理: ext2和ext3都不支持在线碎片整理,即在挂载时会对文件系统进行碎片整理。ext4 通过 e4defrag 解决了这个问题,它是一个在线、内核模式、文件系统感知、块和区段级别的碎片整理实用程序。
缺点:1> 一般产品环境ext4文件系统不会超过50-100TiB; 2> 如果磁盘上的数据真的因为物理原因(如宇宙射线)损坏了,比如一个比特坏了,因为没有校验方案,ext4无法检测到这种错误。
目前没有任何备用的文件系统作为主线内核的一部分而内置和直接支持!
如果要使用其他的文件系统,一般的做法是: 使用 ext4 作为根文件系统,但是将大部分数据存储在 ZFS 或 Btrfs 池中。
64位的日志文件系统
2001年开始内置于Linux内核中
大型文件系统,为高度并发提供高性能
RHEL7开始作为RHEL的默认文件系统
由Sun开发
提供卷管理(能够在单个文件系统中处理多个单独的存储设备),块级加密校验和(允许以极高的准确率检测数据损坏),自动损坏修复(其中冗余或奇偶校验存储可用),快速异步增量复制,内联压缩等等。
存在许可证问题(CDDL许可证,与GPL冲突)
由 Chris Mason 于 2007 年在 Oracle 任职期间发布
对标ZFS,如提供多种设备管理、每块校验、异步复制、直列压缩等
比较稳定,但存在严重的性能问题
SUSE Enterprise Linux 在 2015 年采用它作为默认文件系统,而 Red Hat 于 2017 年宣布它从 RHEL 7.4 开始不再支持 Btrfs
即Filesystem in Userspace(FUSE)。这个文件系统将文件系统请求通过 VFS 发送回用户空间。所以,如果需要创建自己的文件系统,那么可以通过使用 FUSE 进行开发。
磁盘分区表主要有两种格式,一种是限制较多的 MBR 分区表,一种是较新且限制较少的 GPT 分区表。
MBR 分区表: 第一个扇区最重要,里面有:主要开机区(Master boot record, MBR)及分区表(partition table), 其中 MBR 占有 446 Bytes,而 partition table 则占有 64 Bytes。
GPT 分区表: 除了分区数量扩充较多之外,支持的磁盘容量也可以超过 2TB。
磁盘文件名:
/dev/sda[1-128]:
基本上,所有实体磁盘的文件名都已经被仿真成 /dev/sd[a-p] 的格式,第一颗磁盘文件名为 /dev/sda. 而分区的文件名若以第一颗磁盘为例,则为 /dev/sda[1-128] .
/dev/vd[a-p]
除了实体磁盘之外,虚拟机的磁盘通常为 /dev/vd[a-p] 的格式。
/dev/VGNAME/LVNAME
若有使用到软件磁盘阵列的话,那还有 /dev/md[0-128] 的磁盘文件名。使用的是 LVM 时,文件名则为 /dev/VGNAME/LVNAME 等格式。
LVM可以将一个分区格式化为多个文件系统,LVM和RAID也能够将多个分区合成一个文件系统。
superblock:记录文件系统的整体信息,包括:inode/block的总量、使用量、剩余量, 以及文件系统的格式与相关信息等;
inode:记录文件的属性,一个文件占用一个inode,同时记录此文件的数据所在的 block 号码;
block:实际记录文件的内容,若文件太大时,会占用多个 block 。
如下图所示,文件系统先格式化出 inode 与 block 的区块,假设某一个文件的属性与权限数据是放置到 inode 4 号(下图较小方格内),而这个 inode 记录了文件数据的实际放置点为 2, 7, 13, 15 这四个 block 号码,此时我们的操作系统就能够据此来排列磁盘的读取顺序,可以一口气将四个 block 内容读出来! 那么数据的读取就如同下图中的箭头所指定的模样了。
图 1. inode/block 数据存取示意图
这种数据存取的方法我们称为索引式文件系统(indexed allocation)。
U盘使用的文件系统一般为 FAT 格式。FAT 这种格式的文件系统并没有 inode 存在,所以 FAT 没有办法将这个文件的所有 block 在一开始就读取出来。每个 block 号码都记录在前一个 block 当中, 他的读取方式有点像下面这样:
图 2. FAT文件系统数据存取示意图
上图中我们假设文件的数据依序写入1->7->4->15号这四个 block 号码中, 但这个文件系统没有办法一口气就知道四个 block 的号码,他得要一个一个的将 block 读出后,才会知道下一个 block 在何处。 如果同一个文件数据写入的 block 分散的太过厉害时,则我们的磁头将无法在磁盘转一圈就读到所有的数据, 因此磁盘就会多转好几圈才能完整的读取到这个文件的内容!
常常会听到所谓的“磁盘重组”吧? 需要磁盘重组的原因就是文件写入的 block 太过于离散了,此时文件读取的性能将会变的很差所致。
图 3. ext2文件系统示意图
在整体的规划当中,文件系统最前面有一个开机扇区(boot sector),这个开机扇区可以安装开机管理程序, 这是个非常重要的设计,因为如此一来我们就能够将不同的开机管理程序安装到个别的文件系统最前端,而不用覆盖整颗磁盘唯一的 MBR, 这样也才能够制作出多重开机的环境啊!
inode 记录的文件数据至少有下面这些:
该文件的存取模式(read/write/excute);
该文件的拥有者与群组(owner/group);
该文件的容量;
该文件创建或状态改变的时间(ctime);
最近一次的读取时间(atime);
最近修改的时间(mtime);
定义文件特性的旗标(flag),如 SetUID...;
该文件真正内容的指向 (pointer);
inode 的数量与大小也是在格式化时就已经固定了,除此之外 inode 还有些什么特色呢?
每个 inode 大小均固定为 128 Bytes (新的 ext4 与 xfs 可设置到 256 Bytes);
每个文件都仅会占用一个 inode 而已;
承上,因此文件系统能够创建的文件数量与 inode 的数量有关;
系统读取文件时需要先找到 inode,并分析 inode 所记录的权限与使用者是否符合,若符合才能够开始实际读取 block 的内容。
Superblock 记录的信息主要有:
block 与 inode 的总量;
未使用与已使用的 inode / block 数量;
block 与 inode 的大小 (block 为 1, 2, 4K,inode 为 128Bytes 或 256Bytes);
filesystem 的挂载时间、最近一次写入数据的时间、最近一次检验磁盘 (fsck) 的时间等文件系统的相关信息;
一个 valid bit 数值,若此文件系统已被挂载,则 valid bit 为 0 ,若未被挂载,则 valid bit 为 1
inode 本身并不记录文件名,文件名的记录是在目录的 block 当中。也就是说目录所占用的 block 内容在记录如下的信息:
图 4. 记载于目录所属的 block 内的文件名与 inode 号码对应示意图
Linux 使用的方式是通过一个称为异步处理 (asynchronously) 的方式:
当系统载入一个文件到内存后,如果该文件没有被更动过,则在内存区段的文件数据会被设置为干净(clean)的。 但如果内存中的文件数据被更改过了(例如用vim去编辑过这个文件),此时该内存中的数据会被设置为脏的 (Dirty)。此时所有的动作都还在内存中执行,并没有写入到磁盘中! 系统会不定时的将内存中设置为“Dirty”的数据写回磁盘,以保持磁盘与内存数据的一致性。 你也可以利用sync指令来手动强迫写入磁盘。
内核通过VFS 的功能来管理所有的 filesystem, 省去我们需要自行设置读取文件系统的定义啊~方便很多!整个 VFS 可以约略用下图来说明:
图 5. VFS 文件系统的示意图
EXT 家族当前较伤脑筋的地方:支持度最广,但格式化超慢! 当TB 以上等级的传统 ext 家族文件系统在格式化的时候,光是系统要预先分配 inode 与 block 就消耗好多好多的时间了。
xfs 文件系统在数据的分布上,主要规划为三个部份,一个数据区 (data section)、一个文件系统活动登录区 (log section)以及一个实时运行区 (realtime section)。
数据区(data section)
数据区与 ext 家族的 block group 类似,分为多个储存区群组 (allocation groups) 来分别放置文件系统所需要的数据。 每个储存区群组都包含了:
1> 整个文件系统的 superblock
2> 剩余空间的管理机制
3> inode的分配与追踪。
与 ext 家族不同的是:
1> inode与 block 都是系统需要用到时, 这才动态配置产生,所以格式化动作超级快!
2> xfs 的 block 与 inode 有多种不同的容量可供设置,block 容量可由 512Bytes ~ 64K 调配
文件系统活动登录区(log seciton)
类似日志区!文件的变化会在这里纪录下来,直到该变化完整的写入到数据区后, 该笔纪录才会被终结。
实时运行区(realtime section)
当有文件要被创建时,xfs 会在这个区段里面找一个到数个的 extent 区块,将文件放置在这个区块内,等到分配完毕后,再写入到 data section 的 inode 与 block 去! 这个 extent 区块的大小得要在格式化的时候就先指定,最小值是 4K 最大可到 1G。一般非磁盘阵列的磁盘默认为 64K 容量,而具有类似磁盘阵列的 stripe 情况下,则建议 extent 设置为与 stripe 一样大较佳。这个 extent 最好不要乱动,因为可能会影响到实体磁盘的性能。
找到一篇前同事在2010年9月的文章,写得透彻而详细。以下仅引用开头以及与ext系列文件系统的对比部分。原文见参考文献3.
作者将Btrfs文件系统的功能或特性分为了4个部分:
首先是扩展性 (scalability) 相关的特性,btrfs 最重要的设计目标是应对大型机器对文件系统的扩展性要求。 Extent,B-Tree 和动态 inode 创建等特性保证了 btrfs 在大型机器上仍有卓越的表现,其整体性能而不会随着系统容量的增加而降低。
其次是数据一致性 (data integrity) 相关的特性。系统面临不可预料的硬件故障,Btrfs 采用 COW 事务技术来保证文件系统的一致性。 btrfs 还支持 checksum,避免了 silent corrupt 的出现。而传统文件系统则无法做到这一点。
第三是和多设备管理相关的特性。 Btrfs 支持创建快照 (snapshot),和克隆 (clone) 。 btrfs 还能够方便的管理多个物理设备,使得传统的卷管理软件变得多余。
最后是其他难以归类的特性。这些特性都是比较先进的技术,能够显著提高文件系统的时间 / 空间性能,包括延迟分配,小文件的存储优化,目录索引等。
原文与ext系列文件系统进行比较的部分,主要集中于“扩展性相关的特性”这一部分。以下是详情。
btrfs 文件系统中所有的 metadata 都由 BTree 管理。使用 BTree 的主要好处在于查找,插入和删除操作都很高效。可以说 BTree 是 btrfs 的核心。
妨碍 ext2/3 扩展性的一个问题来自其目录的组织方式。目录是一种特殊的文件,在 ext2/3 中其内容是一张线性表格。如下图所示:
图 6. ext2 directory
上图展示了一个 ext2 目录文件的内容,该目录中包含四个文件。分别是 "home1","usr","oldfile" 和 "sbin" 。如果需要在该目录中查找目录 sbin,ext2 将遍历前三项,直至找到 sbin 这个字符串为止。这种结构在文件个数有限的情况下是比较直观的设计,但随着目录下文件数的增加,查找文件的时间将线性增长。 2003 年,ext3 设计者开发了目录索引技术,解决了这个问题。目录索引使用的数据结构就是 BTree 。如果同一目录下的文件数超过 2K,inode 中的 i_data 域指向一个特殊的 block 。在该 block 中存储着目录索引 BTree 。 BTree 的查找效率高于线性表,
但为同一个元数据设计两种数据结构总是不太优雅。在文件系统中还有很多其他的元数据,用统一的 BTree 管理是非常简单而优美的设计。
Btrfs 内部所有的元数据都采用 BTree 管理,拥有良好的可扩展性。 btrfs 内部不同的元数据由不同的 Tree 管理。在 superblock 中,有指针指向这些 BTree 的根。如下图所示:
图 7. btrfs btree
FS Tree 管理文件相关的元数据,如 inode,dir 等; Chunk tree 管理设备,每一个磁盘设备都在 Chunk Tree 中有一个 item ; Extent Tree 管理磁盘空间分配,btrfs 每分配一段磁盘空间,便将该磁盘空间的信息插入到 Extent tree 。查询 Extent Tree 将得到空闲的磁盘空间信息; Tree of tree root 保存很多 BTree 的根节点。比如用户每建立一个快照,btrfs 便会创建一个 FS Tree 。为了管理所有的树,btrfs 采用 Tree of tree root 来保存所有树的根节点; checksum Tree 保存数据块的校验和。
现代很多文件系统都采用了 extent 替代 block 来管理磁盘。 Extent 就是一些连续的 block,一个 extent 由起始的 block 加上长度进行定义。Extent 能有效地减少元数据开销。为了进一步理解这个问题,我们还是看看 ext2 中的反面例子。
ext2/3 以 block 为基本单位,将磁盘划分为多个 block 。为了管理磁盘空间,文件系统需要知道哪些 block 是空闲的。 Ext 使用 bitmap 来达到这个目的。 Bitmap 中的每一个 bit 对应磁盘上的一个 block,当相应 block 被分配后,bitmap 中的相应 bit 被设置为 1 。这是很经典也很清晰的一个设计,但不幸的是当磁盘容量变大时,bitmap 自身所占用的空间也将变大。这就导致了扩展性问题,随着存储设备容量的增加,bitmap 这个元数据所占用的空间也随之增加。而人们希望无论磁盘容量如何增加,元数据不应该随之线形增加,这样的设计才具有可扩展性。
下图比较了 block 和 extent 的区别:
图 8. 采用 extent 的 btrfs 和采用 bitmap 的 ext2/3
在 ext2/3 中,10 个 block 需要 10 个 bit 来表示;在 btrfs 中则只需要一个元数据。对于大文件,extent 表现出了更加优异的管理性能。
Extent 是 btrfs 管理磁盘空间的最小单位,由 extent tree 管理。 Btrfs 分配 data 或 metadata 都需要查询 extent tree 以便获得空闲空间的信息。
为了理解动态 inode 分配,还是需要借助 ext2/3 。下面列举了 ext2 文件系统的限制:
ext2的最大文件数量: 文件系统空间 V/8192,比如100GB的文件系统,其中能创建的文件个数最大为13107200个。
下图显示了 ext2 的磁盘布局:
图 9. ext2 layout
在 ext2 中 inode 区是被预先固定分配的,且大小固定,比如一个 100G 的分区中,inode table 区中只能存放 13107200 个 inode,这就意味着不可能创建超过 13107200 个文件,因为每一个文件都必须有一个唯一的 inode 。
为了解决这个问题,必须动态分配 inode 。每一个 inode 只是 BTree 中的一个节点,用户可以无限制地任意插入新的 inode,其物理存储位置是动态分配的。所以 btrfs 没有对文件个数的限制。
鸟哥-认识Linux文件系统
深入理解ext4等文件系统
新一代Linux文件系统btrfs简介
Linux文件系统剖析
Linux文件系统详解
(完)