Appearance
文件系统挂载
2026 大纲 四(三)4 文件系统挂载(mounting)。
好几个文件系统,怎么变成用户看到的那一棵树
上一节的 VFS 解决了"不同文件系统怎么共用同一套系统调用"。 但还剩一个更基础的问题没答:用户是怎么走到它们的?
用户眼里只有一棵树,从 / 开始。可实际上系统盘、U 盘、光盘是三个互不相干的文件系统, 各有各的超级块、各有各的根目录、各有各的 inode 编号空间。 这三棵独立的树,是怎么接成用户看到的那一棵的?
先看不接会怎样。插上一块盘,它的数据是完整的——超级块、inode 区、目录、文件全在。 但你在 / 下面怎么翻都找不到它。原因很硬:目录项里存的是 inode 号, 而 inode 号只是本分区 inode 数组的下标。系统盘上的任何一条目录项, 写的都是系统盘自己的 inode 号,没有任何一条目录项能指向另一个分区的文件。 从根出发,走不到。
挂载做的就是补上这条通路,而它的做法只有一件事: 在目录树上安一个"跳板"——路径解析走到挂载点这个目录时, 不再去读它自己的数据块,改去被挂文件系统的根目录继续找。
这一句能直接推出本节几乎全部结论。挂载点原有的内容会被"遮蔽"而不是删除 (它的目录项和数据块原封不动躺在原文件系统里,只是暂时没有路径能到达); 因此那些空间不会被释放;因此挂载前已经打开这些文件的进程照常读写 (fd 走的是内存 inode,与路径解析无关); 因此"挂载点应为空目录"只是建议而不是硬性限制。
一、挂载为什么是必需的
路径解析从根目录的 inode 出发,读它的数据块找到 mnt 的 inode 号,再读 mnt 的数据块找 photos……每一步都只能在"当前这个文件系统内部"往下走——因为目录项里存的是 inode 号,而 inode 号只是本分区 inode 数组的下标,换个分区同一个号指的是完全不同的文件。
所以一个没挂载的分区,数据、目录、inode 全都是完整的,只是目录树上没有入口。挂载安上的那个"跳板"告诉系统:走到 /mnt 这个 inode 时,不要再读它自己的数据块,改去另一个文件系统的根目录继续找。
挂载点原有内容被遮蔽,也直接来自这条机制:它的数据块从此不被读,里面的目录项自然无人能查到。这些文件并没有被删除或覆盖,umount 之后跳板撤掉,路径解析恢复成读该目录自己的数据块,被遮蔽的内容立刻全部回来。
会真出事的情形是这一个:往一个本该挂载、却挂载失败的目录里写了大量数据,事后挂载成功,那些数据就再也看不见了——但它们一直占着原文件系统的块,df 里的已用空间不会因为遮蔽而减少。这是一类很隐蔽的"空间去哪了"。同理,若挂载前已有进程打开了挂载点下的某个文件,它手里的 fd 指向的是系统打开文件表项 → 内存 inode,与路径解析无关,照常读写——这和"文件被删了但进程还能读"是同一个道理:能不能通过路径找到它,与它是否还存在,是两件事。
二、挂载与卸载各做了什么
| 挂载步骤 | 为什么必须做这一步 |
|---|---|
| ① 读超级块、验证文件系统 | 后面所有事都依赖超级块里的"块多大、inode 区在哪、根目录是几号 inode"。魔数不对就说明用错了驱动,此时强行往下走会按错误格式解释磁盘内容并可能写坏它 |
| ② 检查挂载点 | 挂载是往目录树上接分支,接点必须真实存在;必须是目录而非普通文件,因为只有目录才可能被"往下走" |
| ③ 在内存中建立关联 | 挂载表条目就是那个跳板。它只存在于内存——文件系统在外存上是完整的,但"它接在树的哪个位置"是本次运行才有的事实 |
| ④ 后续路径解析自动重定向 | 这是前三步的结果,不是一个独立的动作 |
| 卸载步骤 | 为什么必须做这一步 |
|---|---|
| ① 检查使用情况 | 强行摘掉的话,那些进程手里的 fd 指向的内存 inode 会悬空,且还没写回的脏数据会丢失 |
| ② 写回脏数据 | 延迟写机制下 write 返回不代表已落盘。不做这一步,这块盘拿到别的机器上就是一个不一致的文件系统 |
| ③ 删除挂载表条目 | 撤掉跳板,路径解析恢复正常 |
| ④ 挂载点恢复原状 | 第 ③ 步的自然结果,不是额外动作 |
两边检查对象相反,判据只有一条——"这一步可能破坏什么":挂载是往树上接,风险在接入点;卸载是把分支摘走,风险在被摘走的那一侧。
mount 与 VFS 超级块对象的衔接
| 谁 | 做什么 |
|---|---|
| VFS 层 | 创建一个空的超级块对象,并按 文件系统类型 找到已注册的那个文件系统驱动,调用它的"读超级块"接口 |
| 具体文件系统驱动 | 填充这个对象:把设备上的磁盘超级块读出来、按自己的格式解析,把块大小、总块数、根 inode 等填进 VFS 规定的字段,并把自己那组函数指针装上 |
| VFS 层 | 拿回填好的对象,为它的根目录建立 inode 对象与 dentry 对象,再把这个 dentry 与挂载点的 dentry 关联起来 |
这正是 VFS "对下适配"的具体形态——它自己不认识任何磁盘格式,只知道"该找谁去读"。
三、挂载表与路径解析中的挂载点穿越
| 字段 | 路径解析时用来做什么 |
|---|---|
| 设备标识 | 卸载时按它定位;也用于防止同一设备被重复挂载 |
| 挂载点 | 解析时用它判断"当前这一级是不是挂载点" |
| 文件系统类型 | 决定挂载时调用哪个驱动 |
| 超级块指针 | 换根时由它拿到被挂文件系统的根目录 |
| 挂载选项 | 每次写操作前检查(如只读挂载要拒绝写) |
解析 /mnt/photos/a.jpg 时:从 / 找到 mnt 的 inode → 发现它是挂载点,查挂载表 → 顺着超级块指针切换到 U 盘文件系统的根目录 → 在那里继续找 photos/a.jpg。比普通目录多的就是一次查表 + 一次换根。
VFS 的超级块对象表示"有这么一个已挂载的文件系统",但它自己不知道被接在树的哪个位置;挂载表存的正是"挂载点 ↔ 超级块对象"这条绑定关系,是把一堆孤立超级块对象接成一棵完整目录树的唯一粘合剂。
四、根文件系统的挂载:自举怎么打破循环
上面所有步骤都有一个前提:挂载点是一条路径。可路径要靠已挂载的文件系统才能解析——那么第一个文件系统怎么挂?这与操作系统引导里"要运行程序需要操作系统,而操作系统本身也是程序"是同构的循环,打破方式也同构:第一次不用路径。
- 引导阶段,PBR 里那段与文件系统配套的代码按扇区位置(而非路径)把内核映像读进内存——完全绕开文件系统抽象
- 内核初始化时,根文件系统所在的设备由引导参数直接给定(一个设备号,不是路径),内核用它调对应驱动读超级块、建 VFS 超级块对象与根目录的 dentry
- 这个根目录的 dentry 被直接指定为整棵目录树的根
/——它不需要挂到某个已有目录上,因为此刻还没有任何目录 - 从这一刻起路径解析可用,后续所有文件系统都走正常的挂载流程
这与引导链里"固件只从一个约定死的位置读一段代码"是同一种手法:用一个硬编码的约定,换来之后所有一般规则得以成立。
考点速记
- 挂载 = 把一个文件系统关联到目录树中某个位置(挂载点)。它做的唯一一件事是在目录树上安一个"跳板":解析到挂载点时不再读它自己的数据块,改去被挂文件系统的根目录继续找。
- 不挂载为什么访问不到:目录项里存的是 inode 号,而 inode 号只是本分区 inode 数组的下标。未挂载的分区数据完整,但目录树上没有任何目录项指向它。
- 挂载点原有内容被"遮蔽"而非删除:目录项与数据块原封不动躺在原文件系统里,
umount后原样出现。 - 遮蔽的两个后果:① 磁盘空间不会被释放("空间去哪了"的一类隐蔽来源)② 挂载前已打开这些文件的进程照常读写——fd 走的是内存 inode,与路径解析无关。
- "挂载点应为空目录"是建议而非硬性限制,为的是避开上面两个意外。
- 挂载四步:① 读超级块、验证类型(魔数不对说明用错驱动,硬来会按错误格式解释并写坏盘)② 检查挂载点存在且是目录 ③ 在内存建挂载表条目(这条记录就是跳板)④ 后续路径解析自动重定向。
- VFS 超级块对象:VFS 创建、具体文件系统填充。 这就是
mount必须指定文件系统类型的原因——VFS 手里只有设备名,无法判断该调哪个驱动。 - 挂载表是内存结构(存设备标识、挂载点、文件系统类型、超级块指针、挂载选项),所以每次开机都要重新挂载。
- 挂载表与 VFS 的分工:VFS 管"进去之后怎么走",挂载表管"什么时候该换一个文件系统"。
- 挂载检查挂载点、卸载检查被挂文件系统,是同一条判据(这一步可能破坏什么)的两次应用。
- 根文件系统的挂载是唯一一次"挂载点不是路径"的挂载,靠引导参数给出的设备号打破自举循环。
这一节在真题里被考过的形式:
文件系统挂载至今不单独成题,本页下方因此没有练习区。
它在大纲里(四(三)4),不能跳过,但投入按"读懂即可"来定。 它在真题里以两种间接方式出现:
- 作为"这套结构在内存还是在磁盘"这类判断的素材。 速记第八条(挂载表是内存结构、 每次开机都要重新挂载)与操作系统引导那条 "在磁盘上的只建一次,在内存里的每次开机都要建"是同一条判据的两个实例 ——os-2022-24 考的就是那一条。
- 作为 VFS 题的背景。 os-2025-28 问 VFS 的定义时, "把多个文件系统接成一棵树"这件事正是挂载表干的, 而 VFS 管的是"进去之后怎么走"(速记第九条)。两者常被混为一谈。
复习优先级:读懂第一、二条即可,不必细记四步流程。 核心就一句: 挂载安的是一块跳板,被遮蔽的内容还在、空间还占着。 第七条(mount 为什么要指定文件系统类型)能顺带解释 VFS 那一节的分工,值得看一眼。
易错:认为挂载会把被挂文件系统的内容"复制"到挂载点。它只是安了一块跳板,改变的是路径解析的走向。
易错:认为挂载点原有的文件被删除了。只是被遮蔽——空间照占,
umount后原样出现。
易错:认为挂载后,之前打开挂载点下文件的进程会失效。照常读写——fd 走内存 inode,与路径解析无关。
易错:认为"挂载点必须是空目录"是硬性规定。是建议,为的是避开遮蔽带来的两个意外。
易错:认为挂载表在磁盘上。它是内存结构,所以每次开机都要重新挂载。
易错:把挂载表和 VFS 的职责混为一谈。VFS 管"进去之后怎么走",挂载表管"什么时候该换一个文件系统"。
教材出处
- 孙钟秀、费翔林《操作系统教程》(第 6 版)印刷版 p174–p175:UNIX "通过 mount 命令将子树安装到现有文件系统树目录的某个目录结点上,于是该子树对应的所有子目录和文件就是该目录结点的子目录和文件,直到使用 unmount 命令显式地卸载该文件系统";"安装新的文件系统时需要指定文件系统的类型、所在物理设备名、安装点等信息。而当文件系统正在被使用时,此文件系统是不能被卸载的"。
- 同书 p254:
mount时内核"为远程目录构造一个 v 节点",并把它与具体文件系统(或远程节点)的结构关联起来——这是本篇"VFS 创建超级块/根节点对象、具体文件系统填充"这条分工的依据。