乐趣区

关于运维:文件系统考古4如何支持多个文件系统

明天这篇是系列文章“50 years in filesystems”的最初一篇,再次感激作者 KRISTIAN KÖHNTOPP 带来的这组精彩文章,为计算畛域的硬核爱好者提供了一次难得的学习和思考机会。对于文件系统的历史和倒退,你有什么样的观点?欢送在后盾给咱们留言。

Steve Kleiman 在 1986 年撰写了《Vnodes: An Architecture for Multiple File System Types in Sun UNIX》一文。这篇论文幅较短,大部分内容是数据结构的列举,以及 C 语言构造之间互相指向的图表。

Steve Kleiman 是分布式文件系统畛域的专家,在 Sun Microsystem 工作了多年,曾参加开发 Sun Network File System(NFS)等我的项目,为分布式文件系统畛域做出了重要奉献。

Kleiman 心愿在 Unix 中可能领有多个文件系统,并心愿这些文件系统可能共享接口和内存。具体而言,他心愿设计一个可能提供以下性能的架构:

一个能够反对多个实现的通用接口;
反对 BSD FFS,以及两个近程文件系统 NFS 和 RFS,还有特定的非 Unix 文件系统,如 MS-DOS;
接口定义的操作须要是原子性的。

并且,可能在不影响性能的状况下动静地解决内存和数据结构,反对重入(reentrant) 和多核,并且具备肯定面向对象进行编程的个性。

重入(reentrant) 是指程序或子程序在尚未实现上一次调用之前,能够再次被调用且不会出错或发生冲突。

两个抽象概念

Steven 钻研了文件系统的各种操作,决定将他们形象为两个概念:

  • vfs,虚构文件系统,代表文件系统
  • vnode,虚构 inode,代表文件

vfs,虚构文件系统,它提供对立的接口,使操作系统能够以统一的形式拜访不同的文件系统,无论是本地文件系统还是网络文件系统。

vnode,虚构 inode, 示意一个文件,每个文件都有一个相关联的索引节点,其中蕴含了文件的元数据(如文件权限、所有者、大小等)以及指向文件数据存储地位的指针。

采纳了 C++ 格调(理论应用 C 语言),每一个类型会匹配一个虚函数表,通过虚函数表,零碎在运行时依据对象的理论类型来调用适当的虚函数,实现动静绑定:

  • 对于 vfs 类型,其虚函数表 struct vfsops,蕴含了一系列的函数指针,用来执行诸如 mount、unmount、sync 和 vget 等操作。在论文的前面,会解释这些函数的原型和性能;
  • 对于 vnode 类型也是相似的,其虚函数表 struct vnodeops,蕴含 open、rdwr 和 close 等函数,还有 create、unlink 和 rename 等函数。一些函数是针对特定的文件类型的,比方 readlink、mkdir、readdir 和 rmdir。

通过 vfs 对象来进行跟踪理论的挂载,其虚函数表 struct vfsops 指向实用于该特定子树的文件系统操作。

相似地,vnode 实例用来进行跟踪关上的文件。它蕴含 struct *vnodeops 指针,作为 vfs 的一部分,有指针 struct *vfs 指向文件系统实例。

vfs 和 vnode 这两个构造体都须要一些用于存储特定实现数据的字段(如“子类公有字段”)。他们都以 caddr_t ...data 指针结尾。这些公有数据并不是 vfs 和 vnode 的一部分,而是位于其余地位,并通过指针进行援用。

Vnodes 实操

tu

在论文中,有一整页的内容专门用于展现各种互相指向的构造。乍一看可能会感到困惑,但一旦追踪下来,就会发现它十分直观和优雅。

Kleiman 具体解释了如何应用 lookuppn() 函数来解释事物的工作原理,该函数代替了传统 Unix 中的 namei() 函数。相似于 namei(),这个函数承受一个门路,并返回示意该门路所代表的 vnode 的 struct vnode 指针。

门路遍历始于根 vnode 或以后过程的当前目录 vnode,具体取决于门路的第一个字符是否为 /。

而后,这个函数会顺次取出门路的每一个子项,并调用以后 vnode 的 lookup 函数,它承受一个门路子项和一个假如是目录的以后 vnode,并返回代表那个子项的 vnode。

当一个目录是个挂载点,它的 vfsmountedhere 会被设置为一个指向 struct vfs 的指针。lookuppn 函数会追随这个指针,并调用 vfs 的根函数,以获取该文件系统的根 vnode,替换以后正在解决的 vnode。

反过来也是可能的:当解析父目录(”.. “)时,如果以后 vnode 的 “flags” 字段中设置了根标记,咱们会追随 vfsmountedhere 指针从以后 vnode 到 vfs。而后,咱们能够应用该 vfs 中的 vnodecovered 字段来获取下层文件系统的 vnode。

无论如何,在胜利实现后,会返回一个 struct vnode 指针,即所应用的门路。

新增的零碎调用

为了使零碎高效地运行,须要增加一些新的零碎调用来欠缺接口。

在 Unix 的历史中,咱们看到引入了 statsfs 和 fstatsfs,通过这两个函数能够取得与用户空间中的文件系统进行交互的接口。getdirentries 函数能够让用户一次性获取多个目录条目(取决于提供的缓冲区大小),这大大放慢了近程文件系统的目录读取速度。

在 Linux 零碎中

通过查看 Linux 内核源代码,咱们能够找到 Kleiman 设计的总体构造,只管 Linux 内核的复杂性和丰富性覆盖了其中大部分内容。Linux 内核领有丰盛的文件系统类型,并且还增加了许多在 40 年前的 BSD 中不存在的性能。因而,咱们能够找到更多的数据结构和零碎调用,它们被用于实现命名空间、配额、属性、只读模式、目录名称缓存等性能。

文件

如果你仔细观察,原始的构造依然能够找到:Linux 内存中的文件相干构造分为两局部,一个是已关上的文件,它是一个带有以后地位的 inode;另一个是 inode,它代表整个文件。

咱们能够在此处找到文件对象,struct file 的实例。在文件的所有其余内容中,最值得注意的是一个字段 loff_t f_pos,它示意文件以后地位间隔文件起始地位的偏移量(以字节为单位)。

文件的类 ) 是通过一个虚函数表来定义。咱们能够找到一个指针 struct file_operations *f_op。它展现了文件能够执行的所有操作,其中最常见的是关上(open)、敞开(close)、定位(lseek)、读取(read)和写入(write)。

文件还蕴含指向 inode 的指针,即 struct inode *f_inode

索引节点

对于不须要偏移量的文件操作,它们是针对整个文件进行的,定义为 struct inode *

查看此处的定义。咱们能够看到这里还有其余的定义,40 年前的 BSD 中没有相似的定义,比方 ACL(访问控制列表)和属性(attributes)。

咱们发现 inode 的类 ) 通过虚函数表来定义,即 struct inode_operations *i_op。同样的,这其中很多函数波及新个性,比方 ACL(访问控制列表)和扩大属性,但咱们也会找到咱们冀望的性能,比方链接(link)、删除(unlink)、重命名(rename)等。

Inode 还蕴含一个指向文件系统的指针,即 struct super_block *i_sb

超级块

挂载点用 struct super_block 来示意,在此处查看其定义。同样地,它有 struct super_operations *s_op 定义的各个操作,在此处查看其定义。

反对的文件系统不再无限,能够通过内核模块动静地增加新的文件系统,通过数据结构 struct file_system_type 来示意,它只有一个用于创立 superblock 的工厂函数 mount。

小结

Unix 产生了变动。它的运行时变得更加简单,减少了许多新的性能,并减少了零碎调用。零碎变得更有构造。

然而,由 Steve Kleiman 和 Bill Joy(BSD 操作系统的独特创始人之一)构思的原始设计和数据结构依然存在,在以后的 Linux 零碎中依然能够找到,尽管曾经过来了 40 年。

  • 文件系统考古 1:1974-Unix V7 File System
  • 文件系统考古 2:1984 – BSD Fast Filing System
  • 文件系统考古 3:1994 – The SGI XFS Filesystem
退出移动版