概况
参考博客和Linux官方文档。文件系统主要负责数据如何存储在外存,即解决存放位置以及存放大小满足等问题。下面介绍EXT4文件系统的磁盘空间组织结构以及相关主要数据结构设计,这是文件系统设计的核心。
Block
EXT4文件系统最基本的分配单元是block,其由一组连续的sectors组成,大小介于1k-4k之间,为2的整数次幂个sector,连续的block再组成group
Group(未启用flexible group)
EXT4将磁盘划分为一个个block后,将它们组成一个个group。group的大小最大为128M,计算公式如下:
Group_size = (blk_size*8)*blk_size=4096*8*4096= 32768*4096=128M
至于为什么这样算,是因为group的block数量由一个block大小的bitmap决定,一个位代表一个block,所以就有blok_size*8个block在一个group里
其中,第一个group即group0会比较特殊,会含有文件系统的总体控制信息,总体结构如下:
每个功能区详解如下:
- 引导块:前1024字节用作其他特殊作用如引导,仅限group0会保留
- Super Block:大小约为720 bytes,如果block大小为2k或4k,就放在第一个block中。如果block为1k,就放在第二个block中,其他以此类推
- Group Descriptors Blocks(GDBs):每个描述符64 bytes,总占用大小与卷大小有关
- Reserved GDT blocks(RGDTBs):为卷扩容预留空间、
- Data Block Bitmap:用于描述所在group内各个block块的使用状态,一个bit对应一个block,0表示空闲。占用一个block的大小
- Inode Bitmap:用于描述所在group的inode表中的inode使用情况,一个bit对应一个inode。Inode Bitmap在每个group中占用一个block,每个group中的文件数由其决定,如4k的block可以创建4096*8=32768个inode
- Inode Table:存放indoe数据,一个inode大小约为256 bytes,Inode Table默认占用为512个block,则group中的文件数默认为512*4096/256=8192个
- Data Block:剩下的block用于存放普通数据
功能 | 占用块数 | 备注 |
---|---|---|
引导块 | 1024 bytes | 仅group0含有 |
Super Block | 1 block | 仅某些group含有 |
Group Descriptors | many blocks | 仅某些group含有 |
Reserved GDT Blocks | many blocks | 仅某些group含有 |
Data Block Bitmap | 1 block | 所有group均含有 |
inode Bitmap | 1 block | 所有group均含有 |
inode Table | many blocks | 所有group均含有 |
Data Blocks | many more blocks | 所有group均含有 |
Flexible Group
所谓Flexible Block Groups,就是将连续的多个物理block groups绑在一起组成一个逻辑块组,这个逻辑块组就称之为Flex_group(也就是flex_bg)。原来每个块组中都有描述该块组内部的数据块位图,节点位图,节点表,启用这个特性后,每个块组中这些数据就被移动到第一个块组中去了,后面的块组就没有数据块位图,节点位图,节点表了,就剩下数据块了,不过偶尔会插入块组描述符和备份的超级块了,他们会在块组的开头。
在一个Flex_group中,第一个物理block group是存放当前Flex_group的bitmap、inode表。比如Flex_group大小是4,那么group0将(按顺序)存放共superblock、group descriptors、group0~3的data block bitmaps、group0~3的inode bitmap,group0~3的inode tables,group0剩余的空间存放data(普通数据)。剩下的其他group除开头可能有超级块的备份或者块组描述符的备份,其他都作为数据库。
而之前EXT3系统,bitmaps (block bitmap, inode bitmap)及inode table是分散在各个group中分别管理的。如未启动Flexible group的Group示意图所示。
EXT4将管理数据集中起来的好处:
- 减少磁盘寻道操作,将频繁访问的block group资源放在一块连续的磁盘区域,即数据块是连续的
- 可以一次性分配更多的block:以前的group将磁盘划分为众多不连续的空间片段,导致一次分配请求最多只能申请属于同一个group管理的blocks,即最多128M中的空闲block
Meta Block Groups
因为一个块组最大为128M,而一个块组描述符大小为64 bytes,假设全部拿来存块组描述符,也只能存2^27/64=2^21个,所以文件系统最大支持256TB。
启用Meta Block Groups特性后,文件系统会被分为多个元块组集(metablock groups),每个由多个块组组成(连续物理地址单元),组成一个metablock groups的块组描述符都放在一个block中,所以在block为4k大小时,一个元块组会包含2^12/64=64个group块组。Meta Block Groups特性将原本只存放在文件系统的第一个块组的块组描述符放在每个metablock group的第一个group中,第二和最后一个group存放备份块组描述符。
EXT4支持48位block寻址方式,所以最大卷大小为2^48个block,大小为2^48*2^12=2^60=1EB,有2^60/2^27=2^33个group
Meta Block Groups特性的出现使得Ext3和Ext4的磁盘布局有了一定的变化,以往超级块后紧跟的是变长的GDT块,现在是超级块依然决定于是否是3,5,7的幂,而块组描述符集则存储在元块组的第一个,第二个和最后一个块组的开始处,如下图:
引用:Filesystems can either be created using this new block group descriptor layout, or existing filesystems can be resized on-line, and the field s_first_meta_bg in the superblock will indicate the first block group using this new layout.
当增加新块组时,我们不需要给组描述符表预留空间,而是在当前文件系统后面直接添加新的元块组就可以了。
Flexible block group集合metablock group之后,假设flexible block group为16个块组为一单位,metablock group以64个块组为一单位,结构如下:
块和Inode分配策略
数据局部性是衡量文件系统的质量标准之一。在磁盘中,数据块邻近能够减少磁头的移动进而加快磁盘IO。在SSD中,数据邻居可以增加每次传输request的大小,进而减少总的request数量,同时可以尽可能的集中擦写块,从而提高重写速度。所以要尽量减少碎片化。EXT4提供了几个策略来增加数据局部性来减少碎片化:
- multi-block allocator,当文件第一次创建时,分配器假设分配的空间会在不久后被写入,就会投机地分配8k的磁盘空间。文件关闭时,没有用的空间会被释放,如果投机成功就会被写入这个extent中
- delayed allocation,当文件需要很多block进行写入时,文件系统不会立即确定确切的文件空间位置给这次写入,而是等到脏数据缓存真正被写回磁盘的时候,一般是sync()被调用等。确定最后的写入数据,这样可以让文件系统可以为每次写入做最好的位置决策
- 将文件数据块和它的inode放在同一个块组中,减少寻址延迟
- 在同一个目录中的所有inode会被尽量放在和目录所在的同一个块组中
- 将磁盘空间划分为128M的块组,进而保证数据局部性
区块树
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位 */
};
文件数据组织结构如下图所示: