分类: LINUX
2014-10-18 15:41:52
Ext4 的EXT4_IOC_GROUP_EXTEND命令用于扩展文件系统最后一个块组的大小,它通过扩展文件系统最后一个块组的方式来扩展文件系统的大小。能扩展的最大范围是将文件系统的最后一个块组扩展为一个完整的块组(128MB)。用户可以通过ioctl函数使用Ext4文件系统的Ioctl命令 EXT4_IOC_GROUP_EXTEND 将用户希望扩展后最终的文件系统所具有的(文件系统)数据块的个数给ioctl的第三个参数unsigned int arg:
ioctl(fd, EXT4_IOC_GROUP_EXTEND, arg )
利用Ext4 的EXT4_IOC_GROUP_EXTEND命令扩展文件系统最后一个块组的大小的方式来扩展文件系统的大小,它对文件系统的扩展结果受到以下限制:
(1)文件系统所在分区必须要有多余的空闲空间,且这些空间不属于当前文件系统;
(2)该ioctl命令不用于缩小文件系统。也就是要求最终扩展到的数据块个数不能少于文件系统现有数据块个数;
(3)如果用户设置希望扩展后最终的文件系统所具有的(文件系统)数据块的个数(ioctl的第三个参数arg)为0或者与文件系统现有数据块个数相同,则该命令不做任何事而直接返回。也就是既不对文件系统进行扩展,也不报错。
(4)用户指定扩展到的数据块数目不超过最后一个块组成为完整块组时文件系统具有的数据块数目时,扩展能够正常进行;
(5)用户指定扩展到的数据块数目超过最后一个块组成为完整块组时文件系统所具有的数据块个数时,扩展能正常进行,但实际只扩展到使最后一个块组成为完整块组为止;
(6) 用户指定扩展到的数据块数目使得文件系统的大小超过16TB(2^32个数据块)时,则不进行扩展。
(1) 首先确定用户对文件具有访问权限且对文件所在的文件系统具有写权限(因为设置inode标志成功会引起元数据更新操作)。并将用户指定扩展到的数据块数目(ioctl命令的第三个参数)拷贝到内核空间。
(2) 确认进程对文件系统具有写权限;
(3) 调用ext4_group_extend()函数扩展文件系统的最后一个块组。如果文件系统支持日志,那么要在日志中建立扩展文件系统最后一个块组的日志事务,并设置barrier(作用如同字面意思,barrier之后提交的操作必须要在barrier释放之后才能处理)。日志事务处理完毕后刷新日志并释放设置的barrier。扩展文件系统最后一个块组函数ext4_group_extend()主要执行流程如下:
a) 获取文件系统现有数据块个数以及块组个数;
b) 检查用户设置的文件系统最终扩展到的数据块的数目是否有效。是否有效的依据参见上一小节的(2)、(3)、(4)、(5)、(6)小点的描述;
c) 因为扩展的是最后一个数据块组,所以先要获取当前文件系统的最后一个块组的块组号和最后一个数据块在块组中的偏移offset;
d) 接着要计算扩展最后一个块组成功,实际需要在最后一个块组中增加的数据块的个数。这个时候会按以下步骤处理:
i. 如果步骤c)计算的当前文件系统最后一个数据块的偏移是0,也就意味着最后一个块组已是完整块组,那么无法使用扩展最后一个块组的方法扩展文件系统了,因而报错退出;
ii. 如果步骤c)计算的当前文件系统最后一个数据块的偏移不为0,那么实际最多可加入的数据块的个数为EXT4_BLOCKS_PER_GROUP(sb) – offset,即一个完整块组包含的数据块的个数减去当前文件系统最后一个数据块的在块组中的偏移;
iii. 如果加入的数据块的个数过多,导致数据块个数溢出,则告警退出。这种情况出现在当前的文件系统已经相当大了,差不多是系统能够支持的最大文件系统的情况,此时虽然最后一个块组未完整,但是如果补充使其完整,那么就会导致文件系统数据块个数溢出。(但是这种情况永远不会出现,因为传入的参数不超过32位);
iv. 如果加入的数据块的个数小于允许加入的最大个数,则加入实际的数据块个数;
v. 如果加入的数据块的个数大于允许加入的最大个数,则仅加入允许加入的最大数据块个数;
e) 确定了最终增加的数据块的个数之后呢,接着就要看文件系统所在的设备(分区)大小是否满足要求了。如果设备剩余空间不够,那么也只能告警退出了;
f) 接着发起一个日志事务,这个事务要修改三个数据块(超级块、数据块位图、块组描述符)的内容;
g) 获取超级块sb->s_resize_lock互斥锁。如果获取到互斥锁后,发现当前文件系统的数据块个数已经发生改变,则意味着已经有其他调整大小的程序运行过,那么先前计算的加入数据块的个数什么的都已经无效了,只好打印提示信息退出了,同时释放获取的互斥锁;
h) 获取超级块sb->s_resize_lock互斥锁后发现文件系统数据块个数没有改变,那么就可以开始执行扩展大小的核心操作了。首先修改与超级块相关的日志事务(当然如果修改出错也会告警退出同时释放互斥锁),接着统计扩展后文件系统的数据块的个数,然后标记超级块为脏。这样就完成了超级块的修改,因而可以释放掉超级块sb->s_resize_lock互斥锁。
i) 接下来,就是加入数据块的操作了。调用ext4_add_groupblocks(handle, sb, o_blocks_count, add)将add个数据块加入到文件系统的最后一个块组中。这一步操作成功后,会将文件系统的最后一个块组的块组描述符所在的数据块以及最后一个块组上的数据块位图都标记为脏,然后更新到磁盘。ext4_add_groupblocks()函数的具体操作流程如下:
1. 获取加入的数据块所在的块组号以及加入的数据块起始数据块偏移;
2. 检查加入数据块的个数是否导致块组跨界;
3. 读取块组中的块位图与块组描述符;
4. 判断加入数据块是否会导致出错。因为除了超级块和块组描述符表外,其他元数据的位置不固定,因而可能造成出错。以下情况会导致加入数据块出错:
a) 块组的数据块位图所在的数据块号数位于加入块组的起始物理数据块号与加入后的所有数据块总和之间——块组中无数据块位图;
b) 块组的inode位图所在的数据块号数位于加入块组的起始物理数据块号与加入后的所有数据块总和之间——块组中无inode位图;
c) 加入块组的起始物理数据块号位于inode表中间——inode表不完整;
d) 加入数据块后的最后一个数据块位于inode表中间——inode表不完整。
5. 在位图中加入数据块,然后修改块组描述符;
6. 清除块位图新加入的counts个数据块的标志,使其为空闲可用状态;
7. 锁定块组,更新块组描述符的相关内容,如块组中空闲块个数,块组校验和等。操作成功后解锁块组;
8. 如果设置了flex_bg特性,计算块组所在的flex_bg,进而修改flex_bg描述符信息,也就是空闲数据块的个数;
9. 设置块组需初始化位,重新载入具有新的位图信息的块组描述符元数据。这主要是为了更新内存中块组描述符的信息(更新块组的空闲数据块个数、更新块组中连续2^n个数据块构成的片段的个数);
10. 将数据块位图所在数据块标记为脏;
11. 将块组描述符所在数据块标记为脏。
j) 提交日志事务,结束日志操作。主要是更新前面提到的三个数据块(超级块、数据块位图、块组描述符)的日志提交及更新到磁盘;
k) 更新文件系统的备份元数据(主要是超级块和块组描述符表的备份)。
(4) 结束对文件系统的写操作,返回;