到底什么是挂载?

article/2025/11/7 7:24:04
  • 首发公号:Rand_cs

L i n u x Linux Linux 的文件系统中,有个很重要的概念就是挂载,挂载大家应该都很熟悉,除了根文件系统,其他所有文件系统都要先挂载到根文件系统中的某个目录之后才能访问。

所谓的根文件系统就是系统启动的时候安装的第一个文件系统,它也是内核映像所在的文件系统。而 挂载到某个目录某个目录 就是所谓的挂载点。

L i n u x Linux Linux 中有专门的命令来挂载文件系统,mount device dir d e v i c e device device 为要挂载的设备文件名, d i r dir dir 为挂载点。这里所说的设备不是真的指单个实体设备,而是其上的逻辑设备,比如说一个磁盘上的不同分区都可以看作是不同的设备。

每个设备都有一个设备号来标识,设备号可以分为两部分,一部分叫做主设备号 M a j o r Major Major N u m b e r Number Number,它用来标识某一类型的设备,比如说磁盘。另一部分叫做次设备号 M i n o r Minor Minor N u m b e r Number Number,它来标识某一具体设备,比如说磁盘上的某一具体分区。

当文件系统挂载到某个目录后,我们就可以通过这个目录来访问该文件系统,很多地方就只是简单的这样讲了一下,但其实这只能说是挂载的作用,那到底什么是挂载,要想解决这个问题还是只能从源码着手。下面我将根据 L i n u x Linux Linux 0.11 0.11 0.11 的代码来讲述挂载,涉及的东西比较多,我们只讨论相关的部分。

数据结构

m_inode,内存中的 inode

struct m_inode {/********略*********/unsigned short i_mode;   //文件类型和属性unsigned char i_mount;   //是否有文件系统挂载到这儿unsigned short i_zone[9]; //索引取,如果是块/字符设备文件i_zone[0]是设备号/********略*********/
};

super_block,内存中的超级块

struct super_block {/********略*********/unsigned short s_magic;    //文件系统魔数/********略*********/unsigned short s_dev;      //设备号/********略*********/struct m_inode * s_isup;   //被挂载的文件系统的根目录inodestruct m_inode * s_imount; //该文件系统被安装到此inode
};

所谓的内存中的超级块和 i n o d e inode inode 指的是两者在内存中的缓存:

struct super_block super_block[NR_SUPER];
#define NR_SUPER 8
struct m_inode inode_table[NR_INODE];
#define NR_INODE 32

可以看出在 L i n u x Linux Linux 0.11 0.11 0.11 里面内存中最多同时存在 8 个超级块和 32 个文件的 i n o d e inode inode。这个缓存与我们平时所说的缓存差不多,当系统要想获取一个 i n o d e inode inode 时,会先在 i n o d e _ t a b l e inode\_table inode_table 中寻找有没有该 i n o d e inode inode,如果有的话就直接返回,如果没有,就从设备上将 i n o d e inode inode 读到 i n o d e _ t a b l e inode\_table inode_table 之后再返回。

相关操作

在看 m o u n t mount mount 挂载之前,先来看看一些操作函数。

static struct super_block * read_super(int dev);

如果缓存区中没有该设备的超级块,则先找一个空闲的超级块槽,然后从设备上读取超级块到找到的空闲超级块槽。如果该设备的超级块已经在缓存区中且数据有效,则直接返回该超级块的指针。

struct super_block * get_super(int dev);

根据设备号 d e v dev dev 从超级块数组当中获取超级块。

void put_super(int dev);

释放指定的设备超级块,也就是将超级块的 s _ d e v s\_dev s_dev 字段清 0,如此使得该超级块槽空闲出来。

struct m_inode * namei(const char * pathname);

n a m e i namei namei 函数根据路径 p a t h n a m e pathname pathname 获取末尾文件的 i n o d e inode inode,这个函数应该是有印象的吧,在 x v 6 xv6 xv6 中也有类似的函数。如果不太清楚的话,我这里举个例子:如果参数路径是 /a/b/c,调用 n a m e i namei namei 之后就会返回文件 c c c i n o d e inode inode

挂载

挂载的实现还是挺简单的,来看代码

int sys_mount(char * dev_name, char * dir_name, int rw_flag) //将名称为dev_name的设备上的文件系统挂载到目录dir_name上
{struct m_inode * dev_i, * dir_i;struct super_block * sb;int dev;if (!(dev_i=namei(dev_name))) //没有找到该设备return -ENOENT;dev = dev_i->i_zone[0];if (!S_ISBLK(dev_i->i_mode)) { //不是块设备iput(dev_i);return -EPERM;}iput(dev_i);   //释放该设备的inodeif (!(dir_i=namei(dir_name)))  //解析获取挂载点的inodereturn -ENOENT;if (dir_i->i_count != 1 || dir_i->i_num == ROOT_INO) { //如果挂载点的引用数不等于1获取挂载点为根目录iput(dir_i);return -EBUSY;}if (!S_ISDIR(dir_i->i_mode)) {  //挂载点不是目录iput(dir_i);return -EPERM;}if (!(sb=read_super(dev))) {  //将设备上的文件系统的超级块读取到内存中iput(dir_i);return -EBUSY;}if (sb->s_imount) {  //如果该文件系统已挂载iput(dir_i);return -EBUSY;}if (dir_i->i_mount) {  //如果挂载点已经挂载了其他文件系统iput(dir_i);return -EPERM;}sb->s_imount=dir_i;   //将挂载点的inode记录到设备的超级块中dir_i->i_mount=1;     //表示该挂载点已经挂载了该文件系统dir_i->i_dirt=1;		/* NOTE! we don't iput(dir_i) */ //我们不会释放挂载点的inodereturn 0;			/* we do that in umount */ //我们在umount卸载文件系统时释放
}

其流程图为:

上图为 m o u n t mount mount 的实现过程,除了各种检查之外, m o u n t mount mount 实际上只做了两件事:

  1. 将要挂载的文件系统的超级块读到内存里的超级块槽
  2. 设置该超级块的 s b → s _ i m o u n t sb \rightarrow s\_imount sbs_imount 字段为 挂载点的 i n o d e inode inode,表示文件系统挂载到这个目录上,设置挂载点 i n o d e inode inode i _ m o u n t i\_mount i_mount 字段为 1 表示该目录上挂载的有文件系统

另外再解释几点:

  1. 字符设备,提供连续的数据流,应用程序可以顺序读取数据,通常不支持随机存取,像前面提到过的键盘串口都属于字符设备。块设备,应用程序能够随机访问设备的数据,程序可以自行确定数据的位置,比如说硬盘就是典型的块设备。很明显地文件系统只能存放在块设备上
  2. 挂载点只能是个目录文件,文件系统都是挂载到目录上的
  3. 挂载文件系统时,挂载点这个目录文件只能在当前引用,也就是说在挂载文件系统的时候,还有其他地方正在使用挂载点目录的话就不对

这就是挂载的本质,有没有感觉简单的同时又还是模模糊糊的?为什么将文件系统挂载到某个目录之后,这个目录就能表示被挂载的文件系统。解决这个问题还是要再来捋捋文件系统是如何寻找一个文件的,也就是 n a m e i namei namei 函数,比如说给定一个路径 /a/b,这是一个绝对路径,如何从最开始的根目录寻到文件 b b b 的呢?

这个问题我在 x v 6 xv6 xv6 文件系统里面也详细说过, L i n u x Linux Linux 里也类似。这里我们假设 a a a 文件都是目录文件, b b b 是一个普通文件。首先根目录文件就是一个个目录项,在其中寻找文件名为 a a a 的目录项,从中获取 a a a 目录文件的 i n o d e _ a inode\_a inode_a,根据 i n o d e _ a inode\_a inode_a 的索引字段找到 a a a 目录文件的数据,也是一个个目录项,在其中寻找文件名为 b b b 的目录项,从中获取普通文件 b b b i n o d e _ b inode\_b inode_b 然后返回。

上述所说的获取某个 i n o d e inode inode,使用的函数是, i g e t ( d e v , n r ) iget(dev, nr) iget(dev,nr),其意为从设备 d e v dev dev 中获取编号为 n r nr nr i n o d e inode inode。这个函数就会判断编号为 n r nr nr i n o d e inode inode 上是否挂载的有文件系统,来看相关代码:

struct m_inode * iget(int dev, int nr){/*********略**********/inode = inode_table;    //从inode表中第一个元素开始while (inode < NR_INODE+inode_table) {  //扫描内存里缓存的inode表if (inode->i_dev != dev || inode->i_num != nr) { //如果设备号对不上或者inode编号对不上inode++;   //下一个continue;}wait_on_inode(inode);   //等待该inode解锁if (inode->i_dev != dev || inode->i_num != nr) { //因为等待过程中inode可能会发生变化,所以再次判断inode = inode_table;continue;}inode->i_count++;  //找到了该inode,将其引用数加1if (inode->i_mount) {   //如果该inode上挂载的有文件系统int i;for (i = 0 ; i<NR_SUPER ; i++)   //在内存中缓存的超级块中寻找挂载点为当前inode的超级块if (super_block[i].s_imount==inode)  //找到了,breakbreak;if (i >= NR_SUPER) {  //没找到,返回printk("Mounted inode hasn't got sb\n");if (empty) iput(empty);return inode;}iput(inode);   //释放当前inodedev = super_block[i].s_dev;  //将设备号重新设置为被挂载的文件系统所在的设备号nr = ROOT_INO;    //将要寻找的inode编号重新设置为根目录的inode编号inode = inode_table;   //从内存的inode表第一个元素重新开始寻找inodecontinue;}if (empty)  //释放临时找的空闲inodeiput(empty);  return inode;   //返回获取到的inode}
}

其完整的流程图如下:

这是我根据赵炯画的图改编,因为没有详细讲述 i g e t iget iget 的代码,所以主要关注虚线方框里面的就行。

如果在 i n o d e _ t a b l e inode\_table inode_table 中找到相应的 i n o d e inode inode,就判断 i n o d e → i _ m o u n t = = 1 inode \rightarrow i\_mount == 1 inodei_mount==1,如果为真,表示该 $inode $表示的目录文件上面挂载的有文件系统。此时这个目录应该表示被挂载的文件系统的根目录,所以设置 d e v = 超 级 块 表 示 的 设 备 dev = 超级块表示的设备 dev= n r = 1 nr = 1 nr=1,原目录就被隐藏掉了。举个例子再说明一下,假如调用 i g e t ( 1 , 99 ) iget(1, 99) iget(1,99),本来我是要获取 1 号设备的第 99 个 i n o d e inode inode,然后发现这个 i n o d e inode inode 指向的目录上面挂载的有 2 号设备的文件系统,那么我们就去寻找 2 号设备的根目录 i n o d e inode inode 然后返回。所以看起来调用 i g e t ( 1 , 99 ) iget(1, 99) iget(1,99) 实则调用的 i g e t ( 2 , 1 ) iget(2, 1) iget(2,1),这也就是为什么说将文件系统挂载到某个目录之后,这个目录就被屏蔽了的原因所在

到此,对文件系统的挂载应该有个很清晰的认识呢,最后来看看文件系统的卸载,基本上就是挂载的逆操作,来简单看看:

int sys_umount(char * dev_name)
{struct m_inode * inode;struct super_block * sb;int dev;if (!(inode=namei(dev_name)))  //解析获取设备文件的inodereturn -ENOENT;dev = inode->i_zone[0];   //对于块/字符设备,设备号记录在i_zone[0]if (!S_ISBLK(inode->i_mode)) {  //如果不是块设备iput(inode);return -ENOTBLK;}iput(inode);  //释放设备文件的inodeif (dev==ROOT_DEV)  //如果要卸载的是根文件系统return -EBUSY;if (!(sb=get_super(dev)) || !(sb->s_imount)) //如果没有获取到设备的超级块或者如果挂载点为空return -ENOENT;if (!sb->s_imount->i_mount) //如果挂载点的挂载标识为空printk("Mounted inode has i_mount=0\n");//检查是否有进程在使用将要卸载文件系统上的文件for (inode=inode_table+0 ; inode<inode_table+NR_INODE ; inode++) if (inode->i_dev==dev && inode->i_count)return -EBUSY;sb->s_imount->i_mount=0;  //挂载标识设为0iput(sb->s_imount);   //释放挂载点的inodesb->s_imount = NULL;  //超级块的挂载点字段设为空iput(sb->s_isup);    //释放被卸载的文件系统的根目录inodesb->s_isup = NULL;   //根目录inode字段清0put_super(dev);    //释放设备超级块sync_dev(dev);     //更新的信息同步到设备return 0;
}

文件系统的卸载主要就是释放超级块,然后将一些字段值复原,具体见上面注释就不细说了。

好了本文关于文件系统的挂载就这么多,所以回到开头什么是挂载,但从实现上来说,就是将超级块加载到内存里面,因为超级块就是一个文件系统的元信息集合,超级块就能代表一个文件系统,所以将超级块加载到内存里面,我们就可以认为挂载了相应的文件系统。当然挂载这个机制不可能就只是靠超级块是否在内存里面来决定实现,还需要其他的函数来辅助,就比如说获取 i n o d e inode inode i g e t iget iget 函数,这个函数就会来判断当前获取的这个 i n o d e inode inode 是否为挂载点,如果是,那就需要屏蔽当前这个 i n o d e inode inode 指向的目录文件,然后将其替换为被挂载的文件系统的根目录。这些总总加起来应该才算是挂载这个机制的实现,而不是说单靠一个 m o u n t mount mount 函数就实现了挂载的机制。

O K OK OK 本文就到这里了,有什么问题还请批评指正,也欢迎大家来同我探讨交流一起学习一起进步。

  • 首发公号:Rand_cs

http://chatgpt.dhexx.cn/article/7PkDwSsm.shtml

相关文章

磁盘挂载

挂载数据盘 1.使用fdisk -l查看磁盘情况 2.使用df -h查看文件系统以及挂载点等磁盘信息 3.手动添加一块数据盘 这里演示挂载这个新添加的20G的硬盘。 4.fdisk -l看一下 已经有磁盘&#xff0c;新磁盘名字为/dev/sdb。 容量为20G&#xff0c;并且使用df -h可以看到并没有挂载…

设备文件名、挂载-挂载点、分区

设备文件名 在Linux系统中&#xff0c;每个设备都被当初一个文件来对待&#xff0c;因此&#xff0c;各种设备在Linux中是有他们独特的文件名的&#xff0c;也就是设备文件名。 举例&#xff1a;sda1 上述例子中 sa的含义&#xff1a;SCSI硬盘接口/SATA硬盘接口&#xff0c;…

Linux系统分区及挂载点

一、关于Linux的分区情况 虽然硬盘分区表中最多能存储四个分区&#xff0c;但我们实际使用时一般只分为两个分区&#xff0c;一个是主分区&#xff08;Primary Partion&#xff09;一个是扩展分区(extended partition)两种&#xff0c;主分区可以马上被使用但不能再分区&…

mount point does not exist.挂载点不存在

翻译过来是挂载点不存在。 方法&#xff1a; 创建挂载目录mkdir &#xff08;挂载目标目录&#xff09;重新挂载 mount&#xff08;目标目录&#xff09; 挂载查询命令&#xff0c;检验是否挂载成功&#xff1a;lsblk -f

Linux中的挂载点

最近因为学习和项目需要就安装了一波mint系统(Linux下的一个发行版,用户友好做的很不错,大家如果是刚从Windows转Linux学习的建议可以先从这一个发行版本入手,用户体验很棒。附上桌面图) 言归正传,在安装mint的过程中重新分配磁盘的时候需要手动配置挂载点,由于对里面有…

LINUX 挂载点目录及其作用

什么是挂载点? Linux 使用字母和数字的组合来指代磁盘分区 Linux 是一切皆文件&#xff0c;整个系统都是当做文件来管理&#xff0c;在Windows中的磁盘分区&#xff0c;硬件设备就是Linux的挂载点&#xff0c;对磁盘的访问就相当于对Linux中挂载点文件的读写操作。 挂载点相…

linux下的挂载点和分区是什么关系(详解挂载点)

百度定义&#xff1a; 挂载点实际上就是linux中的磁盘文件系统的入口目录&#xff0c;类似于windows中的用来访问不同分区的C:、D:、E:等盘符。其实winxp也支持将一个磁盘分区挂在一个文件夹下面&#xff0c;只是我们C:、D:这样的盘符操作用惯了&#xff0c;一般没有将分区挂到…

Linux文件系统——文件系统、挂载点、目录结构

目录 一、目录结构 1.1 基本介绍 1.2 详细说明目录作用 二、挂载点 一、目录结构 1.1 基本介绍 Linux是一切皆文件&#xff0c;将所用的东西当做文件处理 目录结构就是一个单一的树状结构 整个的目录树只有一个树根&#xff1a;/ 根目录 文件夹分门别类的放到根目录…

CMD如何切换目录

1.如果想要修改其他盘符&#xff0c;只需要在命令提示符输入“D&#xff1a;”&#xff0c;然后按enter确认即可。 2.如果想进入D盘的某个文件&#xff0c;则需要“cd路径” 3.如果想要查看文件夹的使用&#xff0c;名称&#xff0c;结构的话&#xff0c;就输入“dir”

Windows | cmd切换盘路径以及Git Bash切换盘路径

前言 Windows下经常用到cmd和git bash工具&#xff0c;而且常常需要使用管理员身份。 管理员身份的路径通常是默认在C盘&#xff0c;这时候就需要我们去切换路径。 而且这二者的命令还不一样&#xff0c;老是弄混&#xff0c;写篇blog记录一下。 cmd切换路径 git bash切换路径…

windows的cmd中切换路径 cd指令

windows的cmd中的cd指令无法切换路径&#xff1f; 1.输入cd&#xff0c;可以显示当前目录的名称&#xff1b; 2.关于盘符&#xff0c;我的电脑有C、D、E盘&#xff0c;打开cmd窗口&#xff0c;默认路径是用户文档路径&#xff0c;是C盘下的一个路径&#xff0c;如果想要切换到…

cmd 路径由C到D盘切换

1、按WINR键打开运行&#xff0c;输入cmd并回车; 2、默认路径为用户文档路径&#xff0c;如果想要切换到C盘中的某个文件夹&#xff0c;比如AppData&#xff0c;可以执行命令cd AppData; 3、但如果想切换到D盘&#xff0c;输入cd d:是不行的; 4、如果我们要切换盘符的目录&…

如何windows中cmd命令行下怎么切换目录

1、首先打开CMD之后&#xff0c;发现默认是在C:\Users\SILVER这个文件夹 2、这里输入 E:&#xff0c;即可切换到E盘。 3、输入 dir 可以查看当前目录下的文件。 4、输入 cd 文件夹名 可以进入子文件夹。比如这里输入 cd Game&#xff0c;即进入了E:\Game 5、另外&#xff0…

window下cmd命令进入和切换目录

开始->运行->CMD 一般默认进入c盘&#xff0c;如果想切换进入某个磁盘&#xff0c;直接盘符代号 如 D&#xff1a;&#xff0c;不用CD 命令切换 进入所在磁盘后想进入根录以下的文件夹 cd 文件夹路径 例如&#xff1a;我要进入 E:/Program Files/github_file 就输入…

CMD命令切换至D盘

1.使用快捷键winr进入如下所示界面&#xff1a; 2.在框内输入CMD 后点击确定&#xff0c;即可进入如下界面&#xff1b; 3.输入d: 后按下enter即可转换成功&#xff1b; 补充一些CMD命令&#xff1a; 1. appwiz.cpl&#xff1a;程序和功能 2. calc&#xff1a;启动计算器 3.…

win11系统中cmd窗口cd进行路径切换

在win11系统中会出现无法切换路径的情况 如图&#xff1a; cd无法切换到正确路径 这种情况只需要再次键入转换的盘符 如图

cmd下使用命令切换目录

cmd下使用命令切换目录 参考&#xff1a;https://www.yalewoo.com/cmd-base1.html 1. 在当前盘符 如果要切换的目录和当前目录在一个盘符下&#xff08;比如都是C盘&#xff09;可以直接用下面的命令来进行切换&#xff1a; cd 路径例如 cd c:\windows\system就会切换到c:…

windows10命令行窗口cmd路径切换

回到根目录 cd/ C:\Users>cd/ C:\>回到上一级目录 cd.. C:\Users\administrator>cd.. C:\Users>通过绝对路径切换目录 E:\CARLA_0.9.9.4\WindowsNoEditor>cd /d D:\Sublime Text 3 D:\Sublime Text 3>先进入指定盘区根目录&#xff0c;再用相对路径切换目…

关于如何更改cmd命令行的路径的方法

一、使用cmd命令行时&#xff0c;如果我想对我D盘里名字为“matlab程序文件" 的文件夹里的东西进行操作时&#xff0c;我就要更改我的路径到对应位置 一、我的初始打开路径如图 二&#xff1a;查询我想操作的对象的位置&#xff0c;发现在 D:\matlab程序文件 中 三&#…

cmd中切换至指定文件夹

老是忘记怎么搞&#xff0c;三次了&#xff0c;每次都要搜索半天&#xff0c;这回记下来&#xff0c;再忘记就来看看&#xff0c; 这是搬运&#xff0c;防止挂掉&#xff0c;原创网址是https://www.cnblogs.com/mmit/p/11911480.html 怎么在cmd命令中切换到其他盘&#xff0c;切…