此段代码是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)表示了存放在二级间接块
中偏移值。