回忆一下大家众所周知的mount指令的含义:
首先mount是一个应用层的指令,对应的是一个系统调用,系统层函数应该是sys_mount,(这里针对的是linux0.11源码),其本质就是,将一个设备挂载到一个目录上
例如我们准备将一个分区挂载到了/home/machao这个目录上,挂载之前访问/home/machao这个目录,看到的内容是啥呢?应该就是挂载到根目录的文件系统上的/home/machao里的东西吧,但是挂载之后,我们访问/home/machao这个目录,看到的就是新挂上去的文件系统里的"/"路径下的内容了,那么这是为什么呢?mount这个步骤是怎么做到这个对应的文件系统的改变的呢?那这一切得从open函数开始说起,也就是系统层的sys_open这个系统调用:
sys_open这个函数主要在干嘛呢?找inode:
int sys_open(const char * filename,int flag,int mode)
{
struct m_inode * inode;
struct file * f;
int i,fd;
//找一个空白的fd,fd就是file_talbe的索引
for(fd=0 ; fd<NR_OPEN ; fd++)
if (!current->filp[fd])
break;
//从磁盘读取inode信息
if ((i=open_namei(filename,flag,mode,&inode))<0) {
current->filp[fd]=NULL;
f->f_count=0;
return i;
}
f->f_inode = inode;
return (fd);
}
上图中核心代码就是,先找一个fd,也就是当前进程的file_table的索引 ,然后呢,open_namei(filename,flag,mode,&inode)
从磁盘中读取inode信息,然后,
f->f_inode=inode进行关联,
好了,至此我们这个进程打开了一个文件,通过fd就可以找到对应的inode了。我们再看sys_read系统调用:
int sys_read(unsigned int fd,char * buf,int count)
{
struct file * file;
struct m_inode * inode;
inode = file->f_inode;
if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode)) {
if (count+file->f_pos > inode->i_size)
count = inode->i_size - file->f_pos;
if (count<=0)
return 0;
return file_read(inode,file,buf,count);
}
}
可以看到核心代码就是先找到inode:
inode = file->f_inode;
然后再调用普通文件的读函数file_read,读的时候当然就是要传这个inode。
所以,至此,我们猜测mount这个操作,大概率就是在修改inode这个东西,到底在修改什么东西呢,我们再看一下sys_open里的open_namei(filename,flag,mode,&inode)这个函数,这个函数是再读inode,里面调用了
dir_namei(pathname,&namelen,&basename)
dir_name里面调用了get_dir(pathname)
而我们看下get_dir:
static struct m_inode * get_dir(const char * pathname)
{
while(1){
if (!(bh = find_entry(&inode,thisname,namelen,&de))) {
iput(inode);
return NULL;
}
inr = de->inode;
idev = inode->i_dev;
if (!(inode = iget(idev,inr)))
return NULL;
}
}
}
可以看到find_entry在读目录, 这个de就是读出来的dir_entry,我们知道这个dir_entry结构里存放的就是众所周知的普通文件(或者目录)的文件名以及对应的盘块号了,然后再iget读磁盘,读完之后,因为外面套了一个while循环,会不断递归的往里找。
那么我们就能联想到,iget的时候,从哪个设备去读取inode信息,那么读到的目录内容就是对应设备的啦。所以mount大概率就是在修改/home/machao这个目录的inode里的i_dev信息了。我们再看下mount内核代码:
int sys_mount(char * dev_name, char * dir_name, int rw_flag)
{
struct m_inode * dev_i, * dir_i;
struct super_block * sb;
int dev;
if (!(dev_i=namei(dev_name)))
if (!(dir_i=namei(dir_name)))
sb->s_imount=dir_i;
dir_i->i_mount=1;
dir_i->i_dirt=1; /* NOTE! we don't iput(dir_i) */
return 0; /* we do that in umount */
}
核心代码就是这句sb->s_imount=dir_i,将superblock的s_imount指向了当前的这个inode,也就是/home/machao这个inode。
然后我们看,在mount之后,再open的时候,此时通过get_dir这个函数获取/home/machao这个目录inode时,调用iget方法读取inode信息的时候,贴一下iget的代码:
struct m_inode * iget(int dev,int nr)
{
while (inode < NR_INODE+inode_table) {
//当这个inode已经被标记为被挂载了的时候
if (inode->i_mount) {
int i;
for (i = 0 ; i<NR_SUPER ; i++)
//找到对应的superblock,这里就对应mount时候的
//sb->s_imount=dir_i;相对应了
if (super_block[i].s_imount==inode)
break;
if (i >= NR_SUPER) {
printk("Mounted inode hasn't got sb\n");
if (empty)
iput(empty);
return inode;
}
iput(inode);
dev = super_block[i].s_dev;
nr = ROOT_INO;
inode = inode_table;
continue;
}
if (empty)
iput(empty);
return inode;
}
if (!empty)
return (NULL);
inode=empty;
inode->i_dev = dev;
inode->i_num = nr;
read_inode(inode);
return inode;
}
这里iget获取inode信息的 时候,发现/home/machao这个目录inode被标记为被挂载了,就会去读对应的superblock,然后设置这个inode对应的dev为那个superblock对应的dev信息
dev = super_block[i].s_dev;
inode->i_dev = dev;
最后read_inode从新的文件系统读取inode信息啦