首页 理论教育深入剖析Linux内核与设备驱动:VFS框架详解

深入剖析Linux内核与设备驱动:VFS框架详解

【摘要】:VFS的实现是以“一切皆是文件”为需求出发点的。要理解VFS的框架首先看一下VFS和系统的静态关系框图,如图5-1所示。从图5-1可见,VFS是用户层的直接接口,是面向用户的服务。图5-1VFS和系统静态关系图在图5-2中每个节点被使用时都会在VFS层中创建dentry,这样可以快速通过文件名进行查找和定位,Linux内核中对dentry的管理组织形式如图5-3所示。以上是系统运行时VFS对文件名的组织管理,这是VFS管理的一个方面。

VFS的实现是以“一切皆是文件”为需求出发点的。要理解VFS的框架首先看一下VFS和系统的静态关系框图,如图5-1所示。

从图5-1可见,VFS是用户层的直接接口,是面向用户的服务。其封装了不同文件系统的差异,这样用户只把注意力集中在VFS中即可。在VFS中有dcache和inode cache,这两部分是两种管理实体的kmem_cache,cache中分别存放着管理实体dentry(directory entry)和inode(index node)。这两种管理实体分别代表文件管理的两个重要方面,一方面是文件组织管理,另一方面是物理文件管理,包括元数据管理(包括时间、权限等)、文件数据管理以及相关的操作管理。

1ᤫ文件组织管理

VFS中dentry代表文件组织的管理实体,能够通过它以树状结构来对各种类型的文件进行管理,其中树状结构的根是通过d_alloc_root进行分配的。注意这里说的是树状结构而不是目录结构,树状结构只是表示组织形式,而目录结构容易和目录文件混淆从而产生歧义。dentry表示系统运行时文件的组织,文件的组织是以文件名及层次的方式实现的,先看看Linux系统是如何组织这些文件的(要符合FHS标准),如图5-2所示。

978-7-111-49426-3-Chapter05-1.jpg

图5-1 VFS和系统静态关系图

在图5-2中每个节点被使用时都会在VFS层中创建dentry,这样可以快速通过文件名进行查找和定位,Linux内核中对dentry的管理组织形式如图5-3所示。

dentry的d_subdirs字段指向子目录项的第一个元素,子目录项之间通过d_child相互链接。dentry的d_parent字段指向目录项的父目录项,如果是文件系统的根,则指向自己。这些属性就可以建立文件的树状层次组织管理结构;dentry_hashtable和d_hash则是为了能够加速文件的查找,另外dentry作为动态资源也是需要管理的,相应的管理算法由dentry_un-used组成的链表进行管理,在必要的时候进行资源释放。这样在功能、性能资源各方面都对文件名组织进行了管理。

仅有组织的管理是不够的,还要能通过文件名访问其中的数据。来看看dentry数据结构的内容,明确如何从组织转到具体的文件以及其他的管理。

978-7-111-49426-3-Chapter05-2.jpg

图5-2 Linux文件组织层次

978-7-111-49426-3-Chapter05-3.jpg

图5-3 VFS层dentry管理组织形式

978-7-111-49426-3-Chapter05-4.jpg

在dentry结构中除了之前介绍的与组织管理相关的属性外,还有一些比较重要的属性,分别如下:

●d_inode指向inode实体的指针,inode表示文件的具体属性。

●d_name表示文件的名字。qstr是quick string的缩写包含字符串、长度和hash值,这里用它是对文件名进行封装以便进行快速查找。如果文件名很长,通过文件名计算hash值会很费时,这里可以空间换时间。

●d_op是对文件名的一些计算和操作接口。VFS针对通常的文件名操作已经提供了基本的接口,但是对特定的文件系统,标准的操作是不能满足需要的。如fat32文件系统对文件名不区分大小写,这时进行文件名比较就需要特定的操作接口。在VFS中定义dentry_operations相关接口,具体文件系统根据需要对文件名的操作进行重定义来满足文件系统自身的需要。

●d_sb指向dentry对象所在文件系统super block的实体。为什么需要该指针呢?因为在Linux系统中,对用户来说,文件组织整个是一棵树。但实际情况是在这一棵树上挂载了不同的文件系统,而每个文件系统本身就是一棵树,所以表示文件名的管理实体dentry同样应该和所在的文件系统(子树)关联(通过该指针实现)。用户看到的树是多个文件系统子树拟合形成的,拟合的过程涉及挂载点,由d_mounted表示是否为挂载点,这样可以在文件名的层次上关联文件系统的挂载。实际挂载的管理将在后续介绍。

以上是系统运行时VFS对文件名的组织管理,这是VFS管理的一个方面。把文件名和物理文件管理分离,这样在VFS层就很容易实现符号文件的快速定位。

2ᤫ物理文件管理

VFS管理的另一方面是系统运行时对物理文件的管理。相应的管理实体是inode。注意inode是VFS层对物理文件的管理实体,主要由于VFS是抽象文件系统,其中的管理实体应该是抽象的概念,而inode在VFS中代表物理文件。这就有些奇怪了,如何做到抽象概念到物理概念的转换呢?了解了其中的过程就明确了如何从VFS过渡到实际文件系统,可以说inode是抽象文件系统VFS到物理文件系统的关键点。在面向对象的设计中通常通过继承关系表示具体实例化,C语言则是通过包含方式来实现实例化,以ext2文件系统为例:

978-7-111-49426-3-Chapter05-5.jpg

其中包含VFS的inode即vfs_inode,之前提到的VFSinode cache实际是各个具体文件系统inode_info的cache,如ext2就是ext2_inode_info的kmem_cache。在VFS中,对物理文件进行管理的实体在运行中的数据会直接对应到具体文件系统的物理文件管理实体,而inode是从各种文件系统物理文件管理中抽象出来的属性集合,注意这些都是运行时的信息。要了解如何从inode转换到具体的文件系统操作,首先需要看看inode的具体内容:

978-7-111-49426-3-Chapter05-6.jpg

978-7-111-49426-3-Chapter05-7.jpg

从内容可见inode包含大量的信息。

首先类似于dentry,通过链表在VFS层中对inode完成各种不同的分类管理。对文件的最后访问时间、最后修改时间和最后修改inode的时间分别由i_atime、i_mtime和i_ctime进行管理。文件大小相关信息由i_size和i_blocks表示。使用两种表现方式是因为文件系统的物理内容通常保存在块设备中,所以分别通过字节大小和所占块数表示文件的大小,以方便进行操作。i_rdev表示文件所在实际的设备号(如果是设备则是设备号)。另外inode中还通过i_mode体现了“一切皆是文件”。其中可以表示特殊的文件,也可通过以下的宏表示不同类型的文件。

978-7-111-49426-3-Chapter05-8.jpg

如果inode表示设备,还可以通过union中的具体设备指针直接关联到设备的结构,i_devices会和设备管理实体cdev或者bdev关联。

在关联到具体的文件系统操作方面就要依靠操作接口。inode中有i_op和i_fop两种操作接口,前者主要是面向文件的元数据及与inode相关的特殊操作,后者则面向文件中数据的操作,通过这些操作接口的重定向就可以转向实际的文件系统的操作,另外由于文件可以mapping到虚拟地址空间,也需要转换到具体文件系统给定的操作中,这个转换由i_mapping中相关的操作接口完成,VFS还为mapping的方式设计了direct IO框架,以便进行相关的操作,这样就与buffer以及page cache直接关联起来。

这样整个系统就完成了虚拟文件系统和实际文件系统的转换,这几种重要的数据结构已在VFS中进行介绍,所以不再对实际的文件系统进行详述,实际文件系统主要是对块设备的空间进行组织和管理以存放实际的数据。

看了这么多,都还是在VFS层的管理实体,并没有见到如何从进程角度理解文件并和VFS相关实体进行关联,这些都属于系统运行时的状态信息。其关联如图5-4所示。

978-7-111-49426-3-Chapter05-9.jpg

图5-4 VFS和进程动态关联图

从图5-4可见,在用户进程中文件就是file结构体,该结构会在文件打开时创建。下面来看看其主要内容:

978-7-111-49426-3-Chapter05-10.jpg(www.chuimin.cn)

978-7-111-49426-3-Chapter05-11.jpg

注意这里通过path结构表示路径(path结构中dentry表示所在子树的文件层次属性,mnt表示整体文件树的层次),path中的dentry可与物理文件的管理实体inode关联起来。为了方便文件内容的操作,file中的f_op与inode中的f_op有关联,但不一定相同,这样可以按需变化,提高系统的灵活性,后续介绍设备的时候再进行详细介绍。

3ᤫVFS与进程关联

从整体上看看系统是如何将VFS内部以及和进程进行关联的,细节如图5-5所示。

978-7-111-49426-3-Chapter05-12.jpg

图5-5 VFS和进程数据结构关系图

图5-5中还有一些数据结构没有说明。mnt_namespace可以实现用户文件系统的虚拟化管理;vfsmount则是用来管理文件系统的挂载,之前见到的path就是通过vfsmount将不同的文件系统子树拟合成用户所见的文件路径。挂载同样是有层次的,相应的层次关系由vfs-mount的链表以及与文件层次管理dentry的关联来完成,具体到属性就是mnt_parent表示挂载的层次;mnt_mountpoint表示挂载的上层文件系统中的目录文件dentry;mnt_root表示挂载的文件系统的根目录文件dentry,另外对于上层的vfsmount会通过其链表mnt_mounts与下层vfsmount的mnt_child进行关联,这样文件系统挂载的整个层次就形成了。

以一个例子来理解文件树状组织在内核中是如何体现的。要形成如图5-6所示的文件层次。

978-7-111-49426-3-Chapter05-13.jpg

图5-6 文件层次示例

图5-6中可见有两个文件系统,一个作为根挂载到r1目录文件,另一个挂载到文件系统的b目录文件上,g文件并没有实际的打开过。对应的dentry实际情况如图5-7所示。对应的vfsmount实际情况如图5-8所示。从图5-7可见g没有被访问所以并没有g文件对应的dentry,这正说明相关内容都是当前系统运行状态的体现。

有了以上的示例,基本可以理解文件层次在内核运行状态下如何表示了。

4ᤫ具体文件系统管理

剩下的还有就是对具体文件系统的管理,在VFS中同样要对具体的文件系统进行管理,相应的有file_system_type和super_block,前者管理的是内核中注册的所有文件系统,后者主要是管理文件系统的元数据(包括实际设备以及块设备组织的元信息等)和文件系统通过元数据管理文件的操作接口。这些都与具体的文件系统关系紧密就不进行详述。

978-7-111-49426-3-Chapter05-14.jpg

图5-7 示例文件层次dentry实际表示

978-7-111-49426-3-Chapter05-15.jpg

图5-8 示例文件层次vfsmount实际表示

5ᤫVFS操作的用户通知

文件作为用户的接口,除了具体的操作功能之外,操作本身也是用户关心的一个方面,这就需要内核能够提供对文件进行相关操作的通知,这些通知直接嵌入到VFS文件操作接口中即可,相当于监测统计点,这也是VFS提供的功能,这些监测统计通过fsnotify_xxx上报给事件管理模块inotify,而inotify负责管理用户的监测请求并通知用户,内核提供监测接口如下:

●fsnotify_move文件从一个目录移动到另一个目录。

●fsnotify_nameremove文件从目录中删除。

●fsnotify_inoderemove自删除。

●fsnotify_create创建新文件。

●fsnotify_mkdir创建新目录。

●fsnotify_access文件读。

●fsnotify_modify文件写。

●fsnotify_open文件打开。

●fsnotify_close文件关闭。

●fsnotify_xattr文件的扩展属性被修改。

●fsnotify_change文件被修改或原数据被修改。

内核会将如下的事件上报应用层

978-7-111-49426-3-Chapter05-16.jpg

应用程序可以通过inotify接口进行所关心文件的监测。inotify属于应用层的内容,就不进行详述了。

至此整体上在VFS层为用户提供的各种功能都进行了框架分析和总结。