如何挂载一个文件系统


挂载一个文件系统中最重要的数据结构有以下3个:
1.          file_system_type 要挂载的文件系统类型。
2.          super_block其中有怎样获取该文件系统相关数据的方法。
3.          vfsmount 这个数据结构起到被挂载文件系统和挂载点文件系统的枢纽所用
这几个数据结构的关系,请着重看下图的红色椭圆形区域:



 
 
对照上图,我们可以得出结论,如果要挂载一个文件系统需要做的工作:
1.查找相应的文件系统类型,such as ext2 ext3 or ntfs or yaffs2 or rootfs etc.
2.查找相应的挂载点,方法:一路摸索,顺藤摸瓜。找到其dentry和inode.
3.生成一个vfsmount,这个数据结构是挂载点目录以及正在挂载的这个文件系统的根目录(依据的数据结构是:mnt_mountpoing和mnt_root)的枢纽(非常重要),并将这个vfsmount放在到hashtable中。这个hashtable的hash值运算依据的是挂载点目录以及挂载点inode.除了挂载到hashtable中外,还要链到父挂载点的子链表中。
放在hashtable的原因是,将来在lookup其内的目录或者文件时,需要根据挂载点的目录和挂载点的inode取hash值快速得到vfsmount。
4.主要的工作完成后,还需要把正在挂载的这个文件系统的根目录的inode和dentry取出来放在内存中,其中dentry的值还要赋给vfsmount的mnt_root.
当然这个第4步骤和第3步骤可能会有混合,主要是为了给vfsmount->mnt_root赋值,所以,需要取该文件系统的根目录。
 
按照以上的分析,我们对照review一下内核的源码的流程(为了方便,将与分析无关的语句去掉了):
2393 SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
2394                 char __user *, type, unsigned long, flags, void __user *, data)
2395 {
…….//做些参数检测,然后将用户空间的相关变量拷贝到内核区间
2419
2420         ret = do_mount(kernel_dev, kernel_dir, kernel_type, flags,
2421                 (void *) data_page);
-------------------------------------------------------------------------------------
2191 long do_mount(char *dev_name, char *dir_name, char *type_page,
2192                   unsigned long flags, void *data_page)
2193 {
…做些参数检测工作,然后寻找挂载点。
2211         retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);
2212         if (retval)
2213                 return retval;
….
//下面根据参数,选择相应的函数,我们假定最一般的情况,选择do_new_mount
2244         if (flags & MS_REMOUNT)
2245                 retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags,
2246                                     data_page);
2247         else if (flags & MS_BIND)
2248                 retval = do_loopback(&path, dev_name, flags & MS_REC);
2249         else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
2250                 retval = do_change_type(&path, flags);
2251         else if (flags & MS_MOVE)
2252                 retval = do_move_mount(&path, dev_name);
2253         else
2254                 retval = do_new_mount(&path, type_page, flags, mnt_flags,
2255                                       dev_name, data_page);
-------------------------------------------------------------------------------------------
1877 /*
1878  * create a new mount for userspace and request it to be added into the
1879  * namespace's tree
1880  */
1881 static int do_new_mount(struct path *path, char *type, int flags,
1882                         int mnt_flags, char *name, void *data)
1883 {
…做一些其他工作后,开始调用do_kenn_mount,从传参的情况,可以推测,要做的工作是:
1.       找到相关的文件系统
2.       从即将挂载的设备中,取出节点,然后初始化vfsmount
3.       将返回值赋给mnt.
那么最重要的vfsmount已经得到
1894         mnt = do_kern_mount(type, flags, name, data);
1895         if (IS_ERR(mnt))
1896                 return PTR_ERR(mnt);
1897
1898         err = do_add_mount(mnt, path, mnt_flags);
1899         if (err)
1900                 mntput(mnt);
1901         return err;
1902 }
-------------------------------------------------------------------------------------------

do_new_mount->do_kern_mount-> vfs_kern_mount
964 vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
 965 {
下面分配一个vfsmount
 975         mnt = alloc_vfsmnt(name);
 976         if (!mnt)
 977                 goto out;
...
根据情况,调用不同的函数,将vfsmount数据结构填好。以及分配superblock,以及相关的dentry和inode.
 992         if (type->mount) {
 993                 root = type->mount(type, flags, name, data);
 994                 if (IS_ERR(root)) {
 995                         error = PTR_ERR(root);
 996                         goto out_free_secdata;
 997                 }
 998                 mnt->mnt_root = root;
 999                 mnt->mnt_sb = root->d_sb;
1000         } else {
1001                 error = type->get_sb(type, flags, name, data, mnt);
1002                 if (error < 0)
1003                         goto out_free_secdata;
----------------------------------------------------------------------------------------------------------------
do_new_mount->do_add_mount
1938 static int do_add_mount(struct vfsmount *newmnt, struct path *path, int mnt_flags)
1939 {
….
为了防止前面在获取文件系统内部根目录时其他进程已经挂载到该挂载点(可能是其他文件系统,也可能是正在挂载的文件系统)因此要检测一下
1956         if (path->mnt->mnt_sb == newmnt->mnt_sb &&
1957             path->mnt->mnt_root == path->dentry)
1958                 goto unlock;
1959
1960         err = -EINVAL;
1961         if (S_ISLNK(newmnt->mnt_root->d_inode->i_mode))
1962                 goto unlock;
1963
1964         newmnt->mnt_flags = mnt_flags;
//把vfsmount插入到合适的链表中,注意,在这里面还将vfsmount中的成员mnt_mountpoint重新赋值为path->dentry.而不是在vfs_kern_mount时赋的mnt_root
1965         err = graft_tree(newmnt, path);
1966
1967 unlock:
1968         up_write(&namespace_sem);
1969         return err;
1970 }
 
从这个代码跟踪,可以发现,基本上挂载一个文件系统依据了我们前面分析的步骤。
欢迎批评指正。
<完>

评论

此博客中的热门博文

Linux/ARM Page Table Entry 属性设置分析

提交了30次才AC ---【附】POJ 2488解题报告

笔记