虚拟文件系统
虚拟文件系统为用户空间提供了文件和不同文件系统相关的通用接口。
通用文件系统接口
VFS使得用户可以直接使用诸如open(),read()和write()的系统调用接口,而不需要考虑具体的文件系统和物理介质。它把不同的文件系统抽象后采用统一的方式。
文件系统抽象层
由于内核在底层的文件系统接口之上建立了一个抽象层,底层文件系统通过实现VFS期望的抽象接口和数据接口,这样用户空间就可以调用统一的系统调用。
以write()为例,这个系统调用会被一个sys_write()进行处理,sys_write会找到文件描述符fd对应的真实文件系统调用该文件系统的写方法。
Unix文件系统
Unix使用了四种和文件系统相关的抽象概念:文件、目录项、索引节点和挂载点。
这是一种分层存储结构的结构。在unix中,文件系统挂载在一个特定的挂载点,相当于是一个命名空间,所有已安装的文件系统都作为根文件系统树的树叶出现在系统上。
文件通过目录组织起来,但VFS把目录也看做一种文件。Unix会把文件的相关信息诸如权限,创建时间,创建者这些元数据和文件区分对待,这些元数据存储在inode结点中,这是一个单独的数据结构。
文件的元数据和文件系统的控制信息息息相关,文件系统的控制信息存储在超级块,这样一个数据结构上。
VFS对象及其数据结构
VFS主要有四种对象类型:
- 超级块对象,一个具体的已安装文件系统;
- 索引节点对象,一个具体的文件;
- 目录项对象,代表一个目录项,是路径的一个组成成分;
- 文件对象,由进程打开的文件(注意目录也是一种文件)
超级块对象
每个文件系统都必须要实现超级块对象,用来存储特定文件系统的信息。超级块对象由super_block结构体表示。
索引结点对象
索引节点对象包含了内核在操作文件时所需要的全部信息,如果一个文件系统没有索引结点,这些文件系统会将文件描述的信息作为文件的一部分存放。无论如何,索引结点信息必须要创建。
索引结点对象由结构体inode表示
目录项对象
考虑这一个路径/bin/vi,那么/,bin和vi都属于目录项对象,前两个是目录,后一个是普通文件。由于路径中每个部分都是目录项对象,为了路径查找方便,就引入了目录项对象。目录项对象由结构体dentry表示。目录项对象没有对应的磁盘数据结构,因为目录项对象不是保存在物理硬盘中的。
目录项状态
目录项对象有三种状态:被使用、未被使用和负状态。
一个被使用的目录项对应一个有效的索引结点,并且该对象存在使用者;一个未被使用的目录项对应一个有效的索引结点,但未被使用;而负状态的目录项则没有对应的有效索引点,只是目录项还存在。
目录项缓存
由于VFS层遍历路径并解析其成dentry对象非常耗时,因此可以使用目录项缓存:
- 被使用的目录项链表
- 最近被使用的双向链表;
- 散列表用来快速将给定路径解析成目录项对象;
这样VFS会现在缓存中查找路径,如果找不到了,就必须要自己解析路径,找到dentry对象并将其假如到dcache中。
文件对象
文件对象表示的是进程已经打开的文件在内存中的表示,涉及到诸如访问模式,位偏移等信息。一个文件对应的文件对象不是唯一的,但对应的目录项和索引结点是唯一的。
文件对象由结构体file表示。
文件操作由结构体file_operations表示,内定义了大量的函数指针。
和进程相关的数据结构
有三个结构体将VFS和系统的进程紧密地联系在一起:file_struct, fs_struct和namespace。
file_struct
1 | struct files_struct { |
进程描述符的files域指向该结构体,包含了所有与单个进程相关的文件信息。
fs_struct
1 | struct fs_struct{ |
该结构体包含了进程与文件系统的相关信息,由fs域指向的;
namespace结构体
1 | struct mmt_namespace { |
该结构体使得进程在系统中看到唯一的安装文件系统,由进程描述符的mmt_namespace域指向。