Linux文件系统的挂载过程代码分析

linux系统中是可以通过mount挂载一个文件系统
mount命令格式:

mount [-t vfstype] [-o options] device dir

1.-t vfstype 指定文件系统的类型,通常不必指定。mount 会自动选择正确的类型。常用类型有:

  光盘或光盘镜像:iso9660

  DOS fat16文件系统:msdos

  Windows 9x fat32文件系统:vfat

  Windows NT ntfs文件系统:ntfs

  Mount Windows文件网络共享:smbfs

  UNIX(LINUX) 文件网络共享:nfs
2.-o options 主要用来描述设备或档案的挂接方式。常用的参数有:

  loop:用来把一个文件当成硬盘分区挂接上系统

  ro:采用只读方式挂接设备

  rw:采用读写方式挂接设备

  iocharset:指定访问文件系统所用字符集
  
3.device 要挂接(mount)的设备。

4.dir设备在系统上的挂接点(mount point)。

系统本身也有一个文件目录结构,如果把aufs创建的dentry树绑定到系统本来的dentry树上并建立链接,就可以从原来的系统树遍历到aufs的dentr树。

其实挂载就是这样的 首先是判断dentry的d_mounted成员,如果该成员不为0,说明该dentry是个挂载点,有文件系统挂载,需要特殊处理。当文件系统被挂载的时候,它的vfsmount结构就被链接到内核的一个全局链表—mount_hashtable数组链表 这个数组的每个成员都是一个hash链表,被链接的文件系统的vfsmount被链接到mount_hashtable。这样当发现mnt目录是个特殊的目录时,就从mount_hashtable数组找到hash链表头,再遍历整个hash链表,就能找到被挂载的文件系统的vfsmount,然后mnt目录的dentry就被替换,置换为新文件系统的根目录。

文件系统的挂载就是将文件系统的dentry结构嫁接到目的文件系统的dentry 并且把vfsmount连接到mount_hashtable里去。

一般而言文件系统挂载通过系统调用sys_mount来执行,sys_mount又调用do_mount,do_mount首先获得挂载点目录的dentry结构以及目的系统的vfsmount结构,这些信息保存在一个nameidata结构中。然后根据mount选项调用不同的函数执行mount。aufs第一次执行Mount 所以调用的是do_new_mount函数。

do_new_mount函数实现代码

static int do_new_mount(struct nameidata *nd, char *type, int flags,
            int mnt_flags, char *name, void *data)
{
    struct vfsmount *mnt;

    if (!type || !memchr(type, 0, PAGE_SIZE))
        return -EINVAL;

    /* we need capabilities... */
    if (!capable(CAP_SYS_ADMIN))
        return -EPERM;
    /* 创建超级块对象和root dentry和inode */
    mnt = do_kern_mount(type, flags, name, data);
    if (IS_ERR(mnt))
        return PTR_ERR(mnt);
    /* 调用do_add_mount函数执行挂载部分 */
    return do_add_mount(mnt, nd, mnt_flags, NULL);
}

do_add_mount函数实现代码

int do_add_mount(struct vfsmount *newmnt, struct nameidata *nd,
         int mnt_flags, struct list_head *fslist)
{
    int err;

    down_write(&namespace_sem);
    /* Something was mounted here while we slept */
    /* 判断dentry的d_mounted成员 检查目录本身是否是挂载点目录 如果是就调用follow_down来寻找真正的dentry结构和vfsmount结构 */
    while (d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry))
        ;
    err = -EINVAL;
    if (!check_mnt(nd->mnt))
        goto unlock;

    /* Refuse the same filesystem on the same mount point */
    err = -EBUSY;
    /* 检查同一个文件系统是否在挂载点目录挂载了 如果已经挂载 返回错误 */
    if (nd->mnt->mnt_sb == newmnt->mnt_sb &&
        nd->mnt->mnt_root == nd->dentry)
        goto unlock;

    err = -EINVAL;
    /* 判断源文件系统的根inode是否符号链接,如果是符号链接 返回错误 */
    if (S_ISLNK(newmnt->mnt_root->d_inode->i_mode))
        goto unlock;

    newmnt->mnt_flags = mnt_flags;
    /* 调用graft_tree把aufs的dentry树和目的文件系统的dentry树嫁接到一起 */
    if ((err = graft_tree(newmnt, nd)))
        goto unlock;
    /* 当前fslist为空 跳过 */
    if (fslist) {
        /* add to the specified expiration list */
        spin_lock(&vfsmount_lock);
        list_add_tail(&newmnt->mnt_expire, fslist);
        spin_unlock(&vfsmount_lock);
    }
    up_write(&namespace_sem);
    return 0;

unlock:
    up_write(&namespace_sem);
    mntput(newmnt);
    return err;
}

graft_tree函数实现代码

static int graft_tree(struct vfsmount *mnt, struct nameidata *nd)
{
    int err;
    /* 判断源文件系统是否可以被挂载 Linux系统的一些特殊的文件系统是不能被挂载的 */
    if (mnt->mnt_sb->s_flags & MS_NOUSER)
        return -EINVAL;
    /* 检查挂载点目录是否是一个目录文件以及源文件系统的根inode是否目录文件 */
    if (S_ISDIR(nd->dentry->d_inode->i_mode) !=
          S_ISDIR(mnt->mnt_root->d_inode->i_mode))
        return -ENOTDIR;

    err = -ENOENT;
    mutex_lock(&nd->dentry->d_inode->i_mutex);
    /* 检查挂载点目录是否是废弃的目录 */
    if (IS_DEADDIR(nd->dentry->d_inode))
        goto out_unlock;

    err = security_sb_check_sb(mnt, nd);
    if (err)
        goto out_unlock;

    err = -ENOENT;
    /* 检查挂载点目录是一个根目录或者挂载点目录被缓存到dentry cache中 */
    if (IS_ROOT(nd->dentry) || !d_unhashed(nd->dentry))
        /* 调用attach_recursive_mnt执行挂载操作 mnt是aufs的vfsmount nd是目的文件系统的vfsmount */
        err = attach_recursive_mnt(mnt, nd, NULL);
out_unlock:
    mutex_unlock(&nd->dentry->d_inode->i_mutex);
    if (!err)
        security_sb_post_addmount(mnt, nd);
    return err;
}

attach_recursive_mnt 函数实现代码如下

static int attach_recursive_mnt(struct vfsmount *source_mnt,
            struct nameidata *nd, struct nameidata *parent_nd)
{
    LIST_HEAD(tree_list);
    struct vfsmount *dest_mnt = nd->mnt;
    struct dentry *dest_dentry = nd->dentry;
    struct vfsmount *child, *p;

    if (propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list))
        return -EINVAL;
    /* 处理shared */
    if (IS_MNT_SHARED(dest_mnt)) {
        for (p = source_mnt; p; p = next_mnt(p, source_mnt))
            set_mnt_shared(p);
    }

    spin_lock(&vfsmount_lock);
    /* parent_nd为空 执行else分支  */
    if (parent_nd) {
        detach_mnt(source_mnt, parent_nd);
        attach_mnt(source_mnt, nd);
        touch_mnt_namespace(current->nsproxy->mnt_ns);
    } else {
        mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt);
        commit_tree(source_mnt);
    }

    list_for_each_entry_safe(child, p, &tree_list, mnt_hash) {
        list_del_init(&child->mnt_hash);
        commit_tree(child);
    }
    spin_unlock(&vfsmount_lock);
    return 0;
}

mnt_set_mountpoint 函数实现代码分析

void mnt_set_mountpoint(struct vfsmount *mnt, struct dentry *dentry,
            struct vfsmount *child_mnt)
{
    child_mnt->mnt_parent = mntget(mnt);
    /* 源文件系统的vfsmount对象的mnt_mountpoint指向目的dentry */
    child_mnt->mnt_mountpoint = dget(dentry);
    /* d_mounted+1 判断是否为挂载点有用 */
    dentry->d_mounted++;
}
static void commit_tree(struct vfsmount *mnt)
{
    struct vfsmount *parent = mnt->mnt_parent;
    struct vfsmount *m;
    LIST_HEAD(head);
    struct mnt_namespace *n = parent->mnt_ns;

    BUG_ON(parent == mnt);
    /* 将源文件系统的vfsmount对象链接到namespace的链表尾部 */
    list_add_tail(&head, &mnt->mnt_list);
    list_for_each_entry(m, &head, mnt_list)
        /* vfsmount对象的namespace为父对象的namespace */
        m->mnt_ns = n;
    list_splice(&head, n->list.prev);
    /* 源vfsmount对象链接到mount_hash表 */
    list_add_tail(&mnt->mnt_hash, mount_hashtable +
                hash(parent, mnt->mnt_mountpoint));
    /* 源vfsmount对象链接到父vfsmount对象的链表 */
    list_add_tail(&mnt->mnt_child, &parent->mnt_mounts);
    touch_mnt_namespace(n);
}

你可能感兴趣的:(Linux设备驱动分析)