Linux 内核101:虚拟文件系统的使命

1,035 阅读4分钟

本文参考了:

  • 《Understanding the Linux Kernel, Third Edition》12.1章

简介

Linux 的成功有一部分原因在于能够很好的支持不同的文件系统,你能够轻松透明地把 Windows、其他 Unix 系统、甚至是占有极小份额的 Amiga 使用的文件系统 mount 到Linux 的文件系统中。这是通过Virtual Filesystem(以下简称 VFS)实现的。

VFS 背后的 idea 是在 kernel 中抽象出不同文件系统,针对具体的文件系统,Linux kernel 实现具体的操作方法。当系统调用readwrite发生时,kernel 根据操作的具体文件系统,比如 native Linux文件系统、NTFS(Windows NT)等,调用相对应的函数。

实例:cp

要执行一个cp指令:

cp /floppy/TEST /tmp/test

其中/floppy是一个 mount 的MS-DOS文件系统,而/tmp是 ext2。VFS 就是应用程序和底层文件系统实现之间的一个抽象层cp不需要知道/floppy/TEST/tmp/test的文件系统类型,它只需要调用标准的系统调用,比如readwrite这些,把底层文件系统不同带来的复杂度交给 kernel

示例的代码如下:

inf = open("/floppy/TEST", O_RDONLY, 0);
outf = open("/tmp/test",
       O_WRONLY|O_CREAT|O_TRUNC, 0600);
do {
    i = read(inf, buf, 4096);
    write(outf, buf, i);
  } while (i);
close(outf);
close(inf);

示意图:

代码和截图来源于 《Understanding the Linux Kernel, Third Edition》 P457

VFS 支持的文件系统

VFS 支持嗯文件系统一共分为下面三大类:

  1. Disk-based FS

本地的 disk。包括:

  • ext2, etx3, ext4
  • Unix 家族,比如 sysv 文件系统、UFS(BSD,solaris)、MINIX等
  • Microsoft 文件系统,比如 MS-DOS、NTFS
  • ISO9660 CD-ROM(之前的High Sierra Filesystem)
  • 其他冷门
  1. Network FS

这一类支持访问远程的文件系统,比如 NFS、Coda、AFS 等。

  1. Special FS

/proc虚拟文件系统。

通常来说,root 目录为 Linux 原生的ext2ext3ext4,其他类型的文件系统通过mount形式 mount 到某个特定子目录。

Common File Model

VFS 背后的核心idea: 用common file model表示所有现实中的FS。这个模型严格使用原生Unix FS 模型,每个特定的 FS 都需要将自己的硬件结构翻译成 common file model。

比如在 common file model 中,目录也被看做文件,包含其他的文件盒目录。然而,一些非 Unix 的 FS,使用的是 file allocation table(FAT),这种情况下,目录不是文件。但是为了遵循 common file model的规则,Linux 对这种 FAT-based 的 FS,必须能够抽象出一个遵循common file model 的接口。

更加具体点,Linux kernel 在处理readioctl这些系统调用的时候,不能直接硬编码,使用某个特定的底层函数。kernel实际上针对每一个操作,使用的是一个指针,这个指针指向了针对此文件系统专门的处理函数。

让我们来看看kernel 是如何完成上面提到的cp操作的。

应用层调用read(),kernel 实际上会调用sys_read() service routine(其他的系统调用也一样)。MS-DOS FS 的文件被 kernel memory 中的一个数据结构表示,这个数据结构包含一个f_op字段,指向的是针对 MS-DOS 的 read 函数。sys_read()找到这个函数然后调用它。所以整个过程可以看作是:

read() -> sys_read() -> file data structure -> f_op -> read_for_msdos()

调用write()也一样,这个系统调用会触发针对ext2 FS 的写调用。

简要来说,对于每个 open()创建的 file object,kernel 需要负责正确赋值此 file object的指针,指向针对此文件系统的特定函数,然后调用这些函数。

CFM 包含哪些 object types

  • superblock object:包含的是 mounted FS 的数据。
  • indoe object:特定文件的数据。对于 disk-based FS,这个 object 一般对应一个 file control block。每个 inode object 都有一个 inode number,唯一指定 FS 中的某一个文件。
  • file object:一个open file 和进程之间的交互信息。这个 object 只在进程打开文件的时候在kernel memory 中存在
  • dentry object:文件名到inode的映射,不同的 FS 有不同的底层实现。

总结一下

VFS 是应用和特定文件系统之间的一层抽象,有些操作能够直接在 VFS 层完成,不需要涉及到底层的具体文件系统。比如说,当进程close file的时候,disk 上的文件本身是不会改变的,所以 VFS 只要把对应的 file 对象释放掉就行了。再比如说,lseek()系统调用,改变的也是内存中的 file 对象,而不需要设计底层的文件系统。