得到文件的逻辑块号

    技术2024-07-24  66

    1.3.2 得到文件的逻辑块号

    继续走,233行,设置map_bhb_page字段为当前page。随后进入循环,对于页中的每一块,调用ext2文件系统的get_block函数,作为参数传递pageinode、相对于文件起始位置的块索引block_in_filemap_bh进去,最后返回相对于磁盘分区开始位置的逻辑块号,即相对于磁盘或分区开始位置的块索引,存放在结果参数map_bhb_blocknr字段中。所以这里我们重点关注get_block的原型,ext2_get_block函数,来自fs/ext2/inode.c

     

    547int ext2_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create)

     548{

     549        int err = -EIO;

     550        int offsets[4];

     551        Indirect chain[4];

     552        Indirect *partial;

     553        unsigned long goal;

     554        int left;

     555        int boundary = 0;

     556        int depth = ext2_block_to_path(inode, iblock, offsets, &boundary);

     557

     558        if (depth == 0)

     559                goto out;

     560

     561reread:

     562        partial = ext2_get_branch(inode, depth, offsets, chain, &err);

     563

     564        /* Simplest case - block found, no allocation needed */

     565        if (!partial) {

     566got_it:

     567                map_bh(bh_result, inode->i_sb, le32_to_cpu(chain[depth-1].key));

     568                if (boundary)

     569                        set_buffer_boundary(bh_result);

     570                /* Clean up and exit */

     571                partial = chain+depth-1; /* the whole chain */

     572                goto cleanup;

     573        }

     574

     575        /* Next simple case - plain lookup or failed read of indirect block */

     576        if (!create || err == -EIO) {

     577cleanup:

     578                while (partial > chain) {

     579                        brelse(partial->bh);

     580                        partial--;

     581                }

     582out:

     583                return err;

     584        }

     585

     586        /*

     587         * Indirect block might be removed by truncate while we were

     588         * reading it. Handling of that case (forget what we've got and

     589         * reread) is taken out of the main path.

     590         */

     591        if (err == -EAGAIN)

     592                goto changed;

     593

     594        goal = 0;

     595        if (ext2_find_goal(inode, iblock, chain, partial, &goal) < 0)

     596                goto changed;

     597

     598        left = (chain + depth) - partial;

     599        err = ext2_alloc_branch(inode, left, goal,

     600                                        offsets+(partial-chain), partial);

     601        if (err)

     602                goto cleanup;

     603

     604        if (ext2_use_xip(inode->i_sb)) {

     605                /*

     606                 * we need to clear the block

     607                 */

     608                err = ext2_clear_xip_target (inode,

     609                        le32_to_cpu(chain[depth-1].key));

     610                if (err)

     611                        goto cleanup;

     612        }

     613

     614        if (ext2_splice_branch(inode, iblock, chain, partial, left) < 0)

     615                goto changed;

     616

     617        set_buffer_new(bh_result);

     618        goto got_it;

     619

     620changed:

     621        while (partial > chain) {

     622                brelse(partial->bh);

     623                partial--;

     624        }

     625        goto reread;

     626}

     

    ext2_get_block函数代码比较复杂,怎么吃透它呢?首先,请大家访问博客“Ext2数据块分配” http://blog.csdn.net/yunsongice/archive/2010/08/18/5822495.aspx补充一下数据块寻址的预备知识;然后我在网上找了一个ext2_get_block函数调用层次图,如下:

     

    ext2_get_block()将对文件系统的逻辑块号转换为对块设备的逻辑块号,这种转换关系是由ext2_inode结构中i_block[]数组描述的。i_block[]的前12项为直接块索引表,第13项为间接索引块指针,第14项为二重索引块指针,第15项为三重索引块指针。当文件长度不超过12个块时(一个块是1024字节,12个块就是12k),可通过直接块索引表直接定位目标块,当文件长度超过12块并且剩余的部分不超过间接索引块索引数量时,就在间接索引块中定位目标块,依次类推。

     

    ext2_get_block函数功能是从相对于文件开始位置的块索引转换为相对于磁盘分区开始位置的逻辑块号,若对应逻辑块被删除,则重新分配得到它间接块路径。系统是以块索引查找逻辑块的。例如,要找到第100个逻辑块对应的逻辑块,因为25612>100>12,所以要用到一次间接块,在一次间接块中查找第88项,此项内容就是对应的逻辑块的地址。

     

    首先556行,ext2_block_to_path得到block_in_file位于直接块还是n次间接块。该函数只返回四个可能的值:如果是直接块中,则返回1;如果是间接块则返回2;如果是二次间接则返回3;如果是三次间接则返回4

     

    static int ext2_block_to_path(struct inode *inode, long i_block, int offsets[4], int *boundary)

    {

     

        //每块地址数=块大小 / 每个指针大小即32位,一般为1kbyte/32bit=256

        int ptrs = EXT2_ADDR_PER_BLOCK(inode->i_sb);

        int ptrs_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);

     

        //直接块数direct_blocks =12

        const long direct_blocks = EXT2_NDIR_BLOCKS,

     

        //一次间接块数indirect_blocks =256  256k字节)

        indirect_blocks = ptrs,

     

        //二次间接块数double_blocks =256*256 65536k字节,64MB字节)

        double_blocks = (1 << (ptrs_bits * 2));

        int n = 0;

     

        if (i_block < 0) {

            ext2_warning (inode->i_sb, "ext2_block_to_path", "block < 0");

        } else if (i_block < direct_blocks) {

            offsets[n++] = i_block;//直接块

        } else if ( (i_block -= direct_blocks) < indirect_blocks) {

            offsets[n++] = EXT2_IND_BLOCK;//一次间接块

            offsets[n++] = i_block;

        } else if ((i_block -= indirect_blocks) < double_blocks) {

            offsets[n++] = EXT2_DIND_BLOCK;//二次间接块

            offsets[n++] = i_block >> ptrs_bits;//一次间接块

            offsets[n++] = i_block & (ptrs - 1);//直接块

           } else if (((i_block -= double_blocks) >> (ptrs_bits * 2)) < ptrs) {

            offsets[n++] = EXT2_TIND_BLOCK;//三次间接块

            offsets[n++] = i_block >> (ptrs_bits * 2);//二次间接块

            offsets[n++] = (i_block >> ptrs_bits) & (ptrs - 1); //一次间接块

            offsets[n++] = i_block & (ptrs - 1); //直接块

        } else {

            ext2_warning (inode->i_sb, "ext2_block_to_path", "block > big");

        }

        if (boundary)

            *boundary = (i_block & (ptrs - 1)) == (final - 1);

        return n;

     

    }

     

    ext2_block_to_path的返回值保存在ext2_get_block函数的内部变量depth中,然后ext2_get_block执行562行的ext2_get_branch函数,从逻辑块中读取数据到chainbuffer中。

     

    函数的参数说明如下:

    inode: 文件VFS的索引节点

    depth: 间接块深度(1 —— 一次间接块指针)

    offsets: 间接逻辑块的指针数组

    chain: 存储读取逻辑块的数据

    err: 存储错误标识

    函数ext2_get_branch的功能是填充Indirect结构的数组,如果运行正常,则返回NULL

     

    typedef struct {

        u32  *p;   索引块中索引项的地址

        u32  key; 索引块中索引项的值

        struct buffer_head *bh; 索引块所在的缓冲区

    } Indirect;

    static inline void add_chain(Indirect *p, struct buffer_head *bh, __le32 *v)

    {

           p->key = *(p->p = v);

           p->bh = bh;

    }

    static Indirect *ext2_get_branch(struct inode *inode,

                    int depth,

                    int *offsets,

                    Indirect chain[4],

                    int *err)

    {

        struct super_block *sb = inode->i_sb;

        Indirect *p = chain;

        struct buffer_head *bh;

     

        //初始化chain

        add_chain (chain, NULL, EXT2_I(inode)->i_data + *offsets);

        //depth为间接块深度,如三次间接块深度为4

        while (--depth) {

            bh = sb_bread(sb, le32_to_cpu(p->key));

            ……

            //读出逻辑块数据到chain中,pchain数组的指针

            add_chain(++p, bh, (__le32*)bh->b_data + *++offsets);

            if (!p->key)

                goto no_block;

        }

        return NULL;

     

    ……

    }

     

    这个函数比较绕脑子,我们还是举个例子吧。假如block_in_file的磁盘索引节点逻辑块号较大,比方说16741216,其大于256*256,小于256*256*256,那么肯定是3次间接块。所以在ext2_block_to_path函数中,offsets[0]=EXT2_TIND_BLOCK,也就是15,作为三次间接块存放二次间接块的逻辑块号;offsets[1]= 16741216 >> (8*2),等于255,也就是作为二次间接块存放一次间接块的逻辑块号;offsets[2]=16741216 >> 8 & (256 -1)等于65395,作为一次间接块存放直接寻址块的逻辑块号;offsets[3]=16741216 & (256 -1)等于16741216,就是直接块,也就是我们的block_in_file的逻辑块号。

     

    那么进入ext2_get_branch函数以后,首先调用add_chain函数初始化作为参数传递进来的Indirect变量空数组chain,有4个元素,仅初始化第一个Indirect元素的p字段为对应ext2_inode_info结构的i_data + *offsets,就是i_data[15]的值。随后进入while (--depth)循环,depth这里等于4,首先通过sb_bread读取i_data[15]对应的块到块设备页高速缓存中,然后调用add_chain函数将i_data[15]对应的块存放的第255项的内容,也就是第二次间接寻址的逻辑块号加入到chain[1]p中。这样第二次、第三次循环后,chain[2]chain[3]就分别对应一次间接块地址和直接地址的逻辑地址,就存放在Indirect结构的p字段中,而对应具体的逻辑块号就存在他们的key字段中,同时保留对应缓冲块的buffer_head结构。眼睛看晕了就看看我画的关系图:

     

     

    如图,经过ext2_block_to_pathext2_get_branch这么一折腾,ext2_get_block中的内部变量chain[4]就被赋上值了,其中chain[3]key字段就存放了相对于文件开始位置的block_in_file(也就是我们例子中的16741216)对应的逻辑块号。而partial内部变量是NULL

     

    随后跳到ext2_get_block567行,调用map_bh函数将bh_result这个buffer_headb_bdevb_blocknrb_size字段分别设置为该文件超级块的设备,chain[3]key字段(block_in_file对应的逻辑块号)和块大小1024然后释放brelse刚才chain数组中用了临时存放块的高速缓存并返回。

    最新回复(0)