linux 文件系统(二)图解mount系统调用之挂载关系

在分析代码之前,先看一下mount的图解分析,通过对linux mount的分析,可以进一步了解linux vfs的工作原理。

为了说明的方便,我们下面以这样的场景为例进行描述:

1. 系统中有xfs, ext2和minix等若干文件系统模块

2. 现有/dev/sda1和/dev/sdb1上存在xfs文件系统,/dev/sda2上为ext2文件系统,/dev/sdc1上为minix文件系统

3. 将/dev/sda1挂载到/mnt/a上,将/dev/sdb1挂载到/mnt/b上,将/dev/sdc1不挂载

4. 在第三步之后,将/dev/sda2也挂载到/mnt/a上。再将/dev/sda1同时挂载到/mnt/x上。

1 file_system_type + super block + mount的关系

从file_system_type到super_block到mount实例的角度来看,上述关系大致是这样的:

linux 文件系统(二)图解mount系统调用之挂载关系_第1张图片

系统中存在三个文件系统,也就有三种file_system_type被注册。sda1和sdb1都是xfs文件系统,所以xfs的file_system_type的fs_supers把这两个同为xfs文件系统的super_block串连在自己下面。sda2是ext2文件系统,所以它挂在ext2的file_system_type下。sdc1是minix文件系统,在sdc1的设备上存在着super_block信息,但是我们这里说的super_block是指内存中的,由于sdc1没有被挂载使用,所以没有它的super_block信息被读入内存。

从挂载实例上看,sda1和sda2都挂载到/mnt/a上,但是从上述关系中很难表述它们的区别,需要借助mount和dentry的关系来说明,下面再具体说明。sda1同时挂在了/mnt/a和/mnt/x上,所以它有两个挂载实例对应同一个super_block。sdc1没有被挂载,所以没有挂载实例和它对应。

mount_hashtable是一个全局挂载实例的哈希表,系统中除了根挂载点以外所有的挂载实例都记录在它下面,搜索一个mount实例时需要借助这个mount的父mount实例和dentry实例来计算的出。比如说/mnt上挂载着一个文件系统,/mnt/a和b上分别又挂载着文件系统,此时要想检索/mnt/a(或b),需要以/mnt上的挂载实例和/mnt/a(或者b)的dentry结构为依据计算hash数值从mount_hashtable上得到一个头指针,这个头指针下就是所有父文件系统是在/mnt上且挂载点是/mnt/a(或b)的挂载到/mnt/a或b下的mount实例。(如何理解这句话,我觉比如是/mnt/a这个挂载点,首先a这个目录结构,必然是要存在父文件系统中,该父文件系统必然也要挂载到别的文件系统中,所以必然有一个mount结构来记录该父文件系统的挂载信息,而a作为一个目录,必然有一个dentry相对应,所谓挂载到a目录,需要新的文件系统和挂载点目录产生联系,以便在文件搜扫中从元文件系统的目录dentry跳转到所挂载的文件系统的root dentry,那么如何产生联系,父文件系统的mount挂载实例和父文件系统的挂载点dentry结构为依据计算hash并链入mount_hashtable)(这里其实有一个不太容易注意到的地方,到底什么情况下同一个父文件系统下的同一个dentry下会有多个挂载实例呢?我们不在本文讨论,以后有机会再讨论这个问题。)

上图可以看出file_system_type, super_block和mount实例之间的关系,但是不能看出来父子文件系统之间的相互关系。下面让我们看一下当一个文件系统挂载到另一个文件系统的子目录下的情况。

2 父子挂载点的挂载关系

假设在/mnt上挂载着一个文件系统,根据上面条件我们以/dev/sda1挂载到/mnt/a上为例,来解释一下/mnt/a上这个挂载实例和/mnt的挂载实例的关系,如下图所示:

linux 文件系统(二)图解mount系统调用之挂载关系_第2张图片

父文件系统代表/mnt上的文件系统,子文件系统代表/mnt/b上的文件系统(带颜色的地方为重点要注意的地方)。父子文件系统通过mnt_parent, mount_child, mnt_mounts等成员来联系在一起,每个挂载实例的mnt_sb都指向这个挂载实例所属文件系统的super_block。每个挂载实例的mnt_root都指向这个文件系统的根dentry。

根dentry就是一个文件系统的路径的起始,也就是"/"。比如一个路径名/mnt/a/dir/file。在/mnt/a这个文件系统下看这个文件是/dir/file,这个起始的"/"代表/mnt/a下挂载的文件系统的根,也就是如上图红色所示的dentry,它是这一文件系统的起始dentry。当发现到了一个文件系统的根后,如果想继续探寻完整路径应该根据/mnt/a的挂载实例向上找到其父文件系统,也就是/mnt下挂载的文件系统。/dev/sda1挂载在了/mnt/a上,这里的/mnt/a代表/mnt下文件系统的一个子dentry,如图绿色部分所示。注意红色和绿色是两个文件系统下的两个不同的dentry,虽然不是很恰当的说它们从全局来看是一个路径名。那么从/mnt所在的文件系统看/mnt/a就是/a。最后再往上就到了rootfs文件系统,也就是最上层的根"/"。所以我们之前说过,表示一个文件的路径需要二元组来共同确定。

子文件系统的mnt_mountpoint就指向了父文件系统的一个dentry,这个dentry也就是子文件系统的真正挂载点。可以说子文件系统在挂载后会新创建一个dentry,并在此构建这个文件系统下的路径结构。

宗上所述,/mnt/a上这个新挂载的文件系统创建了一个新的mount, super_block, 根inode和根dentry。在看懂了一个简单的父子文件系统挂载关系后,我们来看下多个文件系统挂载到同一路径名下时又是什么样子

3 多文件系统单挂载点的挂载关系

就像上面所叙述的/dev/sda1挂载到了/mnt/a上,之后/dev/sda2也挂载到/mnt/a上的情况,当两个以上的文件系统先后挂载到同一个路径名下时会是怎样一种情况呢?如下图所示:

linux 文件系统(二)图解mount系统调用之挂载关系_第3张图片

如上图,子文件系统A代表/dev/sda1,子文件系统B代表/dev/sda2。父文件系统和子文件系统A的挂载在之前一节已经说明过了,当/dev/sda2在sda1之后也挂载到/mnt/a上时,其关系就像在上一节基础上又添加了子文件系统B的关系。实际上子文件系统A就是子文件系统B的父文件系统,而唯一不同的是子文件系统B的mnt_mountpoint指向了子文件系统A的根dentry。而新的子文件系统B还是有自己的mount, super_block, 根dentry和根inode。

两个文件系统挂载在同一路径名下会造成之前挂载的文件系统被隐藏,这种实现我们在后续讲解。其实从上面的关系也应该可以想到一些解决的方式。最后再来看一下一个文件系统被挂载到多个不同的路径下的情况。

4 单文件系统多挂载点的挂载关系

同一个文件系统被挂载到不同的路经下,就像上面例子中/dev/sda1被挂载到/mnt/a和/mnt/x两个位置一样,如下图所示:

linux 文件系统(二)图解mount系统调用之挂载关系_第4张图片

一个文件系统对应一个super_block,所以同一个文件系统当然只有一个super_block。但是因为挂载了两次,所有每一次挂载对应一个挂载实例struct mount,也就是有两个mount实例。此外同一个文件系统只有一个根,也就是两个挂载实例公用一个根dentry。但是因为挂载在两个不同的路经下,所以每个挂载实例的mnt_mountpoint指向不同的dentry。由于/mnt/a和/mnt/x都属于同一文件系统的下的两个子目录,所以两个子mount才指向同一个父mount(这个不是必须的)。

你可能感兴趣的:(文件系统,linux,文件系统)