虚拟文件系统(以下简称vfs)是置于具体文件系统之上的抽象层,指定内核和具体文件系统的接口。允许client以统一方式访问不同的具体文件系统。比如可以通过VFS透明访问本地硬盘和网络设备就像在同一个设备访问一样。
superblock
每个挂载的文件系统都由vfs superblock表示,其包含
- Device: 储存文件系统所在的设备
- Inode pointers:
mounted
inode指针指向文件系统的第一个inode,convered
inode指针指向文件系统挂载的目录。root文件系统没有convered
- Blocksize: 文件系统的块大小(单位bytes)
- Superblock Operations: 指向文件系统的一组用于读写inode和superblock的routine
- File System Type: 文件系统类型,指向文件系统数据结构
- File System Specific: 指向文件系统所必需的信息
Inode
每个文件目录都是一个vfs inode表示。存在于内核内存中,并保存在vfs inode缓存
inode在访问后加入缓存,那么在下次访问同样的目录时就会更快
- device: 保存该文件的设备的设备标识符
- inode number: inode序号,唯一存在于文件系统。
- mode: 描述访问权限
- user ids: 所有者id
- times: 创建、修改、写入此时
- block size: 文件块大小(单位byte)
- inode operations: 指向执行inode的操作
- count: 系统组件正在使用inode的数量。也就意味着如果是0的话,可以被丢弃或者重用。
- lock: 锁住inode
- dirty: inode是否被写入。
注册文件系统
-
文件系统可以在构建Linux内核时启动,也可以通过构建成模块。
模块既可以在需要时加载也可以通过
isnmod
手动加载 -
注册到vfs,通过文件系统类型表示。文件系统类型包含
-
superblock read routine:
在装载文件系统的一个实例时,VFS会调用该例程
-
file system name:
文件系统名
-
device needed:
是否需要设备来支持。比如对于/proc文件系统就不需要块设备
-
挂载文件系统
mount -t fuse -o ro /dev/sda1 /mnt/data
从上面的命令中可以看出,挂载需要三个关键信息
- 文件系统名字
- 包含文件系统的设备
- 新文件系统在已经存在文件系统的拓扑挂载点
那么对于vfs来讲需要做的有
-
找到文件系统
这是通过查找已知文件系统获取到目标文件系统的数据结构,而后读取superblock
-
挂载物理设备
找到挂载点目录的vfs inode,检查是否有其他文件系统挂载到同一个挂载点,接着vfs必须分配一个vfs superblock,并将装载信息传递给该文件系统的superblock read routine,而该routine需要从物理设备读取信息填充vfs superblock
寻找文件
解析目录名,寻找名称中每个中间目录的vfs inode。具体的查找通过目录具体文件系统的vfs inode检查是否存在
目录缓存
目录缓存是为了加速常用目录
在真正文件系统查找目录时,其详细信息添加到目录缓存。在下次查找同一个目录时,就会从目录缓存中找到
只有较短的目录项会被缓存(最多15字符),因为较短的往往也是最常用的
目录缓存由一个散列表组成,其中每个条目指向一个具有相同散列值的目录缓存条目列表。散列函数使用保存文件系统的设备的设备号和目录名来计算散列表的偏移量或索引。
为了保持缓存的有效性并及时更新,VFS保存了最近最少使用(LRU)目录缓存项的列表。
在第一次查找目录项时,即第一次将目录项放入缓存时,将其添加到第一级LRU链表的末尾。在再次访问该目录项时,它被提升到第二个LRU缓存链表的后面。
inode缓存
与目录缓存类似,inode缓存也是为了加速对挂载文件系统的访问
inode缓存也是一个散列表,哈希值根据其inode编号和包含该文件系统的底层物理设备的设备标识符计算得出。
在访问中,如果可以在缓存中找到该inode,则将其计数加一,表明有另一个使用者。否则将找到一个空闲的vfs inode,并调用具体文件系统的routine,填充该inode
Ref
- https://en.wikipedia.org/wiki/Virtual_file_system
- http://www.science.unitn.it/~fiorella/guidelinux/tlk/node102.html#SECTION001120000000000000000