本篇文章将介绍一下Ext4文件系统是如何管理文件内数据。为了兼容之前版本的文件系统,Ext4整体实现比较复杂,本文重点介绍其extent模式。相对于Ext3文件系统,Ext4文件系统在数据存储方面做了一些优化处理,可以针对小文件和大文件有不同的模式,从而提高文件访问的性能。
Ext3文件数据管理方式
在Ext4之前,也就是Ext2和Ext3文件系统中,都是通过间接块的方式存储大文件的数据的。具体如下图所示,文件数据的位置通过inode中i_block成员(15个32为整数成员的数组)指出,其前面12个成员直接指向12个数据块,第13个成员(block12)指向的磁盘块存储的不是文件数据,而是一个指向数据块的指针列表,我们称为一级块,一级间接块最多有block size / 4个指针,block size就是数据块的大小,因为一个索引是4个字节,所以除以4。以此类推,block13通过二级间接块指向具体的数据,而block14则通过三级间接块指向具体的数据。通过这种间接指向的方式实现对大文件的管理。
文件大小 按照上述方式计算下来,最大的文件可以使用的总块数为:12 + (block size/4) + (block size/4)^2 + (block size/4)^3,如果block size大小为4K,则为(12 + 2^10 + 2^20 + 2^30) * 2^12 约等于4T。
Ext4文件数据管理方式
Ext4文件系统有两种数据管理方式,一种是inline的方式,可以将数据存储在inode节点内部,另一种是通过extent的方式,将文件数据组织成为一个B树。当然,为了兼容Ext3及之前的文件系统,Ext4也实现了间接块的方式。
Ext4文件系统文件数据管理参考了现代文件系统的实现方式,也即extent方式。如下图所示,其数据管理的入口仍然是inode节点的i_block成员。差异是此时i_block并非一个32位整数数组,而是一个描述B树结构的数据结构(包含ext4_extent_header和ext4_extent_idx)。在该数据结构中,只有叶子节点中存储的数据包含文件逻辑地址与磁盘物理地址的映射关系。在数据管理中有3个关键的数据结构,分别是ext4_extent_header、ext4_extent_idx和ext4_extent。
ext4_extent_header 该数据结构在一个磁盘逻辑块的最开始的位置,描述该磁盘逻辑块的B树属性,也即该逻辑块中数据的类型(例如是否为叶子节点)和数量。如果eh_depth为0,则该逻辑块中数据项为B树的叶子节点,此时其中存储的是ext4_extent数据结构实例,如果eh_depth>0,则其中存储的是非叶子节点,也即ext4_extent_idx,用于存储指向下一级的索引。
struct ext4_extent_header {
__le16 eh_magic; /* 魔数 */
__le16 eh_entries; /* 可用的项目的数量 */
__le16 eh_max; /* 本区域可以存储最大项目数量 */
__le16 eh_depth; /* 当前层树的深度 */
__le32 eh_generation;
};
ext4_extent_idx 该数据结构是B树中的索引节点,该数据结构用于指向下一级,下一级可以仍然是索引节点,或者叶子节点。
struct ext4_extent_idx {
__le32 ei_block; /* 索引覆盖的逻辑块的数量,以块为单位 */
__le32 ei_leaf_lo; /* 指向下一级物理块的位置,*/
__le16 ei_leaf_hi; /* 物理块位置的高16位 */
__u16 ei_unused;
};
ext4_extent 描述了文件逻辑地址与磁盘物理地址的关系。通过该数据结构,可以找到文件某个偏移的一段数据在磁盘的具体位置。
struct ext4_extent {
__le32 ee_block; /* 该extent覆盖的第一个逻辑地址,以块为单位 */
__le16 ee_len; /* 该extent覆盖的逻辑块的位置 */
__le16 ee_start_hi; /* 物理块的高16位 */
__le32 ee_start_lo; /* 物理块的低16位 */
};
上图是一个示意图,表达了通过若干级索引指向磁盘物理块的关系。实际情况是未必有这么多级,可能比这个多,也可能比这个少。如果文件特别小,可能没有索引层,而是i_block中直接是ext4_extent,直接指向磁盘物理块的位置。
本文仅仅介绍关键的数据结构,如果想详细了解文件数据的管理细节,需要针对代码了解B树的管理实现。后续在另起文章进行介绍。