免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2256 | 回复: 5
打印 上一主题 下一主题

明明白白我的心--mount series [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-08-17 19:59 |只看该作者 |倒序浏览
明明白白我的心--mount series(1)
文章地址:http://blog.chinaunix.net/u3/114767/showart_2291293.html

作者:GTT
本文档归属http://oldtown.cublog.cn/.转载请注明出处!
请提出宝贵意见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的实现不是本文要讨论的问题,所以绕开它。就直接看内核的实现。
  1. SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
  2.         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()了
  1. SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
  2.         char __user *, type, unsigned long, flags, void __user *, data)
  3. {
  4.     int ret;
  5.     char *kernel_type;
  6.     char *kernel_dir;
  7.     char *kernel_dev;
  8.     unsigned long data_page;

  9.     ret = copy_mount_string(type, &kernel_type); //从user space 拷贝文件系统类型的名称

  10.     if (ret < 0) goto out_type;

  11.     kernel_dir = getname(dir_name); //从user space 拷贝安装目录的名称

  12.     if (IS_ERR(kernel_dir)) {
  13.         ret = PTR_ERR(kernel_dir);
  14.         goto out_dir;
  15.     }

  16.     ret = copy_mount_string(dev_name, &kernel_dev); //从user space 拷贝设备的名称

  17.     if (ret < 0) goto out_dev;

  18.     ret = copy_mount_options(data, &data_page); //从user space 拷贝设置的参数

  19.     if (ret < 0) goto out_data;

  20.     ret = do_mount(kernel_dev, kernel_dir, kernel_type, flags, (void *) data_page);

  21.     free_page(data_page);
  22. out_data:
  23.     kfree(kernel_dev);
  24. out_dev:
  25.     putname(kernel_dir);
  26. out_dir:
  27.     kfree(kernel_type);
  28. out_type:
  29.     return ret;
  30. }
复制代码
do_mount只是根据flags进行一些简单的设定。

但是kern_path()是根据给定的目录名进行查找,这个过程很复杂,这里先忽略,以后

会详细介绍。
  1. long do_mount(char *dev_name, char *dir_name, char *type_page,
  2.           unsigned long flags, void *data_page)
  3. {
  4.     struct path path;
  5.     int retval = 0;
  6.     int mnt_flags = 0;

  7.     /* Discard magic */
  8.     if ((flags & MS_MGC_MSK) == MS_MGC_VAL) flags &= ~MS_MGC_MSK;

  9.     /* Basic sanity checks */

  10.     if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE))
  11.         return -EINVAL;

  12.     if (data_page) ((char *)data_page)[PAGE_SIZE - 1] = 0;

  13.     /* ... and get the mountpoint */
  14.     retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);
  15.     if (retval) return retval;

  16.     retval = security_sb_mount(dev_name, &path, type_page, flags, data_page);
  17.     if (retval) goto dput_out;

  18.     /* Default to relatime unless overriden */
  19.     if (!(flags & MS_NOATIME)) mnt_flags |= MNT_RELATIME;

  20.     /* Separate the per-mountpoint flags */
  21.     if (flags & MS_NOSUID) mnt_flags |= MNT_NOSUID;
  22.     if (flags & MS_NODEV) mnt_flags |= MNT_NODEV;
  23.     if (flags & MS_NOEXEC) mnt_flags |= MNT_NOEXEC;
  24.     if (flags & MS_NOATIME) mnt_flags |= MNT_NOATIME;
  25.     if (flags & MS_NODIRATIME) mnt_flags |= MNT_NODIRATIME;
  26.     if (flags & MS_STRICTATIME) mnt_flags &= ~(MNT_RELATIME | MNT_NOATIME);
  27.     if (flags & MS_RDONLY) mnt_flags |= MNT_READONLY;

  28.     flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE |
  29.            MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |
  30.            MS_STRICTATIME);

  31.     if (flags & MS_REMOUNT)
  32.         retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags, data_page);
  33.     else if (flags & MS_BIND)
  34.         retval = do_loopback(&path, dev_name, flags & MS_REC);
  35.     else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
  36.         retval = do_change_type(&path, flags);
  37.     else if (flags & MS_MOVE)
  38.         retval = do_move_mount(&path, dev_name);
  39.     else
  40.         retval = do_new_mount(&path, type_page, flags, mnt_flags, dev_name, data_page);
  41. dput_out:
  42.     path_put(&path);
  43.     return retval;
  44. }

复制代码
例如MNT_NODEV的意思就是没有设备文件。还有只读啦等等。可以参考定义文件看看实际意义。

然后就调用do_new_mount()了。
  1. static int do_new_mount(struct path *path, char *type, int flags,
  2.             int mnt_flags, char *name, void *data)
  3. {
  4.     struct vfsmount *mnt;

  5.     if (!type) return -EINVAL;

  6.     /* we need capabilities... */
  7.     if (!capable(CAP_SYS_ADMIN)) return -EPERM;

  8.     lock_kernel();
  9.     mnt = do_kern_mount(type, flags, name, data);
  10.     unlock_kernel();
  11.     if (IS_ERR(mnt)) return PTR_ERR(mnt);

  12.     return do_add_mount(mnt, path, mnt_flags, NULL);
  13. }

复制代码
do_new_mount 实在是太简单了,等do_kern_mount()mount完了之后,把vfsmount

挂载到vfsmout树上。

继续...
  1. struct vfsmount * do_kern_mount(const char *fstype, int flags, const char *name, void *data)
  2. {
  3.     struct file_system_type *type = get_fs_type(fstype);
  4.     struct vfsmount *mnt;
  5.     if (!type) return ERR_PTR(-ENODEV);
  6.     mnt = vfs_kern_mount(type, flags, name, data);
  7.     if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) && !mnt->mnt_sb->s_subtype)
  8.         mnt = fs_set_subtype(mnt, fstype);
  9.     put_filesystem(type);
  10.     return mnt;
  11. }
复制代码
根据文件系统名称fstype得到对应的struct file_system_type。

然后就到vfs_kern_mount()这个过程相对比较复杂,主要分析这部分。
  1. struct vfsmount * vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
  2. {
  3.     struct vfsmount *mnt;
  4.     char *secdata = NULL;
  5.     int error;

  6.     if (!type) return ERR_PTR(-ENODEV);

  7.     error = -ENOMEM;
  8.     mnt = alloc_vfsmnt(name);
  9.     if (!mnt) goto out;

  10.     if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {
  11.         secdata = alloc_secdata();
  12.         if (!secdata) goto out_mnt;

  13.         error = security_sb_copy_data(data, secdata);
  14.         if (error) goto out_free_secdata;
  15.     }

  16.     error = type->get_sb(type, flags, name, data, mnt);
  17.     if (error < 0) goto out_free_secdata;
  18.     BUG_ON(!mnt->mnt_sb);

  19.      error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata);
  20.      if (error) goto out_sb;

  21.     /*
  22.      * filesystems should never set s_maxbytes larger than MAX_LFS_FILESIZE
  23.      * but s_maxbytes was an unsigned long long for many releases. Throw
  24.      * this warning for a little while to try and catch filesystems that
  25.      * violate this rule. This warning should be either removed or
  26.      * converted to a BUG() in 2.6.34.
  27.      */
  28.     WARN((mnt->mnt_sb->s_maxbytes < 0), "%s set sb->s_maxbytes to "
  29.         "negative value (%lld)\n", type->name, mnt->mnt_sb->s_maxbytes);

  30.     mnt->mnt_mountpoint = mnt->mnt_root;
  31.     mnt->mnt_parent = mnt;
  32.     up_write(&mnt->mnt_sb->s_umount);
  33.     free_secdata(secdata);
  34.     return mnt;
  35. out_sb:
  36.     dput(mnt->mnt_root);
  37.     deactivate_locked_super(mnt->mnt_sb);
  38. out_free_secdata:
  39.     free_secdata(secdata);
  40. out_mnt:
  41.     free_vfsmnt(mnt);
  42. out:
  43.     return ERR_PTR(error);
  44. }

复制代码
这个过程太漫长,需要有一定的耐心才能看下去,代码的可读性稍微有那么点.......

自己体会吧!series(2)继续......

论坛徽章:
0
2 [报告]
发表于 2010-08-17 20:02 |只看该作者
明明白白我的心--mount series(2)
文章地址  : http://blog.chinaunix.net/u3/114767/showart_2291298.html

先来看看mount的详细流程:



注册文件系统时要提供两个方法,
get_sb 和 kill_sb 分别在mount 和umount时调用。
而文件系统在内核里是以树状结构存在的,也就是
一个文件系统里可以mount其他的类型的文件系统,
当然也可以mount同类型的文件系统。
每个文件系统可以看作是VSF的一个实例。
mount文件系统就分配一个vfsmount结构。
vfsmount把各个文件系统用各种链表串起来。这样
以后查找文件系统时,根据条件在链表里查找。
命令mount 需要提供几个的参数,简单的格式如下
mount -t vfstype device dir
vfstype就是文件系统名称,device就是设备了,硬盘,cdrom,usb等等
dir 就是要安装的节点。
这个文档是分析最简单的文件系统ramfs,因为它不需要设备,
这样也不需要根buffer cache, device driver等打交道,适合了解文件系统。
不过我认为如果想掌握或者精通文件系统这块,必须分析几个文件系统,
minix比较简单,然后ext2,分析实际的文件系统,掌握这两个其它的就自然
通了,如果做网络通讯的话,sockfs也应该看看,不过有前几个文件系统的
基础的话,sockfs还是比较简单的。至于sysfs, procfs,内核开发经常用,应该
看看。不说废话了,继续分析。
第一步分配vfsmount,并初始化他的各种链表。方法名alloc_vfsmnt()
第二步分配super block, super block 简称sb,这个相当于国骂啦。我是
这个反映,或者是softbank. 为了得到super block 就调用注册文件系统时
注册的get_sb() . get_sb是个漫长的过程。不过内核VSF提供了几个通用的
得到super block 的方法。有
get_sb_nodev  没有设备文件 ramfs 等
get_sb_ns       有设备文件    minix, ext2等
get_sb_single   文件系统只有一个super block 如sysfs等
get_sb_pseudo 不能被客户端mount的文件系统sockfs, pipefs, bdev等
ramfs 是利用ram 的文件系统,所以不需要实际的设备。所以就
用get_sb_nodev这个方法了。
int get_sb_nodev(struct file_system_type *fs_type, int flags, void *data,
int (*fill_super)(struct super_block *, void *, int), struct vfsmount *mnt)
需要提供fill_super call back 方法。
ramfs的文件系统定义如下,
static struct file_system_type ramfs_fs_type = {
    .name  = "ramfs",
    .get_sb  = ramfs_get_sb,
    .kill_sb = ramfs_kill_sb,
};
get_sb的定义如下
int ramfs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct vfsmount *mnt)
{
    return get_sb_nodev(fs_type, flags, data, ramfs_fill_super, mnt);
}
所以callback 方法是 ramfs_fill_super。

论坛徽章:
0
3 [报告]
发表于 2010-08-17 20:04 |只看该作者
明明白白我的心--mount series(3)

文章地址:  http://blog.chinaunix.net/u3/114767/showart_2292125.html

先把mount实现的source code贴出来,稍微注释了一点。主要是跟上一篇文章对应以下。







论坛徽章:
0
4 [报告]
发表于 2010-08-17 20:10 |只看该作者
mount series 只是代码简单说明了一下,详细的请参看

明明白白我的心--fs series
文章地址
明明白白我的心--fs series(1)   : http://blog.chinaunix.net/u3/114767/showart_2292149.html
明明白白我的心--fs series(2)   : http://blog.chinaunix.net/u3/114767/showart_2292171.html
明明白白我的心--fs series(3)   : http://blog.chinaunix.net/u3/114767/showart_2292187.html
明明白白我的心--fs series(4)   : http://blog.chinaunix.net/u3/114767/showart_2292196.html
明明白白我的心--fs series(5)   : http://blog.chinaunix.net/u3/114767/showart_2300161.html
明明白白我的心--fs series(6)   : http://blog.chinaunix.net/u3/114767/showart_2300171.html
明明白白我的心--fs series(7)   : http://blog.chinaunix.net/u3/114767/showart_2300174.html
明明白白我的心--fs series( 8 ) : http://blog.chinaunix.net/u3/114767/showart_2300176.html
明明白白我的心--fs series(9)   : http://blog.chinaunix.net/u3/114767/showart_2300215.html

论坛徽章:
5
摩羯座
日期:2014-07-22 09:03:552015元宵节徽章
日期:2015-03-06 15:50:392015亚冠之大阪钢巴
日期:2015-06-12 16:01:352015年中国系统架构师大会
日期:2015-06-29 16:11:2815-16赛季CBA联赛之四川
日期:2018-12-17 14:10:21
5 [报告]
发表于 2010-08-17 20:30 |只看该作者

楼主的图上作业做得很好啊
向楼主学习

论坛徽章:
0
6 [报告]
发表于 2010-08-17 20:44 |只看该作者
楼主的图上作业做得很好啊
向楼主学习
T-Bagwell 发表于 2010-08-17 20:30


一般一般啦。你的文章刚才我也看过了,很不错!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP