学无止境~ 看LKD进行的粗浅整理,目标是能够做到设计上面的理解~
Linux操作系统上支持多种文件系统,如本地文件系统EXT4、XFS、EXT3 等,同时还支持NFS、CIFS以及一些特殊的文件系统,同时在上层调用文件管理时又不感知不同文件系统的类型、存储的类型,之所以能做到这一点,最大的功臣就是虚拟文件系统,英文简称VFS。
VFS是定义一个通用的文件模型,满足上层对文件的处理应用,同时能够完整兼容所有文件系统的特殊需求和能力。
之所以VFS能够屏蔽掉不同文件系统的差异,让上层无感我们以写文件为例
vfs存在于用户与文件系统之间,这样就可以在虚拟文件系统层面实现屏蔽差异。
虽然内核整体采用C语言实现的,但是VFS本身的设计思想更多的是面向对象的,所以在使用C语言实现过程中会有些晦涩,整体思路是以结构体作为对象,对象的方法由不同的函数指针来实现。VFS中有几个关键的数据结构,分别是:超级块对象、索引节点对象、目录项对象、文件对象。下面的图可以简要描述这几个对象的关系:
学习过程中, 我也是围绕这几个关键的数据结构进行的理解。
超级块对象(super_block)
代表已安装的具体的文件系统,存储的事文件系统本身的一些信息,通常对应于磁盘或分区特定扇区中的文件系统超级块或控制块。
索引节点对象(inode)
代表具体文件,在Linux一切皆文件,所以索引节点不仅代表传统意义上的文件,还有如目录等;主要包含了内核在操作文件时需要的全部信息。
文件对象
这个名字很容易被误解,会与inode搞混,文件对象描述的是由进程打开的文件,即同一个目录项由不同进程打开就会生成不同的文件对象, 换句话说文件对象是与进程相互绑定的,文件对象为进程提供了打开文件的相关信息与交互接口。
目录项对象(dentry)
是路径的一个组成部分,开始学习时很难理解目录项对象,目录项对象与Linux应用中传统意义上的文件夹不是等同或相近的。目录项对象的引入是为了解决文件查找繁琐的问题,所以目录项对象更多的意义在于文件与文件之间的关系,如/var/log/messages 这个文件中,包含的目录项对象有: / var/ log/ messages 四个, 其中 根目录、var目录、log目录是不同层级的文件夹,messages是一个文件。
目录项对象还有一个比较重要的参数是状态,不同状态描述如下:
- 空闲:没有被vfs使用,内存由slab处理,无关联inode
- 未使用:没有被内核使用(d_count = 0),有关联的inode
- 正在使用:正在被内核使用(d_count >0),有关联的inode,不能被丢弃
- 负状态:有inode(被删除或不存在的)d_inode = NULL
既然目录项是为了方便查找,那么就需要一定的数据结构或设计方案来支持这一特性,其中目录项高速缓存解决了这一问题。目录项高速缓存由两个结构体组成:
- 一个双向链表,包含了正在使用、未被使用、负状态的目录项对象,该双向链表形成一个先进先出的执行队列。以此实现一个“最近最少使用(LRU)”的缓存
- 一个散列表,通过目录名和文件名从中能够快速获取一个目录项对象(目录项对象中与inode相关联,找到对应的目录项对象就能够定位到inode)
语言描述往往很难理解, 所以我就以查找/var/log/messages这个文件为例子描述一下目录项查找的逻辑:
查找一个文件的目录项查询逻辑
目标:查找/var/log/messages
高速缓存工作流如下:
1. 查找LRU链表:首先,文件系统会检查目录项高速缓存中的最近最少使用(LRU)链表。这是为了快速查找最近被访问过的目录项。如果目标文件(在这里是 `messages`)的目录项恰好位于LRU链表的头部或近期被访问过,那么它可能会直接从缓存中提取出来,避免了进一步的磁盘访问。
2. 查找散列表:如果LRU链表中没有找到目标目录项,文件系统会转向散列表(通常称为哈希表)。散列表根据目录项的名称进行快速查找。这里,文件系统会按照路径组件(`/`、`var`、`log`、`messages`)的顺序,逐个查找每个组件的目录项。每个找到的目录项都会成为下一个查找步骤的起点。
- 首先,查找根目录(`/`)的目录项。
- 然后,使用根目录的目录项作为起点,查找 `var` 子目录的目录项。
- 接着,使用 `var` 目录项作为起点,查找 `log` 子目录的目录项。
- 最后,使用 `log` 目录项作为起点,查找 `messages` 文件的目录项。
在每个步骤中,如果目录项在散列表中不存在,那么它会根据需要从磁盘上读取,并添加到散列表和相应的链表中(例如,最近使用链表)。
3. 添加新目录项:如果在查找过程中发现某个目录项在缓存中不存在,那么该目录项会从磁盘上读取出来,并被添加到目录项高速缓存中。新添加的目录项会被放置在最近使用链表的头部,表示它是最近访问过的。
4. 缓存管理:随着时间的推移,当缓存空间不足时,文件系统会根据一定的策略(如LRU算法)从缓存中移除最少使用的目录项,以释放内存空间
以上逻辑就能够明白目录项的作用和逻辑了。
除了上述4个重要的数据结构外,还有一些特定的数据结构:
与文件相关的数据结构
- file_system_type: 特定的文件类型
- vfsmount:代表一个安装点
与进程相关的数据结构
- files_struct: 与进程相关的文件信息(如:一个进程最多能够打开多少个文件等配置)
- fs_struct:文件系统和进程的相关信息(如:pwd,进程根目录等)
- mmt_namespace:单进程命名空间,进程在系统中能够看到的唯一的安装文件系统