linux系统中是可以通过mount挂载一个文件系统 mount命令格式:
mount [-t vfstype] [-o options] device dir1.-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); }