分类: LINUX
2010-08-09 12:19:49
虚拟文件系统所隐含的思想是:在内核的内存中表示文件和文件系统的对象包含着广泛的信息。其中有一个域或函数,它能支持Linux支持的任何实际文件系统所提供的任何操作。对于所访问的每个读,写或其他的函数,内核都能把他们替换成实际的函数,这种实际的函数支持Linux的本地文件系统, NT文件系统,或者文件所在的任何其他文件系统。
Virtual FileSystem(或者被称为Virtual Filesystem Switch或者VFS),属于内核软件层,用来处理与Unix标准文件系统有关的所有系统调用。其强壮表现在能为各种文件系统提供一个通用的接口。
VFS支持的文件系统类型:基于磁盘的文件系统,网络文件系统,特殊文件系统。
Unix的目录建立了一棵树,其根为目录“/”。根目录包含在跟文件系统(root filesystem)中,在Linux中这个根文件系统通常就是Ext2类型。其他所有的文件系统都可以被“安装(mount)”在根文件系统的子目录中。
注:当一个文件系统被安装在某一目录上时,再父文件系统中的目录内容不再能够访问,因为包含在安装点中的路径名将指向已安装的文件系统。但是,当被安装的文件系统卸载时,原目录的内容又可再现。这种令人惊讶的Unix文件系统特点可以由系统管理员用来隐藏文件,他们只需把一个文件系统安装在要隐藏文件的目录中即可。
虚拟文件系统所隐含的主要思想在于引入了一个通用的文件模型,这个模型能够表示所有支持的文件系统。该模型严格遵守传统Unix文件系统提供的文件模型。不过要实现每个具体的文件系统,必须将其物理组织结构转换为虚拟文件系统的通用文件模型。
通用文件模型由下列对象类型组成:超级块对象(superblock object),索引节点对象(inode object),文件对象(file object),目录项对象(dentry object)。
参考369页图“进程与VFS对象的交互”
VFS是应用程序和具体的文件系统之间的一个层。不过,在某些情况下,一个文件操作可能由VFS本身去执行,无需调用下一层程序。
每个VFS对象都存放在一个适当的数据结构中,其中包括对象的属性和指向对象方法表的指针。内核可以动态地修改对象的方法,因此可以为对象设置专用的行为。
Linux通过周期性地将所有“脏”的超级块写回磁盘来减少突然断电后文件系统崩溃带来的危害。
超级块对象super_block结构 ;超级块操作super_operation结构;
书372页图“超级块链表”。
散列表加快了对索引节点对象的搜索,前提是系统要知道索引节点号及对应文件所在文件系统的超级块对象的地址。由于散列技术可能引发冲突,所以,索引节点对象设置一个i_hash域,其中包括向前和向后的两个指针,分别指向散列到同一地址的前一个索引节点和后一个索引节点,该域因此创建了由这些索引节点组成的一个双向链表。
索引节点对象inode结构;索引节点操作inode_operation结构。
文件对象描述的是进程怎样与一个打开文件交互的过程。文件对象是在文件被打开时创建的,由一个file结构组成,在磁盘上没有对应的映像,因此,file结构中没有设置“脏”域来表示文件对象是否已被修改。
存放在文件对象中的主要信息是文件指针,即文件中当前操作的位置。由于几个进程可能并发访问同一个文件,因此文件指针不能存放在索引节点对象中。
每个文件系统都有自己的文件操作(file operation)集合,执行诸如读写文件的操作。当内核将一个索引节点从磁盘装入内存时,会在file_operation结构中存放一个指向这些文件操作的指针,该结构的地址存放在该索引节点对象的inode_operation结构的default_file_ops域中。当进程打开这个文件时,VFS就用存放在索引结点中的这个地址初始化新文件对象的f_op域,使得对文件操作的后续调用能够使用这些函数。如果需要,VFS随后也可以通过在f_op域存放一个新值而修改这一文件操作的集合。
只要某目录文件的索引节点对象被修改,变量global_event的值就增1,并把这个新的版本标记值存放在该对象的i_version域中。只要一个新的文件对象被创建或者其文件指针被修改,global_event变量的值就增1,并把这个新的版本标记值存放在该对象的f_version域中。这样一来,当VFS处理readdir()系统调用时,首先检查存放在该对象i_version域中的版本标记值与f_version域中的版本标记值是否一致,如果不一致,说明该目录可能再上一次执行readdir()后被其他进程修改过。
一旦目录项被读入内存,VFS就把它转换为基于dentry结构的一个目录项对象,对于进城查找的路径命中的每个分量,内核都为其创建一个目录项对象。目录项对象将每个分量与其对应的索引节点相联系。例如,在查找卢晶明/tmp/test时,内核为根目录"/"创建一个目录项对象,为根目录下的tmp项创建一个第二级目录项对象,为/tmp目录下的test项创建一个第三级目录项对象。
目录项对象在磁盘上并没有对应的映像,因此在dentry结构中不包括“该对象已被修改”的域。目录项对象存放在称为dentry_cache的slab分配器高速缓存中。
目录项高速缓存的作用也相当于索引节点高速缓存(inode cache)的控制器。内核内存中,余未使用目录项相关的索引节点未被丢弃,这是由于目录项高速缓存仍在使用它们,因此,它们的i_count域不为空。因此,这些索引节点对象保存在RAM中,并能够借助相应的目录项快速引用它们。
散列表是由dentry_hashtable数组实现的。数组中的每个元素是一个指向链表的指针,这种链表就是把具有相同散列标志的目录项进行散列而形成的。该数组的长度取决于系统已安装RAM的数量。目录项对象的d_hash域包含指向具有相同散列值的链表中的相邻元素。散列表产生的值是由目录及文件名的目录项对象的地址计算出来的。
与目录项对相关联的方法称为目录项操作,由dentry_operation结构加以描述。
每个进程都有它自己当前的工作目录和它自己的根目录,该信息存放在一类型位fs_struct的内核表中;files_struct结构表示进程房前打开的文件,地址存放在进程描述符的files域。
或者在启动系统时,或者在安装某个文件系统的模块式,都需要进行注册。一旦一个文件系统完成注册,那么它具体的函数对内核就是可用的了。因此,这个文件系统就可以安装在系统的目录树上。
每个文件系统都有它自己的根目录,如果某文件系统的根目录是系统目录树的根,那么该文件系统称为根文件系统。而其他文件烯烃可以安装到系统的目录树上,把这些文件系统要插入的那些目录就成为安装点。
用户在为自己的系统编译内核时可以把linux配置为能够识别所有需要的文件系统。但是,文件系统的源代码实际上要么包含在内和映像中,要么作为一个模块被动态装入。VFS必须对其代码在内核映像的所有文件系统进行跟踪,执行文件系统注册就实现了这一目标。
在系统初始化期间,正好在filesystem_setup()调用之后执行mount_root()函数。
一旦完成对根文件系统的初始化,就可以安装其他的文件系统。其中的每一个都有自己的安装点,安装点仅仅是系统目录树中现有的一个目录。所有已安装的文件系统都包含在一个链表中,每个元素都是类型位vfsmount的一个结构。
参考书上394页图“安装一个文件系统”。
Mount()系统调用用来安装一个文件系统。它的服务例程sys_mount()作用的参数和执行的操作在书上392页。
Umount()系统调用用来卸载一个文件系统,相应的服务例程sys_umount()作用的参数和执行的操作见书395页。
当进程必须标识一个文件时,他把它的文件路径名传递给某个VFS系统调用。执行这一任务的标准过程就是分析路径名并把它拆分成一组文件名。除了最后一个文件名,所有的文件名都必定是目录。
在对最初目录的索引节点进行处理的过程中,代码要检查与第一个名字匹配的目录项以获得相应的索引节点。然后,从磁盘读出包含那个索引节点的目录文件,并检查与第二个名字匹配的目录项以获得相应的索引节点。对于包含在卢镜中的每个名字,反复执行这个过程。
Open()系统调用
Read()和write()系统调用
Close()系统调用
进程不管是劝告锁还是强迫锁,它们都能利用共享读锁和独占写锁。在文件的某个区域上,可以由任意多个进程进行读,但在同一时间只能由一个进程进行写。此外,当其他进程对同一个文件都拥有自己的读锁时,就不可能获得一写锁,反之亦然。
linux支持文件加锁的所有方式为:劝告锁和强制锁,以及fcnt1()flock()和lockf()系统调用。
File_lock数据结构表示文件锁。
当进程试图获得一个劝告锁或强制锁时,它必须被挂起,直到分配给同一文件区域的前一个所被释放为止。
Flock()调用sys_flock()服务例程,作用于两个参数。
在文件内部不能指定一个区域,这种缩总是应用于整个文件。
Fcnt1()调用sys_fcnt1()服务例程,作用于三个参数。
该锁能够保护任意一个文件区,甚至一个单独的字节。Flock结构的三个域指定要加锁的区域。
Linux 2.4的VFS处理八种新的文件系统,其中有处理DVD的udf。文件长度的最大值已经被极大地增加(至少从VFS的观点看),这是通过把索引节点的I_size域从32位扩充到64位实现的。