[文件系统]文件系统学习笔记(八)---mount系统调用(代码相关)

一,mount系统调用--相关代码
源码位置:kernel/fs/Namespace.c文件的do_mount()函数,

[cpp] view plaincopy

  1. long do_mount(char *dev_name,char*dir_name,char*type_page,unsigned long flags,void *data_page)

dev_name指的是要挂载文件系统的名字,如tmpfs,
dir_name指的是文件系统要被挂载的目标目录
type_page指的是要挂载的文件系统的类型
flags指的是挂载选项,如MS_RDONLY等等
data_page指的是一些额外选项等,如wait关键字

1,do_mount()函数首先会做一些参数检查,dir_name不能为空并且大小不能超过一个PAGE大小,将data_page超过一个PAGE大小的部分截断。

if(!memchr(dir_name,0,PAGE_SIZE))

检查data_page的长度是否超过一个page,如果超过,则将超出的部分截断。

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

do_mount()函数首先调用kern_path()函数,将dir_name转换为struct path结构体,

[cpp] view plaincopy

  1. struct path{
  2. struct vfsmount *mnt;
  3. struct dentry *dentry;
  4. };

2,然后根据不同的flag参数去设置不同的mnt_flags临时变量,如果flag中没有包含MS_REMOUNT,MS_BIND,MS_MOVE,MS_SHARED,MS_PRIVATE,等,那么最后会调用
  do_new_mount()函数。

3,do_new_mount()函数

[cpp] view plaincopy

  1. static int do_new_mount(struct path*path,char*type,int flags,int mnt_flags,char*name,void*data)

path参数是dir_name经过kern_path()转换后的path结构体
   type参数指的是文件系统的类型
   flags参数指的是一些mount选项
   mnt_flags参数指的是一些monut选项
   name参数指的是要挂载文件系统的名字,如tmpfs
   data参数指的是一些额外选项等,如wait关键字

do_new_mount()函数首先调用do_kern_mount(type,flags,name,data)函数,该函数的作用是建立一块新的安装块区域,获取一个vfsmount实例,获取源文件系统vfsmount结构,并通过特定文件系统的操作装载到系统系统中,返回装载点的根目录,然后调用do_add_mount(real_mount(mnt),path,mnt_flags)函数,该函数的作用是将mount实例挂载到mount树上去,将源文件系统增加到目的文件系统中。

[cpp] view plaincopy

  1. 1651 static int do_new_mount(struct path *path, char *type, int flags,
  2. 1652                         int mnt_flags, char *name, void *data)
  3. 1653 {
  4. 1654         struct vfsmount *mnt;
  5. 1655
  6. 1656         if (!type)
  7. 1657                 return -EINVAL;
  8. 1658
  9. 1659         /* we need capabilities... */
  10. 1660         if (!capable(CAP_SYS_ADMIN))
  11. 1661                 return -EPERM;
  12. 1662
  13. 1663         lock_kernel();
  14. 1664         mnt = do_kern_mount(type, flags, name, data);
  15. 1665         unlock_kernel();
  16. 1666         if (IS_ERR(mnt))
  17. 1667                 return PTR_ERR(mnt);
  18. 1668
  19. 1669         return do_add_mount(mnt, path, mnt_flags, NULL);
  20. 1670 }

do_kern_mount()函数细节,do_kern_mount()首先调用get_fs_type()函数返回要挂载文件系统的file_system_type实例,file_system_type是在各个文件系统在系统启动的时候 注册进内核的,所有注册的文件系统形成一个单链表,然后do_kern_mount()调用vfs_kern_mount()函数,vfs_kern_mount()函数的作用是分配一个struct mount结构体,然后vfs_kern_mount()调用各个文件系统file_system_type结构的mount成员函数(如ext4则会调用ext4_mount函数),该函数的作用是创建该文件系统的超级快对象,返回该文件系统的根目录(root)的dentry实例,最后将创建的超级快对象赋值给新创建的vfsmount结构所指的超级快,同时vfsmount所指的mnt_root点赋值为超级快所指的根dentry.
   
   do_add_mount()函数细节,该函数作用是将当前mount实例加到mount树上,do_add_mount()函数的两个关键点,lock_mount()函数和graft_tree()函数,lock_mount()检查如果当前要挂载的目录之前已经挂载其它文件系统,则要进行文件系统切换动作,graft是嫁接的意思,是将将要mount的目录树与当前目录的文件系统的目录树连接起来,很像嫁接技术,而原来文件系统的目录树没损伤。

lock_mount()函数主要调用lookup_mnt()函数,该函数返回一个struct vfsmount的实例,lookup_mnt()函数调用__lookup_mnt()函数返回一个struct mount的实例,在同个父文件系统下的同个目录可以作为多个子文件系统的挂载点,所以如果真的挂载了多个子文件系统,那么这几个子文件系统通过散列函数肯定会被放在哈希表里的同一条链表上。__lookup_mnt()函数就是返回该目录下最后挂载的文件系统mount的实例。__lookup_mnt()函数如下所示:

[cpp] view plaincopy

  1. 414 struct vfsmount *__lookup_mnt(struct vfsmount *mnt, struct dentry *dentry,
  2. 415                               int dir)
  3. 416 {
  4. 417         struct list_head *head = mount_hashtable + hash(mnt, dentry);
  5. 418         struct list_head *tmp = head;
  6. 419         struct vfsmount *p, *found = NULL;
  7. 420
  8. 421         for (;;) {
  9. 422                 tmp = dir ? tmp->next : tmp->prev;
  10. 423                 p = NULL;
  11. 424                 if (tmp == head)
  12. 425                         break;
  13. 426                 p = list_entry(tmp, struct vfsmount, mnt_hash);
  14. 427                 if (p->mnt_parent == mnt && p->mnt_mountpoint == dentry) {
  15. 428                         found = p;
  16. 429                         break;
  17. 430                 }
  18. 431         }
  19. 432         return found;
  20. 433 }

graft_tree()函数的实现细节,graft_tree()函数主要调用attach_recursive_mnt()函数,static int attach_recursive_mnt(struct mount*source_mnt,struct path*path,struct path*parent_path),attach_recursive_mnt()函数做的主要操作是1.通过mnt_set_mountpoint()将子vfsmount中的mnt_parent指向父vfsmount,将子vfsmount的mnt_mountpoint指向位于父文件系统中的挂载点dentry;2.通过commit_tree()将子文件系统添加到内核的文件系统哈希表中,并将子文件系统添加到父文件系统对应的子文件系统链表中;
   commit_tree()函数的作用是1.将当前文件系统的名字空间设置为父名字空间,父vfsmount通过当前vfsmount中的mnt_parent获取;再将其连接到父名字空间链表中。2.将当前vfsmount加入到对应哈希值的冲突链表当中,哈希值通过hash()计算。其中,mnt_hash作为链表元素。3.将当前vfsmount加入到父vfsmount对应的子文件系统链mnt_mounts中。其中,mnt_child作为链表元素。

时间: 2024-10-26 10:42:03

[文件系统]文件系统学习笔记(八)---mount系统调用(代码相关)的相关文章

Linux System Programming 学习笔记(八) 文件和目录管理

1. 文件和元数据 每个文件都是通过inode引用,每个inode索引节点都具有文件系统中唯一的inode number 一个inode索引节点是存储在Linux文件系统的磁盘介质上的物理对象,也是LInux内核通过数据结构表示的实体 inode存储相关联文件的元数据 ls -i 命令获取文件的inode number /* obtaining the metadata of a file */ #include <sys/types.h> #include <sys/stat.h>

Lua学习笔记(八):数据结构

table是Lua中唯一的数据结构,其他语言所提供的数据结构,如:arrays.records.lists.queues.sets等,Lua都是通过table来实现,并且在Lua中table很好的实现了这些数据结构. 1.数组 在Lua中通过整数下标访问table中元素,既是数组,并且数组大小不固定,可动态增长.通常我们初始化数组时,就间接地定义了数组的大小,例如: 1 a = {} -- new array 2 for i=1, 1000 do 3 a[i] = 0 4 end 5 6 --数

iOS学习笔记(八)——iOS网络通信http之NSURLConnection

转自:http://blog.csdn.net/xyz_lmn/article/details/8968182 移动互联网时代,网络通信已是手机终端必不可少的功能.我们的应用中也必不可少的使用了网络通信,增强客户端与服务器交互.这一篇提供了使用NSURLConnection实现http通信的方式. NSURLConnection提供了异步请求.同步请求两种通信方式. 1.异步请求 iOS5.0 SDK NSURLConnection类新增的sendAsynchronousRequest:queu

laravel3学习笔记(八)

原作者博客:ieqi.net ==================================================================================================== Web开发框最大的存在意义无疑是为开发者提供便利,这种被提供的便利不仅表现在对于整个应用接口上的梳理.组织和解耦,也表现在更多细微方便,比如各种工具的提供.Laravel3为开发者提供了各种有关web开发的工具,让开发者在开发应用时只需要关注于自身的业务,而不必在拘泥

angular学习笔记(八)

本篇介绍angular控制视图的显示和隐藏: 通过给元素添加ng-show属性或者ng-hide属性来控制视图的显示或隐藏: ng-show: 绑定的数据值为true时,显示元素,值为false时,隐藏元素 ng-hide: 绑定的数据值为true时,隐藏元素,值为false时,显示元素 (其实只要用到其中一个就可以了) 下面来看个简单的例子,点击按钮可以显示/隐藏元素: <!DOCTYPE html> <html ng-app> <head> <title>

六、Android学习笔记_JNI_c调用java代码

1.编写native方法(java2c)和非native方法(c2java): package com.example.provider; public class CallbackJava { // C调用java空方法 public void helloFromJava() { System.out.println("hello from java"); } // C调用java中的带两个int参数的方法 public int Add(int x, int y) { int res

马哥学习笔记八——LAMP编译安装之PHP及xcache

1.解决依赖关系: 请配置好yum源(可以是本地系统光盘)后执行如下命令: # yum -y groupinstall "X Software Development" 如果想让编译的php支持mcrypt扩展,此处还需要下载如下两个rpm包并安装之: libmcrypt-2.5.7-5.el5.i386.rpm libmcrypt-devel-2.5.7-5.el5.i386.rpm 2.编译安装php-5.4.13 首先下载源码包至本地目录. # tar xf php-5.4.13

初探swift语言的学习笔记八(保留了许多OC的实现)

尽管swift作为一门新语言,但还保留了许多OC的机制,使得swift和OC更好的融合在一起.如果没有OC基础的先GOOGLE一下. 如:KVO,DELEGATE,NOTIFICATION. 详见DEMO. import Foundation @objc // 需要打开objc标识,否则@optional编译出错 protocol kvoDemoDelegate { func willDoSomething() @optional func didDoSomething() //可选实现, }

Android学习笔记_JNI_c调用java代码

1.编写native方法(java2c)和非native方法(c2java): package com.example.provider; public class CallbackJava { // C调用java空方法 public void helloFromJava() { System.out.println("hello from java"); } // C调用java中的带两个int参数的方法 public int Add(int x, int y) { int res

《Hibernate学习笔记八》:组件映射

<Hibernate学习笔记八>:组件映射 前面介绍了一对一的单向.双向外键关联,例如,学生证和学生是一个一对一的关系.这篇博文主要是介绍下组件映射,即一个是另一个的一部分,例如,学生证的信息也可以作为学生信息的一部分,即在数据库中只存在学生一个表,而不是有学生和学生证两个表,并且这两个表中有一个一对一的关联关系. 如下: 有人或许会说,那我们就将学生和学生证的信息写在一个类中,则就不需要组件映射了,确实可以这样,但是,根据类的设计原则,我们一般都会将其设计为两个类,然后将学生证的信息作为一个