/* * linux/fs/super.c * * (C) 1991 Linus Torvalds */
/* * super.c contains code to handle the super-block tables. */ #include <linux/config.h> #include <linux/sched.h> #include <linux/kernel.h> #include <asm/system.h>
#include <errno.h> #include <sys/stat.h>
int sync_dev(int dev);//对指定设备进行高速缓冲数据与设备上数据的同步操作
void wait_for_keypress(void);//等待按键
/* set_bit uses setb, as gas doesn't recognize setc */ #define set_bit(bitnr,addr) ({ \ register int __res __asm__("ax"); \ __asm__("bt %2,%3;setb %%al":"=a" (__res):"a" (0),"r" (bitnr),"m" (*(addr))); \ __res; })//测试指定偏移处比特位的值,并返回该原比特位值
struct super_block super_block[NR_SUPER];// 超级块结构数组(共8 项)
/* this is initialized in init/main.c */ int ROOT_DEV = 0;// ROOT_DEV 已在init/main.c 中被初始化 */
static void lock_super(struct super_block * sb)// 锁定指定的超级块
{ cli();//关中 段
while (sb->s_lock) sleep_on(&(sb->s_wait));//将当前进程睡眠,链入sb->s_wait队列
sb->s_lock = 1; sti();//开中断
}
static void free_super(struct super_block * sb)// 对指定超级块解锁。(如果使用ulock_super 这个名称则更妥帖)
{ cli();//关中断
sb->s_lock = 0; wake_up(&(sb->s_wait));//唤醒*p所指向的任务。*p为任务等待队列头指针,由于新等待任务是插入等待队列头指针处的,因此唤醒的是最后进入等待队列的任务
sti();//开中断
}
static void wait_on_super(struct super_block * sb)// 睡眠等待超级块解锁
{ cli();//关中断
while (sb->s_lock) sleep_on(&(sb->s_wait));//将当前任务置为不可中断的等待状态,并将此放入睡眼队列P中
sti();//开中断
}
struct super_block * get_super(int dev)// 取指定设备的超级块。返回该超级块结构指针,查找超级块列表
{ struct super_block * s;
if (!dev)// 如果没有指定设备,则返回空指针。
return NULL; s = 0+super_block;// s 指向超级块数组开始处。
while (s < NR_SUPER+super_block)//搜索整个超级块数组,寻找指定设备的超级块
if (s->s_dev == dev) { wait_on_super(s);// 睡眠等待超级块解锁
if (s->s_dev == dev)//在睡眠期间该超级块有可能被其他设备使用,所以还要进一次的判断,如果是则返回
return s; s = 0+super_block;//否则,则头再一次查找
} else s++; return NULL; }
void put_super(int dev)// 释放指定设备的超级块
{ struct super_block * sb; struct m_inode * inode; int i;
if (dev == ROOT_DEV) {// 如果指定设备是根文件系统设备,则显示警告信息“根系统盘改变了,准备生死决战吧”,并返回
printk("root diskette changed: prepare for armageddon\n\r"); return; } if (!(sb = get_super(dev)))// 取指定设备的超级块。返回该超级块结构指针,如果没有则返回
return; if (sb->s_imount) {//被安装到的i 节点,安装的i节点还没有被处理过
printk("Mounted disk changed - tssk, tssk\n\r"); return; } lock_super(sb);//锁定指定的超级块
sb->s_dev = 0;//置该超级块对应的设备号字段为0,也即即将放弃该超级块
for(i=0;i<I_MAP_SLOTS;i++)//释放该设备i 节点位图在缓冲区中所占用的缓冲块
brelse(sb->s_imap[i]); for(i=0;i<Z_MAP_SLOTS;i++)//释放该设备逻辑块位图在缓冲区中所占用的缓冲块
brelse(sb->s_zmap[i]); free_super(sb);// 对指定超级块解锁
return; }
static struct super_block * read_super(int dev)// 从设备上读取超级块到缓冲区中
{//如果在超级块列表中,则返回此指针,如果没有设备上读取到缓冲区再复制到超级块表中
struct super_block * s; struct buffer_head * bh; int i,block;
if (!dev)// 如果没有指明设备,则返回空指针
return NULL; check_disk_change(dev);//检查磁盘是否更换,如果已更换就使对应所有高速缓冲区无效
if (s = get_super(dev))//如果该设备的超级块已经在高速缓冲中,则直接返回该超级块的指针,,,取指定设备的超级块,返回该超级块结构指针,在高速缓冲区超级块列表中查找
return s; for (s = 0+super_block ;; s++) {// 否则,首先在超级块数组中找出一个空项(也即其s_dev=0 的项)。如果数组已经占满则返回空指针
if (s >= NR_SUPER+super_block) return NULL; if (!s->s_dev) break; } s->s_dev = dev;// 找到超级块空项后,就将该超级块用于指定设备
s->s_isup = NULL;// 被安装的文件系统根目录的i 节点。(isup-super i)
s->s_imount = NULL;// 被安装到的i 节点。
s->s_time = 0;// 修改时间。
s->s_rd_only = 0;// 只读标志。
s->s_dirt = 0;// 已修改(脏)标志。
lock_super(s);// 锁定指定的超级块
if (!(bh = bread(dev,1))) {//读此设备的1号磁盘块内容(超级块的信息)到高速缓冲块区中来,0号磁盘块内容为引导块信息
s->s_dev=0;// 超级块所在的设备号,为0时,说明此超级块项空闲
free_super(s);// 对指定超级块解锁。
return NULL; } *((struct d_super_block *) s) = *((struct d_super_block *) bh->b_data);//将设备上读取的超级块信息复制到超级块数组相应项结构中
brelse(bh);// 释放存放读取信息的高速缓冲块
if (s->s_magic != SUPER_MAGIC) {//从现在开始,我们得到了对应设备的超级块,s->s_magic文件系统魔数,如果读取的超级块的文件系统魔数字段内容不对,说明设备上不是正确的文件系统
s->s_dev = 0; free_super(s); return NULL; } for (i=0;i<I_MAP_SLOTS;i++)//初始化内存超级块结构中i 节点位图空间
s->s_imap[i] = NULL; for (i=0;i<Z_MAP_SLOTS;i++)//初始化内存超级块结构中逻辑块位图空间
s->s_zmap[i] = NULL; block=2;//从第2号块开始,0为引导块号,1为超级块号,2为i 节点位图开始的块号(最多8个),再往后是逻辑块位图开始的块号(最多8个),再往后是数据区
for (i=0 ; i < s->s_imap_blocks ; i++)//取设备i 节点位图信息到内存超级块列表中
if (s->s_imap[i]=bread(dev,block)) block++; else break; for (i=0 ; i < s->s_zmap_blocks ; i++)//取设备逻辑块号位图信息到内存超级块列表中
if (s->s_zmap[i]=bread(dev,block)) block++; else break; if (block != 2+s->s_imap_blocks+s->s_zmap_blocks) {//如果读出的位图逻辑块数不等于位图应该占有的逻辑块数,说明文件系统位图信息有问题,超级块初始化失败。因此只能释放前面申请的所有资源,返回空指针并退出
for(i=0;i<I_MAP_SLOTS;i++) brelse(s->s_imap[i]);// 释放i 节点位图和逻辑块位图占用的高速缓冲区
for(i=0;i<Z_MAP_SLOTS;i++) brelse(s->s_zmap[i]);// 释放i 节点位图和逻辑块位图占用的高速缓冲区
s->s_dev=0;////释放上面选定的超级块数组中的项
free_super(s);// 对指定超级块解锁
return NULL; }//以下一切成功
s->s_imap[0]->b_data[0] |= 1;//对于申请空闲i 节点的函数来讲,如果设备上所有的i 节点已经全被使用,则查找函数会返回0 值。因此0 号i 节点是不能用的,所以这里将位图中的最低位设置为1,以防止文件系统分配0 号i 节点
s->s_zmap[0]->b_data[0] |= 1; free_super(s);// 对指定超级块解锁
return s; }
int sys_umount(char * dev_name)//卸载文件系统的系统调用函数,参数dev_name 是设备文件名
{ struct m_inode * inode; struct super_block * sb; int dev;
if (!(inode=namei(dev_name)))// 首先根据设备文件名找到对应的i 节点,
return -ENOENT; dev = inode->i_zone[0];//并取其中的设备号,设备文件所定义设备设备号保存在其i 节点的i_zone[0]中
if (!S_ISBLK(inode->i_mode)) {//由于文件系统需要存放在块设备中,如果不是块设备文件,则释放刚申请的i 节点dev_i,返回出错码
iput(inode);// 释放inode节点(写入设备)
return -ENOTBLK; } iput(inode);// 释放设备文件名的i 节点,为取设备号而建立的inode节点已完成使命
if (dev==ROOT_DEV)// 如果设备是根文件系统,则不能被卸载,返回出错号
return -EBUSY; if (!(sb=get_super(dev)) || !(sb->s_imount))// 如果取设备的超级块失败,或者该设备文件系统没有安装过,则返回出错码
return -ENOENT; if (!sb->s_imount->i_mount)// 如果超级块所指明的被安装到的i 节点没有置位其安装标志,则显示警告信息。
printk("Mounted inode has i_mount=0\n"); for (inode=inode_table+0 ; inode<inode_table+NR_INODE ; inode++)// 查找内存中i 节点表,看是否有进程在使用该设备上的文件,如果有则返回忙出错码
if (inode->i_dev==dev && inode->i_count)// i_dev, i 节点所在的设备号。i_count,i 节点被使用的次数,0 表示该i 节点空闲。
return -EBUSY; sb->s_imount->i_mount=0;// 复位被安装到的i 节点的安装标志
iput(sb->s_imount);//并释放该i 节点
sb->s_imount = NULL;//置超级块中被安装i 节点字段为空
iput(sb->s_isup);//并释放设备文件系统的根i 节点
sb->s_isup = NULL;//置超级块中被安装系统 根i 节点指针为空
put_super(dev);// 释放该设备的超级块以及位图占用的缓冲块
sync_dev(dev);//并对该设备执行高速缓冲与设备上数据的同步操作
return 0; }
int sys_mount(char * dev_name, char * dir_name, int rw_flag)//安装文件系统调用函数
{//参数dev_name 是设备文件名,dir_name 是安装到的目录名,rw_flag 被安装文件的读写标志
struct m_inode * dev_i, * dir_i; struct super_block * sb; int dev;
if (!(dev_i=namei(dev_name)))//首先根据设备文件名找到对应的i 节点,
return -ENOENT; dev = dev_i->i_zone[0];//并取其中的设备号,对于块特殊设备文件,设备号在i 节点的i_zone[0]中
if (!S_ISBLK(dev_i->i_mode)) {// 如果不是块设备文件,则释放刚取得的i 节点dev_i,返回出错码
iput(dev_i); return -EPERM; } iput(dev_i);// 释放该设备文件的i 节点dev_i,为取设备号而建立的inode节点已完成使命
if (!(dir_i=namei(dir_name)))// 根据给定的目录文件名找到对应的i 节点dir_i
return -ENOENT; if (dir_i->i_count != 1 || dir_i->i_num == ROOT_INO) {// 如果该i 节点的引用计数不为1(仅在这里引用),或者该i 节点的节点号是根文件系统的节点 号1,则释放该i 节点,返回出错码
iput(dir_i); return -EBUSY; } if (!S_ISDIR(dir_i->i_mode)) {// 如果该节点不是一个目录文件节点,则也释放该i 节点,返回出错码
iput(dir_i); return -EPERM; } if (!(sb=read_super(dev))) {//从设备上读取超级块到缓冲区中,读取将安装文件系统的超级块,如果失败则也释放该i 节点,返回出错码
iput(dir_i); return -EBUSY; } if (sb->s_imount) {// 如果将要被安装的文件系统已经安装在其它地方,则释放该i 节点,返回出错码
iput(dir_i); return -EBUSY; } if (dir_i->i_mount) {// 如果将要安装到的i 节点已经安装了文件系统(安装标志已经置位),则释放该i 节点,返回出错码
iput(dir_i); return -EPERM; } sb->s_imount=dir_i;// 被安装文件系统超级块的“被安装到i 节点”字段指向安装到的目录名的i 节点
dir_i->i_mount=1;// 设置安装位置i 节点的安装标志
dir_i->i_dirt=1;//节点已修改标志 /* NOTE! we don't iput(dir_i) */
return 0; /* we do that in umount */ }
void mount_root(void)//安装根文件系统,该函数是在系统开机初始化设置时(sys_setup())调用的
{ int i,free; struct super_block * p; struct m_inode * mi;
if (32 != sizeof (struct d_inode))// 如果磁盘i 节点结构不是32 个字节,则出错,死机
panic("bad i-node size"); for(i=0;i<NR_FILE;i++)// 初始化文件表数组(共64 项,也即系统同时只能打开64 个文件),将所有文件结构中的引用计数设置为0
file_table[i].f_count=0; if (MAJOR(ROOT_DEV) == 2) {// 如果根文件系统所在设备是软盘的话,就提示“插入根文件系统盘,并按回车键”,并等待按键
printk("Insert root floppy and press ENTER"); wait_for_keypress(); } for(p = &super_block[0] ; p < &super_block[NR_SUPER] ; p++) {// 初始化超级块数组(共8 项)
p->s_dev = 0; p->s_lock = 0; p->s_wait = NULL; } if (!(p=read_super(ROOT_DEV)))//读根设备上超级块到缓冲区p中
panic("Unable to mount root"); if (!(mi=iget(ROOT_DEV,ROOT_INO)))//从根设备上读取文件系统的根i 节点(1)到内存i 节点列表中,(第0号节点为空,同理第0号逻辑块也为空,可能是系统 的规定)
panic("Unable to read root i-node"); mi->i_count += 3 ;// 该i 节点引用次数递增3 次 /* NOTE! it is logically used 4 times, not 1 */
p->s_isup = p->s_imount = mi;// 置该超级块的被安装文件系统i 节点和被安装到的i 节点为该i 节点。
current->pwd = mi;// 设置当前进程的当前工作目录,此时当前进程为1号进程
current->root = mi;// 设置当前进程的根目录,此时当前进程为1号进程
free=0; i=p->s_nzones;// 统计该设备上空闲块数。首先令i 等于超级块中表明的设备逻辑块总数
while (-- i >= 0) if (!set_bit(i&8191,p->s_zmap[i>>13]->b_data)) free++; printk("%d/%d free blocks\n\r",free,p->s_nzones); free=0; i=p->s_ninodes+1;// 统计该设备上空闲i节点数。首先令i 等于设备上i 节点总数+1
while (-- i >= 0) if (!set_bit(i&8191,p->s_imap[i>>13]->b_data)) free++; printk("%d/%d free inodes\n\r",free,p->s_ninodes); }
|