Linux可以支持其它OS文件格式的磁盘或分区,是通过“虚拟文件系统”实现的。调用的每个读写或其它函数,内核都能把它们替换成真正文件系统的实际函数,是一个内核软件层。VFS支持的文件系统可分为三个类型:1.磁盘文件系统,如Ext2,UFS,NTFS等。2.网络文件系统如NFS,AFS等。3.特殊文件系统如/proc。所有的文件系统可安装到根文件系统的子目录中,mount后父目录中的原内容被隐藏。Sun公司在1986年的SunOS中第一次包含了VFS后,多数的Unix都包含VFS。Linux的VFS支持最广泛的文件系统,如FAT。FAT存放的是目录,将每个文件在目录中的位置存在文件分配表FAT中,Linux在必要时能快速建立起对应于目录的文件且这些文件只作为内核内存的对象而存在。内存中存在以下常用的对象来实现VFS。超级块对象存放已安装文件系统的相关信息;索引节点对象存放具体文件的信息,一个inode对象唯一地标识一个文件;文件对象存放文件与进程交互时的有关信息,它仅当进程访问文件时存在;目录项(即文件或目录名)对象存放目录项与对应文件。VFS是一种“通用”文件系统,在必要时依赖具体的文件系统。分析以上4种数据结构:
1.超级块对象:结构super_block。由双向链表串起,对基于磁盘的文件系统,它一般放在文件系统控制块中。字段s_fs_info指向具体文件系统的超级块信息,其中主要有磁盘分配位图及与通用文件系统无关的数据。这些内容一般会从磁盘拷贝到内存,在内存修改后为保持与磁盘内容同步,结构采用s_dirt标志。Linux同期性将数据写入磁盘。具体的超级块操作的地址放在s_op中。VFS要调用一个read_inode时,执行的是sb->s_op->read_inode即化为具体的真实文件系统的函数。
2.inode对象:表示具体的文件信息,它需要引用一些磁盘inode信息。在超级块对象中有s_inode字段,指出此文件系统的所有inode对象,其中所有对象连成双向循环链表,表头存在s_inode中;单纯一个链表不够用,按以下情况再分为三个链表:用于高速缓存的、正在使用的不为脏的、脏的。此外,inode对象也是常用对象,为方便查找,放于inode_hashtable中,用文件系统中inode号来hash,冲突用拉链法解决。
3.文件对象:在文件被打开时,创建file结构。File对象在磁盘上并不存在,不同进程打开同一文件会有多个文件对象创建,因为要考虑到文件指针问题。文件对象本身通过slab调整缓存分配。在相应的文件系统的超级块对象中,存放着此文件系统中正在使用的文件对象的链表头。文件对象也可被dup、fork这样的设为共享,其中的f_count字段标志了共享。每个文件系统有自己的操作,内核将一个磁盘inode信息装入inode对象时,相关的操作也拷到了inode对象的i_fop指向的结构中,再当进程打开文件创建file对象时,inode对象中的i_fop中的地址会将file对象中的这个地址初始化。
4.目录项对象:VFS将目录看作一个普通文件,内容是若干子目录与文件。目录项被读入内存后,VFS会将它转换成基于dentry结构的目录项对象。注意,目录项对象在磁盘上也没有对象的映像。每个目录项对象有4状态:1.空闲:目录项对象不包括有效信息,内存由slab分配器处理;2.未使用:目录项对象当前并未被内核使用,d_count为0,但已经包含有效信息;3.正在使用:d_count为正;4.负状态:与目录项相关的索引节点已经不存在。
读目录项并构造对象要花大量时间,Linux使用目录项缓存。缓存如此组织:由于每个索引节点可能由若干个目录项指向(如硬链接),所以每个inode对象指向一个正在使用的目录项的链表,同时未使用的目录项对象存在最近最少使用的双向链表,当高速缓存空间变小,则从其尾部删除相应对象。当正在使用的目录项对象由于inode被删除变负状态时,也插入此链表,为方便查找,用hash表hash目录项名字。
进程描述符的fs_struc字段表示进程工作目录等信息,files字段指出进程打开的文件表,这是个files_struct结构,其中有文件描述符位图等全局信息,更重要的是一指针数组fd,指向同在此结构中的fd_array,后者存放的就是文件对象。Fd数组下标就是文件描述符,current->files->fd[fd]即为文件描述符对应的文件对象,文件对象再找目录项对象,后者再找inode对象,inode对象找到文件中的数据。
文件系统的安装是指将某文件系统关联到一个目录,此后用这个目录访问那个文件系统。Linux同一文件系统可被多次安装,这样就必须用一些数据结构来管理安装信息。已安装文件系统描述符是vfs_mount结构,它保存了此文件系统安装的信息。该结构串成双向循环链表管理,同时由父文件系统的已安装描述符与目录项对象地址一起hash。
系统初始化时,安装根文件系统,这很麻烦,现假设根文件系统存放于硬盘分区,分两阶段:1.安装特殊的rootfs文件系统,它提供一个初始安装点的空目录。2.内核初始化完成前安装根文件系统,覆盖footfs。路径名查找时,除了检查目录项,获得inode外仍需考虑访问权限、符号链接、跨文件系统。符号链接文件就是将它的文件内容解释为路径。