Ext4文件系统架构分析(一)

Ext4文件系统架构分析(一)

2023年7月27日发(作者:)

Ext4⽂件系统架构分析(⼀)本⽂描述Ext4⽂件系统磁盘布局和元数据的⼀些分析,同样适⽤于Ext3和Ext2⽂件系统,除了它们不⽀持的Ext4的特性外。整个分析分两篇博⽂,分别概述布局和详细介绍各个布局的数据结构及组织寻址⽅式等。感兴趣的看官敬请留意和指导!1. Ext4⽂件系统布局综述⼀个Ext4⽂件系统被分成⼀系列块组。为减少磁盘碎⽚产⽣的性能瓶颈,块分配器尽量保持每个⽂件的数据块都在同⼀个块组中,从⽽减少寻道时间。以4KB的数据块为例,⼀个块组可以包含32768个数据块,也就是128MB。1.1 磁盘布局Ext4⽂件系统的标准磁盘布局如下:Ext4⽂件系统主要使⽤块组0中的超级块和块组描述符表,在其他⼀些特定块组中有超级块和块组描述符表的冗余备份。如果块组中不含冗余备份,那么块组就以数据块位图开始。当格式化磁盘成为Ext4⽂件系统的时候,mkfs将在块组描述符表后⾯分配预留GDT表数据块(“Reserve GDT blocks”)以⽤于将来扩展⽂件系统。紧接在预留GDT表数据块后的是数据块位图与inode表位图,这两个位图分别表⽰本块组内的数据块与inode表的使⽤,inode表数据块之后就是存储⽂件的数据块了。在这些各种各样的块中,超级块、GDT、块位图、Inode位图都是整个⽂件系统的元数据,当然inode表也是⽂件系统的元数据,但是inode表是与⽂件⼀⼀对应的,我更倾向于将inode当做⽂件的元数据,因为在实际格式化⽂件系统的时候,除了已经使⽤的⼗来个外,其他inode表中实际上是没有任何数据的,直到创建了相应的⽂件才会分配inode表,⽂件系统才会在inode表中写⼊与⽂件相关的inode信息。1.2 Flexible 块组(flex_bg)Flexible 块组(flex_bg)是从Ext4开始引⼊的新特性。在⼀个flex_bg中,⼏个块组在⼀起组成⼀个逻辑块组flex_bg。Flex_bg的第⼀个块组中的位图空间和inode表空间扩⼤为包含了flex_bg中其他块组上位图和inode表。⽐如flex_bg包含4个块组,块组0将按序包含超级块、块组描述符表、块组0-3的数据块位图、块组0-3的inode位图、块组0-3的inode表,块组0中的其他空间⽤于存储⽂件数据。同时,其他块组上的数据块位图、inode位图、inode表元数据就不存在了,但是SB和GDT还是存在的。Flexible块组的作⽤是:(1) 聚集元数据,加速元数据载⼊;(2) 使得⼤⽂件在磁盘上尽量连续;即使开启flex_bg特性,超级块和块组描述符的冗余备份仍然位于块组的开头。 Flex_bg中块组的个数由2^ext4_super_block.s_log_groups_per_flex 给出。1.3 元块组(Meta Block Groups)通常,在每个冗余备份的超级块的后⾯是⼀个完整的(包含所有块组描述符的)块组描述符表的备份。这样会产⽣⼀个限制,以Ext4的块组描述符⼤⼩64 Bytes计算,⽂件系统中最多只能有2^21个块组,也就是⽂件系统最⼤为256TB。使⽤元块组Meta Block Groups特性,每个块组都包含该块组⾃⼰的描述符的冗余备份。因此可以创建2^33个块组,也就是⽂件系统最⼤1EB。48位数据块,每个块组128MB,因⽽可以创建2^33个块组。元块组实际上是可以⽤⼀个块组描述符块来进⾏描述的块组集,简单的说,它由⼀系列块组组成,同时这些块组对应的块组描述符存储在⼀个块中。它的出现使得Ext3和Ext4的磁盘布局有了⼀定的变化,以往超级块后紧跟的是变长的GDT块,现在是超级块依然决定于是否是3,5,7的幂,⽽⼀个块组描述符块则存储在元块组的第⼀个,第⼆个和最后⼀个块组的开始处(见下图)在两种情况下我们可能会⽤到这种新布局:

(1) ⽂件系统创建时。⽤户可以指定使⽤这种布局。

(2) 当⽂件系统增长⽽且预留的组描述符块耗尽时。⽬前超级块中有⼀个域s_first_meta_bg⽤于描述第⼀个使⽤元块组的块组。

当增加新块组时,我们不需要给组描述符表预留空间,⽽是在当前⽂件系统后⾯直接添加新的元块组就可以了。

1.4 Lazy 块组初始化如果块组中的相应标志已设置,那么块组中的inode位图和inode表将不被初始化。这样可以减少mkfs时间,如果开启了块组描述符校验和功能,甚⾄连块组都可以不初始化。1.5 特殊inodesExt4预留了⼀些inode做特殊特性使⽤,见下表:表 1 Ext4的特殊inodeInode号 ⽤途0 不存在0号inode1 损坏数据块链表2 根⽬录3 ACL索引4 ACL数据5 Boot loader6 未删除的⽬录7 预留的块组描述符inode8 ⽇志inode11 第⼀个⾮预留的inode,通常是lost+found⽬录1.6 数据块和Inode分配策略在机械磁盘上,保持相关的数据块相互接近可以总的磁头移动时间,因⽽可以加速磁盘IO。在SSD上虽然没有磁头转动,数据局部性可以增加每次IO请求的传输的数据⼤⼩,因⽽减少响应IO请求的传输次数。数据的局部性对单个擦除块的写⼊产⽣影响,可以加速⽂件重写的速度。因⽽尽可能减少碎⽚是必要的。inode和数据块的分配策略可以保证数据的局部集中。以下为inode和数据块的分配策略:(1) 多块分配可以减少磁盘碎⽚。当⽂件初次创建的时候,块分配器预测性地分配8KB的磁盘空间给⽂件。当⽂件关闭的时候,未使⽤的空间当然也就释放了。但是如果推测是正确的,那么⽂件数据将写到⼀个多个块的extent中。(2) 延迟分配。当⼀个⽂件需要更多的数据块引起写操作时,⽂件系统推迟决定新数据在磁盘上的存放位置,直到脏的buffer写到磁盘为⽌。(3) 尽量保持⽂件的数据块与其inode在同⼀个块组中。可以减少磁盘寻道时间.(4) 尽量保持同⼀个⽬录中的所有inodes与⽬录位于同⼀个块组中。这样的假设前提是⼀个⽬录中的⽂件是相关的。(5) 磁盘卷被分成128MB的块组。当在根⽬录中创建⽬录时,inode分配器扫描块组并将新⽬录放到它找到的使⽤负荷最⼩的块组中。这可以保证⽬录在磁盘上的分散性。(6) 即使上述机制⽆效,仍然可以使⽤e4defrag整理碎⽚⽂件。1.7 超级块超级块记录整个⽂件系统的⼤量信息,如数据块个数、inode个数、⽀持的特性、管理信息,等待。如果设置sparse_super特性标志,超级块和块组描述符表的冗余备份仅存放在编号为0或3、5、7的幂次⽅的块组中。如果未设置sparse_super特性标志,冗余备份存在与所有的块组中。以下是2.6.32.18内核中对Ext4超级块的描述:

3.0的内核中,Ext4的超级块加⼊了以下相关元数据:快照、⽂件系统错误处理相关、挂载选项、配额⽂件inode、超级块校验和等,见下图。⽬前没有深⼊研究这些新的元数据。

1.8 块组描述符⼀个块组中,具有固定位置的数据结构是超级块和块组描述符。其他数据结构位置都可以不固定。Flex_bg机制使⽤这个性质将⼏个块组聚合成⼀个flex块组,将flex_bg中所有位图和inode 表放到flex_bg的第⼀个块组中。详细情况可以参考我的上⼀篇Ext4分析博⽂的Flexible 块组(flex_bg)部分。 如果设置了meta_bg特性标志,⼏个块组结合成⼀个meta group。在meta_bg的情况下,在meta group中的第⼀个和最后两个块组中仅包含meta group中的块组的块组描述符。Flex_bg和Meta_bg互斥因⽽不能共同出现。

1.9 数据块位图与inode位图数据块位图跟踪块组中数据块使⽤情况。Inode位图跟踪块组中Inode使⽤情况。每个位图⼀个数据块,每⼀位⽤0或1表⽰⼀个块组中数据块或inode表中inode的使⽤情况。如果⼀个数据块⼤⼩是4KB的话,那⼀个位图块可以表⽰4*1024*8个数据块的使⽤情况,这也是单个块组具有的最⼤数据块个数。这样可以算出⼀个块组⼤⼩是128MB。当然⼀个位图块也可以表⽰4*1024*8个inode的使⽤情况,但是实际上⼀个块组中即使存满了⽂件,也不会⽤到这么多的inode,因为实际系统中基本不会出现所有⽂件⼤⼩都⼩于等于1个数据块⼤⼩的情况。实际上⼀个块组中有多少个inode,在块组描述符中是确定的,在⽂件系统格式化过程中也会看到这个数值,如果没记错的话,⼤概是每4个还是8个数据块分配⼀个inode空间。

1.10 Inode表为了找到与⼀个⽂件相关的信息,必须遍历⽬录⽂件找到与⽂件相关的⽬录项,然后加载inode找到该⽂件的元数据。Ext4在⽬录项中⽤⼀位存储了⽂件类型(通常存储在inode中)的拷贝,这对性能提升有益。Inode表的⼤⼩为ext4_super_block.s_inode_size * ext4_super_block.s_inodes_per_group Bytes。

Ext4的inode的数据结构⼤⼩为156 bytes,但是Ext4的标准inode的⼤⼩是256 bytes。

1.11 查找inode每个块组包含ext4_super_block.s_inodes_per_group个inodes。因为0号inode不存在,可以通过如下的算式计算inode所在的块组:bg=(inode_num -1)/ ext4_super_block.s_inodes_per_groupinode在块组中inode表中的索引index可以通过如下的算式计算:index=(inode_num -1) % ext4_super_block.s_inodes_per_groupinode在inode表中的地址偏移为:offset=index * ext4_super_block.s_inode _size

1.12 inode.i_block0[]s的内容取决于⽂件类型,inode.i_blocks[]使⽤的⽅式不同。⼀般来说,常规⽂件和⽬录⽤inode.i_blocks[]作为⽂件数据块索引信息,特殊⽂件将inode.i_blocks[]⽤于特殊⽤途。常规⽂件⽤inode.i_blocks[]作为⽂件数据块索引信息的三级索引结构会在后⾯直接、间接块地址中详细介绍。

1.13 符号链接如果符号链接的⽬标字符串长度⼩于60字节,那么就将其存储在inode.i_blocks[]中,inode中inode.i_blocks[]占据的⼤⼩刚好是60KB。这⾥要注意到的是,有些⽂件其内容是跟⽂件的元数据放在⼀起的,因⽽就没有了数据块。也就是说不是每个⽂件数据都必然占据着⼀个数据块。

1.14 直接/间接块地址 Ext2/Ext3中数据块映射⽅式如下表

1.15 Extent 树Ext4中⽤extent树代替了逻辑块映射。使⽤extents,⽤⼀个struct ext4_extent结构就可以映射多个数据块,减少元数据块的使⽤。如果设置了flex_bg,甚⾄可以⽤⼀个extent分配⼀个⾮常⼤的⽂件。使⽤extent特性,inode必须设置extents flag。Extents以树的⽅式安排。Extent树的每个节点都以⼀个ext4_extent_header开头,如果节点是内部节点(ext4_extent__depth>0),ext4_extent_header后⾯紧跟的是ext4_extent_header .eh_entries个索引项struct ext4_extent_idx,每个索引项指向该extent树中⼀个包含更多的节点的数据块。如果节点是叶⼦节点(ext4_extent__depth==0),ext4_extent_header后⾯紧跟的是ext4_extent_header .eh_entries个struct ext4_extent数据结构。这些ext4_extent结构指向⽂件数据块。Extent树的根结点存储在inode.i_blocks中,可以存储⽂件的前4个extents⽽不需额外的元数据块。ext4_extent_header:struct ext4_extent_idx:extent树的内部节点,也称为索引节点。ext4_extent:extent树的叶⼦节点。

1.16 Extent树数据块校验和:可能加⼊的新元数据由于extent树的根在inode中,因⽽Extent树数据块指extent树的除根据节点外的所有内部节点和叶⼦节点。Extent的树根节点和叶⼦节点的数据块中存储完xt4_extent_idx和xt4_extent数据结构后⾄少会留下4 ((2^x%12)>=4) bytes的空间。因⽽可以加⼊⼀个结构struct ext4_extent_tail,其中存储32位的校验和。位于inode中的4个extents⽆需校验和,因为inode已经做了校验和。

1.17 ⽬录项Ext4⽂件系统中,⼀个⽬录差不多是⼀个平⾯⽂件,映射任意长度的字符串到⽂件系统中的⼀个inode。⽂件系统中存在多个⽬录项引⽤同⼀个inode——硬链接,这也是硬链接不能链接其他⽂件系统中的⽂件的原因。

1.18 线性(经典)⽬录缺省地,⽬录⽂件中包含⼀个线性的⽬录项数组。未使⽤的⽬录项标记为inode =0。Ext4⽂件系统默认地使⽤struct ext4_dir_entry_2记录⽬录项,除⾮没有设置filetype特性标志。在没有设置filetype特性标志的情况下,使⽤struct ext4_dir_entry记录⽬录项。

1.19 哈希树⽬录线性⽬录项不利于系统性能提升。因⽽从ext3开始加⼊了快速平衡树哈希⽬录项名称。如果在inode中设置EXT4_INDEX_FL标志,⽬录使⽤哈希的B树(hashed btree ,htree)组织和查找⽬录项。为了向后只读兼容Ext2,htree实际上隐藏在⽬录⽂件中。Ext2的惯例,树的根总是在⽬录⽂件的第⼀个数据块中。“.”和“..”⽬录项必须出现在第⼀个数据块的开头。因⽽这两个⽬录项在数据块的开头存放两个struct ext4_dir_entry_2结构,且它们不存到树中。根结点的其他部分包含树的元数据,最后⼀个hash->block map查找到htree中更低的节点。如果dx_ct_levels不为0,那么htree有两层;htree根结点的map指向的数据块是⼀个内部节点,由⼀个minorhash索引。Htree中的内部节点的minor_hash->block map之后包含⼀个零化的(zeroed out) structext4_dir_entry_2找到叶⼦节点。叶⼦节点包括⼀个线性的struct ext4_dir_entry_2数组;所有这些项都哈希到相同的值。如果发⽣溢出,⽬录项简单地溢出到下⼀个叶⼦节点,哈希的least-significant位(内部节点的map)做相应设置。 以htree的⽅式遍历⽬录,计算要查找的⽬录⽂件名称的哈希值,然后使⽤哈希值找到对应的数据块号。如果树是flat,该数据块是⽬录项的线性数组,因⽽可被搜索到;否则,计算⽂件名称的minor hash,并使⽤minor hash查找相应的第三个数据块号。第三个数据块是⽬录项线性数组。Htree的根 :struct dx_rootHtree的内部节点: struct dx_nodeHtree 树根和节点中都存在的 Hash map: struct dx_entry

1.20 扩展属性EA扩展属性(xattrs)通常存储在磁盘上的⼀个单独的数据块中,通过inode.i_file_acl*引⽤。扩展属性的第⼀应⽤是存储⽂件的ACL以及其他安全数据(selinux)。使⽤user_xattr挂载选项就可为⽤户存储以“user”开头的所有扩展属性。这样的限制在3.0内核中已经消失。可以在两个地⽅找到扩展属性:⼀是在⼀个inode项结尾到下⼀个inode项开头的地⽅;⼆是inode.i_file_acl指向 的数据块之中,到3.0为⽌,这个数据块中不包含指向第⼆个扩展属性数据块的指针。理论上可以将每个属性值存储到⼀个单独的数据块中,但是3.0内核为⽌仍然没有这样做。当扩展属性不存储在⼀个inode之后的时候,就会有⼀个头部ext4_xattr_ibody_header扩展属性数据块的开头是ext4_xattr _header紧跟在ext4_xattr_ibody_header或者ext4_xattr _header后⾯的是结构数组 struct ext4_xattr_entry扩展属性值可以紧跟在ext4_xattr_entry项表后⾯。考虑4 bytes对齐。扩展属性值从扩展属性数据块的末尾开始向ext4_xattr _header /ext4_xattr_entry表的⽅向增长。当发⽣溢出时,溢出的部分放到⼀个单独的磁盘数据块上。

1.21 ⽇志(JBD2)⽂件系统在磁盘上保留⼀段⼩的连续区域(默认128MB),作为尽可能需要快速写⼊磁盘的“重要”数据的存放地。⼀旦该重要数据事务完全写到磁盘,将其从磁盘写缓存中刷出。被提交的数据⼀份记录也被写到⽇志。⼀段时间后,⽇志在擦除提交记录前将事务写到它们在磁盘上的最终位置(可能包含⼤量的寻道或者⼤量的读-写-擦除)。从性能⽅⾯考虑,Ext4默认直接将⽂件系统元数据写到⽇志。因⽽不能保证⽂件数据块的⼀致性。 ⽇志的inode为8。⽇志inode的前68 bytes复制了ext4 超级块。⽇志⽂件在⽂件系统中是普通⽂件,但是隐藏不可见。⽇志⽂件通常消耗⼀个完整的块组,可以通过mke2fs将⽇志⽂件放在磁盘的中间。Ext4和Ocfs2都使⽤JBD2。1.21.1 布局⽇志布局⼀个事务以描述符和⼀些数据或者block revocation链表开始。⼀个结束的事务总是以⼀个提交块结束。如果没有提交记录(或者校验和不匹配),事务在⽇志重演的时候将被丢弃。1.21.2 数据块头部⽇志中的每个数据块的开头都是⼀个12 bytes的数据结构 struct journal_header_s1.21.3 超级块⽇志的超级块⽐Ext4的超级块简单。保存在⽇志的超级块中是⽇志的关键数据。⽇志超级块使⽤数据结构struct journal_superblock_s表⽰,⼤⼩为1024 bytes。1.21.4 描述数据块Descriptor BlockDescriptor Block包含⼀个⽇志数据块tags的数组,这些tags描述了⽇志中接下来的数据块的最终位置。⽇志数据块tags具有如下格式:由数据结构struct journal_block_tag_s表⽰,可以是8,12,24或38bytes。1.21.5 数据块Data Block存放的是通过⽇志写到磁盘的数据块。但是如果数据块的前4 bytes与jbd2的魔数匹配,那么这些4 bytes⽤0代替,并且在Descriptor Block中设置escaped。

1.21.6 Revocation Block

Revocation block⽤于记录本事务中的数据块链表,取代任何潜在⽇志中的更陈旧的数据块这样可以加速恢复,因为陈旧的数据块不必写到磁盘。Revocation block使⽤ structjbd2_journal_revoke_header_s结构表⽰ 1.21.7 提交块提交快表明了⼀个事务已完整写到⽇志。⼀旦提交块到达⽇志,存储在该事务中的数据可以写到它们在磁盘中的最终位置。提交快由数据结构struct commit_header表⽰:

发布者:admin,转转请注明出处:http://www.yc00.com/web/1690465609a353327.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信