余自庚寅年麦月误入Linux领域,先从事文件系统与IO之技,后及性能基准之术,上诸述之领域,吾虽有知晓,然未能精通,实为憾事!
全部博文(31)
分类: LINUX
2014-02-13 08:27:58
Ext4 的EXT4_IOC_GROUP_ADD命令用于增加块组来扩展文件系统的大小,它通过向文件系统中加入新的块组的方式来扩展文件系统的大小。单次能扩展的最大范围是向文件系统中增加一个完整的块组(128MB,4KB数据块计算)。用户可以通过ioctl函数使用Ext4文件系统的Ioctl命令 EXT4_IOC_GROUP_ADD将用户希望加入的新块组的相关信息struct ext4_new_group_input的地址传给ioctl的第三个参数unsigned int arg(因此用户对当前文件系统必须要有先验知识):
ioctl(fd, EXT4_IOC_GROUP_ADD, arg )
利用Ext4 的EXT4_IOC_GROUP_ADD命令向文件系统中增加新块组的方式来扩展文件系统的大小,它对文件系统的扩展结果受到以下限制:
1. 文件系统层面的限制,这些限制在函数ext4_group_add()中给出:
(1)如果input对应的块组所在的位置为GDT中某个数据块的第一个块组,且文件系统不是以稀疏方式管理元数据,那么不允许增加块组,退出;
(2)若加入块组中包含的数据块个数使得加入后文件系统后数据块个数溢出,则不允许增加块组,退出;
(3)若加入块组后使得加入后文件系统inode个数溢出,则不允许增加块组,退出;
(4)如果文件系统没有预留用于预留块组描述符的inode,或者文件系统预留GDT数据块个数为0,那么系统报无预留GDT数据块无法调整大小的错误。 如果以上都没有问题的话,那么就获取预留块组描述符的inode,准备增加块组。
2.新增加的块组的自身参数导致的限制,由函数verify_group_input()检验这些限制:
(1)新增加的块组的块组号是否有效。新加入的块组号要比系统现有块组号大1,否则块组号无效;
(2)现有文件系统最后一个块组不是完整块组则不允许增加新块组;
(3)新加入的块组中预留数据块超过块组中数据块个数的20%,则不允许增加该新块组;
(4)新加入的块组中没有空闲块,也就是新加入的块组的数据块个数存放的只能是块组的元数据,甚至是仅仅存放元数据的空间都不够,则不允许增加该新块组;
(5)新块组中最后一个数据块的内容读取不了(主要原因是空间不足),则不允许增加该新块组;
(6)新加入的块组指定的块位图不在新块组中,则不允许增加该新块组;
(7)新加入的块组指定的Inode位图不在新块组中,则不允许增加该新块组;
(8)新加入的块组指定的Inode表不在新块组中,则不允许增加该新块组;
(9)新块组的块位图与Inode位图是同一个数据块,则不允许增加该新块组;
(10)新加入的块组指定的块位图在新块组的inode表中,则不允许增加该新块组;
(11)新加入的块组指定的Inode位图在新块组的inode表中,则不允许增加该新块组;
(12)新加入的块组指定的块位图在新块组的GDT中,则不允许增加该新块组;
(13)新加入的块组指定的Inode位图在新块组的GDT中,则不允许增加该新块组;
(14)新加入的块组指定的Inode表在新块组的GDT中,则不允许增加该新块组;
正是由于这些限制的存在,使得增加一个新块组没有像扩展最后一个块组的操作那样简单,所以在增加块组之前必须做够充足的准备,以防止以上限制导致增加块组失败。获取这些信息的最简单方式是使用tune2fs -l 命令:
1. 调用函数setup_new_group_blocks()为新增块组创建并初始化数据块位图、inode位图以及Inode表。该函数的执行步骤如下:
(1)获取块组的第一个数据块号,获取块组中预留GDT数据块的个数,获取块组中块组描述符表使用的数据块的个数,这几个数据对块组的数据块位图有影响;
(2)发起一个文件系统超级块更新的事务,后续(增加块组操作成功后)更新超级块,这个超级块更新事务就是调整文件系统大小,因为调整大小需要修改超级块元数据;
(3)获取s_resize_lock互斥锁用于调整文件系统大小;
(4)检查新增加块组是否有效。块组号不等于当前系统拥有的块组个数,表明已经有其他增加块组的程序运行过或者正在运行,因而不能增加相同的块组,要退出;
(5)将新块组中的块位图置零;
(6)如果新的块组中应当含有超级块的备份,则将块位图的第一位置位,标记为已使用;
(7)拷贝所有的GDT数据块到新块组的备份元数据块中,并在块位图中将相应的数据块设置为1;
(8)将新块组中的预留GDT数据块中全部写0填充,并在块位图中将相应的数据块设置为1;
(9)依次在块位图中将块位图所在的块的位置1、将inode位图所在的块的位置1;
(10)初始化inode表。将新块组中的inode表数据块中全部写0填充,然后将该inode表数据块对应的块位图中的位置1,每次这样处理一个inode表数据块;
(11)设置块位图中的其他块(这些块不存在,因而块位图置1);
(12)标记新块组中块位图数据块为脏;
(13)将新块组中inode位图数据块清零;
(14)设置inode位图中的其他inode(这些inode不存在,因而inode位图置1);
(15)标记新块组中inode位图数据块为脏;
(16)释放s_resize_lock互斥锁;
(17)提交日志,结束新块组内元数据更新事务。
2. 添加新块组后至少要修改超级块以及一个GDT数据块. 如果加入块组超出当前的最后一个GDT数据块,则还需要修改inode(预留GDT的inode)和GDT数据块自身.如果加入的块组具有超级块/GDT的备份,则需要更新的元数据更多;
3. 获取s_resize_lock互斥锁,准备更新文件系统超级块等元数据;
4. 判断文件吸引是否已被其他程序调整,如果文件系统已被其他程序调整,则退出;
5. 准备更新超级块;
6. 更新GDT元数据;
7. 至此,已经建立了新的块组,现在要激活它,使其可用;
(1) 最关键的是 sbi->s_groups_count:只要它还包含旧的值,那么就访问不到新块组.因而,首先要为新块组更新用于新块组的所有的描述符元数据;然后更新总的磁盘数据块数目;然后更新块组数目以激活块组;最后更新空闲数据块数目以使得系统可以使用新的磁盘数据块;
a) 为新块组更新块组描述符块;
b) 为新块组在内存中创建块组描述符信息
(2) 接下来,使得新的数据块和inodes有效。在增加文件系统中的块组数目之前先进行这一步,这样一旦块组激活,它上面的所有数据块和inodes都已生效;
a) 获取文件系统最新数据块个数;
b) 获取文件系统最新inode个数;
(3) 更新超级块的s_groups_count元素,即更新全局文件系统域,增加块组个数;
(4) 更新空闲数据块个数,然后更新空闲inode个数;
(5) 如果文件系统支持Flex_bg特性,则更改新块组所在的flex_bg的信息(修改空闲数据块个数、空闲inode个数);
8. 标记超级块为脏;
9. 释放s_resize_lock互斥锁;
10. 提交日志事务更新超级块;
11. 更新文件系统中超级块的拷贝,然后更新文件系统中GDT的拷贝。