IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

Linux内核文件系统挂载分析

edsionte's TechBlog 2014-11-28 12:47:03 累计浏览 3,048 次
本机暂存

本文将针对内核版本3.2.0中的mount系统调用实现过程进行简单说明。

1.数据结构

下面将对文件系统挂载过程中涉及到的两个主要数据结构vfsmount和path进行节本说明。

1.1 struct vfsmount

每个挂载在内核目录树中的文件系统都将对应一个vfsmount结构,下面将对该结构中的部分字段进行说明。假设设备/dev/sdc为ntfs文件系统,现需要将其挂载在文件系统为ext3的/home/edsionte/work下。因此,/home/edsionte/work可以被称为ntfs文件系统的挂载点,并且称ntfs文件系统与ext3文件系统形成父子文件文件系统关系。同时ntfs也可称为源文件系统,而ext3也可称为目的文件系统。

struct list_head mnt_hash;

内核将系统内所有已挂载的文件系统通过散列表的形式进行组织,每个vfsmount将处于其对应哈希值的冲突链表当中。mnt_hash字段则为具体冲突链表的元素。

struct list_head mnt_mounts;

如果当前文件系统下挂载了其他的子文件系统,那么这些子文件系统将通过自身vfsmount中的mnt_child字段组成一个链表,该链表头为父文件系统中的mnt_mounts字段。

struct list_head mnt_child;

当前文件系统将通过该字段与其他父文件系统下的子文件系统组成一个链表。

struct vfsmount *mnt_parent;

该字段指向父文件系统对应的vfsmount结构。即指向ext3文件系统对应的vfsmount结构。

struct dentry *mnt_mountpoint;

该字段表示源文件系统在目的文件系统中挂载点对应的dentry结构。/home/edsionte/work为挂载点,则该字段指向目录项work。

struct dentry *mnt_root;

指向当前文件系统的根目录项。对于源文件系统ntfs来说,根目录项相对为/,但在整个系统目录树中,根目录项为work。

struct super_block *mnt_sb;

每个文件系统都将对应一个super_block结构,该字段指向/dev/sdc设备上文件系统对应的超级块。

struct list_head mnt_list;

所有处于一个名字空间的文件系统通过mnt_list字段链接在一起,而该链表的表头为该名字空间结构中的list字段。

struct mnt_namespace *mnt_ns;

该字段表示当前vfsmount所对应的名字空间结构。

1.2 struct path

path结构由vfsmount结构和dentry结构组成。该结构在挂载文件系统时表示目的文件系统的vfsmount结构和挂载点dentry。

2.函数调用关系图

do_mount

3.实现

3.1 mount系统调用服务例程

mount()系统调用服务例程为:

SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, char __user *, type, unsigned long, flags, void __user *, data)

其内部实现主要是将用户态的参数依次复制到内核态,接着调用内核函数do_mount()。

3.2 do_mount()

该函数内部首先通过kern_path()获取目的文件系统的path结构,即挂载点目录项以及目的文件系统的vfsmount结构;接着,通过检查flags对挂载操作进行不同目的的分发。这里我们只讨论最普通的情形,即将一个文件系统挂载在一个新的挂载点中,这种情况调用do_new_mount()。

3.3 do_new_mount()

这个函数描述的是挂载一个新文件系统最普遍的情形,主要包括以下几点:

1.文件系统类型、操纵权限检查等;

2.通过do_kern_mount()获取源文件系统的vfsmount结构;

3.通过do_add_mount()将源文件系统增加到目的文件系统中;

3.4 do_add_mount()

1.flags参数合法性检查;

2.检查指定的目的文件系统是否为当前文件系统。如果是,则失败;

3.检查源文件系统的根inode是否为链接文件。如果是,则失败;

4.通过graft_tree()将源文件系统装载到目的文件系统中。其内部graft又封装了attach_recursive_mnt();

3.5 attach_recursive_mnt()

该函数的主要作用是设置父子文件系统的映射关系。具体操作为:

1.通过mnt_set_mountpoint()将子vfsmount中的mnt_parent指向父vfsmount,将子vfsmount的mnt_mountpoint指向位于父文件系统中的挂载点dentry;

2.通过commit_tree()将子文件系统添加到内核的文件系统哈希表中,并将子文件系统添加到父文件系统对应的子文件系统链表中;

3.6 commit_tree()

1.将当前文件系统的名字空间设置为父名字空间,父vfsmount通过当前vfsmount中的mnt_parent获取;再将其连接到父名字空间链表中。

2.将当前vfsmount加入到对应哈希值的冲突链表当中,哈希值通过hash()计算。其中,mnt_hash作为链表元素。

3.将当前vfsmount加入到父vfsmount对应的子文件系统链表mnt_mounts中。其中,mnt_child作为链表元素。

从整个挂载的处理流程上看,挂载的本质就是将源文件系统的vfsmount结构连接到目的文件系统对应的vfsmount结构中,即具体涉及到两个vfsmount中字段的指向问题。两个vfsmount具体父子等级关系,这也对应着内核中目录树的父子等级关系。

参考资料:

1.深入理解Linux内核:http://book.douban.com/subject/2287506/;

2.深入Linux内核架构:http://book.douban.com/subject/4843567/;

3.Linux内核探秘:http://book.douban.com/subject/25817503/;

同分类推荐文章

  1. 等了十年的 Go 链式管道,终于来了:seq 让你像写 Scala 一样写 Go (2026-06-25 18:38:18)
  2. Go 实验特性详解 (2026-06-21 10:05:27)
  3. amd64 微架构级别对 Go 程序性能提升多少? (2026-06-21 09:38:49)

查看更多 后端 文章 →

建议继续学习

  1. Linux如何统计进程的CPU利用率 (累计阅读 16,308)
  2. 我的 RHCA 之路 (累计阅读 14,013)
  3. Linux内存点滴 用户进程内存空间 (累计阅读 13,230)
  4. 给程序员新手的一些建议 (累计阅读 13,089)
  5. Linux 性能监控、测试、优化工具 (累计阅读 13,011)
  6. 关于linux内存free的一些事情 (累计阅读 12,867)
  7. ps - 按进程消耗内存多少排序 (累计阅读 12,688)
  8. Google怎么用linux (累计阅读 12,581)
  9. Linux Used内存到底哪里去了? (累计阅读 11,867)
  10. find命令的一点注意事项 (累计阅读 11,866)