您的位置:首页 > 运维架构 > Linux

linux文件系统2:虚拟文件系统

2011-10-27 15:17 453 查看
linux文件系统2:虚拟文件系统



图 13-5 虚拟文件系统和实际文件系统之间的关系示意图

图 13-5 是虚拟文件系统和实际文件之间的关系示意图。虚拟文件系统必须管理所有可在任意时刻挂装的不同文件系统。为此,Linux 虚拟文件系统维护一些描述整个虚拟文件系统,以及实际已挂装文件系统的数据结构。虚拟文件系统对文件的描述方法和 Ext2 文件系统类似,也使用了超块和索引节点。为此,有必要描述虚拟文件系统的超块和索引节点,以便能够正确区分 VFS 和 Ext2。

每个文件系统在初始化时,首先在 VFS 中进行注册。如果文件系统内建于内核中,则初始化过程发生在系统引导时;如果文件系统作为内核可装载的模块,则实际挂装某个文件系统时进行初始化。当某种基于块设备的文件系统(包括 root 文件系统)被挂装,VFS 必须读取其超块。不同类型的文件系统所对应的超块读取例程必须能够理解实际文件系统的拓扑结构,并且能够将实际的超块结构映射为 VFS 超块结构。每个 VFS 超块包含了文件系统信息,并且还包含一些完成特定功能的函数指针。例如,某个超块代表一个已挂装的 Ext2 文件系统,则超块中包含有专门读取 Ext2 文件系统索引节点的函数地址。每个 VFS 超块中包含指向实际文件系统第一个 VFS 索引节点的指针。对于 root 文件系统而言,第一个索引节点就是代表“/”目录的节点。对 Ext2 文件系统来说,这种映射关系非常高效,而对其他文件系统则略微有些低效。

进程在访问目录和文件的过程中,会调用系统例程对 VFS 节点进行遍历。因为每个文件和目录均由一个索引节点表示,因此,有相当多的索引节点会被重复访问。由于这一原因,为了提高索引节点的访问速度,VFS 将这些节点保存在索引节点高速缓存中。如果某个节点当前不在高速缓存中,则调用专用于某种文件系统的索引节点读取例程,以便读取适当的索引节点。读取的索引节点会保存在高速缓存中,而较少使用的 VFS 索引节点会从高速缓存中剔除。

VFS 同时维护一个目录高速缓存,以便能够快速找到频繁使用的目录索引节点。目录高速缓存并不保存目录本身的索引节点,这些索引节点应当保存在索引节点高速缓存中;目录高速缓存实际保存的是完整目录名到对应索引节点编号的映射关系。

所有的 Linux 文件系统使用共同的缓冲区高速缓存,所有的文件系统以及 Linux 内核使用同一个缓冲区高速缓存。缓冲区高速缓存不依赖于任何文件系统,它和 Linux 内核用来分配、读取和写入数据缓冲区的机制集成在一起。Linux 文件系统独立于底层介质以及设备驱动程序而存在,这种机制具有非常明显的优点。所有的块设备在 Linux 内核中注册,并且提供一致的、基于块的、异步的接口。当文件系统从物理介质中读取数据时,实际是向控制该设备的驱动程序发送读取物理数据块的请求。缓冲区高速缓存负责将块设备驱动程序的接口集成在一起。文件系统在读取数据块时,这些块被保存在全局的缓冲区高速缓存中。缓冲区高速缓存中的缓冲区由唯一的设备标识符以及块编号标识,当进程频繁访问相同的数据时,这些数据就可以直接从高速缓存中读出,而不必进行实际的磁盘读取操作。

13.2.1 VFS 超块

每个已挂装的文件系统由一个 VFS 超块代表。VFS 超块中包含的信息由表 13-5 列出。

表 13-5 VFS 超块中的信息

设备 包含文件系统的块设备标识符。对于 /dev/hda1,其设备标识符为 0x301。

索引节点指针 这里包含两个索引节点指针。mounted 索引节点指针指向文件系统的第一个节点;covered 指针指向代表文件系统挂装目录的节点。root 文件系统的 VFS 超块没有 covered 指针。

数据块大小 文件系统中数据块的大小,以字节为单位。

超块操作集 指向一组超块操作例程集的指针。这些例程由 VFS 用来读取和写入索引节点以及超块。

文件系统类型 指向文件系统的 file_system_type 数据结构的指针。

文件系统的特殊信息 该文件系统的特殊信息。
13.2.2 VFS 索引节点

和 Ext2 文件系统一样,VFS 中的文件、目录等均由对应的索引节点代表。每个 VFS 索引节点中的内容由文件系统专属的例程提供。VFS 索引节点只存在于内核内存中,实际保存于 VFS 的索引节点高速缓存中。VFS 索引节点中的信息由表 12-6 给出。

表 12-6 VFS 索引节点中的数据域



13.2.3 文件系统的注册



图 13-6 注册的文件系统

如前所述,对特定文件系统的支持可内建于 Linux 内核之中,也可以以可装载模块的形式动态装载。如果内建于内核,则系统启动时进行文件系统的注册和初始化,否则在模块装载时注册,而在模块卸载时注销。每个文件系统的初始化例程在 VFS 中注册,并由一个 file_system_type 数据结构代表,其中包含了文件系统的名称以及一个指向对应 VFS 超块读取例程的地址。图 13-6 所示是内核中的 file_system_type 链表,链表头由 file_systems 指定。每个 file_system_type 结构成员由表 13-7 给出。

表 13-7 file_system_type 结构成员



查看 /proc 文件系统的 filesystems 文件内容,可了解当前注册的文件系统类型:

$ cat /proc/filesystems

Ext2

msdos

nodev proc

13.2.4 文件系统的挂装和卸装

用户(一般是 root)在挂装文件系统时,要指定三种信息:文件系统的名称、包含文件系统的物理块设备和文件系统在已有文件系统中的挂装点。例如:

$ mount -t iso9660 /dev/hdc /mnt/cdrom

虚拟文件系统对上述命令的执行过程如下:

1. 寻找对应的文件系统信息。VFS 通过 file_systems 在 file_system_type 组成的链表中根据指定的文件系统名称搜索文件系统类型信息。

2. 如果在上述链表中找到匹配的文件系统,则说明内核具有对该文件系统的内建支持。否则,说明该文件系统可能由可装载模块支持,VFS 会请求内核装入相应的文件系统模块,此时,该文件系统在 VFS 中注册并初始化。

3. 不管是哪种情况,如果 VFS 无法找到指定的文件系统,则返回错误。

4. VFS 检验给定的物理块设备是否已经挂装。如果指定的块设备已被挂装,则返回错误。

5. VFS 查找作为新文件系统挂装点的目录的 VFS 索引节点。该 VFS 索引节点可能在索引节点高速缓存中,也有可能需要从挂装点所在的块设备中读取。

6. 如果该挂装点已经挂装有其他文件系统,则返回错误。因为同一目录只能同时挂装一个文件系统。

7. VFS 挂装代码为新的文件系统分配超块,并将挂装信息传递给该文件系统的超块读取例程。系统中所有的 VFS 超块保存在由 super_blocks 指向的 super_block 数据结构指针数组中。

8. 文件系统的超块读取例程将对应文件系统的信息映射到 VFS 超块中。如果在此过程中发生错误,例如所读取的超块幻数和指定的文件系统不一致,则返回错误。

9. 如果成功挂装,则所有已挂装的文件系统形成如图 13-7 所示的数据结构。



图 13-7 已挂装的文件系统数据结构

从图 13-7 中可看到,每个已挂装的文件系统由 vfsmount 结构描述,所有的 vfsmount 结构形成了一个链表,该链表头由 vfsmntlist 指针代表。系统中还有两个指针,vfsmnttail 和 mru_vfsmnt,分别指向链表尾和最近使用过的 vfsmount 结构。每个 vfsmount 结构包含保存该文件系统的块设备号,文件系统挂装点的目录名称,以及指向为该文件系统分配的 VFS 超块的指针。而 VFS 超块中则包含描述文件系统的 file_system_type 结构指针和该文件系统根节点指针。

如果文件系统中的文件当前正在使用,该文件系统是不能卸装的。如果文件系统中的文件或目录正在使用,则 VFS 索引节点高速缓存中可能包含相应的 VFS 索引节点。检查代码在索引节点高速缓存中,根据文件系统所在设备的标识符,查找是否有来自该文件系统的 VFS 索引节点,如果有且标志为“脏”,则说明该文件系统正在被使用,因此,文件系统不能被卸装。否则,查看对应的 VFS 超块。如果该文件系统的 VFS 超块标志为“脏”,则必须将超块信息写入实际的文件系统。上述过程结束之后,对应的 VFS 超块被释放,vfsmount 数据结构从 vfsmntlist 链表中断开并释放。

13.2.5 VFS 中文件的定位

为了在虚拟文件系统中定位给定文件的索引节点,VFS 必须每次分解文件路径名中的一个目录名,并依次搜索每个中间目录名的 VFS 索引节点。因为 VFS 在 VFS 超块中记录了每个文件系统根的 VFS 索引节点,因此搜索过程从根索引节点开始,具体过程和 Ext2 文件系统的文件定位过程类似。在搜索目录索引节点时,VFS 在目录高速缓存中搜索,如果能够在目录高速缓存中找到对应的索引节点编号,则在索引节点高速缓存中搜索索引节点,否则从底层文件系统中获取。

13.2.6 VFS 索引节点高速缓存

VFS 索引节点实际是一个哈希表(或散列表),哈希表的每个入口包含一个 VFS 索引节点链表的头指针,该链表中的每个 VFS 索引节点具有相同的哈希值。节点的哈希值根据文件系统所在块设备的标识符,以及索引节点的编号计算得到。当虚拟文件系统要访问某个节点时,它首先在 VFS 索引节点中搜索。每个要搜索的 VFS 索引节点可计算得到一个对应的哈希值,然后,VFS 将该哈希值作为访问哈希表的索引,如果对应的哈希表入口指向的索引节点链表中包含有要搜索的索引节点(相同的设备标识符和相同的节点编号),则说明该索引节点包含在高速缓存中,这时,找到的索引节点访问计数加 1,否则,必须寻找一个空闲的 VFS 索引节点,并从底层文件系统中读取该索引节点。VFS 可选择多种方法获取空闲索引节点。如果系统能够分配更多的 VFS 节点,则 VFS 从内核中分配内存页并将其划分为新的空闲节点,然后放在节点列表中。系统所有的 VFS 节点保存在由 first_ionde 指向的链表中。如果系统已经具备了所有可利用的索引节点,则必须寻找一个可重复利用的索引节点。当前使用计数为 0 的索引节点可作为重复利用的节点,对文件系统的根节点来说(以及其他重要的节点),其使用计数始终大于 0,因此不会被重复使用。如果找到可重复利用的索引节点,则必须在使用之前清除该节点。如果该节点标志为“脏”,则需要写回到实际的文件系统中;如果该节点被锁定,则必须等待解锁。

无论利用哪种方法获得了空闲的 VFS 节点,VFS 会调用文件系统专有的例程从底层文件系统中读取信息并填充该索引节点。在该节点被填充时,这一新的 VFS 节点的使用计数为 1,同时被锁定,以免其他进程访问该节点。填充之后,节点被解锁。

为了获得实际使用的 VFS 节点,文件系统可能要访问许多其他的节点。读取目录内容时,只有最后一个目录的索引节点是实际要使用的,但同时必须读取中间目录的索引节点。

13.2.7 VFS 目录高速缓存

为了加速对频繁目录的访问过程,VFS 维护一个目录项高速缓存。实际文件系统读取目录之后,该目录项的细节添加到目录高速缓存中。下次搜索相同的目录项时,可从高速缓存中快速得到。但是,只有名称相对较短的目录项(15 个字符长度)才被高速缓存。

目录高速缓存页由一个哈希表组成。哈希值由保存文件系统的设备号以及目录名称计算得到,该值作为访问哈希表的索引。

为了保证缓存的有效性并及时更新,VFS 保持一组“最近使用 (LRU)”目录缓存项表。当某个目录项第一次放入高速缓存时,被添加到第一级 LRU 表的后面。当高速缓存全部占用时,该操作会取代第一级 LRU 表前面的已有项。当该目录再次被访问时,该目录项自动提升到第二级目录项缓存列表的后面,同样会取代第二级缓存列表前面的二级缓存目录项。这样,处于各个缓存表前面的项实际是最近没有被访问的目录项,否则,它们应当处于各目录项列表的后面。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息