Linux VFS
Linux VFS与ext4的关系
ext4是一种支持日志操作的Linux文件系统。VFS定义了通用文件系统都支持的,概念上的接口。ext4文件系统只有支持并实现了这些接口,被注册到VFS后,VFS才可以识别ext4文件系统。
常见文件系统
ext2:Linux基本文件类型
ext3:ext2的增强版本
ext4:ext文件系统的第四个版本
ntfs:windows默认文件系统,功能非常强大
nfs:网络文件系统,适合Linux或UNIX机器间共享
swap:交换文件系统
VFS的架构
VFS(Virtual File System)虚拟文件系统,也称为虚拟文件系统开关(Virtual Filesystem Switch)。VFS存在于内存中,用于实现各种文件系统之间的统一接口。当Linux系统调用读写位于不同物理介质上的不同文件系统时,VFS为各类文件系统提供了一组通用的编程接口,用户可以使用这些接口来访问各种不同的文件系统,而无需关心底层文件系统的实现细节。
VFS是一个抽象层,其向上提供了统一的文件访问接口,而向下则兼容了各种不同类型的文件系统,如Ext2、Ext3、Ext4、XFS、windows家族的NTFS等常用的文件系统,还可以是目录、字符设备、块设备、 套接字、进程、线程、管道等伪文件系统和设备,如块设备,可以理解为在文件夹/dev下面的文件。
VFS部分对象介绍
以下是super block
对象,inode
对象,dentry
对象,file
对象的简单介绍。
Super block超级块
将物理相邻的若干个扇区称为一个块,文件系统中第一个块被称为超级块,用super_block
结构体表示,存储文件系统的元信息,包括inode
、block
的总量、使用量、剩余量,安装权限文件系统类型、大小、区块数以及操作方法s_op
等。
inode索引节点
保存的是元数据和记录block块位置的数据。元数据就是对文件属性的描述,例如文件大小、文件拥有者(owner)、文件属组(group)、文件权限、文件读取或修改的时间戳、link数量。
Linux内核访问文件都是通过inode
。inode
是唯一的,一个inode
表示一个文件。当创建一个文件(包括目录)的时候,就给文件分配一个inode
,方便用户识别文件。
dentry目录项
由于VFS经常执行目录相关的操作,比如切换到某个目录、路径名的查找等。为了提高查找性能,VFS引入了目录项dentry
。dentry
没有对应的磁盘描述,只存在于内存的dentry cache
(目录项缓存)中。
不管是目录还是最终的文件,都是属于目录项,所有的目录项在一起构成一颗庞大的目录树。例如:打开一个文件/opt/data/a.txt
,那么/、opt、data、a.txt都是一个目录项,VFS在查找的时候,根据一层一层的目录项找到对应的每个目录项的inode,那么沿着目录项进行操作就可以找到最终的文件。
file文件
文件对象是进程打开的文件在内存中的实例。由于多个进程可以同时打开和操作同一个文件,所以同一个文件,在内存中也存在多个对应的文件对象,但对应的索引节点和目录项是唯一的。
挂载具体原理
在挂载的过程中,最为重要的数据结构是vfsmount
,它代表一个挂载点。其次是dentry
和inode
,这两个都是对文件的表示,且都会缓存在哈希表中以提高查找的效率。其中inode
是对磁盘上文件的唯一表示,其中包含文件的元数据和文件数据(但不含文件名称)等内容,还包含关键操作的函数指针。比如对于文件读写和属性更改等操作接口都存在该结构体中。而dentry
则是为了Linux内核中查找文件方便虚拟出来的一个数据结构,其中包含文件名称、子目录(如果存在的话)和关联的inode等信息。
所以文件系统的挂载过程,其实就是构建上述几个结构体的过程,特别是inode结构体的初始化。
以Ext4为例,在挂载的时候就会将其中的address_space
、inode_operations
和file_operations
函数指针初始化为Ext4文件系统的函数。因此当对文件进行访问的时候,只要找到这个inode,就能知道是什么类型的文件系统。不同文件系统的差异其实就是inode中初始化的函数指针的差异。
操作系统怎么通过fd实现不同类型文件系统的访问
fd
(file descriptor),即文件描述符。所有的操作都是对文件进行操作,而对文件的操作是利用fd
(file descriptor)来实现的。
在Linux操作系统中,文件的打开必须要与进程(或者线程)关联,也就是说一个打开的文件必须隶属于某个进程。在linux内核当中一个进程通过task_struct
结构体描述,而打开的文件则用file
结构体描述,打开文件的过程也就是对file结构体的初始化的过程。在打开文件的过程中会将inode
部分关键信息填充到file
中,特别是文件操作的函数指针。在task_struct
中保存着一个file
类型的数组,而用户态的文件描述符其实就是数组的下标。这样通过文件描述符就可以很容易到找到file
,然后通过其中的函数指针访问数据。
ext4如何注册到VFS
ext4和VFS两者是怎么关联的
这里涉及如下几个处理流程:
- 挂载,也就是具体文件系统(例如Ext4)的挂载
- 打开文件,我们在访问一个文件之前首先要打开它(
open
) - 文件访问,进行文件的读写操作(
read
或write
)
其中第1个流程其实是建立VFS和诸如Ext4文件系统的关联,这样当用户在后面打开某个文件的时候,VFS就知道应该调用那个文件系统的函数实现。
而第2个流程则是初始化文件系统必要的数据结构和操作函数(例如read
和write
等),为后面的具体操作做准备
ext4注册的步骤
- 首先,在ext4文件系统上安装文件系统模块,这可以通过在Linux终端中运行
modprobe ext4
命令完成。 - 安装完成后,需要执行
mount
命令,将ext4文件系统挂载到VFS,以便其他程序可以访问它。 - 接下来,需要启动ext4文件系统,这可以通过在Linux终端中运行
sudo mount -t ext4 /dev/<disk-name> /mnt/<mount-point>
命令完成。 - 最后,需要检查文件系统是否已成功注册到VFS,可以通过在Linux终端中运行
df -h
命令来查看。如果出现了ext4文件系统的信息,则表示已成功注册到VFS。
这些命令将ext4文件系统挂载到VFS,以便可以访问分区内的文件和文件夹。
open()函数怎么打开ext4上的某个文件
open函数原型如下:int open(const char *pathname,int flag);
其中pathname
表示要打开的文件名,flag
表示访问模式(如只读、只写或读写)。
当用户open
一个文件时,首先,系统找到第一个参数(文件名pathname
),根据dentry
目录项找到对应的每个目录项的inode
号;一个inode
号对应一个文件。然后,通过inode
号,在inode table
中找到inode
信息,最后在inode
信息中找到文件数据所在的block
,然后就可以处理文件内容了。