JFS 文件系统概述及布局分析3
|
块分配映射表用来为整个聚集跟踪分配或释放的磁盘块。由于聚集内所有的文件集共享相同的磁盘块池,在分配或释放磁盘块时,聚集内所有的文件集可使用该分配映射表。 块分配映射表本身是聚集 inode 2 描述的文件。当初始创建聚集时,分配包括聚集空间的映射表数据块。映射表将随着聚集的扩充或紧缩而相应动态地增大或缩小。 块分配映射表跟踪是否每个个别的聚集块被分配还是释放。 映射表的每页长度为 4K。映射表包含三种类型的页:bmap 控制页、dmap 控制页和 dmap 页。 每个 dmap 包含表示每个聚集块的一位。第 i 位表示第 i 个逻辑聚集块的分配状态。它由 struct dmap_t 的 jfs_dmap.h 文件定义。每个 dmap 页包括 8K 的聚集块。 因为块分配映射表可能有许多 dmap 页,它们由 dmap 控制页组织。这些页改进了查找空闲块的大盘区的性能。聚集的大小将决定需要多少页和多少层。至多有三层,它允许的聚集块的最大尺寸是 2(43)。如果不是所有层都需要,块映射表 inode 是每个没有使用层的第一页有“洞”的稀疏文件。 JFS 使用提交策略确保控制数据可靠更新。可靠更新意味着一旦系统出错时,要维持一致的 JFS 结构和资源分配状态。为了保证块分配映射表是一致状态,JFS 维护 dmap 结构中的两张映射表,工作映射表和持续映射表。工作映射表记录当前分配状态。持续映射表记录提交的分配状态,由磁盘上找到的或 JFS 日志或提交的 JFS 事务内的记录描述的分配状态组成。当释放聚集块时,首先更新永久映射表。当分配聚集块时,首先更新工作映射表。位值为 0 表示空闲资源,值为 1 表示已分配资源。 块分配映射表的 dmap 控制页包含与 dmap 结构中树相似的树,除叶层包含 1024 个元素外。dmap 控制页由 struct dmapctl_t 定义。可在 jfs_dmap.h.文件中找到它。 要注意,dmap 结构中的这一字段是一个平面数组,但它表示图中显示的树。树跟踪除最底层之外的每层上连续块的最大号。树的最底层,从树 [85] 到树 [341],包含下面描述的工作映射表的二进制搭档表示法。树的其它层包含来自下一较低层的四个部分的最大数目相连空闲块。 二进制搭档系统用来完成每个摘要树的叶层。通过首先为位图的每个字获得空闲位的最长二进制搭档字符串而形成 dmap 结构的树。字符以 2 的幂编码,-1 用来表示已分配全部。 然后,使用二进制搭档系统完成树的叶。通过取得从指定索引开始、只包括其以 2 的幂显示的搭档的最大数目空闲块,可形成此树。 请注意,只有完全空闲的字才与其完全空闲的搭档组合。组合时,最右搭档变成 -1,以指示它由另一项所表示。 块分配映射表的 dmap 控制页包含与 dmap 结构中树相似的树,除叶层包含 1024 个元素外。这些元素是树 [0] 为紧跟下面的 1024 个映射表页的二进制搭档表示法。对于 L0 页,它是接下来的 1024 个 dmap 页,对于 L1 页,它是接下来的 1024 个 L0 页,而对于 L2 页,它是接下来 1024 个 L1 页。 在块分配映射表的顶部,有映射表控制结构 structdbmap_t 。该结构包含摘要信息,能加快查找比平均空闲空间多的 AG。可在 jfs_dmap.h 中找到该结构。 块分配映射表没有记日志:它能在恢复期间由 logredo 修复,或者由 fsck 重构。在 fsck 或 logredo 后工作和持续映射表,都必需是相同状态。 扩展聚集以增大文件系统 要解决该问题,通常 JFS 为块分配映射表分配的空间多于索引聚集地址空间所需的空间。每个映射表都有额外页空间用于存放位图,如果该页指向另一层摘要树,则该映射表就需额外页存放所需的摘要信息。这种额外空间使得 JFS 可以在必要时将聚集分为更小的单位,以扩大聚集至所需的大小。扩展聚集,需采取以下步骤: 如果现有聚集的空间足以扩展块分配映射表,使其能索引新聚集的所有块,那么,JFS 不做任何特殊处理,将聚集扩展至整个空间。仅当需要考虑聚集将来的扩展时,块分配映射表才需增加额外页。 另一种表示法:二进制编码搭档表示法 struct dmap 定义块分配映射表的最下层。每个 dmap 页包括 8K 的聚集块。 /*
二进制编码搭档系统的每一项都有三个字段: type , size 和 bitmap 。 type 字段表示块空闲、已分配、用位图表示或不由该字段表示 (don"t care)。如果类型是"don"t care"则这些块由左搭档表示, size 字段忽略。如果 type 是位图,则位图字段的 32 位和 32 块一一对应,表示其空闲或已分配。位值 0 表示空闲块,1 表示块已分配。size 是 2 的幂次方,表示该项描述的聚集块的个数。 对于每个全空闲项,如果其相同大小的左搭档也完全空闲,则右搭档设为"don"t care"类型,且右搭档的空间合并入左搭档。当分配块时,仅当搭档分配在同一盘区才合并。必须维护"don"t care"类型,以便 logredo 修正映射表。 结构 dmap 包含一个摘要树。其它每个映射层都包含一个摘要树。摘要树提高了查找空闲块大盘区的性能。摘要信息足以判断 dmap 页是否有足够的空闲位,这样就无需查看 dmap 页,从而可以避免无效搜索。 要注意,dmap 结构中的这一字段是一个平面数组,但它表示图中显示的树。树的每一层都索引最大数目个相邻的块。树的最底层,树[21]至树[84],映射至工作映射表中的二进制编码搭档表示。树的其它层包含来自下一较低层的四个部分的最大数目相连空闲块。块分配映射表的其它层可能有一个相似的树,除了叶节点层有 1024 个元素。这些元素映射至树[0]的二进制编码搭档表示,树[0]指向后面的 dmap 页。 如果要合并的四个都为"don"t care"类型,则合并项大小标记为 -1。这些项的搭档项负责标记正确的状态。 inode 分配 正向查找: 给定 inode 号,找到磁盘上的 inode 。文件查找是一种典型的正向查找。 inode 分配映射表 每个 IAG 大小为 4K,描述磁盘上 128 个物理 inode 盘区。由于每个 inode 盘区包含 32 个 inode ,所以每个 IAG 描述 4096 个 inode 。IAG 可以位于聚集的任意位置。IAG 的所有 inode 盘区位于一个分配组,由此 IAG 和 AG 绑定在一起直至释放所有的 inode 盘区。任意 AG 可以分配空间给一个 inode 盘区,然后该 IAG 就与那个 AG 绑定。IAG 由 struct iag_t 定义(见 jfs_imap.h)。 /* /* summary map: 1 bit per inode extent */ inode 分配映射表最前面 4k 大小的页是控制页。该页包含 inode 分配映射表的摘要信息。 dinomap_t 结构的定义见 jfs_imap.h。 逻辑上,inode 分配映射表是动态可扩展的 IAG 结构的数组: struct iag inode_allocation_map [ 1.. N ]; JFS 使用提交策略确保控制数据可靠更新。可靠更新意味着一旦系统出错时,要维持一致的 JFS 结构和资源分配状态。为确保 inode 分配映射表的一致性,每个 IAG 都同时维护两个映射表,工作映射表和持续映射表。工作映射表记录当前分配状态。持续磁盘记录递交的分配状态,包括磁盘上记录的分配状态或是 JFS 日志中提交的 JFS 事务记录描述的分配状态。 映射表中的每一位记录相应 inode 是空闲还是已分配的。位值 0 表示 inode 空闲,1 表示 inode 已分配。IAG 的每一个控制区内都有一个摘要映射表,用以提高查找空闲 inode 的性能。摘要映射表映射到 IAG 的工作位图。摘要映射表使用一位映射工作映射表的相邻 32 位。每一位表示相应的 inode 可用(0),或相应的 inode 不可用(1)。(如果没有已分配的盘区,那么该 inode 摘要映射位为 1,表明没有可用的 inode ,) IAG 还包含 inode 盘区描述符,该描述符描述相应的 inode 盘区。每个 IAG 有 128 个描述符。IAG 的每个控制区内都有一个摘要映射表,用于改进空闲 inode 盘区查找的性能。摘要映射表用一位映射一个 inode 盘区。0 表示空闲的 inode 盘区,1 表示已分配的 inode 盘区。 如果给定 inode 号,用 inode 分配映射表,通过以下步骤,可以找到 inode 的物理位置: 1. 找到描述该 inode 的 IAG。需要找到 inode 分配映射表在 B+ 树中的键(字节偏移量)。 iag key = ((Inode number / Inodes per iag) * Inodes per iag) + 4096 (EQ 1) 2. 查找已找到的 IAG 中引用的 inode 。这可用于在 IAG 工作映射表和持续映射表中索引。 iag inode index = (Inode number) mod (Inodes per iag) (EQ 2) 3. 查找 IAG 中的 inode 盘区描述符,该描述符描述包含指定 inode 的 inode 盘区。 inode extent descriptor = (iag inode index) / (Inode per inode extent) (EQ 3) 4. 要找的 inode 位于找到的 inode 盘区内、适当的偏移量处。 inode offset = ((iag inode index) mod (Inodes per inode extent) inode 分配映射表本身由聚集 inode 表中文件集的分配映射表 inode 描述。 通过前面介绍的公式,将 inode 号,#9157,转换成一个偏移量: iag key = ((inum / num_inodes_per_iag ) * (num_inodes_per_iag )) + 4096 为简化 JFS 维护命令,及便于理解布局策略的动态性,inode 分配映射文件盘区的大小总为 4KB。 当新文件集创建时,必须分配一个 IAG 以及第一个 inode 盘区,以处理文件集的元数据文件。(即,保留的 inode 和根目录 inode )。 AG 空闲 inode 列表 AG 列表从表头开始插入。当分配新的 inode 盘区,或当因盘区占满而删除一个 inode 时,会有插入操作。当一个 IAG 所有的 inode 盘区都满时,从列表中删除该 IAG。 注意 AG3 中的 IAG 没有任何相应的 inode 盘区可供分配。所以,这些 inode 未在 AG 空闲 inode 列表中表示。 此表没有记日志;但可以在恢复时由 logredo 恢复,或由 fsck 重建。AG 空闲列表结构定义是 struct dinomap_t,见 jfs_imap.h 文件。 当盘区中所有的 inode 都已删除,则释放该 inode 盘区的磁盘块。当 IAG 的一个 inode 盘区被删除时,该 IAG 插至所属的 AG 空闲 inode 盘区列表的表头。当创建新的 IAG,并分配一个 inode 盘区时,该 IAG 号插至 AG 空闲 inode 盘区列表的表头。当 IAG 的所有 inode 盘区分配完时,从列表中删除该 IAG。当释放 IAG 的所有 inode 盘区时,从列表中删除该 IAG 同时加到IAG 空闲列表中。当 AG 需要分配 inode 盘区时, 则使用 AG 空闲列表头上的第一项。 此表没有记日志;但可以在恢复时由 logredo 恢复,或由 fsck 重建。 表的结构定义见 jfs_imap.h, struct dinomap_t . IAG 空闲列表 对于聚集 IAG 空闲列表头是聚集自用 inode 的一个字段。对于每个文件集 IAG 空闲列表头是文件集分配映射表 inode 的一个字段。该列表没记日志;但可在恢复时由 logredo 修复,或由 fsck 重建。 IAG 空闲列表的结构定义 struct inomap_t 在文件 jfs_dinode.h 中。 下一个空闲 IAG 文件集分配 inode 文件 符号链接 目录 目录 inode 的 di_size 字段仅表示目录 B+ 树的叶子页。如果 inode 中包含目录的叶节点,则 di_size 字段为256。 目录中没有特定项表示自身 (".") 和父目录 ("..")。而在 inode 中表示。自身就是目录自己的 inode 号。父目录是 inode 中的特殊字段, idotdot,struct dtroot_t ,见文件 jfs_dtree.h。 目录 inode 包含 B+ 树的根,处理方法和一般文件类似。只是目录 B+ 树以名为键。目录 B+ 树的叶节点包含目录项,且以目录项的全名作为键值。目录 B+ 树最下层内部节点使用后缀压缩。其它内部节点采用相同的压缩后缀。后缀压缩将名字缩至最短,正好足以区分当前目录项和前一目录项。 由于 B+ 树项的大小是可变的,JFS 需要处理这些项的方案。JFS 想要避免在删除一项时引起的项移动,平均一项有2K的数据。 B+ 树节点的内容: 固定个数的目录槽,个数取决于节点的大小。这些槽用于存储目录槽数组和目录项或路由项。目录槽的大小总是 32 字节。固定大小的目录槽使得 JFS 在删除目录项不必移动,从而还避免了内部碎片。 初始目录项存储在目录嵌入数据区中。 访问控制列表 (ACL) 虽然在磁盘上和内存中 ACL 的表示方式没有规定,但从 DFS 外部所看到的“外部”表示是固定的。ACL 大小的唯一限制是其外部表示必须适合 8192 字节大小的 dfs_acl 结构。 任意 JFS 对象都可有一个管理该对象存取的 ACL;这种 ACL 称为常规 ACL。目录对象在创建时可能用到两个关联的可选 ACL;初始目录 ACL和初始文件 ACL。初始 ACL 的作用范围是目录中的所有文件。 ACL 体系结构未指定 ACL 的存储方式,但建议 ACL 有字段标识或命名其辅助对象,这样通过简单的等同性检查就可以检测到文件集中的共享关系。因此,JFS 在每个文件集中用一个文件(ACL 文件)存储文件集的 ACL;文件集 inode 1 就是 ACL 文件。文件集中的每个 inode 在 ACL 文件中存放一个索引。 ACL 文件需要一个存储 ACL 空闲区域的位图。ACL 文件有一个 4K 大小的位图,标识 8M 的 ACL 项,如有必要可扩增。位图中的一位代表 256 字节连续磁盘空间;位图不描述自身的状态。 ACL 文件的数据未日志化。 扩展属性(EA) EA 可以存放在 inode 内,或存放在单独盘区内。EA 描述符的标志字段指示存储的方式。由于此空间也可用于存放文件 xtree 附加的 xad 项,所以 inode 的 di_mode 字段指明该空间是否可用。如果该字段值为 INLINEEA,则表明空间可用。 如果 EA 存于 inode 内,则忽略 EA 描述符的 offset 和 length 字段。EA 描述符的大小表示数据的字节数。 如果 EA 存于盘区内,EA 描述符将描述该盘区。JFS 不希望 EA 数据太大,所以 JFS 不支持每个 inode 有多于一个盘区的 EA 数据。 EA 项包括 EA 名称和其值。要访问某个 EA,JFS 只是线性搜索 EA 数据。 EA 数据未日志化,但它是写同步的(即数据不是旧数据,就是新数据,但绝不可能是部分更新的数据)。JFS 在日志中记录 EA 数据的位置。嵌入 EA 数据是日志化的。 流 磁盘 inode 的四部分的第二部分有一个字段描述流描述符。由于附加到一个对象的流数目是可变的,所以流描述符是一个 inode 号,以允许流增加或缩减。流描述符 inode 指向的数据称为流列表。 流没有关联的扩展属性,所以从不使用流的 inode 四部分的最后一个部分-扩展属性。实际上该部分用于附加的流项。B+ 树的数据如同目录项。每个流都有自己的 inode ,它们依次记录流数据存放的数据块地址。 结束语 JFS 小组最重要的目标是创建可靠的,高性能的文件系统。本文讨论了 JFS 磁盘布局结构,以及实现可伸缩性、可靠性和高性能的机制。同时详细探讨了 JFS 如何在整个文件系统中使用 B+ 树提高文件系统操作。 源代码网供稿. |
