虚拟文件系统
前面几节讨论的都是”一个文件系统”。但真实的机器上同时存在好几种:硬盘上是 ext4,U 盘上是 FAT32,光盘上是 ISO9660,/proc 下面的东西根本不在任何磁盘上。
这些文件系统的内部实现毫无共同之处——FAT 靠文件分配表追链,ext4 靠 inode 与多级索引,procfs 干脆是内核临时生成的。然而用户程序对它们统统只用 open / read / write / close。
虚拟文件系统就是让这件事成立的那一层。
机制
虚拟文件系统要满足的三个要求
疑问点:虚拟文件系统的作用与实现方式
虚拟文件系统这一概念较为抽象,它具体解决什么问题、如何实现。
它解决的问题可以用一句话说清:让”调用方”和”实现方”彻底解耦。 教材把它拆成三条要求:
① 向上:向用户程序提供统一标准的系统调用接口,从而屏蔽下层各种文件系统实现上的差异。用户程序调用 read 时,不需要知道也无法知道这个文件到底在 ext4 上还是在 U 盘上。
② 向下:要求下层的文件系统必须实现某些规定的函数功能,如 open、read、write 等。一个新的文件系统想要接入,就必须提供 VFS 所要求的这组函数接口——这是接入的门槛,也是 VFS 能够统一分派的前提。
③ 中间:每打开一个文件,VFS 就在主存中新建一个 vnode,用统一的数据结构表示这个文件,无论该文件存储在哪个文件系统上。
三条合起来就是一个完整的分派机制:上面统一入口,下面统一约定,中间用一个统一的对象把两者接起来。
vnode 是怎么把两边接起来的
vnode 中除了保存文件的元数据,还保存一组”功能指针”,这些指针指向具体文件系统提供的那些函数。
于是 read 的执行路径变成:用户调用统一的 read → VFS 由 fd 找到对应的 vnode → 取出 vnode 中的读函数指针 → 跳转到 ext4 或 FAT 各自的实现。
同一条 read 调用,落到不同的文件系统就跳到不同的函数,而用户程序对此一无所知。这就是”统一接口、不同实现”在内核中的具体做法——与面向对象语言中接口与虚函数表的分派机制是同一套原理。
VFS 中的主要对象
| 对象 | 表示什么 |
|---|---|
| 超级块对象 | 一个已挂载的文件系统的整体信息 |
| 索引节点对象(vnode) | 一个文件本体的元数据 |
| 目录项对象 | 路径中的一个组成部分(名字 → 索引节点的映射,用于缓存) |
| 文件对象 | 一次打开的状态 |
注意最后两个的分工,它与 4.1.3 的两级打开文件表完全对应:目录项对象属于”这个名字”,文件对象属于”这一次打开”。
边界
vnode 与 inode 的区别
这是本节最常考的一对,判据只有一句:inode 在外存,vnode 在主存。
inode 是具体文件系统落在磁盘上的结构,格式由该文件系统自己定义,ext4 的 inode 和别的文件系统的完全不同。
vnode 只存在于主存中,是 VFS 的统一抽象。文件被打开时,VFS 把 inode 中的信息复制到 vnode 中,并添加一些新的信息——最重要的就是那组指向具体文件系统函数的功能指针。
所以两者是”落盘格式”与”内存抽象”的关系:vnode 中有的功能指针,inode 里不会有(磁盘上存函数地址毫无意义);inode 中的磁盘地址信息,会被复制进 vnode。
VFS 不是一种文件系统
“虚拟文件系统”这个名字容易让人以为它是又一种像 ext4、FAT 那样的文件系统,这是最常见的误解。
VFS 不定义任何磁盘格式,不管理任何磁盘块,磁盘上不存在属于 VFS 的任何字节。 它是内核中的一个软件层,全部作用是接收请求并转发给真正的文件系统。
判据:凡是能被格式化到磁盘上的才是具体文件系统;VFS 只存在于内存中。
由此还能推出一条:磁盘布局中的超级块和 VFS 中的超级块对象不是同一个东西——前者是落在盘上的那几百个字节,后者是内核中表示”一个已挂载文件系统”的对象。两者可以对应理解,但不在同一层。
VFS 使”非磁盘对象”也能以文件面貌出现
这是 VFS 带来的一个重要能力,也是理解 UNIX”一切皆文件”的关键。
既然接入 VFS 的条件只是”提供那组函数”,那么这些函数背后是什么就完全自由了。 /proc 下的文件读起来像文件,实际上读函数是临时向内核查询数据并格式化成文本,磁盘上根本没有对应的块。
这与 4.1.1 中”特殊文件的读写落到设备驱动”是同一个机制在不同层次的体现:接口统一,实现自由。
VFS 与文件系统的四层结构
4.3.1 的四层描述的是一个文件系统内部的纵向分层;VFS 是横向的分派器,插在系统调用接口与各个具体文件系统之间。
它不是第五层。 准确的位置关系是:系统调用接口 → VFS → (ext4 的四层 / FAT 的四层 / procfs 的实现)。
对照速查
| VFS 的三个要求 | 方向 |
|---|---|
| ① 提供统一标准的系统调用接口 | 向上(屏蔽差异) |
| ② 要求下层实现规定的函数功能 | 向下(接入门槛) |
| ③ 每打开一个文件新建一个 vnode | 中间(统一表示) |
| inode | vnode | |
|---|---|---|
| 在哪 | 外存(磁盘) | 主存 |
| 属于谁 | 具体文件系统 | VFS |
| 格式 | 各文件系统各不相同 | 统一 |
| 有功能指针吗 | 没有 | 有(指向具体文件系统的函数) |
| 何时产生 | 文件创建时 | 文件打开时(由 inode 复制并扩充) |
| VFS 四类对象 | 表示 |
|---|---|
| 超级块对象 | 一个已挂载的文件系统 |
| 索引节点对象(vnode) | 一个文件本体 |
| 目录项对象 | 路径中的一个名字 |
| 文件对象 | 一次打开 |
考点
- VFS 的三个要求(向上统一接口、向下规定函数、中间建 vnode)
- vnode 在主存,inode 在外存;vnode 由 inode 复制并增加功能指针
- VFS 不是一种文件系统,磁盘上没有属于它的任何字节
- 磁盘超级块 ≠ VFS 超级块对象
- VFS 使
/proc这类非磁盘对象也能以文件面貌出现 - VFS 是横向分派器,不是四层结构的第五层
链接
- 🏠 返回总览:操作系统第 4 章:文件管理总览
- ⬅️ 上一节:4.3.3 文件存储空间管理
- ➡️ 下一节:4.3.5 文件系统挂载
- 🔗 分层结构中的位置,见 4.3.1 文件系统结构
- 📖 名词库:第 4 章名词库