1. VFS接口
VFS给用户空间的程序提供同意的系统调用接口,以访问各种文件,不管下面的文件系统是什么。
2. 文件系统抽象层
VFS提供一个通用文件系统模型,以支持各种文件系统,并提供上层统一的抽象接口和数据接口,保证用户空间调用统一的系统调用。
如写数据到文件:user space: write() -> VFS: sys_write() -> 特定FS: fd 的写操作-> Physical data
3. Unix FS
Unix使用4个与FS相关的概念:文件、目录项、索引节点、挂载点。
Unix文件系统使用分层的结构,FS挂载在一个挂载点上,挂载点位于根文件系统树的树叶上。
文件通过目录组织,而目录也是一个文件(VFS将目录视为文件,一视同仁),列出包含在下面的所有文件。
Unix把文件相关的元数据存储在inode
中,文件系统的控制信息存储在super_block
中。
4. VFS对象与数据结构
VFS主要有4个对象类型:
super_block
:代表一个具体的已安装FSinode
:代表一个具体文件dentry
:代表一个目录项,是路径的组成部分file
:代表一个由进程打开的文件
每个对象类型对应一个操作对象,内部使用函数指针指定特定的操作:
super_operations
,inode_operations
,dentry_operations
,file_operations
5. super_block
超级块对象struct super_block
,定义在<linux/fs.h>
中,存储特定文件系统的信息。
它通常存放在磁盘的特定扇区,或者文件系统控制块中,若文件系统是非磁盘型的(如内存型、网络型),则会现场创建并保存在内存中。
5.1. super_block
操作
super_block
有一个重要的域s_op
,指向super_operations
(定义在<linux/fs.h>
中),用于操作super_block
。
拥有的操作比如有:读写超级块、增删inode
、同步文件系统元数据等等。
操作可以不全部实现,不需要实现的函数,函数指针可设为NULL
(调用时,可能不做任何事,或者调用通用函数)。
6. inode
inode
包含内核操作文件或目录时所需的全部信息,它在文件(不仅是普通文件,也可以是设备或者管道这样的特殊文件)被访问时,在内存中创建(内容会保存到磁盘)。
6.1. inode
操作
inode
的域i_op
指向inode_operations
(定义在<linux/fs.h>
中)。
拥有的操作比如有:增删inode
,增删目录,重命名,修改文件大小,修改权限,增删链接(硬/软),查找结点等。
硬链接:
link(struct dentry*, struct inode*, struct dentry*)
- 表示一个文件拥有多个文件名
inode
不会被创建,而是引用计数+1,只要计数为0时,文件内容才会被删除- 不能跨FS使用
软连接:
symlink(struct inode*, struct dentry* const char*)
- 类似快捷方式,创建了新文件,包含另一个文件的位置信息
7. dentry
VFS把目录当成文件,因此每个目录也得消耗一个inode
,而目录需要一些特定的操作,为了加速,因此还得消耗一个dentry
,即一个目录有:1个inode
和1个dentry
。
dentry
定义在<linux/dcache.h>
中,访问目录时,dentry
和对应的inode
都会被创建。
7.1. 目录项状态
三种:
- 被使用:
dentry
的d_inode
域指向一个有效的inode
,且引用计数d_count
为正 - 未被使用:
dentry
的d_inode
域指向一个有效的inode
,引用计数d_count
为0 - 负状态:
dentry
的d_inode
域为NULL
(节点被删除,或路径不准确,保留dentry
以便以后快速的路径查询)
dentry
, inode
可被缓存到slab对象缓存中,加速内存申请和释放。
7.2. dentry
缓存dcache
VFS为了加速遍历,对目录项对象进行缓存(dcache
),包含3个部分:
- 被使用的目录项链表
- 最近被使用的目录项双向链表(可能包含未被使用或者负状态的目录项)
- 散列表,用于快速将路径解析为目录项
VFS会先从缓存中查找,若找不到则遍历文件系统,解析完毕后,将目录项加进缓存中。
7.3. dentry
操作
dentry
的域d_op
指向dentry_operations
(定义在<linux/decache.h>
中)。
包含的操作比如有:判断目录是否有效,为目录生成散列值,比较文件名,删除和释放dentry
等。
8. file
它表示进程已打开的文件,定义在<linux/fs.h>
中。
它被open()
创建,被close()
销毁,一个磁盘上存储的文件,file
可在内存中存在多个,但是它们对应的inode
和dentry
是唯一的。
文件对象file
实际上不会存到磁盘上(和dentry
一样)
判断文件是否为脏,可通过
f_dentry
域指向dentry
,而dentry
会指向对应的索引节点inode
,由它判断是否为脏。
8.1. file
操作
file
的域f_op
指向file_operations
(定义在<linux/fs.h>
中)。
操作包含常见的打开、关闭、读、写、追加等操作。
9. 和FS相关的数据结构
9.1. file_system_type
用于描述特定的文件系统类型,定义在<linux/fs.h>
中。
里面有个函数指针get_sb()
,从磁盘上读取super_block
,当文件系统按照是,在内存中组装super_block
对象。
其它的基本是描述文件系统的属性。
9.2. vfsmount
当一个FS被安装时,一个vfsmount
将被创建(定义在<linux/mount.h>
中)。它代表一个挂载点。
它用于理清文件系统与其它所有安置点的关系,因此它维护了各种链表。
此外有一个mnt_flags
域,可限制该已挂载的文件系统上的文件操作。
10. 和进程相关的数据结构
每个进程要维护自己的打开的文件。而有3个结构体,将进程和VFS联系在一起。
10.1. files_struct
定义在<linux/fdtable.h>
中,task_struct
的files
域指向这个结构体。
所有与单线程相关的文件信息,都保持在该结构体中。
主要的2个域:
fdt
:指向一个struct fdtable
,而它包含一个域fd
,指向当前的file
对象数组fd_array
:缺省的文件对象数组,元素指向已打开的file
对象,若打开文件过多,则系统会分配新数组,并修改fdt
的域将其指向它
10.2. fs_struct
定义在<linux/fs_struct.h>
中,task_struct
的fs
域指向这个结构体。
包含了进程和对应文件系统的相关信息。
10.3. mmt_namespace
定义在<linux/mmt_namespace.h>
中,task_struct
的mmt_namespace
域指向这个结构体。
它使得每一个进程在系统中都看到唯一已安装的文件系统(唯一的根目录,唯一的文件系统层次结构)。
主要的域:
list
:挂载点链表,包含的元素组成了全体命名空间
而不同于之前2个结构体,默认情况下,所有的进程通常共享同样的命名空间,只有进行clone()
时使用CLONE_NEWS
标志时,才会对其进行拷贝。