EXT4 文件系统是从 EXT2 和 EXT3 上发展而来的,这里我们来分析它们是如何将一个文件的内容组织起来,从而在磁盘上“拼凑出一个文件”。
EXT2文件组织方式
出于对EXT4文件系统兼容性的考虑,我们尝试在先从EXT2文件系统的文件组织方式说起。
EXT2文件节点定义:http://lxr.free-electrons.com/source/fs/ext2/ext2.h?v=4.1#L297
EXT2 文件节点变量有点多,我们需要关注 i_block[] 这个成员,这个成员是解析 EXT2 文件组织方式的重要入手点。
__le32 i_block[EXT2_N_BLOCKS]; /* Pointers to blocks */
#define EXT2_NDIR_BLOCKS 12
#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
在上面的代码中我们给出了后面五行宏定义的代码,用来解释 EXT2_N_BLOCKS 数值大小的由来,结合图1进行说明会更加清晰。
图 1 EXT2/EXT3文件组织索引结构
i_block[] 占用了60 Bytes 的固定空间,包含15个32位的无符号整形变量,每一个变量记录了一个块号(Block Number, http://lxr.free-electrons.com/source/fs/ext2/ext2.h?v=4.1#L25)用来指示磁盘上的一个Block。最前面[0 - 11]这12个块号都直接标示了一个数据块用来保存数据,因此可以用来记录 12 * 4KB = 48KB 的文件内容。这太小了,放不下多少东西!我们再来看后面的内容。
i_block[12] 表示一级索引,这个一级索引同样记录了一个块号,不过这个块号所指示的Block并不用来保存数据,而是保存了 4KB / 4B = 1024 个直接索引,这些直接索引在用来指示数据块的位置。因此,以及索引可以用来保存 4KB / 4B * 4KB = 4MB 的文件内容。
依此类推,i_block[13] 和 i_block[14] 分别保存了二级索引和三级索引,分别可以用来保存 4GB 和 4TB 的文件内容。也就是说通过这种分级索引映射的方式,EXT2文件系统最大可以支持单个文件的大小是 4TB + 4GB + 4MB + 48KB。
EXT4文件组织方式
分析完EXT2/3的文件组织方式,发现这种方法有不少缺陷:首先,文件存储会消耗很多额外的磁盘空间,每增加4MB的大小就需要额外的4KB来映射文件内容;另外,4TB的单个文件有点小,要知道现在数据库文件动辄就要几十甚至是上百TB。网上说EXT4文件系统支持最大的单个文件大小是64TB,我也没弄明白这是怎么计算出来的,不过从CentOS 7 开始改为默认采用XFS文件系统,能够支持更大的单个文件尺寸。
EXT4文件系统在组织文件时使用了 extents 方法来改进旧的分层索引映射的方式,所谓的extents方法简单地说就是构建extent树来组织文件,并且记录文件使用的某片区域的起始位置而非一一映射。extent树最重要的两个数据结构式 ext4_extent_idx 和 ext4_extent_header,先把代码贴出来吧。
http://lxr.free-electrons.com/source/fs/ext4/ext4_extents.h?v=4.1#L86
struct ext4_extent_idx {
__le32 ei_block; /* index covers logical blocks from 'block' */
__le32 ei_leaf_lo; /* pointer to the physical block of the next *
* level. leaf or next index could be there */
__le16 ei_leaf_hi; /* high 16 bits of physical block */
__u16 ei_unused;
};
struct ext4_extent_header {
__le16 eh_magic; /* probably will support different formats */
__le16 eh_entries; /* number of valid entries */
__le16 eh_max; /* capacity of store in entries */
__le16 eh_depth; /* has tree real underlying blocks? */
__le32 eh_generation; /* generation of the tree */
};