前提:linux内核版本:v2.6.12
假设存放在/dev/fd0软盘上的ext2文件系统安装在/flp:
mount -t ext2 /dev/fd0 /flp
/dev/fd0 为安装设备,/flp为安装点
1、文件系统注册
Linux内核是可加载的,许多模块式可选的,只有真正需要使用时才加载他们。文件系统注册过程就是把对应某类型文件系统相关的模块加载到内核,并创建相关的数据结构。每个文件系统模块都有一个初始化例程,它的作用就是VFS中进行注册,即填写一个叫做file_system_type的数据结构。所有已注册的文件系统的file_system_type结构形成一个链表,我们把这个链表称为注册链表。该链表如下所示:
图1:文件系统类型链表
每个设备在mount时都要搜索该注册链表,选择适合自己设备文件系统的一项,并从中取出read_super()函数获取设备的超级块(存储在具体设备上,记录存储设备各种信息的一个存储块),并解析其内容。因为每种类型文件系统的超级块的格式不同,并且各自有特定的信息,每种文件系统必须使用对应的解析函数,否则内核就因为不认识该文件系统而无法完成安装。这就是注册文件系统的意义所在。(生成对应file_system_type文件系统结构并提供相应的xxx_get_sb函数)
设备真正mount时,总体数据结构如下图2所示
图2:所有数据关系图
2、文件系统的安装
安装一个文件系统,除了需要“被安装设备”外,还要指定一个“安装点”。“安装点”是已经存在的一个目录节点。例如把 /dev/sda1 安装到 /mnt/win 下,那么 /mnt/win 就是“安装点”。
可是文件系统要先安装后使用。因此,要使用 /mnt/win 这个“安装点”,必然要求它所在文件系统已也经被安装。
也就是说,安装一个文件系统,需要另外一个文件系统已经被安装。
这是一个鸡生蛋,蛋生鸡的问题:最顶层的文件系统是如何被安装的?
答案是,最顶层文件系统的时候是被安装在“根安装点”上的,而根安装点不属于任何文件系统,它对应的 dentry 、inode 是由内核在初始化阶段凭空构造出来的。
最顶层的文件系统叫做“根文件系统”。Linux 在启动的时候,要求用户必须指定一个“根设备”,内核在初始化阶段,将“根设备”安装到“根安装点”上,从而有了根文件系统。这样,文件系统才算准备就绪。此后,用户就可以通过 mount 命令来安装新的设备。
1.path_lookup()函数找到安装点的目录项对象和已安装文件系统对象,即找到flp的dentry和mnt。如图5即:nd->dentry=new_dentry,nd->mnt=mnt;
2.这里只考虑do_new_mount(),如下
1.alloc_vfsmnt()分配一个新的已安装文件系统描述符,并将地址放在mnt局部变量中。对应于图中的ext2_mnt.
2open_bdev_excl()函数打开对应设备文件名的块设备。
3.sget()函数:
在file_system_type对象中有一个fs_supers字段,该字段是具有相同文件系统类型的超级块对象链表的头。即具有相同文件系统类型的超级块对象组成一个双向循环链表,而该链表的地址放在fs_supers字段中。链表元素的向前向后连接存放在超级块对象的s_instances字段中。如图3所示:
图3: 相同文件系统的超级块组成的链表
sget函数则是在这个链表中查找与块设备相关的超级块,若不存在,则调用alloc_super()分配一个超级块对象,并将新分配的超级块对象添加到与它相同的文件系统链表的尾部。同时也将该超级块加入到所有超级块链表中,该链表的第一个元素用super_blocks变量表示。
4.do_kern_mount()函数最后实现图3蓝色线条部分的赋值。
说明:nd->dentry是flp的目录项对象,nd->mnt是/flp所在的已安装的文件系统的对象。
1.do_mountpoint(nd->dentry)函数返回detry->d_mounted的值,即安装在该dentry上的文件系统的数量。
2. follow_down(&nd->mnt,&nd->dentry) 在找到新建的文件系统对象p后,将nd->mnt=p;nd->dentry=p->mnt_root.此处mnt->mnt_root就是/dev/fd0中/的目录项对象。
3.graft_tree()主要实现将新安装的文件系统对象插入到namespace链表,散列表及父文件系统子链表中。
struct vfsmount*lookup_mnt(struct vfsmount*mnt,struct dentry*dentry);
参数:此处mnt是nd->mnt,dentry是nd->dentry.
功能:找到前面创建的安装设备对应的文件系统对象。
由于vfsmount数据结构保存在由父文件系统vfsmount描述符的地址和安装点目录的目录项对象的地址索引的散列表中,散列表存放在mount_hashtable数组中,表中的每一项是具有同一散列值的所有描述符形成的双向循环不链表的头。
所以此处由struct list_head*head=mount_hashtable+hash(mnt,dentry);得到某一散列值的散列链表的头。
通过循环若p为找到的struct vfsmount*的文件系统对象。则p要满足:p->mnt_parent==mnt&&p->mnt_mountpoint==dentry.此时的文件系统对象p才是之前我们创建的文件系统对象。
具有同一散列值的所有描述符形成的双向循环链表,head是mount_hashtable数组中的一个元素。如图4所示
图4:
图5:最终文件系统和目录结构关系图
小知识点:
1 在分配一个新的super_block后,将该超级块对象插入了2个链表。分别是属于同一文件系统的超级块链表,所有超级块链表。
阅读(422) | 评论(0) | 转发(0) |