虚拟文件系统(也称做虚拟文件交换,或者简称VFS)作为内核子系统,为用户空间程序提供了文件系统相关的接口。系统中所有文件不但依赖VFS共存,而且也依靠VFS系统协同工作。通过虚拟文件系统,程序可以利用标准的UNIX文件系统调用对不同介质上的不同文件系统进行读写操作。
- 通用文件系统接口
VFS使得用户可以直接使用open()、read()和write()这样的系统调用而不用考虑具体文件系统和实际物理介质。系统调用可以在不同的文件系统和介质之间执行。现代操作系统引入抽象层,通过虚拟接口访问文件系统,使得这种协作性和通用性称为可能。新的文件系统和新种类的存储介质都能找到进入Linux之路,程序无需重写,甚至无需重新编译。
- 文件系统抽象层
由于内核在它的底层文件系统接口上建立了一个抽象层,所以可以使用这种通用接口对所有类型的文件系统进行操作。该抽象层使Linux能够支持各种文件系统,即便是它们在功能和行为上存在很大差别。为了支持多文件系统,VFS提供了一个通用系统模型,该模型囊括了所能想到的文件系统的常用功能和行为。
VFS抽象层之所以能衔接各种各样的文件系统是因为它定义了所有文件系统都支持的基本的、概念上的接口和数据结构。同时,实际的文件系统也将自身的“如何打开文件”“目录是什么”等概念在形式上与VFS的定义保持一致。因为实际文件系统的代码在统一的接口和数据结构下隐藏了具体的实现细节,所以在VFS层和内核的其他部分看来,所有文件系统都是相同的,它们都支持像文件和目录这样的概念,同时也支持像创建文件和删除文件这样的操作。比如,
用户的write()函数,首先被一个通用系统调用sys_write()处理,sys_write()函数要找到文件描述符所在的文件系统实际给出的是哪个写操作,然后执行该操作。实际文件系统的写方法是文件系统实现的一部分,数据最终通过该操作写入介质。下图描述了从用户空间的write()调用到数据被写入磁盘介质的整个流程。一方面,系统调用是通用VFS接口,提供给用户空间的前端;另一方面,系统调用是具体文件系统的后端,处理实现细节
- Unix文件系统
Unix使用了四种和文件系统相关的传统抽象概念:文件、目录项、索引节点和安装点(mount point)。从本质上讲,文件系统是特殊的数据分层存储结构,它包含文件、目录和相关的控制信息。文件系统的通用操作包含创建、删除和安装等。在Unix中文件系统被安装在一个特定的安装点上,该安装点在全局层次结构中被称为命名空间,所有的已安装文件系统都作为根文件系统树的枝叶出现在系统中。
文件其实可以看做是一个有序字节串,字节串中第一个字节是文件的头,最后一个字节是文件的尾。每一个文件为了便于系统和用户识别,都被分配了一个便于理解的名字。典型的文件操作有读、写、创建和删除等。
文件通过目录组织起来。文件目录好比一个文件夹,用来容纳相关文件。因为目录可以包含子目录,所以目录可以嵌套形成文件路径。路径中的每一部分都被称作目录条目,统称为目录项。在Unix中,目录属于普通文件,它列出包含在其中的所有文件。由于VFS把目录当作文件对待所以可以对目录执行与文件相同的操作。
Unix系统把文件的相关信息和文件本身这两个概念加以区分,例如访问控制权限、大小、拥有者、创建时间等等消息。文件相关信息,有时被称为文件的元数据(也就是说,文件的相关数据),被存储在一个单独的数据结构中,该结构被称为索引节点(inode ,index node)。
所有这些信息都和文件系统的控制信息密切交融,文件系统的控制信息存储在超级块宏,超级快是一种包含文件系统信息的数据结构。有时,把这些收集起来的信息称为文件系统数据元,它是集单独文件信息和文件系统的信息于一身。
一直以来,Unix文件系统在它们物理磁盘布局中也是按照上述概念实现的。比如说在磁盘上,文件(目录也属于文件)信息按照索引节点形式存储在单独的块中;控制信息被集中存储在磁盘的超级块中,等。Linux的VFS的设计目标就是要保证能与支持和实现了这些概念的文件系统协调工作。像FAT或NTFS这样的非Unix风格的文件系统,虽然也可以在Linux上工作,但是它们必须经过封装,提供一个符合这些概念的界面。比如,即使一个文件系统不支持索引节点,它也必须在内存中装配索引节点结构体,就像它本身包含索引节点一样。再比如,如果一个文件系统将目录看做是一种特殊对象,那么要想用VFS,就必须将目录重新表示为文件形式。通常这种转换需要在使用现场(on the fly)引入一些特殊处理,使得非Unix文件系统能够兼容Unix文件系统的使用规则并满足VFS的需求。