4.3. 目录项对象
VFS把目录当作文件对待,所以在路径/bin/vim中,bin和vim都属于文件:bin是特殊的目录文件,而vim是一个普通文件,路径中的每个组成部分都由一个索引节点对象表示。
为了方便查找路径,VFS引入了目录项的概念。每个dentry代表路径中的一个特定部分。比如,/、bin、vim都是目录项对象。在路径中,包括普通文件在内,每一个部分都是目录项对象。目录项对象也可以包括安装点。
目录项对象由dentry结构体表示,在文件<linux/dcache.h>中定义。不同于前面的两个对象,目录项对象没有对应的磁盘数据结构,VFS根据字符串形式的路径名现场创建它。
4.3.1. 目录项状态
目录项有三种有效状态:被使用、未被使用和负状态。
1) 一个被使用的目录项对应一个有效的索引节点(即d_inode指向相应的索引节点)并且表明该对象存在一个或多个使用者(即,d_count为正值)。该目录项不能被丢弃或销毁。
2) 一个未被使用的目录项对应一个有效的索引节点(即d_inode指向相应的索引节点),但是应指明当前没有使用它(即,d_count为正值)。该目录项对象被保留在缓存中以便需要时在使用它,从而使路径查找更加迅速。但是,如果要回收页的话,可以销毁它。
3) 一个负状态的目录项没有对应的有效索引节点(d_inode为NULL),因为索引节点已被删除了,或路径不再正确,但是目录项仍然保留,以便快速解析以后的路径查询。
4.3.2. 目录项缓存
如果VFS层遍历路径名中的所有元素并将它们逐个地解析成目录项对象,这会浪费大量时间,所以内核将目录项对象缓存在目录项缓存中,简称dcache。
目录项缓存包括三个主要部分:
1) “被使用”的目录项链表。该链表通过索引节点对象中的i_dentry项连接相关的索引节点。
2) “最近被使用的”双向链表。该链表包含有未被使用和负状态的的目录项对象。由于该链表以时间顺序插入,所以链头的节点是最新的数据。
3) 散列表和相应的散列函数用来快速地将给定路劲解析为相关的目录项对象。
dentry_hashtable表示,其中每个元素都是一个指向具有相同键值的目录项对象链表的指针。
d_hash函数计算,它是内核提供给文件系统的唯一的一个散列函数。
d_lookup函数,如果该函数在dcache中发现了与其相配的目录项对象,则匹配的对象被返回,否则,返回NULL指针。
4.3.3. 目录项操作
结构体dentry_operations指明了VFS操作目录项的所有方法,该结构定义在<linux/dcache>中。
4.4. 文件对象
文件对象表示进程已经打开的文件,如果我们站在用户空间看待VFS,文件对象会首先进入我们的视野。进程直接处理的是文件,而不是超级块、索引节点或目录项对象。
文件对象是已经打开的文件在内存中的表示。该对象由相应的open()系统调用创建,由
close()系统调用销毁。同一个文件耶可能存在多个对应的文件对象,但是文件对象反过来指向目录项对象(反过来指向索引节点),其实只有目录项对象才表示已经打开的实际文件。文件对象由file结构体表示,在<linux/fs.h>中定义。
文件对象的操作由file_operations结构体表示,在<linux/fs.h>中定义。
5. 和文件系统相关的数据结构
内核还使用了一些标准数据结构来管理文件系统的其他相关数据。第一个结构体是file_system_type,用来描述各种特殊文件系统类型,第二结构体是vfsmount,用来描述一个安装文件系统的实例。
由于Linux需要支持众多不同的文件系统,所以内核必须由一个特殊的结构体描述每种文件系统的功能和行为。
struct file_system_type{
const char *name; /*文件系统名字*/
struct subsystem subsys; /*sysfs子系统对象*/
int fs_flags; /*文件系统类型标志*/
/*从磁盘中读取超级块*/
struct super_block *(*get_sb) (struct file_system_type *,int,char*,void*);
void (*kill_sb)(struct super_block) /*终止访问超级块*/
struct module owner; /*文件系统模块*/
struct file_system_type *next; /*链表中下一个文件系统类型*/
struct list_head fs_supers; /*超级块对象链表*/
};
get_sb函数从磁盘上读取超级块,并在文件系统被安装时,在内存中组装超级块对象。每种文件系统,不管有多少个实例安装到到系统中,还是根本就没有安装到系统中,都只有一个file_system_type结构。
当文件系统被实际安装时,将有一个vfsmount结构体在安装点被创建。该结构体代表文件系统的实例——换句话说,代表一个安装点。
vfsmount结构被定义在<linux/mount.h>中。理清文件系统和所有其他安装点间的关系,是维护所有安装点链表中最复杂的工作。
6. 和进程相关的数据结构
系统中的每个进行都有自己的一组打开的文件,像根文件系统、当前工作目录、安装点等。有三个数据结构将VFS层和系统的进程紧密相连,分别是files_struct、fs_struct和namespace结构体。
files_struct结构体定义在<linux/file.h>中。该结构体由进程描述符中的files域指向。所有与每个进程相关的信息如打开的文件及文件描述符都包含在其中。域fd数组指针指向已经打开的文件对象链表,默认情况下,指向fd_array数组,可以容纳32个文件对象。如果进程所打开的文件对象超过32个,内核将分配一个新数组,并且将fd指针指向它。为了优化性能,管理员可以适当增加NR_OPEN_DEFAULT的预定义值。
第二个结构体是fs_struct。该结构体由进程描述符的fs域指向。它包含文件系统和进程的相关信息,定义在<linux/fs_struct.h>中。该结构包含了当前进程的当前工作目录和根目录。
第三个结构体是namespace。定义在<linux/namespace.h>中,由进程描述符中的namespace域指向。2.4内核以后,单进程命名空间被加入到内核中,它使得每一个进程在文件系统中都看到唯一的安装文件系统——不仅是唯一的根目录,而且是唯一的文件系统层次结构。
上述这些数据结构都是通过进程描述符连接起来的。结构体namespace的使用却与前两种结构体不同,默认情况下,所有进程共享同样的命名空间(即它们都从相同的挂载表中看到同一个文件系统层次结构)。只有进程进行clone()操作时使用CLONE_NEWNS标志,才会给进程一个另外的命名空间结构体的拷贝。多数进程不提供这个标志,所有进程都继承其父进程的命名空间。