目录
12章 - 虚拟文件系统 - vfs的数据结构
下图是vfs结构中对象的大致关系:
超级块对象(super_block)
介绍
字段
字段介绍
所有超级块对象以双向循环链表的形式组织起来
- 第一个元素 -- super_blocks变量
- s_list -- 指向链表相邻元素
s_fs_info -- 指向属于具体文件系统的超级块信息
s_op字段中存放了super_operations结构体的起始指针
- 通过超级块对象的指针,访问其s_op字段,再在其中调用某个操作:
通常,s_fs_info指向的数据会被复制到内存
- 方便我们操作磁盘分配位图,以便分配或释放磁盘块
但是,这样就会造成磁盘上和内存上的超级块不一样
- 所以我们需要更新同步 -- 引入s_dirt标志,表示该超级块是否是脏的(也就是是否进行了修改数据)
- 然后,linux还会定期将所有"脏"的超级块写回磁盘,防止突然断电/其他突发问题,导致大量数据无法写回磁盘
超级块中会存放该文件类型所有的文件对象,以双向链表的形式组织起来(文件对象在下面有介绍)
- 这个链表包含了文件系统中所有已打开文件的文件对象
除此之外,超级块中也会存放该文件类型所有的inode对象,也是一个双向链表 ,s_inodes字段指向该链表
super_operations表
限额系统
是一种系统功能,用于限制用户对系统资源的使用
索引节点对象(inode)
介绍
文件名可以随意修改,但文件和inode之间的链接一般是不变的
字段
字段介绍
和超级块对象类似,它也可以在内存中被修改数据,也需要被更新 -- 通过i_state字段标识该inode是否脏
inode对象总是会位于下面三种双向循环链表中的一种:
- 其中,i_list也是指向相邻元素
除了上面三种,所有的inode对象还会位于一个散列表中,但这个散列表具有前后指针,所以形成了一个双向链表结构
- 通过i_hash可以拿到该结构
索引节点对象也包含在per-filesystem的链表中
- per-filesystem通常用来描述针对每个文件系统的特定操作或数据结构
- 我猜这里指的是 -- inode会存放在对应的文件系统下的对象中,比如超级块对象(超级块对象正是管理整个文件系统的结构,那么它里面存放所有该类文件系统的inode对象也不奇怪了)
链表头存放在s_inodes(超级块对象中的字段):
相邻指针存放在inode中的i_sb_list
和超级块对象类似,也有存放索引节点操作的表,表现形式为inode_operations结构
- i_op字段存放了该结构的指针
i_fop字段存放了该文件类型的默认文件操作
i_dentry字段 指向包含该文件名的目录项对象
- 有助于快速定位到文件的目录项,进而进行文件访问和管理
- (早在之前,我们就已经介绍过,在目录文件的数据块里,会存放文件名和inode的映射关系,通过该字段,可以定位到相关目录项对象,然后拿到文件名到 inode 的映射关系)
inode_operations表
和超级块类似:
文件对象
介绍
一个完全是软件层的结构
- 主要存储了文件的访问信息,可以有效地跟踪和管理进程对文件的访问,确保文件的一致性和安全性
字段
字段介绍
f_pos字段记录了当前的文件偏移量(也就是文件指针当前指向的读写位置)
- 因为存在多个进程打开同一个文件的情况
- 为了保证访问的安全性,文件指针必须是每个进程一个(也就是放在文件对象里),而不能在inode对象里(因为会共享一个inode)
还记得上面在超级块字段中介绍的s_files指针吗,它指向[由该文件系统所有文件对象]组织起来的双向链表
- 而前后指针存放在文件对象的f_list字段中:
- 这也是为什么父子进程中,父进程/子进程关闭某一文件,另一方依然可以使用它的原因 -- 因为它此时的引用计数并不等于0
- 而线程之间共享的文件就不行,因为他们是真正意义上的共享,引用计数不会因为线程的创建而增多,所以一旦其中一方关闭,其他线程就无法操作该文件了
filp_cachep(slab高速缓存)
介绍
也就是说,linux会从内存池中分配file对象:
并且有一个申请的上限值:
和其他对象一样,文件对象也有一个操作方法表
- 也就是说,当进程打开某一文件时,会从该文件对应的inode中拿到i_fop字段,然后用这个字段初始化该文件的文件对象中的f_op字段
- 之后,对该文件的操作方法就可以从f_op字段指向的表中调取了(至于为什么是用f_op,我感觉是因为用户直接操作的就是文件对象指针)
- 当然,也可以通过f_op来修改表中的函数指针,从而自定义文件操作
file_operationa表
和前面的对象类似:
目录项对象
引入
我们之前探讨过文件名和inode的关系,最终得出,目录文件中会存放文件名与inode的映射关系
- 其实也就是这里即将要介绍的目录项对象 -- 它会存放文件名与对应inode的映射关系
介绍
和文件对象相对的,目录也有自己专门的对象类型
- 每个目录项对象代表文件系统中的一个文件名或目录名,它指向一个特定的 inode,该 inode 包含了文件的实际内容和元数据信息
和文件对象类似,他也是一个纯软件层的结构,在磁盘中没有对应的映像
除此之外,它也是通过slab高速缓存分配空间的
四种状态
每个目录项对象可以处于以下四种状态之一:
字段
字段介绍
和前面几种对象类似,目录项对象也有自己的方法表
正在使用的目录项对象被组织成一个双向链表
- 目录项对象中的d_alias字段存放该链表中相邻元素的地址
inode和目录项对象的关联
inode中的i_dentry字段
- 指向该链表中对应的目录项对象(这个目录项对象记录了文件名和与之相关联的 inode 号)
- 由于一个inode可以与多个文件名(也可以叫做硬链接)相关联,所以需要一个链表将它们组织起来
- d_alias字段 -- 指向链表中的相邻元素
目录项对象中的d_inode字段
- 指向与该目录项对象相关联的 inode,表示该目录项对象代表的文件的元数据信息
- 如果一个目录项对象仍然存在于文件系统中,但它指向的文件已经没有任何硬链接,此时他就是负状态,会被移动到LRU链表中,并随着链表移动,最终被释放
目录项对象会被组织成哈希表的形式,以便快速地进行文件名查找和访问
- d_hash字段指向该链表的结点
dentry_operations表
目录项高速缓存
引入
组成
介绍
可以作为inode高速缓存的控制器
- 如果目录项高速缓存仍在使用这些inode,就会将inode放入ram中,以便快速访问
更新缓存
会根据插入时间的顺序将未使用的目录项对象组织成双向链表
- 先将早已插入的目录项对象释放,留下新一点的对象
- 该双向链表的首尾地址存放在dentry_unused变量中
- 目录项对象的d_lru字段 -- 存放该链表中相邻目录项对象的指针