1. VFS简介(Virtual File System, 虚拟文件系统)
在Linux内核中支持40多种文件系统,其来源也多种多样:来自MS-DOS的FAT文件系统, UFS(Berkeley UNIX), Ext2/3/4, 用于CD-ROM的iso9660, 网络文件系统(如coda和NFS)和虚拟的文件系统(如proc)。
Linux为了向用户空间(如C标准库)提供标准的接口,将所有的文件系统,抽象为统一的文件系统接口----VFS。图1-1 Linux文件系统的结构。
图1-1 Linux文件系统的结构
在Linux中,文件系统主要分为下面3种:
(1)基于磁盘的文件系统(Disk-based Filesystem) 是在非易失介质上存储文件的经典方法,用以在多次会话之间保持文件的内容。如Ext2/3/4, Reiserfs, FAT等。
(2)虚拟文件系统(Virtual Filesystem) 在内核中生成,是一种用户应用程序与内核通信的方法。如proc,它不许要在任何类的硬件设备上分配存储空间,所有的信息都是动态在内存中开辟和存储。
(3)网络文件系统(Network Filesystem) 是基于磁盘的文件系统和虚拟文件系统之间的折中。这种文件系统允许访问另一台计算机上的数据,该计算机通过网络连接到本地计算机。在这种情况下,数据实际上存储在一个不同系统的硬件设备上。
由于VFS抽象层的存在,用户空间进程不会看到本地文件系统与网络文件系统之间的区别。
2. VFS的模型与结构
VFS不仅为文件系统提供了方法和抽象,还支持文件系统中对象(或文件)的统一视图。并非每一种文件系统都支持VFS中的所有抽象,如FAT,因为其设计没有考虑到此类对象。定义一个最小的通用模型,来支持内核中所有文件系统都实现的那些功能,这是不实际的。因为这样会损失许多本质性的功能特性,或者导致这些特性只能通过特定文件系统的路径访问。
VFS的方案完全相反:提供一种结构模型,包含了一个强大文件系统所具备的所有组件。但该模型只存在于虚拟中,必须使用各种对象和函数指针与每种文件系统适配。所有文件系统的实现都必须提供与VFS定义的结构配合的例程,以弥合两种视图之间的差异。
VFS是由基于经典文件系统的结构衍化而来,所以VFS与Ext类文件系统类似,从而在处理Ext类文件系统的时候,Ext和VFS之间的转换,几乎不会损失时间。图1-2 底层为Ext2的VFS结构
图 1-2 底层为Ext2的VFS结构
图1-2中绿色方框的部分为Ext2实现的部分,而Ext2中用户表示目录的结构为ext2_dir_entry_2,
struct ext2_dir_entry_2 { __le32 inode; /* Inode number */ __le16 rec_len; /* Directory entry length */ __u8 name_len; /* Name length */ __u8 file_type; char name[EXT2_NAME_LEN]; /* File name */ };
其保存在inode->imapping所指向的空间中。对于dentry_operations,Ext2无须实现本地化,因为采用默认的dentry_operations已能完成所需工作。例,对于FAT,由于在比较两个文件名是否相等时,需要忽略大小写,则必须实现本地化,参见msdos_dentry_operations。图1-2中所涉及的结构体请参考书中的第8,9章和Linux内核源代码。
以下以查询一个目录为例(path_lookup函数),进行简要说明。其中底层文件系统为Ext2。
1. 首先,内核使用nameidata实例规定查找起点,如果名称以/开始,则使用当前根目录的dentry和vfsmount实例;否则,从当前进程的task_struct获得当前的工作目录。
2. 检查当前路径分量对应的cur_inode的权限,计算路径中next_dir的散列值,。
3. 处理.和..,该任务委托给follow_dotdot函数。
4. 根据2中的散列值和cur_inode的dentry,通过d_hash函数计算出next_dir的dentry在dentry缓存中可能存在其的链表,遍历链表,如果存在,跳至i,如果不存在需要调用real_lookup通过cur_inode查找。
5. real_lookup函数通过dentry->i_op->lookup调用ext2_lookup函数实现对cur_inode->i_mapping内存空间的遍历,来查找目录是否存在于该cur_inode中。如果存在,新建next_dir的dentry项,并存入dentry缓存,跳至i;不存在,返回错误。
i. 用next_dir的inode更新cur_inode,并得到一下个需要查找的目录,返回2。如果查找完毕,则成功。
注:在通过当前目录查找某个子目录是否存在的时候,使用了ext2_lookup函数直接遍历该目录的数据域,如果是文本文件会咋样呢?文本文件在建立的时候对其对应的inode的i_op域的初始化为ext2_file_inode_operations。所以文本文件的inode->i_op->lookup将为NULL。
文中部分内容涉及自己对文件系统的理解,难免有不足之处,请多谅解。