Linux内核文件系统 inode.c中_bmap函数理解

时间:2022-03-22 05:15:57

此段代码是Linux0.11版内核中Linux/fs/inode.c文件中_bmap函数代码

首先说一下此函数的主要功能:

_bmap()函数用于文件数据块映射到盘块的处理操作。所带的参数inode 是文件的i 节点指针,block
是文件中的数据块号
,create 是创建标志,表示在对应文件数据块不存在的情况下,是否需要在盘上建
立对应的盘块。该函数的返回值是文件数据块对应在设备上的逻辑块号(盘块号)。当create=0 时,该函
数就是bmap()函数。当create=1 时,它就是create_block()函数。
正规文件中的数据是放在磁盘块的数据区中的,而一个文件名则通过对应的i 节点与这些数据磁盘
块相联系,这些盘块的号码就存放在i 节点的逻辑块数组中。_bmap()函数主要是对i 节点的逻辑块(区
块)数组i_zone[]进行处理,并根据i_zone[]中所设置的逻辑块号(盘块号)来设置逻辑块位图的占用情
况。正如前面所述,i_zone[0]至i_zone[6]用于存放对应文件的直接逻辑块号;i_zone[7]用于存放一次间接逻辑块号;

而i_zone[8]用于存放二次间接逻辑块号。当文件较
小时(小于7K),就可以将文件所使用的盘块号直接存放在i 节点的7 个直接块项中;当文件稍大一些
时(不超过7K+512K),需要用到一次间接块项i_zone[7];当文件更大时,就需要用到二次间接块项
i_zone[8]了。因此,比较文件小时,linux 寻址盘块的速度就比较快一些。

注意上文中加粗两句话中的区别,一个是文件中的数据块号,一个是盘块号,如果没有清楚的理解两者的区别,则很难理解代码

我的理解是

文件中的数据块:如果一个文件的大小为525k,因为一个逻辑块大小为1k,故而此文件数据共占525个逻辑块,这里只讲文件的

数据占525个逻辑块,不算文件的其他信息,例如文件名,创建时间等。那么第250个文件数据块即为文件第250k--251k的数据

盘块号:Linux系统是将盘分为一个个逻辑块来管理的,整个盘有多少个k大小就有多少个逻辑块,盘块为2500的逻辑块在盘中位置

即为距离盘开始2500k大小的地方。

即二者,一个是相对于文件起始位置的逻辑块数,一个是相对于存储设备起始位置的盘块数。


下面直接附上源代码:

 72 static int _bmap(struct m_inode * inode,int block,int create)
 73 {
 74     struct buffer_head * bh;
 75     int i;
 76 
 77     if (block<0)
 78         panic("_bmap: block<0");
 79     if (block >= 7+512+512*512)
 80         panic("_bmap: block>big");
 81     if (block<7) {
 82         if (create && !inode->i_zone[block])
 83             if (inode->i_zone[block]=new_block(inode->i_dev)) {
 84                 inode->i_ctime=CURRENT_TIME;
 85                 inode->i_dirt=1;
 86             }
 87         return inode->i_zone[block];
 88     }
 89     block -= 7;
 90     if (block<512) {
 91         if (create && !inode->i_zone[7])
 92             if (inode->i_zone[7]=new_block(inode->i_dev)) {
 93                 inode->i_dirt=1;
 94                 inode->i_ctime=CURRENT_TIME;
 95             }
 96         if (!inode->i_zone[7])
 97             return 0;
 98         if (!(bh = bread(inode->i_dev,inode->i_zone[7])))
 99             return 0;
100         i = ((unsigned short *) (bh->b_data))[block];
101         if (create && !i)
102             if (i=new_block(inode->i_dev)) {
103                 ((unsigned short *) (bh->b_data))[block]=i;
104                 bh->b_dirt=1;
105             }
106         brelse(bh);
107         return i;
108     }
109     block -= 512;
110     if (create && !inode->i_zone[8])
111         if (inode->i_zone[8]=new_block(inode->i_dev)) {
112             inode->i_dirt=1;
113             inode->i_ctime=CURRENT_TIME;
114         }
115     if (!inode->i_zone[8])
116         return 0;
117     if (!(bh=bread(inode->i_dev,inode->i_zone[8])))
118         return 0;
119     i = ((unsigned short *)bh->b_data)[block>>9];
120     if (create && !i)
121         if (i=new_block(inode->i_dev)) {
122             ((unsigned short *) (bh->b_data))[block>>9]=i;
123             bh->b_dirt=1;
124         }
125     brelse(bh);
126     if (!i)
127         return 0;
128     if (!(bh=bread(inode->i_dev,i)))
129         return 0;
130     i = ((unsigned short *)bh->b_data)[block&511];
131     if (create && !i)
132         if (i=new_block(inode->i_dev)) {
133             ((unsigned short *) (bh->b_data))[block&511]=i;
134             bh->b_dirt=1;
135         }
136     brelse(bh);
137     return i;
138 }

这里挑选代码中较为难理解的几段进行解释

 98         if (!(bh = bread(inode->i_dev,inode->i_zone[7])))
 99             return 0;
100         i = ((unsigned short *) (bh->b_data))[block];
101         if (create && !i)
102             if (i=new_block(inode->i_dev)) {
103                 ((unsigned short *) (bh->b_data))[block]=i;
104                 bh->b_dirt=1;
105             }

首先将一级间接块读入到一个buffer_head结构体中,便于后面找到数据实际的盘块时删除一级间接块,

这里的block已经减去过7,故而此时的block代表的是一级间接块的偏移位置,此偏移位置存放的是实际数据

块的盘块号即i的值。如果此时i的值为0则申请一个逻辑块,返回的盘快号赋值给i,如果i为0则表示申请逻辑块失败

申请成功则将返回的逻辑块盘号存放到一级间接块偏移block位置处。

如果文件数据块号大于7+512则需要二级间接块,block=(block-7-512)为纯用二级间接块表示的数据,直接块能表示7k

一级间接块表示512k,(block>>9)等价于其除以512,结果为某个二级块的盘快号存放在一级间接块中的偏移值,而这个二级盘块存放

有文件数据block块对应的盘块号,那么实际的数据块对应的盘快好存在于二级间接块的什么位置了,(block&511)表示了存放在二级间接块

中偏移值。