虚拟文件系统
一基本概念
传统的操作系统仅能支持一种类型的文件系统,随着信息技术的发展和应用需求的增长,对文件系统的使用提出了新的要求,例如,要求在UNIX系统中支持非UNIX类文件系统,以便运行UNIX的机器上也可访问DOS分区;要求Windows 2000/XP支持高性能文件系统的同时支持FAT文件系统;Linux在设计时便瞄准能同时支持几十种文件系统;随着网络的发展,迫切要求计算机之间共享网络文件系统;甚至一些用户希望能定制自己的文件系统。
为了能同时支持多种文件系统,不同操作系统采用不同技术方案来提供虚拟文件系统,它要实现以下目标:把多种个文件系统纳入统一框架中,不同的磁盘分区可以包含不同的文件系统,对它们的使用和传统单一文件系统没有区别;用户可以通过同一组系统调用来对不同的文件系统及文件进行操作,更进一步,系统调用可以跨物理介质和跨文件系统执行,如从一个文件系统拷贝或移动数据到另一个文件系统;对网络共享文件提供完全支持,访问远程节点上的文件应与访问本地节点的文件一致;开发出新的文件系统后,可以模块方式加入到操作系统中。
虚拟文件系统VFS也称为虚拟文件系统开关(Virtual filesystem Switch),它是内核的一个子系统,提供了一个通用文件系统模型,该模型囊括了所能见到的文件系统常用功能和行为,并为应用程序提供一致
性的文件系统接口,安装的所有物理文件系统不但依赖于VFS共存,而且也依靠VFS协同工作。它的主要设计思想有以下3点:
(1)应用层:VFS模型源于UNIX文件系统,使得用户可以直接使用标准UNIX文件系统调用来操作文件,无需考虑具体文件系统特性和物理存储介质,通过VFS访问文件系统,才使得不同文件系统之间的协作性和通用性成为可能。
(2)虚拟层:在对所有具体文件系统的共同特性进行抽象的基础上,形成一个与具体文件系统实现无关的虚拟层,并在此层次上定义与用户的一致性接口;
(3)实现层:该层使用类似开关表技术进行具体文件系统转接,实现各种文件系统的物理操作细节,每个文件系统是自包含的,包含文件系统实现的各种设施,如超级块、节点区、数据区以及各种数据结构和文件类的操作函数。
一般地说,VFS提供以下功能:
?记录可用的文件系统类型;
?把文件系统与对应的存储设备联系起来;
?处理面向文件的通用操作;
?涉及具体文件系统的操作时,把它们映射到相关的具体文件系统。
VFS抽象层之所以能衔接各种不同的文件系统,是因为它定义了所有文件系统都支持的基
本抽象接口和数据结构,同时具体的文件系统也将自己的诸如"文件如何打开"、"目录如何定义"等概念在形式上与VFS的定义保持一致。对于像FAT和NTFS这类非UNIX风格文件系统,必须经过封装,提供符合VFS概念的接口。比如,一个文件系统不支持inode概念,它也必须在内存中装配inode结构体,就像它本身包含inode一样。这些装配和转换需要在使用现场引入特别处理,使得非UNIX文件系统能够兼容UNIX文件系统的使用规则和满足VFS的需求,这样一来,非UNIX文件系统便可与VFS一道工作,只是性能上会有少许影响。
下面看一个文件操作的例子,假如应用程序执行如下文件操作:write(fd,&buf,len);要求将buf指针指向的、长度为len字节的数据写入文件描述符fd对应的文件的当前位置。用户执行的系统调用首先被VFS的sys_write( )处理,该函数首先处理一些与设备无关的操作,并到f所在的文件系统,再根据VFS结构及它的inode数据结构提供的信息,重定向到具体文件系统中相对应的写函数,由它来处理与特定设备相关的操作,并把数据写到物理介质。
二实例研究:Linux文件系统
Linux VFS采用了面向对象设计思想,文件系统中定义的VFS相当于面向对象系统中的抽象基类,从它出发可以派生出不同的子类,以支持多种具体文件系统,但从效率考虑内核纯粹使用C语言编程,故并没有直接利用面向对象的语义。下面的讨论使用了术语"对象",实际上是一个结构体struct,但它代表的确实是一个对象object。VFS由下列四个对象类型组成:
·超级块(super block)对象--代表一个文件系统。存放已安装的文件系统的信息。如果是基于磁盘的文件系统,该对象便对应于存放在磁盘上的文件系统控制块,亦即每个文件系统都对应一个超级块对象。
·索引节点(inode)对象--代表一个文件。存放一个具体文件的所有信息。如果是基于磁盘的文件系统,该对象通常对应于存放在磁盘上的文件控制块,亦即每个文件都有一个inode对象,而每个inode都有一个inode索引节点号,这个号惟一地标识某个文件系统中的指定文件。
·目录项(dentry)对象--代表路径中的一个组成部分。存放目录项与对应文件进行链接的信息。VFS还把最近最常使用的dentry对象放在目录项高速缓冲中,加快文件路径名搜索过程,以提高系统性能。
·文件(file)对象--代表由进程已打开的一个文件。存放已打开的文件与进程的交互信息,这些信息仅当进程访问文件期间才存于内存中。
每个主要对象都包含一个操作对象,它描述了内核针对主要对象可以使用的方法
,
这些操作对象是:
·super_operation对象,其中包括内核针对特定文件系统所能调用的方法。
·inode_operation对象,其中包括内核针对特定文件所能调用的方法。
·dentry_operation对象,其中包括内核针对特定目录所能调用的方法。
·super-operation对象,其中包括进程针对已打开文件所能调用的方法。
操作对象作为一个指针结构体被实现,该结构体包含指向操作其父对象的函数指针。其中的许多方法可继承使用VFS的通用函数,若通用函数不能满足需要,就必须使用具体文件系统的独有方法填充这些函数指针,使其指向文件系统实例。
VFS使用了大量结构体对象,除上述4个主要对象外,还有描述文件系统特性和能力,在注册时使用的file_system_type对象,描述安装标志、位置,在安装时使用的vfsmount对象。与进程密切相关的对象有:file_struct、fs_struct和namespace。
1 超级块对象
超级块描述一个文件系统的信息。对于每个具体的文件系统来说,都有各自的超级块,如Ext2超级块,并被存放在磁盘特定扇区上。当内核对一个具体文件系统进行初始化和注册时,在内存使用alloc_super( )函数为其分配一个超级块,并从磁盘读取具体文件系统超级块中的信息填充进来,这是VFS超级块。也就是说,VFS超级块是各个具体文件系统安装时才建立的,并在这些具体文件系统卸载时被自动删除,可见VFS超级块仅存于内存中。超级块的数据结构如下:
struct super_block
{
struct list_head s_list; //指向超级块链表的指针
kdev_t s_dev; // 文件系统的主、次设备号
unsigned long s_blocksize; // 字节为单位的块大小
unsigned char s_blocksize_bits;// 以2的幂次表示块大小,如块为4KB,值为12
...
struct file_system_type *s_type; // 指向文件系统file_system_type结构的指针
struct super_operations *s_op; // 指向具体文件系统的用于超级块操作的函数集
struct dentry s_root; //安装目录的dentry对象
...
union
{ //一个联合体,成员是各个具体文件系统的fsname_sb_info数据结构
struct minix_sb_info minix_sb;
struct ext2_sb_info ext2_sb;
struct hpfs_sb_info hpfs_sb;
...
} u;
};
每个被安装的文件系统都有一个super_block结构,以环形双向链表把它们链接在一起,指向该链表第一个元素和最后一个元素的指针存放在该超级块的成员s_list域中。
联合体中的成员super_block.u是实现支持多种文件系统的关键,它指向Linux文件系统所支持的各种具体文件系统的超级块,当系统上安装另一个文件系统时,那么磁盘上的hpfs的超级块被复制到内存的hpfs_
sb_info结构体中,由super_block.u.hpfs_sb指向该结构体,此后允许该文件系统直接对内存超级块的u联合体操作,无需再去读盘。
与超级块关联的方法就是超级块操作对象,这些操作由super_operation结构来描述:
struct super_operation {
viod (*write_super)(struct super_block *);//把超级块信息写回磁盘
viod (*put_super)(struct super_block *); //释放超级块对象
viod (*read_inode)(struct inode *); //读取文件inode
viod (*write_inode)(struct inode *,int); //回写文件inode
viod (*put_inode)(struct inode *); //逻辑上释放inode
viod (*delete_inode)(struct inode *); //物理上释放inode
...
};
结构体中的每一项是一个指向超级块操作函数的指针,而超级块操作函数执行文件系统和索引节点的低层操作。
2 索引节点对象
inode对象内包含了内核在操作文件或目录时需要的全部信息,文件名可以更改,但索引节点对文件是惟一的,且随文件的存在而存在。对于UNIX类文件系统来说,这些信息可以从磁盘索引节点直接读方VFS的inode对象中。如果一个文件系统没有索引节点,那么,不管这些相关信息在磁盘上是如何存放的,文件系统都必须提取这些信息,并构造它的inode。可以把具体文件系统存放在磁盘上的inode称为静态节点,它的内容被读入内存VFS的inode才能工作,后者也称为动态节点,其数据结构的主要域定义如下:
struct inode
{
struct list_head i_hash; //指向散列链表的指针
struct list_head i_list; //指向索引节点链表的指针
struct list_head i_dentry; //指向目录项链表的指针
...
unsigned long i_ino; //inode号
kdevt idev; // 常规文件所在设备号
umode_t i_mode; //文件类型以及存取权限
nlink_t i_nlink; //连接到该inode的硬连接数
uid_t i_uid; //文件属主的用户ID
gid_t i_gid; //文件属主所在组的ID
kdev_t i_rdev; // 特殊文件所在设备号
loff_t i_size; //字节为单位文件大小
...
struct inode_operations *i_op; //指向inode进行操作的函数指针
struct super_block *i_sb; //指向该文件系统超级块的指针
atomic_t i_count; //当前使用该inode的引用计数,0表示空闲unix文件系统
struct file_operations *i_fop; //指向文件操作操作的指针
...
struct page *i_pages; //指向页结构的指针
unsigned long i_state; //索引节点状态
unsigned int i_flags; //文件系统安装标志
...
union
{//联合体成员指向具体文件系统的inode结构
struct minix_inode_info minix_i;
struct E
xt2_inode_info Ext2_i;
struct hpfs_inode_info hpfs_i;
...
} u;
};
一个索引节点代表文件系统中的一个文件,它也可以是设备、套接字或管道这类特殊文件,故索引节
点中会包含特殊的项。与索引节点关联的方法就是索引节点操作对象,这些操作由inode_operation结构来描述:
struct inode_operation {
int (*create)(struct inode *,struct dentry *,int);
struct dentry* (*loopup) (struct inode *,struct dentry *);
int (*link)(struct dentry *,struct dentry *);
int (*symlink)(struct inode *,struct dentry *,const char *);
int (*mkdir)(struct inode *,struct dentry *,int);
int (*rmdir)(struct inode *,struct dentry *);
...
};
其中,主要函数功能如下:create( )创建一个新的inode,loopup( )查一个inode所在的目录,link( )和unlink( )创建和删除一个硬连接,symlink( )为符号链接创建一个inode,mkdir( )和rmdir( ) 为目录项创建和删除一个inode。
3 目录项对象
VFS把每个目录看作一个文件,如在路径/bin/vi中,bin和vi都是文件,bin是目录文件,而vi是普通文件,路径中的每个组成部分都由一个索引节点对象表示。为了方便查,VFS引入目录项的概念,每个dentry代表路径中的一个部分,如/、bin和vi都是目录项对象。前两个是目录,后一个是普通文件。于是在路径查中,VFS为根目录/、dev和test分别创建了3个目录项对象。所以,每一个文件除了有一个inode数据结构外,还有一个dentry数据结构与之关联,dentry结构中的d_inode指针指向相应的inode结构,引入dentry的主要目的是对目录进行缓冲,加快对文件的快速定位,改进文件系统效率。Dentry结构代表逻辑意义上的文件,描述文件的逻辑属性,它在磁盘上并没有对应的映像;而inode结构代表物理意义上的文件,记录文件的物理属性,它在磁盘上有对应的映像。dentry数据结构的主要域定义如下:
Struct dentry {
Atomic_t d_count; //目录项dentry引用计数
unsigned int d_flags; //dentry状态标志
struct inode * d_inode; //与文件关联的索引节点
struct dentry * d_parent; //父目录的dentry结构
struct list_head d_hash; //dentry形成的哈希表
struct list_head d_lru; //未用的LRU双向链表
struct list_head d_child; //父目录的子目录项dentry形成双向链表
struct list_head d_subdirs; //该目录项的子目录形成的双向链表
struct list_head d_alias; //索引节点别名的链表
int d_mounted; //目录项的安装点
struct qstr d_name; //目录项名,用于快速查
unsigned long d_time; //重新生
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论