Linux内核-VFS

Posted by keys961 on April 2, 2019

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:代表一个具体的已安装FS
  • inode:代表一个具体文件
  • 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. 目录项状态

三种:

  • 被使用dentryd_inode域指向一个有效的inode,且引用计数d_count为正
  • 未被使用dentryd_inode域指向一个有效的inode,引用计数d_count为0
  • 负状态dentryd_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可在内存中存在多个,但是它们对应的inodedentry是唯一的。

文件对象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_structfiles域指向这个结构体。

所有与单线程相关的文件信息,都保持在该结构体中。

主要的2个域:

  • fdt:指向一个struct fdtable,而它包含一个域fd,指向当前的file对象数组
  • fd_array:缺省的文件对象数组,元素指向已打开的file对象,若打开文件过多,则系统会分配新数组,并修改fdt的域将其指向它

10.2. fs_struct

定义在<linux/fs_struct.h>中,task_structfs域指向这个结构体。

包含了进程和对应文件系统的相关信息。

10.3. mmt_namespace

定义在<linux/mmt_namespace.h>中,task_structmmt_namespace域指向这个结构体。

它使得每一个进程在系统中都看到唯一已安装的文件系统(唯一的根目录,唯一的文件系统层次结构)。

主要的域:

  • list:挂载点链表,包含的元素组成了全体命名空间

而不同于之前2个结构体,默认情况下,所有的进程通常共享同样的命名空间,只有进行clone()时使用CLONE_NEWS标志时,才会对其进行拷贝。