6.3.1 VFS的工作原理
Linux支持多种实际的文件系统,我们称之为逻辑文件系统。不同的文件系统具有不同的组织结构和不同的处理方式,操作系统必须把各种不同的逻辑文件系统的所有特性进行抽象,建立起一个面向各种逻辑文件系统的转换机制,通过这个转换机制,把各种不同逻辑文件系统转换为一个具有统一共性的虚拟文件系统。这种转换机制称为虚拟文件系统转换VFS。VFS实际上向Linux内核和进程提供了一个处理各种逻辑文件系统的公共接口,通过这个接口使得不同的逻辑文件系统看来都是相同的。
图6-4给出了Linux核心中虚拟文件系统和实际文件系统间的关系。此虚拟文件系统必须能够管理在任何时刻mount到系统的不同文件系统。它通过维护一个描述整个虚拟文件系统和实际已安装文件系统的结构来完成这个工作。
VFS并不是一种实际的文件系统,EXT2等物理文件系统是存在于外存空间的,而VFS仅存在于内存。VFS是在系统启动时建立,在系统关闭时消失,物理文件系统是长期存在于外存。在VFS中包含着向物理文件系统转换的一系列数据结构,如VFS超级块、VFS的inode等,各种操作函数的转换入口。
图6-4 虚拟文件系统的逻辑示意图
VFS使用了和EXT2文件系统类似的方式:超级块和i节点来描述文件系统。象EXT2 i节点一样VFS i节点描述系统中的文件和目录以及VFS中的内容和拓扑结构。注意用VFS i节点和VFS超级块来将它们和EXT2 i节点和超级块进行区分。
(1)文件系统类型注册
某种类型的逻辑文件系统要得到Linux操作系统的支持,首先必须向内核注册。Linux支持的文件系统必须注册后才能使用,文件系统不再使用时则予以注销。向系统内核注册有两种方式,一种是在内核编译过程中确定要支持的文件系统类型,并在系统初始化过程中使用特定的函数调用注册,在系统关闭时注销。另一种是在系统启动完成之后,把某种逻辑文件系统类型作为一个内核模块加入到内核中,加入模块时完成注册,并在模块卸载时注销。
每一个注册过的文件系统类型都初始化为一个称为file_system_type的数据结构,所有这些数据结构组成一个单向链表,称为文件系统类型注册表。对于某一种文件系统类型,可以管理多个磁盘分区,也就是说有多个该类型的文件系统,但是它只占用类型注册表中的一个节点。当某一种类型的文件系统都不再使用时,可以使用卸载模块的方法来注销文件系统类型。每种注册的文件系统类型都登记在file_system_type结构体中,file_system_type结构体组成一个链表,称为文件系统类型注册链表,链表的表头由全局变量file_system给出。
struct file_system_type {
struct super_block *(*read_super)
(struct super_block *, void *, int);
const char *name;
int requires_dev;
struct file_system_type * next;
};
对于EXT2文件系统:
static struct file_system_type ext2_fs_type = {
ext2_read_super, "ext2", 1, NULL
};
图6-5 Linux文件系统类型注册链表。
Linux文件系统类型注册链表实例参见图6-5。
文件系统的注册是通过内核提供的文件系统初始化函数实现的:
init_ext2_fs() ext2文件系统初始化函数;
init_minix_fs() minix文件系统初始化函数;
init_msdos_fs() msdos文件系统初始化函数;
init_proc_fs() proc文件系统初始化函数;
init_sysv_fs() sysv文件系统初始化函数。
在文件系统初始化函数中,把注册结构体作为参数,调用由内核提供的注册函数register_filesystem()。例如,EXT2文件系统的初始化函数:
int init_ext2_fs(void){
return register_filesystem(&ext2_fs_type);
}
int register_filesystem(struct file_system_type * fs) /* 参数fs是注册结构体首址 */
{ struct file_system_type ** tmp;
if (!fs)
return -EINVAL; /* 不支持该文件系统,出错返回 */
if (fs-〉next)
return -EBUSY; /* 文件系统已注册,返回 */
tmp = &file_systems; /* 得到注册链表首址 */
while (*tmp) { /* 从注册链表首开始遍历 */
if (strcmp((*tmp)-〉name, fs-〉name) == 0)
return -EBUSY; /* 文件系统已注册,返回 */
tmp = &(*tmp)-〉next; /* 查看下一个注册结构体 */
}
*tmp = fs; /* 把要注册的文件系统的注册结构体加到链表 */
return 0; /* 注册成功,返回0 */
}
(2)文件系统注册
在使用一个文件系统前,除需要进行文件系统类型注册外,还必须向内核注册和挂载该文件系统,而卸载一个文件系统时,还需向内核申请注销该文件系统。文件系统注册表由系统中所有注册的文件系统组成。也采用单向链表来描述,结点类型为vfsmount,记录着对应文件系统的设备号、安装目录名称、超级块以及存储空间限额管理数据。
在安装Linux时,硬盘上已经有一个分区安装了EXT2文件系统,它被用来作为根文件系统,是在系统启动时自动挂载的。其他逻辑文件系统(如软盘构成的文件卷)可以根据需要作为子系统动态地挂载到系统中。一个逻辑文件系统挂载之后才能使用。
要挂载的文件系统必须已经存在于外存磁盘空间上,每个文件系统占用一个独立的磁盘分区,并且具有各自的树型层次结构。逻辑文件系统挂载后,下挂在根文件系统的一个子目录上,Linux文件系统的树型层次结构中用于挂载其他文件系统的目录称为挂载点或挂载目录。新挂载的逻辑文件系统的所有文件和目录就成为挂载目录下的文件或子目录。由此可见,经过文件系统挂载,根文件系统构造了一个包容多种文件系统类型的、完整目录层次结构的、容量更大的文件系统。
超级用户挂载一个文件系统可以使用mount命令,该命令参数给出了文件系统类型、存储文件系统的块物理设备和文件系统挂载点。执行mount命令进行文件系统挂载过程:
(1)首先搜索文件系统类型注册表file_system,查看是否含有该文件系统类型的file_system_type节点,若有,说明内核支持该文件系统,否则VFS请求内核装入相应文件系统模块,并重新注册初始化。
(2)检查存储该文件系统的物理设备是否存在且尚未挂载,若已挂载,则返回错误。因为块设备只能挂载到一个目录下,不能多次挂载。若肯定回答,mount必须准备挂载点的inode,该挂载点可能在索引节点的高速缓存中,也可能需要从挂载点所在的物理设备上读入。合格的挂载点必须是目录类型,且尚未用做其他文件系统的挂载点。
(3)为新文件系统分配VFS超级块,系统中所有的VFS超级块保存在super_blocks数组中。首先需申请一个空闲的super_blocks数组元素,并在相应的file_system_type节点中取得该文件系统的超级块读取例程指针,调用该例程将待挂载文件系统信息映射到VFS超级块中。
(4)每个被挂装的文件系统需申请填写信息到vfsmount结构,vfsmount结构包含该文件系统所在块设备号、文件系统安装点目录名,以及该文件系统VFS超级块指针。所有的vfsmount结构形成了一个链表,称为已挂载文件系统链表。
已挂载的文件系统用一个vfsmount结构进行描述:
struct vfsmount
{
kdev_t mnt_dev; /* 文件系统所在设备的设备号 */
char *mnt_devname; /* 设备名,如/dev/dsk/hda1 */
char *mnt_dirname; /* 安装点的目录名 */
unsigned int mnt_flags; /* 设备标志 */
struct semaphore mnt_sem; /* 对设备I/O操作时的信号量 */
struct super_block *mnt_sb; /* 指向超级块的指针 */
struct file *mnt_quotas[MAXQUOTAS]; /* 指向配额文件的指针数组 */
time_t mnt_iexp[MAXQUOTAS]; /* inode分配允许延迟时间 */
time_t mnt_bexp[MAXQUOTAS]; /* 数据块分配允许延迟时间 */
struct vfsmount *mnt_next; /* 指向链表中下一结构 */
};
vfsmount结构形成一个链表,vfsmntlist是其头指针,参见图6-6。
图6-6 vfsmount链表
超级用户卸载文件系统用umount命令。在执行umount命令时需检查文件系统超级块的状态,若文件系统的超级块已被修改过,则应将它写回磁盘,若文件系统正在被其他进程使用,该文件系统不能被卸载。若卸载成功,则对应的VFS超级块和vfsmount数据结构将被释放。
VFS是由在内存中建立的一系列数据结构组成的,主要有VFS超级块、VFS inode和文件操作函数指针等组成。通过VFS可以转换到各种不同类型的文件系统,所以VFS的数据结构必须兼容各种文件系统的相应数据结构。
逻辑文件系统要想被Linux支持,就必须按照VFS标准设计自己的操作函数。VFS设计了一些有关操作的数据结构,它实际上就是VFS和逻辑文件系统之间的接口。VFS有关操作的数据结构主要有:
super_operation:主要用来将VFS对超级块的操作转化为文件系统处理这些操作的函数。
inode_operations:这个结构主要用来将VFS对索引节点的操作转化为逻辑文件系统处理相应操作的函数。
file_operations:这个结构主要用来将VFS对file结构的操作转化为逻辑系统处理相应操作的函数。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。