您的位置:首页 > Web前端 > Node.js

文件系统(一)--super.c bitmap.c inode.c 源码分析

2015-06-14 19:37 876 查看
super.c

1 /*

2 * linux/fs/super.c

3 *

4 * (C) 1991 Linus Torvalds

5 */

6

7 /*

8 * super.c contains code to handle the super-block tables.

9 */

10 #include <linux/config.h>

11 #include <linux/sched.h>

12 #include <linux/kernel.h>

13 #include <asm/system.h>

14

15 #include <errno.h>

16 #include <sys/stat.h>

18 int sync_dev(int dev);

19 void wait_for_keypress(void);

20

21 /* set_bit uses setb, as gas doesn't recognize setc */

22 #define set_bit(bitnr,addr) ({ \

23 register int __res __asm__("ax"); \

24 __asm__("bt %2,%3;setb %%al":"=a" (__res):"a" (0),"r" (bitnr),"m" (*(addr))); \

25 __res; })

bt 功能:按照源操作指定的位号,测试目的操作数,当指令执行时,被测试位的状态被复制到进位标志CF
在asm语句中对硬件寄存器的引用必须用“%%”

如果CF为1,setb就会置位后面的寄存器。因此这里就是测试addr开始内存处第bitnr位是1还是0

27 struct super_block super_block[NR_SUPER];

28 /* this is initialized in init/main.c */

29 int ROOT_DEV = 0;

init/main.c:60:#define ORIG_ROOT_DEV (*(unsigned short *)0x901FC)

init/main.c:110: ROOT_DEV = ORIG_ROOT_DEV;

这里的ORIG_ROOT_DEV是在init/main中通过询问BIOS获得并保存的。

31 static void lock_super(struct super_block * sb)

32 {

33 cli();

34 while (sb->s_lock)

35 sleep_on(&(sb->s_wait));

36 sb->s_lock = 1;

37 sti();

38 }

如果其他任务已经锁定了,就等待它释放,然后自己锁定。

40 static void free_super(struct super_block * sb)

41 {

42 cli();

43 sb->s_lock = 0;

44 wake_up(&(sb->s_wait));

45 sti();

46 }

这里其实是解锁操作,叫unlock更好一些

48 static void wait_on_super(struct super_block * sb)

49 {

50 cli();

51 while (sb->s_lock)

52 sleep_on(&(sb->s_wait));

53 sti();

54 }

56 struct super_block * get_super(int dev)

57 {

58 struct super_block * s;

59

60 if (!dev)

61 return NULL;

62 s = 0+super_block;

63 while (s < NR_SUPER+super_block)

64 if (s->s_dev == dev) {

65 wait_on_super(s);

66 if (s->s_dev == dev)

67 return s;

68 s = 0+super_block; //重新从头开始搜索

69 } else

70 s++;

71 return NULL;

72 }

从这里可以看到super_block会保存到super_block数组中。

74 void put_super(int dev) //释放超级块

75 {

76 struct super_block * sb;

77 struct m_inode * inode;

78 int i;

79

80 if (dev == ROOT_DEV) {

81 printk("root diskette changed: prepare for armageddon\n\r");

82 return;
这里的ROOT_DEV指的是根文件系统所在的分区

83 }

84 if (!(sb = get_super(dev)))

85 return;

86 if (sb->s_imount) {

87 printk("Mounted disk changed - tssk, tssk\n\r");

88 return;

89 }

90 lock_super(sb);

91 sb->s_dev = 0;

92 for(i=0;i<I_MAP_SLOTS;i++)

93 brelse(sb->s_imap[i]);

94 for(i=0;i<Z_MAP_SLOTS;i++)

95 brelse(sb->s_zmap[i]);

96 free_super(sb);

97 return;

98 }

100 static struct super_block * read_super(int dev)

101 {

102 struct super_block * s;

103 struct buffer_head * bh;

104 int i,block;

106 if (!dev)

107 return NULL;

108 check_disk_change(dev);

109 if (s = get_super(dev))

110 return s;
获取到直接返回

111 for (s = 0+super_block; ; s++) {

112 if (s >= NR_SUPER+super_block)

113 return NULL;

114 if (!s->s_dev)

115 break;

116 }
从super_block[]中找到一个s_dev为0的super_block。

117 s->s_dev = dev;

118 s->s_isup = NULL;

119 s->s_imount = NULL;

120 s->s_time = 0;

121 s->s_rd_only = 0;

122 s->s_dirt = 0;

初始化它的属性

123 lock_super(s); //锁定

124 if (!(bh = bread(dev,1))) { //读取该设备第一个块号,也就是超级块

125 s->s_dev=0; //读取失败

126 free_super(s);

127 return NULL;

128 }

到这里说明成功读取了超级块

129 *((struct d_super_block *) s) =

130 *((struct d_super_block *) bh->b_data);

用它来为s赋值

131 brelse(bh);

132 if (s->s_magic != SUPER_MAGIC) { //检查魔数

133 s->s_dev = 0;

134 free_super(s);

135 return NULL;

136 }

137 for (i=0;i<I_MAP_SLOTS;i++)

138 s->s_imap[i] = NULL;

139 for (i=0;i<Z_MAP_SLOTS;i++)

140 s->s_zmap[i] = NULL;

初始化imap和zmap

141 block=2;

142 for (i=0 ; i < s->s_imap_blocks ; i++)

143 if (s->s_imap[i]=bread(dev,block)) //读取相应的inode bitmap块,来为s_imap赋值

144 block++;

145 else

146 break;

147 for (i=0 ; i < s->s_zmap_blocks ; i++)

148 if (s->s_zmap[i]=bread(dev,block))

149 block++;

150 else

151 break;

读取相应的逻辑块bitmap来为s_zmap赋值

152 if (block != 2+s->s_imap_blocks+s->s_zmap_blocks) {
正常情况下这里应该相等,否则就释放所有的buffer_head

153 for(i=0;i<I_MAP_SLOTS;i++)

154 brelse(s->s_imap[i]);

155 for(i=0;i<Z_MAP_SLOTS;i++)

156 brelse(s->s_zmap[i]);

157 s->s_dev=0;

158 free_super(s);

159 return NULL;

160 }

161 s->s_imap[0]->b_data[0] |= 1;

162 s->s_zmap[0]->b_data[0] |= 1;
s_imap和s_zmap是buffer_head类型的,它的b_data对应着磁盘数据映射。这里把第0个位都置为1.因为第0个inode节点和逻辑块是不能被分配的。

163 free_super(s); //解锁超级块

164 return s;

165 }

从这里可以看出,super_block数组可以看做系统中super_block的缓存。

167 int sys_umount(char * dev_name)

168 {

169 struct m_inode * inode;

170 struct super_block * sb;

171 int dev;

173 if (!(inode=namei(dev_name)))

174 return -ENOENT;
根据设备名找到对应的inode

175 dev = inode->i_zone[0];
取得设备号(设备文件在其i_zone[0]中存放设备号)

176 if (!S_ISBLK(inode->i_mode)) {

177 iput(inode); //如果不是块设备,放回inode即可

178 return -ENOTBLK;

179 }

180 iput(inode);
是块设备

181 if (dev==ROOT_DEV)

182 return -EBUSY;
如果是根设备,返回-EBUSY。

183 if (!(sb=get_super(dev)) || !(sb->s_imount))

184 return -ENOENT;
没有找到超级块或者该文件系统没有被安装过,返回

185 if (!sb->s_imount->i_mount)

186 printk("Mounted inode has i_mount=0\n");
如果超级块所指明的被安装到的inode节点的i_mount标志没有被置位,则打印信息。

187 for (inode=inode_table+0 ; inode<inode_table+NR_INODE ; inode++)

188 if (inode->i_dev==dev && inode->i_count)

189 return -EBUSY;

这里说明有进程正在使用该设备上的文件,返回-EBUSY;
否则

190 sb->s_imount->i_mount=0; //复位挂载标记

191 iput(sb->s_imount); //放回超级块被安装到的节点

192 sb->s_imount = NULL; //置被安装到的节点为空

193 iput(sb->s_isup); //放回设备文件系统根inode节点

194 sb->s_isup = NULL; //置空

195 put_super(dev); //释放超级块

196 sync_dev(dev); //刷新

197 return 0;

198 }

200 int sys_mount(char * dev_name, char * dir_name, int rw_flag)

201 {

202 struct m_inode * dev_i, * dir_i;

203 struct super_block * sb;

204 int dev;

205

206 if (!(dev_i=namei(dev_name)))

207 return -ENOENT;
根据设备名获取它的inode

208 dev = dev_i->i_zone[0];
取得设备号

209 if (!S_ISBLK(dev_i->i_mode)) {

210 iput(dev_i); //不是块设备,放回inode

211 return -EPERM; //返回

212 }

213 iput(dev_i); //放回inode

214 if (!(dir_i=namei(dir_name)))

215 return -ENOENT;

根据dir_name获取inode

216 if (dir_i->i_count != 1 || dir_i->i_num == ROOT_INO) {

217 iput(dir_i);

218 return -EBUSY;

219 }

如果有其他进程引用该dir inode或者该dir inode是根i节点号
这里文件系统根inode,根i节点号,super block挂载inode之间是什么关系?



220 if (!S_ISDIR(dir_i->i_mode)) {

221 iput(dir_i);

222 return -EPERM;

223 }

224 if (!(sb=read_super(dev))) {

225 iput(dir_i);

226 return -EBUSY;

227 }

读取超级块

228 if (sb->s_imount) {

229 iput(dir_i);

230 return -EBUSY;

231 }

如果已经挂载过,放回dir_i,返回。

232 if (dir_i->i_mount) {

233 iput(dir_i);

234 return -EPERM;

235 }

如果dir_i已经挂载了其他文件系统,返回

236 sb->s_imount=dir_i; //

237 dir_i->i_mount=1;

238 dir_i->i_dirt=1; /* NOTE! we don't iput(dir_i) */

239 return 0; /* we do that in umount */

240 }

上面就是安装文件系统的过程,它必须挂载到目录上,也就是dir_name必须是目录,并且对应的inode没有被其他应用使用。

242 void mount_root(void)

243 {

244 int i,free;

245 struct super_block * p;

246 struct m_inode * mi;

248 if (32 != sizeof (struct d_inode))

249 panic("bad i-node size");
注意这里d_inode是磁盘i节点

250 for(i=0;i<NR_FILE;i++)

251 file_table[i].f_count=0;
file_table中文件引用计数初始化为0

252 if (MAJOR(ROOT_DEV) == 2) {

253 printk("Insert root floppy and press ENTER");

254 wait_for_keypress();

255 }

256 for(p = &super_block[0] ; p < &super_block[NR_SUPER] ; p++) {

257 p->s_dev = 0;

258 p->s_lock = 0;

259 p->s_wait = NULL;

260 }

初始化超级块数组

261 if (!(p=read_super(ROOT_DEV)))

262 panic("Unable to mount root");
读取根设备超级块

263 if (!(mi=iget(ROOT_DEV,ROOT_INO)))
include/linux/fs.h:37:#define ROOT_INO 1

读取文件系统根i节点

264 panic("Unable to read root i-node");

265 mi->i_count += 3 ; /* NOTE! it is logically used 4 times, not 1 */
被引用了4次,266-268行

266 p->s_isup = p->s_imount = mi;

267 current->pwd = mi; //当前进程工作目录为mi

268 current->root = mi; //当前进程根节点为mi

注意这里current就是init进程

269 free=0;

270 i=p->s_nzones;

271 while (-- i >= 0)

272 if (!set_bit(i&8191,p->s_zmap[i>>13]->b_data))

273 free++;

统计空闲逻辑块

274 printk("%d/%d free blocks\n\r",free,p->s_nzones);

275 free=0;

276 i=p->s_ninodes+1;

277 while (-- i >= 0)

278 if (!set_bit(i&8191,p->s_imap[i>>13]->b_data))

279 free++;

统计空闲inode节点

280 printk("%d/%d free inodes\n\r",free,p->s_ninodes);

281 }

总结

超级块位于分区中第一个块(第0个是引导块),它包含个整个文件系统的一些重要信息。系统中所有的超级块保存在super_block数组中。文件系统必须要挂载才能使用,在系统初始化过程中会挂载根文件系统(在init函数中调用了set_up,set_up中又调用了mount_root)。mount_root就是用来挂载根文件系统的,在执行过程中,它会初始化file_table数组和super_block数组,接着读取根设备超级块,并读取根inode节点(序号为1的inode节点),将文件系统挂载到该inode节点上,并设置当前进程的根目录和工作目录为根inode。文件系统也可以挂载到其他目录上。还有一个比较重要的操作就是读取超级块,它首先从super_block数组中查找是否已经存在,如果不是,就从super_block数组中选一个空闲位置,然后从磁盘中读取相应的超级块到高速缓冲区中,并给上面找到的super_block赋值,然后读取相应的inode
bitmap和block bitmap来初始化该super_block的s_imap和s_zmap。前面的文章中多次见到使用sb->s_imap来查找空闲inode节点的情况,这里我们看到,inode bitmap是在读取超级块时就已经读入高速缓冲区并有s_imap引用的,对于block
bitmap也是同样的。

bitmap.c

1 /*

2 * linux/fs/bitmap.c

3 *

4 * (C) 1991 Linus Torvalds

5 */

6

7 /* bitmap.c contains the code that handles the inode and block bitmaps */
inode bitmap和block bitmap分别用来标记inode和逻辑块block的使用情况。

8 #include <string.h>

9

10 #include <linux/sched.h>

11 #include <linux/kernel.h>

13 #define clear_block(addr) \

14 __asm__("cld\n\t" \

15 "rep\n\t" \

16 "stosl" \

17 ::"a" (0),"c" (BLOCK_SIZE/4),"D" ((long) (addr)):"cx","di")

把整个block清零的。

19 #define set_bit(nr,addr) ({\

20 register int res __asm__("ax"); \

21 __asm__ __volatile__("btsl %2,%3\n\tsetb %%al": \

22 "=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \

23 res;})
设置addr地址开始特定的位

25 #define clear_bit(nr,addr) ({\

26 register int res __asm__("ax"); \

27 __asm__ __volatile__("btrl %2,%3\n\tsetnb %%al": \

28 "=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \

29 res;})

清除addr地址处特定的位。

31 #define find_first_zero(addr) ({ \

32 int __res; \

33 __asm__("cld\n" \

34 "1:\tlodsl\n\t" \

35 "notl %%eax\n\t" \

36 "bsfl %%eax,%%edx\n\t" \

37 "je 2f\n\t" \

38 "addl %%edx,%%ecx\n\t" \

39 "jmp 3f\n" \

40 "2:\taddl $32,%%ecx\n\t" \

41 "cmpl $8192,%%ecx\n\t" \

42 "jl 1b\n" \

43 "3:" \

44 :"=c" (__res):"c" (0),"S" (addr):"ax","dx","si"); \

45 __res;})
//从addr开始处寻找第一个0的位置

47 void free_block(int dev, int block)

48 {

49 struct super_block * sb;

50 struct buffer_head * bh;

51

52 if (!(sb = get_super(dev)))

53 panic("trying to free block on nonexistent device");
得到超级块

54 if (block < sb->s_firstdatazone || block >= sb->s_nzones)

55 panic("trying to free block not in datazone");
这里sb->s_firstdatazone是数据区中第一个逻辑块块号
sb->s_nzones是总的逻辑块号。

56 bh = get_hash_table(dev,block);

57 if (bh) {

58 if (bh->b_count != 1) {

59 printk("trying to free block (%04x:%d), count=%d\n",

60 dev,block,bh->b_count);

还有其他任务在使用不能释放。返回

61 return;

62 }

63 bh->b_dirt=0;

64 bh->b_uptodate=0;

这两句主要是用来标记对应的buffer_head无效的。

65 brelse(bh);

66 }

67 block -= sb->s_firstdatazone - 1 ;

68 if (clear_bit(block&8191,sb->s_zmap[block/8192]->b_data)) {

69 printk("block (%04x:%d) ",dev,block+sb->s_firstdatazone-1);

70 panic("free_block: bit already cleared");

include/linux/fs.h:135: struct buffer_head * s_zmap[8];

从逻辑块位图中清除相应的标记

71 }

72 sb->s_zmap[block/8192]->b_dirt = 1;

设置标记,之后会写入磁盘

73 }

可以看到这里block bitmap也是缓存在高速缓冲区中的

75 int new_block(int dev)

76 {

77 struct buffer_head * bh;

78 struct super_block * sb;

79 int i,j;

81 if (!(sb = get_super(dev)))

82 panic("trying to get new block from nonexistant device");

83 j = 8192;

84 for (i=0 ; i<8 ; i++)

85 if (bh=sb->s_zmap[i])

86 if ((j=find_first_zero(bh->b_data))<8192)

87 break;

找到一个未用的逻辑块,j记录位置。

88 if (i>=8 || !bh || j>=8192)

89 return 0;

90 if (set_bit(j,bh->b_data))

91 panic("new_block: bit already set");

设置block bitmap相应的位为1,表示已使用。

92 bh->b_dirt = 1;

93 j += i*8192 + sb->s_firstdatazone-1;
因为i记录的是第z_map的第i项,每一项1K,因此可以记录8K个逻辑块,所以这里需要乘以8192,得到逻辑块在数据区中的偏移,加上数据区第一个块的块号(sb->s_firstdatazone)再减去1,得到的就是第i个zmap项的绝对块号,再加j得到的就是空闲块号的绝对块号。

94 if (j >= sb->s_nzones)

95 return 0;

其中sb->s_nzones是总逻辑块数(包括了引导扇区,超级块,bitmap占用的)

96 if (!(bh=getblk(dev,j)))

97 panic("new_block: cannot get block");
98 if (bh->b_count != 1)

99 panic("new block: count is != 1");

101 bh->b_uptodate = 1;

102 bh->b_dirt = 1;

读取对应的块到buffer_head,101-102行的标记表示这个buffer_head对应的内容是最新的,而且是干净的。
103 brelse(bh);

104 return j;

105 }

所以,上面这个函数的功能就是找到一个空闲块,并把它读到高速缓冲区中。找空闲快是通过super_block的s_zmap数组中的位图来搜索的。

107 void free_inode(struct m_inode * inode)

108 {

109 struct super_block * sb;

110 struct buffer_head * bh;

111

112 if (!inode)

113 return;

114 if (!inode->i_dev) {

115 memset(inode,0,sizeof(*inode));

116 return;

117 }

118 if (inode->i_count>1) {

119 printk("trying to free inode with count=%d\n",inode->i_count);

120 panic("free_inode");

121 }

122 if (inode->i_nlinks)

123 panic("trying to free inode with links");

124 if (!(sb = get_super(inode->i_dev)))

125 panic("trying to free inode on nonexistent device");

126 if (inode->i_num < 1 || inode->i_num > sb->s_ninodes)

127 panic("trying to free inode 0 or nonexistant inode");

128 if (!(bh=sb->s_imap[inode->i_num>>13]))

129 panic("nonexistent imap in superblock");

130 if (clear_bit(inode->i_num&8191,bh->b_data))

131 printk("free_inode: bit already cleared.\n\r");

清除inode bitmap中对应的位。

132 bh->b_dirt = 1;

标记buffer_head为脏,等待写入磁盘

133 memset(inode,0,sizeof(*inode));

清空inode

134 }

136 struct m_inode * new_inode(int dev)

137 {

138 struct m_inode * inode;

139 struct super_block * sb;

140 struct buffer_head * bh;

141 int i,j;

142

143 if (!(inode=get_empty_inode()))

144 return NULL;

145 if (!(sb = get_super(dev)))

146 panic("new_inode with unknown device");

147 j = 8192;

148 for (i=0 ; i<8 ; i++)

149 if (bh=sb->s_imap[i])

150 if ((j=find_first_zero(bh->b_data))<8192)

151 break;

152 if (!bh || j >= 8192 || j+i*8192 > sb->s_ninodes) {

153 iput(inode);

154 return NULL;

155 }

156 if (set_bit(j,bh->b_data))

157 panic("new_inode: bit already set");

158 bh->b_dirt = 1;

159 inode->i_count=1;

160 inode->i_nlinks=1;

161 inode->i_dev=dev;

162 inode->i_uid=current->euid;

163 inode->i_gid=current->egid;

164 inode->i_dirt=1;

165 inode->i_num = j + i*8192;

166 inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;

167 return inode;

168 }

这个也很简单,还是先获取超级块,并从inode bitmap中找到空闲inode,将其置位。

总结

主要包含了对inode和block的操作,我们知道inode bitmap和block bitmap用于记录二者的分配情况。对于inode的分配操作,从inode bitmap(由super_block的s_imap指向)中找到还未使用的位,将其置位表示已经使用,由于内核中所有存在于内存中的inode都要保存在inode_table中,因此需要从inode_table中获取一个空闲inode,然后设置它的属性。通过设置其i_num属性的值就将其与磁盘中相应位置关联起来。可以看到分配inode的操作并没有从磁盘中实际inode位置进行读取操作,因此效率还是比较高的。对于inode的释放操作,与分配相反,首先判断是否允许释放,如果允许就清除相应的inode
bitmap中的标记,随后清空inode结构体。对于逻辑块的分配,第一步也是需要从block bitmap中寻找空闲块,置位,并将对应的逻辑块号返回;对于逻辑块的释放,与之相反,将参数中指定的实际块号转换为block bitmap中对应的位置(super_block中的s_firstdatazone指明数据区中第一个逻辑块块号),清除block bitmap中相应的位。最后需要说明的一点是inode
bitmap
和 block bitmap都是存在高速缓冲区中的,对它的修改要通过将buffer_head标记为脏,并由

缓冲区管理模块在合适的时机自动写回。

inode.c

1 /*

2 * linux/fs/inode.c

3 *

4 * (C) 1991 Linus Torvalds

5 */

6

7 #include <string.h>

8 #include <sys/stat.h>

9

10 #include <linux/sched.h>

11 #include <linux/kernel.h>

12 #include <linux/mm.h>

13 #include <asm/system.h>

15 struct m_inode inode_table[NR_INODE]={{0,},};

inode bitmap与block bitmap后面就是文件系统使用的所有inode了,这里用inode_table来存放它,注意这里NR_INODE为32,这里只是存放在内存中的inode。

17 static void read_inode(struct m_inode * inode);

18 static void write_inode(struct m_inode * inode);

20 static inline void wait_on_inode(struct m_inode * inode)

21 {

22 cli();

23 while (inode->i_lock)

24 sleep_on(&inode->i_wait);

25 sti();

26 }

在inode被锁定的情况下,其他使用它的任务需要等待。这里需要关中断的原因是防止等待队列被破坏,关于这一点可以看之前分析的sleep_on函数的实现过程。

28 static inline void lock_inode(struct m_inode * inode)

29 {

30 cli();

31 while (inode->i_lock)

32 sleep_on(&inode->i_wait);

33 inode->i_lock=1;

34 sti();

35 }

如果已经被其他任务锁定就等待它释放,然后自己锁定它。

37 static inline void unlock_inode(struct m_inode * inode)

38 {

39 inode->i_lock=0; //复位标记

40 wake_up(&inode->i_wait); //唤醒等待在此inode上的其他任务

41 }

43 void invalidate_inodes(int dev)

44 {

45 int i;

46 struct m_inode * inode;

48 inode = 0+inode_table;

49 for(i=0 ; i<NR_INODE ; i++,inode++) {

50 wait_on_inode(inode); //这是等待其他任务解锁该inode的

51 if (inode->i_dev == dev) {

52 if (inode->i_count)

53 printk("inode in use on removed disk\n\r");

54 inode->i_dev = inode->i_dirt = 0;

55 }

56 }

57 }

这个函数主要是用于将inode_table中属于指定设备的inode节点置为无效。

59 void sync_inodes(void)

60 {

61 int i;

62 struct m_inode * inode;

64 inode = 0+inode_table;

65 for(i=0 ; i<NR_INODE ; i++,inode++) {

66 wait_on_inode(inode);

67 if (inode->i_dirt && !inode->i_pipe)

68 write_inode(inode);

69 }

70 }

用于同步inode_table中标记为脏的并且不是pipe类型的inode。

如果下面的函数看不懂可以先看最后的总结部分,再回来接着看。

72 static int _bmap(struct m_inode * inode, int block, int create)

73 {

74 struct buffer_head * bh;

75 int i;

77 if (block<0)

78 panic("_bmap: block<0");

79 if (block >= 7+512+512*512)

80 panic("_bmap: block>big");
这里7+512+512*512就表示一个文件所占用最大块数

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 }

上面是只有直块的情况,这时如果create为true并且相应block为空,就为它创建一个新的块。并修改inode的ctime和dirt标记,返回该数据块号。

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;

如果create为false,并且是一级间接块为空,直接返回。

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 }

根据我们上面的注释,这个函数就是为文件分配逻辑块的(如果参数中加以指明的话),这里的块号是相对文件本身来说的,也就是指的是文件中的第几个块,与实际逻辑块号是不同的,函数最终返回实际块号,没有就返回0。

140 int bmap(struct m_inode * inode,int block)

141 {

142 return _bmap(inode,block,0);

143 }

这里create参数设为0,返回的就是文件第block个数据块在磁盘中实际逻辑块号。

145 int create_block(struct m_inode * inode, int block)

146 {

147 return _bmap(inode,block,1);

148 }

这里create参数为1,如果文件中的第block块不存在就为其分配一个逻辑块,并返回这个分配的逻辑块号。

150 void iput(struct m_inode * inode)

151 {

152 if (!inode)

153 return;

154 wait_on_inode(inode); //等待inode解锁

155 if (!inode->i_count) //free

156 panic("iput: trying to free free inode");

157 if (inode->i_pipe) {

158 wake_up(&inode->i_wait); //唤醒在该管道上等待的任务

159 if (--inode->i_count) //递减引用计数

160 return; //如果还有其他任务引用该管道,就返回

161 free_page(inode->i_size);

注意,对于管道来说,inode->i_size放的是内存页地址。这里释放内存页涉及到页表的操作。
释放内存页与释放inode节点或者释放文件数据块之间有什么关系?

162 inode->i_count=0;

163 inode->i_dirt=0;

164 inode->i_pipe=0;
为什么没有复位inode table中的相应标记

165 return;

166 }

167 if (!inode->i_dev) { //设备号为0的inode,递减它的引用计数,返回。

168 inode->i_count--;

169 return;

170 }

171 if (S_ISBLK(inode->i_mode)) { //如果是块设备文件

172 sync_dev(inode->i_zone[0]); //i_zone[0]中存放着设备号,同步该设备

173 wait_on_inode(inode); //等待inode解锁

174 }

175 repeat:

176 if (inode->i_count>1) { //如果还有其他任务引用该文件,递减引用计数后返回

177 inode->i_count--;

178 return;

179 }

180 if (!inode->i_nlinks) { //链接数为0,释放该文件所有的逻辑块

181 truncate(inode);

182 free_inode(inode); //释放inode

183 return;

184 }

运行到这里说明此inode被其他文件链接,这时不能销毁它

185 if (inode->i_dirt) { //如果文件为脏,写回

186 write_inode(inode); /* we can sleep - so do again */

187 wait_on_inode(inode);

188 goto repeat; //不释放不罢休?跳出循环只有三个出口178行,183行和191行

189 }

190 inode->i_count--; //递减引用计数

191 return;

192 }

194 struct m_inode * get_empty_inode(void)

195 {

196 struct m_inode * inode;

197 static struct m_inode * last_inode = inode_table;

198 int i;

200 do {

201 inode = NULL;

202 for (i = NR_INODE; i ; i--) {

203 if (++last_inode >= inode_table + NR_INODE)

204 last_inode = inode_table;
如果超出最后一项,折回重新找

205 if (!last_inode->i_count) { //如果引用计数为0,说明找到空闲inode

206 inode = last_inode; //

207 if (!inode->i_dirt && !inode->i_lock) // 如果inode是干净的并且没有被锁定

208 break;

209 }

210 }
我们看到这里for循环退出的条件是i=0或者inode的引用计数为0,并且是干净的,而且没有被锁定

211 if (!inode) { //没有找到

212 for (i=0 ; i<NR_INODE ; i++)

213 printk("%04x: %6d\t",inode_table[i].i_dev,

214 inode_table[i].i_num);

215 panic("No free inodes in mem");
打印信息并退出

216 }
运行到这里说明找到了空的inode

217 wait_on_inode(inode); //等待inode解锁
为什么我们前面已经判断过了,它的引用计数为0,这里还要等待解锁呢?这是因为这期间有可能其他任务使用并锁定了该inode,所以我们会在222行while中判断i_count。

218 while (inode->i_dirt) { //

219 write_inode(inode); //刷新

220 wait_on_inode(inode); //

221 }

222 } while (inode->i_count);

223 memset(inode,0,sizeof(*inode));

224 inode->i_count = 1;

225 return inode;

226 }

228 struct m_inode * get_pipe_inode(void)

229 {

230 struct m_inode * inode;

231

232 if (!(inode = get_empty_inode())) //没有空闲inode,返回NULL

233 return NULL;

234 if (!(inode->i_size=get_free_page())) { //这里可以看到inode->i_size指向的是内存页的地址

235 inode->i_count = 0; //如果没有空闲页面,i_count属性设为0

236 return NULL;

237 }

238 inode->i_count = 2; /* sum of readers/writers */ //读进程和写进程都会引用这个管道

239 PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0;

240 inode->i_pipe = 1;

241 return inode;

242 }

我们继续看一下239行,PIPE_HEAD和PIPE_TAIL

include/linux/fs.h:58: #define PIPE_HEAD(inode) ((inode).i_zone[0])

include/linux/fs.h:59:#define PIPE_TAIL(inode) ((inode).i_zone[1])

从这里可以看出i_zone[0]和i_zone[1]分别被设置为0,240行置位i_pipe标记。

244 struct m_inode * iget(int dev,int nr)

245 {

246 struct m_inode * inode, * empty;

247

248 if (!dev)

249 panic("iget with dev==0");

250 empty = get_empty_inode();

251 inode = inode_table;

252 while (inode < NR_INODE+inode_table) {

253 if (inode->i_dev != dev || inode->i_num != nr) {

254 inode++;

255 continue;

256 }

运行到这里说明找到了目标inode

257 wait_on_inode(inode); //等待inode解锁

258 if (inode->i_dev != dev || inode->i_num != nr) { //这里需要重新判断,因为上面经历了睡眠过程

259 inode = inode_table;

260 continue;

261 }

运行到这里说明找到目标inode节点

262 inode->i_count++; //递增引用计数

263 if (inode->i_mount) { //说明此inode是文件系统安装点

264 int i;

266 for (i = 0 ; i<NR_SUPER ; i++)

267 if (super_block[i].s_imount==inode)

268 break;

找到安装到此inode的文件系统超级块

269 if (i >= NR_SUPER) {

270 printk("Mounted inode hasn't got sb\n");

271 if (empty)

272 iput(empty);

这里说明没有找到超级块,如果之前找到的空的inode节点empty,现在就要将其放回。

273 return inode;

274 }

到这里说明找到了安装在inode的超级块

275 iput(inode); //

276 dev = super_block[i].s_dev;

277 nr = ROOT_INO;

278 inode = inode_table;

279 continue;

找到超级块后获取根inode节点号ROOT_INO,设备号,重新搜索相应的inode节点

280 }//

281 if (empty)

282 iput(empty);

283 return inode;

284 }//while

285 if (!empty)

286 return (NULL);

287 inode=empty;

288 inode->i_dev = dev;

289 inode->i_num = nr;

290 read_inode(inode);

291 return inode;

如果最终没有找到相应的inode就创建一个,并读入后,返回

292 }

294 static void read_inode(struct m_inode * inode)

295 {

296 struct super_block * sb;

297 struct buffer_head * bh;

298 int block;

300 lock_inode(inode);

301 if (!(sb=get_super(inode->i_dev)))

302 panic("trying to read inode without dev");
如果没有超级块就退出

303 block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks +

304 (inode->i_num-1)/INODES_PER_BLOCK;
计算它在分区中的块号

305 if (!(bh=bread(inode->i_dev,block)))

306 panic("unable to read i-node block");
读取盘块到高速缓冲区

307 *(struct d_inode *)inode =

308 ((struct d_inode *)bh->b_data)

309 [(inode->i_num-1)%INODES_PER_BLOCK];
读取后赋值给inode

310 brelse(bh);
等待bh解锁,然后挂到等待队列上,并重新调度

311 unlock_inode(inode);
//解锁此inode

312 }

根据上面的分析知道了,该函数是根据内存中inode节点来读取磁盘中的inode节点来赋值给它。整个过程中inode需要锁定,防止其他任务访问到不一致的数据。

314 static void write_inode(struct m_inode * inode)

315 {

316 struct super_block * sb;

317 struct buffer_head * bh;

318 int block;

319

320 lock_inode(inode);

321 if (!inode->i_dirt || !inode->i_dev) { //如果是干净的或者设备号为0,就不需要写回磁盘(因为设备号为0说明不是磁盘文件)

322 unlock_inode(inode);

323 return;

324 }

325 if (!(sb=get_super(inode->i_dev)))

326 panic("trying to write inode without device");

327 block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks +

328 (inode->i_num-1)/INODES_PER_BLOCK;
计算块号,同上面的一致

329 if (!(bh=bread(inode->i_dev,block)))

330 panic("unable to read i-node block");
读取块到高速缓冲区

331 ((struct d_inode *)bh->b_data)

332 [(inode->i_num-1)%INODES_PER_BLOCK] =

333 *(struct d_inode *)inode;
把inode内容写入到高速缓冲区中

334 bh->b_dirt=1; //置位脏标记

335 inode->i_dirt=0; //清除脏标记

336 brelse(bh); //这个函数首先会等待bh解锁,因为这里我们并没有锁定这个bh,所以其它任务可能在此期间锁定了这个bh。其次它会唤醒buffer_wait上等待的任务,这里等待的任务是在遍历所有buffer_head而且没有找到空闲buffer_head的情况下在buffer_wait上等待的任务。(是否空闲是通过b_count来标记的,见buffer.c 225行)

337 unlock_inode(inode); //解锁

338 }

此函数是把内存中的inode写回磁盘的。

总结

这里只做简单的总结,首先对于一个磁盘分区,它的起始两个block中是超级块,随后是inode bitmap,用来记录inode分配情况;再往后是block bitmap,用来记录逻辑块的分配情况;这两个bitmap的大小会随分区的大小而不同,为了界定位置就需要在super_block中记录各自的大小(分别为s_imap_blocks和s_zmap_blocks)。在block
bitmap后面就是inode表,这里存放文件系统中具体的inode。每个block存放的inode数量保存在INODES_PER_BLOCK中,因此给定一个inode编号,我们就可以计算它在磁盘中的块号:block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks + (inode->i_num-1)/INODES_PER_BLOCK,再根据(inode->i_num-1)%INODES_PER_BLOCK就可以计算出inode在block中的位置。其次,inode中还有一个i_zone字段,它是一个数组用于保存磁盘块号,其中0-6是直接块号;7是一级间接块号;8是二级间接块号。不过对于一些设备,比如管道,其i_zone[0]和i_zone[1]就分别作为head与tail指针使用;对于块设备,其i_zone[0]用于保存设备号。其他几个比较重要的字段,i_size指明了数据大小,i_mount指明是否是挂载点。内存中的所有inode都会保存在inode_table之中,inode的读取写入也必须经过高速缓冲区。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: