3.1.1 从VFS到具体文件系统
Linux中的VFS并不是一开始就有的,最早发布的Linux版本并没有VFS。而且,最初VFS并非是基于Linux发明的,它最早于1985年由Sun公司在其SunOS 2.0中开发,主要目的是适配其本地文件系统和NFS文件系统。
VFS通过一套公共的API和数据结构实现了对具体文件系统的抽象。当用户调用操作系统提供的文件系统API时,会通过软中断的方式调用内核VFS实现的函数。表3-1所示为部分文件系统API与内核函数的对应关系。
表3-1 部分文件系统API与内核函数的对应关系
由表3-1可以看出,每个用户态API都有一个内核函数与之对应。当应用程序调用文件系统的API时会触发与之对应的内核函数。这里列举的只是文件系统API中的一个比较小的子集,目的是说明API与VFS的关系。如果大家想了解其他API则自行阅读内核源代码,此处不再赘述。
为了让大家能够对VFS与具体文件系统的关系有一个基本的认识,本节以Ext2的写接口为例来展示一下从API到VFS函数,再到Ext2文件系统函数的调用关系。如图3-3所示,API函数write()通过软中断触发内核ksys_write()函数,该函数经过若干处理后最终会通过函数指针(file->f_op->wirte_iter)的方式调用Ext2文件系统中的ext2_file_write_iter()函数。
看上去很简单,VFS只要调用具体文件系统注册的函数指针即可。但是这里有个问题没有解决,VFS中的函数指针是什么时候被注册的呢?
Ext2文件系统中的函数指针是在打开文件时被初始化的(具体细节请参考3.1.2.2节)。大家都知道,用户态的程序在打开一个文件时返回的是一个文件描述符,在内核中表示文件的结构体file与之对应。在这个结构体中有几个比较重要的成员,包括f_inode、f_ops和f_mapping等,如图3-4所示。
图3-3 Linux文件系统写入数据函数调用流程
图3-4 文件访问的核心数据结构
在图3-4中,f_inode是该文件对应的inode节点。f_ops是具体文件系统(如Ext2)文件操作的函数指针集合,它是在打开文件时被初始化的。VFS正是通过该函数指针集合来实现对具体文件系统访问的。
至此,大家应该对VFS与具体文件系统交互有了一个大致的了解。但是还有很多细节有待层层剥开。比如,在打开文件时函数指针是如何被注册的,具体文件系统是如何使用VFS页缓存的等,相关实现请参考下一节的内容。