windows访问ext4文件系统_VFS(一) 虚拟文件系统概述

1.万物皆文件

Everything is a file。

用过Linux的朋友一定听过类似的话。虽然有一些例外,但在Linux上大部分资源确实都是文件,而且都是通过VFS来访问的。

比如存储数据的文件,可以运行的二进制程序,层次化的目录结构等等。这些文件都是使用基于磁盘硬件的文件系统(比如ext2/3/4,xfs等)来管理的,就是说它们都是存储在真实磁盘设备上的。在Linux中大部分文件都是这种类型的。

还有一部分文件是虚拟文件,并不存储在真实的磁盘硬件设备上,而是使用虚拟文件系统(比如sys,proc,cgroup等)来管理。这些虚机文件系统是内核运行时生成的,从而提供了从用户态通过VFS来和内核态通信的方式。

举个例子,使用stat命令可以看到文件/proc/cpuinfo的长度为0。

f4efff996c9afb3c005c3c6dd79c033f.png
读取/proc/cpuinfo的时候,可以读到内核的关于cpu的信息,这些信息都是内核生成的。

4f5bdbe8d2901367974fee8fe3b3f13a.png

2.文件类型

Linux下一共有七种文件类型,比较常用的文件类型是普通文件,目录,软链等。

使用ls -l命令来区分不同的文件类型,输出的第一个字符代表文件类型。
  • -:普通文件
  • d:目录
  • c:字符设备
  • b:块设备
  • s:套接字
  • p:管道
  • l:软链

一个Linux系统里可能有上百万个上述不同类型的文件。

不同类型的文件底层解释方式并不相同。比如普通文件和套接字的读写逻辑就不一样。

而相同类型的文件的底层解释方式也有可能不同。比如都是普通文件,/var/log/message和/proc/1/cmdline的读写逻辑也不一样,等等。

因为这些文件都使用不同的文件系统实例来管理。比如/var/log/message可能是由ext4来管理的,而/proc/1/cmdline是由procfs管理,而socket是由sockfs来管理。

所以不同文件的解释权在于特定的文件系统实例。

3.VFS虚拟文件系统

Linux支持各种各样的文件系统,在内核源码的fs目录下,可以找到很多常用的文件系统实现,比如ext4,xfs,sysfs等。

e7c1885e5a929f17a79ee8d6e2d8e6e0.png

为了支持各种各样文件系统,Linux在用户进程和文件系统实例中间引入了一个抽象层,对不同文件系统的访问都使用相同的方法,并提供了文件的统一视图。

不同的文件系统的底层实现方式可能有很大的差异,但VFS并不关心这些。通过提供公共组件和统一框架,VFS对上层系统调用屏蔽了具体文件系统实现之间的差异性,为所有文件的访问提供了相同的API,并遵循相同的调用语义。

VFS(Virtual FileSystem Switch)抽象层。

7c2900a0df56b0158bf8a44082f1574d.png

4.VFS数据结构

在操作文件的时候,在用户态看到的是文件描述符fd(一个整数)。

进程内部使用open函数打开一个文件,并返回fd。之后通过read/write/close/ioctl等函数来对fd进行操作。

每使用open打开一个文件,就会分配出一个新的fd来标识该文件。fd只在当前进程内有效。

在内核态操作操作文件要复杂的多,具体的实现会涉及数量庞大的彼此关联的数据结构,以下是一些比较重要的数据结构:

  • struct file
    • 对应到进程内打开的文件,file保存在进程的fdtable的数组中,使用fd作为索引。
    • 每个文件关联一个file_operation,包含文件读写,内存映射,设置文件位置等函数指针。用户态的read/write等对文件进行的函数调用最终都会执行到这里。
  • struct inode
    • 文件索引节点,用来保存文件的元信息。每个文件唯一的对应到一个inode。
    • 每个inode有一个分区内唯一编号,可以使用ls -i来查看。
    • 每个inode关联一个inode_operation,包含相关元数据创建,删除,查找,修改属性等函数指针。
  • struct dentry
    • 目录项缓存,用来加速文件查找结果,建立文件名和inode的映射关系。
    • dentry的实例之间会形成一个小型的类似文件系统的拓扑结构。dentry目录下的文件和子目录都会进入d_subdir链表,d_parent指向父dentry。
    • 每个dentry可能关联一个dentry_operation,包含哈希,比较文件名等函数指针。
  • struct super_block
    • 超级块控制结构。装载文件系统时,借助文件系统实例对应的fill_super函数生成。该结构保存了文件系统的元信息。
    • 每个超级块关联一个super_block_operation,实现特定文件系统实例超级块的操作函数。
  • struct address_space
    • 地址空间结构。每个inode都有一个地址空间。该结构用来建立缓存数据和后备存储器数据之间的映射关系。缓存的结构是内存页(page cache),缓存的数据就是文件内容。
  • struct file_system_type
    • 文件系统类型。每个文件系统都有一个类型,使用register_filesystem注册到内核中。其中比较重要的是mount函数指针,用来挂载特定的文件系统实例。

对于上述数据结构中的<xxx>_operation结构,都是VFS中抽象出来的通用的函数指针,用来将通用的框架和各种各样的文件系统实例的实现进行解耦。各个文件系统的实现通过专用的operation结构和VFS绑定起来。

比如ext4中文件的file_operation为ext4_file_operations,目录的file_operation为ext4_dir_operations,inode的inode_operation为ext4_file_inode_operations。

下图给出这些数据结构彼此之间关系的一个简化版本:

cce0927d53b312e1f572b6efd0ffd46c.png

5.VFS打开文件

5.1.查找文件

在对文件进行读写之前,需要先使用open函数打开这个文件。打开文件需要先对文件进行查找定位。

比如最常用的开启bash,就要打开bash文件,路径位于/usr/bin/bash。文件系统的组织是树状目录,所以查找的第一步是始于根目录/。

根目录/对应的inode是已知的。文件系统挂载时,在构建超级块中会加载根目录/对应的inode(ext4中根目录对应的inode号是2)和dentry。

根目录/(2号inode)中的数据是具体的目录项,每一项都包含一个子项(子目录或者文件)的名字,和子项关联的inode号。

查找的第二步是找到子目录usr对应的inode号。在根目录/(2号inode)中扫描名字为usr的目录项,找到usr对应的inode(5505025号inode)。

然后重复第二步,直到找到bash文件对应的inode(5505117号inode),最后加载对应的文件内容。

dccd319f3d80d0be75d56068f0aeb66e.png

b5f9e6e4a470966257bfb4fe800e437d.png

但这样的查找比较慢,实际上在VFS中会使用dentry_hashtable来加速查找过程。

5.2.打开文件

应用程序使用标准库的open函数来打开一个文件。

在x86_64架构上open函数会执行syscall指令从用户态转换到内核态,并最终调用到do_sys_open函数。

do_sys_open的流程:

f143a01468e01dbccdf51078a955646b.png

do_filp_open的流程:

fd6a52bb3fa9a15372dc2d23196d761d.png
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值