Linux VFS是一个介于应用程序和实体文件系统之间的内核抽象层。对用户层,它负责处理所有文件系统相关的系统调用;对内核,它为各种实体文件系统(EXT2/3,XFS,FAT等)提供统一的接口,将实体文件系统的各种信息表示在VFS层。
VFS的主要设计思想是用一个统一文件模型(common file modle)来表示各种实体文件系统,每种实体文件系统需要将它的物理组织结构转化为VFS的common file model。VFS的统一文件模型将目录当做一个普通文件,只不过目录文件的内容为一串文件名。但是,有些文件系统,例如FAT,目录不同于文件。这样,Linux的FAT实现需要为目录创建一个虚拟的文件节点,这个文件节点只存在于内存。
另一方面,VFS采用一系列cache来能提高系统性能。例如,最近使用到的dentry结构放在dentry cache中,这样可以提高从文件pathname到inode的查找速度。此外,还有inode cache和page cache。
为了完整的表示一个文件系统,VFS实现的关键组件有:Superblock,inode,file和dentry。
Superblock表示文件系统的元数据,包含文件系统类型,空间大小,包含的文件数目,文件系统的创建时间,状态是否clean等。
inode表示一个特定文件的元数据信息,包含inode number,文件大小,各种时间戳,文件内容的地址等。inode number是每个文件的唯一号。
file表示一个opened的文件与一个进程之间的联系,只有当进程打开这个文件时,file才存在于kernel中。如果进程close这个文件或者进程直接退出,这个file结构就回收了。
dentry表示一个文件的目录结构,也就是文件名。kernel通过dentry结构组成的树状目录结构来寻找指定的文件。
虽然VFS是位于应用程序与实际文件系统的中间层,但在有些情况下,文件操作可以直接在VFS层完成,不需要call到实际文件系统层。例如,当一个进程close一个文件时,磁盘上的文件结构通常不需要修改。还有,lseek()系统调用只是改变修改文件offset,文件offset这个属性只是一个opened的文件与进程之间的关系,不需要修改磁盘上的文件结构。
Superblock
superblock结构存在于一个双向链表super_blocks中。superblock对应的操作方法叫super_operations,每个特定的文件系统都有自己的super operations,当VFS调用read_inode()时, 执行
sb->s_op->read_inode(inode);
struct super_operations *super_ops{
alloc_inode();
destroy_inode();
read_inode();
write_inode();
delete_inode();
write_super();
};
inode
inode结构包含了一个文件的所有信息,每个inode有一个对应的inode号,inode与文件是一对一的关系。inode通常会存在于3中双向链表中:有效的未使用的inode链表;
正在使用的inode链表;dirty inode链表。inode对应的操作方法是inode_operations.
struct inode_operations *i_op {
create();
lookup();
mkdir();
rmdir();
}
file
struct file_operations *f_op{
open();
release();
read();
write();
flush();
}
在系统初始化的时候,register_filesystem()会被调用多次来register每个编译进内核的文件系统。
open() 是怎么实现的?
应用程序经常会调用文件系统相关的操作open(), read(), write等。下面以open()为例来介绍。
用户调用的open()是库函数,open库函数实际上是对sys_open()系统调用的封装。sys_open会将参数和系统调用号准备好在寄存器(例如EXA,EXB,EXC等onx86),然后调用系统调用内嵌指令(INT 0X80 on X86),这样系统就会切换到内核空间继续处理。内核首先会检查access mode 和参数,然后调用get_unused_fd()在current->files->fd中获得一个文件表述符号fd,以便最后返回给用户空间。
然后调用path_lookup()找到找到文件名对应的dentry结构,根据dentry结构找到inode结构,将inode结构的i_fop赋值给file结构的f_op结构,最后将fd返回给用户。