Ext4文件系统架构分析(十) ——ioctl源码分析之迁移文件数据块的映射方式

原文来自 http://blog.chinaunix.net/uid-28989651-id-4147043.html

1.  将文件数据块的映射方式由间接索引迁移到extents树映射

Ext4文件系统数据块映射方式迁移是指,将文件的inode管理文件数据块映射方式由间接索引转变为extents 树表示。Ext4文件系统的Ioctl命令EXT4_IOC_MIGRATE用于实现这种数据块映射方式的迁移操作,使用以下命令就可实现调用EXT4_IOC_MIGRATE命令迁移文件系统数据块映射方式的操作:

ioctl(fd, EXT4_IOC_MIGRATE,NULL)

其中fd所引用的文件的数据块的映射方式为间接块索引,ioctl命令成功执行后,文件的数据块的映射方式就为extents 映射了。结果的判断可以从inode的标志位中是否已设置EXT4_EXTENTS_FL标志(普适)或者从分配给该文件的数据块的个数来判断(这一般适用于大文件,4M以上)。

2.  EXT4_IOC_MIGRATE迁移文件的数据块映射方式的限制

1. 如果文件系统不支持extent或者文件已经是基于extent的,则不迁移;

2. 如果文件是链接文件且文件数据块个数为0(文件为硬链接),则不迁移;

3.  迁移文件的数据块映射方式的操作流程

1. 判断执行迁移操作的进程是否有相应的权限;

2. 获取对文件系统的写操作权限;

3. 获取inode_mutex锁,准备迁移;

4. 调用ext4_ext_migrate()迁移文件的数据块的映射方式,该函数的操作流程如下:

(1)检查迁移操作是否受限制,判断的标准见上一小节;

(2)满足迁移条件则发起一个日志事务,用于更新迁移操作修改的元数据;

(3)获取迁移的文件的inode所在的块组的第一个inode的号数;

(4)在inode所属文件的目录中分配一个新的临时inode;

(5)将待迁移的inode的文件的大小写入临时inode中;

(6)设置临时inode的链接数为1,防止在迁移的过程中临时inode被系统回收(删除);

(7)调用函数ext4_ext_tree_init()在临时inode中初始化一个extent树,该函数首先在临时inode(的i_blocks[]字段) 中创建一个ext4_extent_header结构体,这就是extents树的根。然后标记临时inode为脏,同时标记在缓存中标记临时inode无效( EXT4_EXT_CACHE_NO,防止其他进程访问它):

(8)调用函数ext4_orphan_add()将临时inode加入孤儿inode链表(孤儿inode就是文件对应的目录项已经不在,但是inode还存在,在调用iput时,它的inode引用计数不为0,这个inode就不会从内存中删除),先插入磁盘上的孤儿inode链表的表头,然后插入内存中的孤儿inode链表的表头;

(9)发起日志事务,提交临时inode;

(10)锁定源inode,然后将inode标记为正在迁移到extent映射方式,然后解锁inode;

(11)发起日志事务更新源inode状态;

(12)获取源inode对应的文件的第一个数据块的地址,初始化struct list_blocks_struct(该结构用于存储一个extent表示的连续数据块的逻辑和物理起止块号);

(13)获取一个数据块最多能记录的数据块的个数(4KB则记录1024个);

(14)将inode表示的文件的前12个数据块迁移为(在临时inode中用)extent表示,该操作中依次搜索文件的前12个数据块,并调用函数update_extent_range()将文件数物理据块中每连续的几个数据块用一个extent表示,该函数的操作流程如下:

a)  首先查看数据块能否加入到当前已有的extent中,则加入其中;

b) 否则,调用finish_range()函数提交当前的extent,然后为数据块创建一个新的extent;该函数主要调用ext4_ext_insert_extent()函数将当前的extent插入extent树(可以是与某个已存在的extent合并,或者是直接在extent树中插入,如果是插入的情况,在叶子节点空间不足的时候,会创建一个新的叶子节点);

 

(15)如果待迁移文件使用了一级非直接映射块,那么调用update_ind_extent_range()函数将一级非直接映射块中记录的数据块迁移为用extent表示,该函数的操作流程如下:

a) 首先计算一级间接映射索引块能索引的数据块的个数(4KB的数据块则能索引1024个数据块);

b) 接着读取一级间接映射索引块中的内容到buffer中;依次读取buffer中的内容(一个数据块的地址),调用update_extent_range()函数,将一级非直接映射块中记录的数据块迁移为用extent表示;

(16)如果待迁移文件使用了二级非直接映射块,那么调用update_dind_extent_range()函数将二级非直接映射块中记录的数据块迁移为用extent表示,该函数的操作流程如下:

a) 首先计算二级间接映射索引块能索引的数据块的个数(4KB的数据块则能索引1024个数据块);

b)接着读取二级间接映射索引块中的内容到buffer中;依次读取buffer中的内容(一个数据块的地址),针对每个二级间接索引块叶子索引块,调用update_ind_extent_range()函数,将二级非直接映射块中记录的数据块迁移为用extent表示;

(17)如果待迁移文件使用了三级间接映射块,那么调用update_tind_extent_range()函数将三级间接映射块中记录的数据块迁移为用extent表示,该函数的操作流程如下:

a) 首先计算三级间接映射索引块能索引的数据块的个数(4KB的数据块则能索引1024个数据块);

b)接着读取三级间接映射索引块中的内容到buffer中;依次读取buffer中的内容(一个数据块的地址),针对每个三级间接索引块的第二级索引块,调用update_dind_extent_range()函数,将三级非直接映射块中记录的数据块迁移为用extent表示;

(18)提交最后一个extent;

(19)将临时inode中的信息(i_blocks[]字段中的信息以及i_blocks(记录分配给文件的数据块个数))写到源inode中,该操作主要调用ext4_ext_swap_inode_data()函数完成;该函数的操作流程如下:

a) 首先在内存中交换inode的i_block[]字段的信息;

b) 接着获取源inode的i_data_sem信号量,获取对源inode的i_blocs[]字段及inode标志位的修改权限。获取i_data_sem信号量后,会判断源inode是否已被其他进程迁移,如果是则退出;否则设置inode标志位为EXT4_EXTENTS_FL,表示inode的数据块映射方式即将修改为extents数映射方式;

c) 接着就是从临时inode中拷贝i_block[]字段的信息了;

d) 再接着就是更新源inode中的i_blocks字段,也就是更新分配给文件的数据块的个数;

e) 最后,释放i_data_sem信号量,释放源inode的间接索引块,标记源inode为脏;

(20)发起日志,更新源inode;

(21)将临时inode中的文件大小改为0;

(22)设置分配给临时inode文件数据块个数为0;

(23)再次在临时inode中初始化extent树(为什么?);

(24)将临时inode的i_nlink设置为0.以使其可被删除;

(25)结束日志事务处理(更显源inode的事务);

(26)对临时inode放弃使用权,该临时inode可回收也可被其他进程获取使用;

(27)释放临时inode;返回。

5.  释放inode_mutex锁;

6.  释放对文件系统的写操作权限,迁移操作结束;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值