声明:本文为原创
#####请转贴时保留以下内容######
作者:GTT
请提出宝贵意见Mail:mtloveft@hotmail.com
Linux Version:2.6.33
提示:本文是关于mount命令的实现!
用过Linux的人基本都用过mount命令,我最初用它,是因为想用光驱,所以得加载cdrom。当时以为
是光驱驱动程序呢,那时傻吧,linux0.11最初用的是minix FileSystem,就提供了mount命令。
文件系统是OS的很重要的一部分,如果不了解FS将对其它的子系统产生一些障碍,可大可小,例如
linux 网路协议栈这块,socket利用sockfs的实现就是文件系统的实现。
mount命令格式如下:
mount [-afFhnrvVw] [-L<标签>] [-o<选项>] [-t<文件系统类型>] [设备名] [加载点]
就不多解释了,可以man mount看看。
先看看mount的简单实现Flow
User的命令mount 是在用户空间使用的,所以利用Sys Call来实现对内核的mount的调用。
Sys Call的实现不是本文要讨论的问题,所以绕开它。就直接看内核的实现。
SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, char __user *, type, unsigned long, flags, void __user *, data)
|
dev_name就是设备文件名称,dir_name是mount的安装目录,也可以说是安装节点。type是文件系统的名称。flags是对文件系统笼统的分类,mount处理时可以分类处理了。 data是user自己设置的Option。
SYSCALL mount的代码还是比较简单的,就是从user space 得到dev_name,dir_name,data。
然后就直接调用do_mount()了
SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, char __user *, type, unsigned long, flags, void __user *, data) { int ret; char *kernel_type; char *kernel_dir; char *kernel_dev; unsigned long data_page;
ret = copy_mount_string(type, &kernel_type); //从user space 拷贝文件系统类型的名称
if (ret < 0) goto out_type;
kernel_dir = getname(dir_name); //从user space 拷贝安装目录的名称
if (IS_ERR(kernel_dir)) { ret = PTR_ERR(kernel_dir); goto out_dir; }
ret = copy_mount_string(dev_name, &kernel_dev); //从user space 拷贝设备的名称
if (ret < 0) goto out_dev;
ret = copy_mount_options(data, &data_page); //从user space 拷贝设置的参数
if (ret < 0) goto out_data;
ret = do_mount(kernel_dev, kernel_dir, kernel_type, flags, (void *) data_page);
free_page(data_page); out_data: kfree(kernel_dev); out_dev: putname(kernel_dir); out_dir: kfree(kernel_type); out_type: return ret; }
|
do_mount只是根据flags进行一些简单的设定。
但是kern_path()是根据给定的目录名进行查找,这个过程很复杂,这里先忽略,以后
会详细介绍。
long do_mount(char *dev_name, char *dir_name, char *type_page, unsigned long flags, void *data_page) { struct path path; int retval = 0; int mnt_flags = 0;
/* Discard magic */ if ((flags & MS_MGC_MSK) == MS_MGC_VAL) flags &= ~MS_MGC_MSK;
/* Basic sanity checks */
if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE)) return -EINVAL;
if (data_page) ((char *)data_page)[PAGE_SIZE - 1] = 0;
/* ... and get the mountpoint */ retval = kern_path(dir_name, LOOKUP_FOLLOW, &path); if (retval) return retval;
retval = security_sb_mount(dev_name, &path, type_page, flags, data_page); if (retval) goto dput_out;
/* Default to relatime unless overriden */ if (!(flags & MS_NOATIME)) mnt_flags |= MNT_RELATIME;
/* Separate the per-mountpoint flags */ if (flags & MS_NOSUID) mnt_flags |= MNT_NOSUID; if (flags & MS_NODEV) mnt_flags |= MNT_NODEV; if (flags & MS_NOEXEC) mnt_flags |= MNT_NOEXEC; if (flags & MS_NOATIME) mnt_flags |= MNT_NOATIME; if (flags & MS_NODIRATIME) mnt_flags |= MNT_NODIRATIME; if (flags & MS_STRICTATIME) mnt_flags &= ~(MNT_RELATIME | MNT_NOATIME); if (flags & MS_RDONLY) mnt_flags |= MNT_READONLY;
flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT | MS_STRICTATIME);
if (flags & MS_REMOUNT) retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags, data_page); else if (flags & MS_BIND) retval = do_loopback(&path, dev_name, flags & MS_REC); else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) retval = do_change_type(&path, flags); else if (flags & MS_MOVE) retval = do_move_mount(&path, dev_name); else retval = do_new_mount(&path, type_page, flags, mnt_flags, dev_name, data_page); dput_out: path_put(&path); return retval; }
|
例如MNT_NODEV的意思就是没有设备文件。还有只读啦等等。可以参考定义文件看看实际意义。
然后就调用do_new_mount()了。
static int do_new_mount(struct path *path, char *type, int flags, int mnt_flags, char *name, void *data) { struct vfsmount *mnt;
if (!type) return -EINVAL;
/* we need capabilities... */ if (!capable(CAP_SYS_ADMIN)) return -EPERM;
lock_kernel(); mnt = do_kern_mount(type, flags, name, data); unlock_kernel(); if (IS_ERR(mnt)) return PTR_ERR(mnt);
return do_add_mount(mnt, path, mnt_flags, NULL); }
|
do_new_mount 实在是太简单了,等do_kern_mount()mount完了之后,把vfsmount
挂载到vfsmout树上。
继续...
struct vfsmount * do_kern_mount(const char *fstype, int flags, const char *name, void *data) { struct file_system_type *type = get_fs_type(fstype); struct vfsmount *mnt; if (!type) return ERR_PTR(-ENODEV); mnt = vfs_kern_mount(type, flags, name, data); if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) && !mnt->mnt_sb->s_subtype) mnt = fs_set_subtype(mnt, fstype); put_filesystem(type); return mnt; }
|
根据文件系统名称fstype得到对应的struct file_system_type。
然后就到vfs_kern_mount()这个过程相对比较复杂,主要分析这部分。
struct vfsmount * vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data) { struct vfsmount *mnt; char *secdata = NULL; int error;
if (!type) return ERR_PTR(-ENODEV);
error = -ENOMEM; mnt = alloc_vfsmnt(name); if (!mnt) goto out;
if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) { secdata = alloc_secdata(); if (!secdata) goto out_mnt;
error = security_sb_copy_data(data, secdata); if (error) goto out_free_secdata; }
error = type->get_sb(type, flags, name, data, mnt); if (error < 0) goto out_free_secdata; BUG_ON(!mnt->mnt_sb);
error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata); if (error) goto out_sb;
/* * filesystems should never set s_maxbytes larger than MAX_LFS_FILESIZE * but s_maxbytes was an unsigned long long for many releases. Throw * this warning for a little while to try and catch filesystems that * violate this rule. This warning should be either removed or * converted to a BUG() in 2.6.34. */ WARN((mnt->mnt_sb->s_maxbytes < 0), "%s set sb->s_maxbytes to " "negative value (%lld)\n", type->name, mnt->mnt_sb->s_maxbytes);
mnt->mnt_mountpoint = mnt->mnt_root; mnt->mnt_parent = mnt; up_write(&mnt->mnt_sb->s_umount); free_secdata(secdata); return mnt; out_sb: dput(mnt->mnt_root); deactivate_locked_super(mnt->mnt_sb); out_free_secdata: free_secdata(secdata); out_mnt: free_vfsmnt(mnt); out: return ERR_PTR(error); }
|
这个过程太漫长,需要有一定的耐心才能看下去,代码的可读性稍微有那么点.......
自己体会吧!series(2)继续......
阅读(3125) | 评论(0) | 转发(0) |