ext2_allocate_branch()解析

来源:互联网 发布:三维家软件下载 编辑:程序博客网 时间:2024/05/23 16:48

        在之前的多篇博客中我们都比较详细地阐述了ext2_get_block()路径中各个函数的实现原理,今天我们来关注下ext2_allocate_branch()的实现细节,这个函数是非常重要的,而且理解起来也没那么简单,虽然说函数的功能一句话就可能说完:为数据块建立映射路径。

        要理解这个函数,我们首先得理解ext2文件系统的逻辑块至物理块的映射方法(这个在之前已经作了比较仔细的描述)。同样,在分析这个函数的时候,我们也通过举例来说明,这样理解起来会更具体。

        让我们假设这样一种场景,假如目前文件的大小是2KB,但我们现在seek到了268KB位置处,写入2个数据块。这样是可以的,即产生了所谓的文件空洞。至于为什么seek到这样一个特殊的位置,也是有代表性的,下面我会说明。

        回想ext2文件系统的逻辑块至物理块的映射关系我们知道,269KB此处需要采用2级间接映射,而且正好就从这个地放起开始需要采用二级映射的方式(如果文件系统块大小为1KB的话,计算很简单,直接索引方式可管理12KB数据,一级索引方式可管理256KB数据)。在这种场景下进入该函数,让我们看看会发生点什么。

static int ext2_alloc_branch(struct inode *inode,int indirect_blks, int *blks, ext2_fsblk_t goal,int *offsets, Indirect *branch){int blocksize = inode->i_sb->s_blocksize;int i, n = 0;int err = 0;struct buffer_head *bh;int num;ext2_fsblk_t new_blocks[4];ext2_fsblk_t current_block;/* 首先分配直接块和间接块*/num = ext2_alloc_blocks(inode, goal, indirect_blks,*blks, new_blocks, &err);if (err)return err;branch[0].key = cpu_to_le32(new_blocks[0]);/* * metadata blocks and data blocks are allocated. */for (n = 1; n <= indirect_blks;  n++) {/* * Get buffer_head for parent block, zero it out * and set the pointer to new one, then send * parent to disk. */bh = sb_getblk(inode->i_sb, new_blocks[n-1]);branch[n].bh = bh;lock_buffer(bh);memset(bh->b_data, 0, blocksize);branch[n].p = (__le32 *) bh->b_data + offsets[n];branch[n].key = cpu_to_le32(new_blocks[n]);*branch[n].p = branch[n].key;//假如到了最后一级的映射间接块,那需要在间接块中存储直接块的物理块号if ( n == indirect_blks) {current_block = new_blocks[n];/* * End of chain, update the last new metablock of * the chain to point to the new allocated * data blocks numbers */for (i=1; i < num; i++)*(branch[n].p + i) = cpu_to_le32(++current_block);}set_buffer_uptodate(bh);unlock_buffer(bh);mark_buffer_dirty_inode(bh, inode);/* We used to sync bh here if IS_SYNC(inode). * But we now rely upon generic_write_sync() * and b_inode_buffers.  But not for directories. */if (S_ISDIR(inode->i_mode) && IS_DIRSYNC(inode))sync_dirty_buffer(bh);}*blks = num;return err;}
我们首先不妨看看这个函数的都代表何意义:

inode:自然代表了该文件;

indirect_blocks:需要分配多少个间接块,本例中需要采用二级映射,而且该映射路径上的所有间接块均未分配,因此,需要分配2个间接块;

blks:需要分配的数据块数量,本例中也为2;

goal:调用者计算的建议最佳分配块号,这个的计算已经在之前的博客中说明过;

offsets[]:保存了每一级映射路径上的偏移;

branch[]:保存了映射关系的数据结构。

让我们看看函数主要流程:

1. 首先自然要分配间接块和数据块,在函数ext2_allocate_blocks()中完成,这个我们等到下一篇博客中再详述;

2.然后需要建立映射关系,其实就是将映射路径上的映射关系进行填充,主要过程在for循环中完成,这也是我们关注的重点。

再回到我们前面举的例子上来,现在我们间接块和数据块都分配好了,假如都分配成功,但此时映射尚未建立,如下图所示:


这就是我们目前所处的状态,间接块和数据块也已分配,但这种连接尚未建立起来。文件在i_data[13](即二级索引的起始之处)断链了,接下来我们要做的是就是创建这种连接关系。

让我们看看for循环的执行流程:

首先在for循环尚未开始的地方,branch[0].key = new_blocks[0],new_blocks[]数组中存储了ext2_allocate_blocks()分配的间接块和直接块的索引块号(new_blocks[]只有4项,如果分配的块数过多,它如何能记下,其实它的使用有点讲究,因为最多需要三个间接块,所以做多使用前三项来保存间接块号,而该数组并不记录所有的数据块号,只记录数据块的起始块号,因为它好像保证分配的数据块连续),因此,new_blocks[0]中保存的就是第一个间接块的物理块号,经过这一步,第一个链接就此建立起来,如下图:

 

至此,第一个链接就建立起来了,接下来进入for循环,建立剩余的链接,建立链接的过程相对简单:

for (n = 1; n <= indirect_blks;  n++) {/* * Get buffer_head for parent block, zero it out * and set the pointer to new one, then send * parent to disk. */bh = sb_getblk(inode->i_sb, new_blocks[n-1]);branch[n].bh = bh;lock_buffer(bh);memset(bh->b_data, 0, blocksize);branch[n].p = (__le32 *) bh->b_data + offsets[n];branch[n].key = cpu_to_le32(new_blocks[n]);*branch[n].p = branch[n].key;//如果到了最后一个间接块,那么需要在该块中记录数据块的块号,分配的数据块是连续的if ( n == indirect_blks) {current_block = new_blocks[n];/* * End of chain, update the last new metablock of * the chain to point to the new allocated * data blocks numbers */for (i=1; i < num; i++)*(branch[n].p + i) = cpu_to_le32(++current_block);//分配的数据块连续}}
建立链接也很简单,对于每个间接块,从磁盘读出其内容,然后将在相应的偏移处记录下一个间接块的物理块号

branch[n].p = (__le32 *)bh->b_data + offsets[n] //将指针指向间接块的相应偏移处

branch[n].key = cpu_to_le32(new_blocks[n])//设置key,即下一级映射的间接块物理块号

*branch[n].p = branch[n].key//将内容写入间接块的相应位置

最后,在间接块映射完成以后,需要将数据块的物理块号记录在一级间接块中。这就是for循环里面的if判断的作用,因为分配的数据块是连续的,所以只需要知道起始数据块号和块数即可。

至此,所有的链接都已经完成了,形成的效果如下所示::


至此,整个建立连接的过程就已经完成了。理解这个过程非常重要,还好,我们已经把它搞透彻了。


原创粉丝点击