文件系统(一)--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的读取写入也必须经过高速缓冲区。
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的读取写入也必须经过高速缓冲区。
相关文章推荐
- Swap Nodes in Pairs
- 19 Remove Nth Node From End of List(去掉链表中倒数第n个节点Easy)
- 一晚上 -- Populating Next Right Pointers in Each Node II
- [Node.js] 对称加密、公钥加密和RSA
- Node.js学习--基础知识(4)--模块与包
- Node.js学习--基础知识(3)--回调函数与事件
- node.js 初体验
- Node.js学习--基础知识(2)--异步IO与事件式编程
- LeetCode 24:Swap Nodes in Pairs
- Nodejs部署再思考
- nodejs中express安装失败解决方法
- 深入浅出NodeJS笔记(一)
- Swap Nodes in Pairs
- LeetCode 之 Remove Nth Node From End of List — C++ 实现
- Count Complete Tree Nodes
- 24-m-Swap Nodes in Pairs
- Leetcode: Reverse Nodes in k-Group
- node.js发送邮件
- leetCode:Count Complete Tree Nodes
- Leetcode: Swap Nodes in Pairs