vfscore核心数据结构
数据结构介绍
四个非常重要的数据结构:vfscore_file,dentry,mount,vnode
1、文件结构体
文件对象描述进程怎样与一个打开的文件进行交互。文件对象是在文件被打开时创建的。
1 | struct vfscore_file { |
1.1 int fd
unikraft定义了这么一个结构体,叫做fdtable
,其具体定义如下:
1 | struct fdtable { |
当文件被进程打开时,会创建一个fd作为该文件的文件标识符,用于快速检索文件所在的位置,而文件则会被file[fd]所指向,在bitmap中也会占用一个bit用来标识fd已被分配。
2、目录项结构体
VFS把每个目录看作一个文件,由若干子目录和文件组成。对于进程查找路径名中的每个分量,内核都为其创建一个目录项对象;目录项对象将每个分量与其对应的索引节点相联系。例如/tmp/test,内核会分别为“/”,“tmp”,“test”创建目录项。目录项也可以用于标识普通文件。
1 | struct dentry { |
2.1 struct uk_hlist_node d_link;
dentry节点会被挂载在一个hashtable中,其定义在lib/vfscore/dentry.c中,其定义为:
static struct uk_hlist_head dentry_hash_table[DENTRY_BUCKETS];
dentry中存储的路径会与其指向的mount节点的信息计算一个hash值,然后根据假设计算出的hash值为k,那么该节点就会插入dentry_hash_table[k]
所代表的链表中,d_link则是用于哈希链表连接的一个指针项。哈希表可以用来根据路径值和挂载点快速检索某个dentry。
以下为struct uk_hlist_head
的定义:
1 | struct uk_hlist_head { |
可以看到该d_link项仅仅就是用来链接不同的dentry项,哈希表示意图如图所示:
2.2 struct vnode *d_vnode;
该dentry目录项所对应的vnode项,而vnode项是描述该目录项所代表文件的各种属性,如文件类型,文件大小等,详见vnode具体分析。
2.3 struct dentry *d_parent;
目录项其实是一种类似于树形结构的组织结构(其实每个目录项下面的子目录项的链接是个环形结构),每个目录项节点会有一个指向父目录的指针。
2.4 struct uk_list_head d_names_link;
每个vnode可能同时有多个不同的dentry同时指向,所以指向同一个vnode的所有dentry便自己组成一个环形链接结构。由vnode中的v_names则是这个链接结构的一个节点(头部),所有的uk_list_head
结构其实都是环形双向链表结构,在遍历的时候的停止条件则可以设成遍历到本身就停止,方便遍历。
2.5 struct uk_list_head d_child_list; 与 struct uk_list_head d_child_link;
d_child_list与d_child_link的关系就像vnames与d_names_link的关系一样,d_child_list是子目录项所构成的环形链接结构的一个节点(头部),而d_child_link则是该层级目录结构下所有的目录项相互链接的链接项,父级目录与子级目录之间的关系如图所示:
3、挂载结构体
mount挂载的作用,就是将一个设备(存储设备)挂接到一个已知目录下,访问该目录就是访问该存储设备
linux操作系统将所有的设备都看作文件,它将整个计算机的资源都整合成一个大的文件目录。我们要访问存储设备中的文件,必须将文件所在的分区挂载到一个已存在的目录上,然后通过访问这个目录来访问存储设备。挂载就是把设备放在一个目录下,让系统知道怎么管理这个设备里的文件,了解这个存储设备的可读写特性之类的过程。
1 | struct mount { |
3.1 struct vfsops *m_op;
通常来讲,一个mount挂载设备对应的是一个文件系统(如ext2,9p等),而m_op指向的是该类型文件系统的基本操作,vfsops结构体的定义如下:
1 | struct vfsops { |
之所以该vfscore可以兼容多种不同的文件系统在同一个大的文件系统中,是因为vfs定义了统一的抽象接口,而下层不同的文件系统只要实现这些接口,便可以被注册到vfs中统一管理并使用。
3.2 char m_path[PATH_MAX];
该mount点所指向的绝对路径(相对于整个文件系统根目录的路径),而前文dentry中的d_path指向的是相对路径,即以mount挂载点为root(路径为/)的相对路径。
3.3 struct dentry *m_root; 与 struct dentry *m_covered;
该mount点所指向的dentry目录项为m_covered。假设我们需要将device挂载在目录/home/user/fs0
下面,那么该m_covered所指向的为/home/user/fs0
路径所对应的dentry,挂载之后,在该mount之下的所有dentry项的d_path都会变成相对于该mount点的相对路径,而m_root则是指向挂载mount点后所创建的root项。mount示意图如图所示:
3.4 struct uk_list_head mnt_list;
关联所有mount实例的链接,所有的mount实例都会通过该mnt_list链接成一个双向链接的环
4、vnode文件信息管理结构体
vnode用于管理文件(目录也是一种文件)的具体信息
1 | struct vnode { |
4.1 struct uk_list_head v_link;
所有的已经激活的vnode(也就是已经被opened)都会被挂载进vnode hashtable中,其定义为static struct uk_list_head vnode_table[VNODE_BUCKETS];
,具体作用与dentry hashtable如出一辙。哈希计算参数为挂载点mount与inode编号ino。
4.2 struct vnops *v_op;
每个vnode都会有很多的操作,比如open,close,write等,vnops的定义为:
1 | struct vnops { |
而该vnops也有对外封装调用的接口:
1 |
同vfs能够纳入多种不同的文件系统,vfs能够允许不同文件存在同一个文件系统的原因也是统一定义了文件操作的抽象,不同文件的区别就在于其vnode所对应的操作项的实施方式不同,而在vfs层的接口可以屏蔽这些实现细节的不同,而调用统一的抽象方法,故而一个大的文件系统可以容纳不同种类的文件。
4.3 int v_type;
标识该文件的具体类别,而在unikraft文件系统中,文件类型被封装为一个枚举变量,其中枚举了所有可能的文件类型:
1 | enum vtype { |
4.4 struct uk_list_head v_names;
用于关联每一个指向vnode的dentry项。d_child_list与d_child_link的关系就像vnames与d_names_link的关系一样,具体图示见2.5