嵌入式 Linux 虚拟系统文件交换器剖析

  Linux 最显著特征是灵活性和扩展性,例如它的虚拟文件系统交换器(VFS)。您可以在各种设备上创建文件系统,包括传统的磁盘、USB flash 驱动、内存以及其他储存设备。您还可以在另一个文件系统环境中嵌入文件系统。探索导致 VFS 如此强大的因素,并了解 VFS 的主要接口和进程。

    Linux 文件系统的灵活性和扩展性支持直接源于一组抽象接口。这组接口的核心就是虚拟文件系统交换器(VFS)。

    VFS 为上层应用程序提供一组标准接口,用于对不同的文件系统执行文件 I/O。这组接口在一个或多个底层设备上支持多个并发文件系统。另外,这些文件系统可以不是静态的,可以根据储存设备而变化。

    VF与VFS

    您还可以看到 VFS 还被定义为虚拟文件系统(virtual file system),但定义为虚拟文件系统转换器(virtual file system switch)更能说明其作用,因为虚拟层跨多个文件系统转换(即多路复用)请求。/proc 文件系统在这里带来很多的混淆,因为它通常也被称为虚拟文件系统。

    例如,一个典型的 Linux 桌面在可用硬盘上支持 ext3 文件系统,并且在可用的 CD-ROM(或者称为 CD-ROM 文件系统 或 CDFS)上支持 ISO 9660 文件系统。因为 CD-ROM 是可以插入和移除的,所以 Linux 内核必须适应这些包含不同内容和结构的新文件系统。可以通过网络文件系统(Network File System,NFS)访问远程文件系统。在此时,Linux 还可以挂载来自本地硬盘的 Windows?/Linux 双引导系统的 NT File System (NTFS) 分区,并且能够向其读写数据。

    最后,可移除的 USB flash 启动(UFD)是可以热插拔的,它构成另一个文件系统。概而言之,可以在这些设备中使用同一组文件 I/O 接口,从而允许底层的文件系统和物理设备能够从用户中抽象出来(见图 1)。

    图 1. 在不同文件系统和储存设备之间提供统一接口的抽象层

    不同文件系统和储存设备的统一抽象层图示

    分层抽象

    现在,我们向 Linux VFS 提供的抽象特性添加一些具体的架构。图 2 从 VFS 的角度显示 Linux 结构的高级视图。在 VFS 之上的是标准的内核系统调用接口(SCI)。这个接口允许用户空间发出要求转换到内核的调用(在不同地址空间中)。在这个域中,调用 POSIX open 调用的用户空间应用程序经过 GNU C 库(glibc)进入内核和系统调用去多元化(de-multiplexing)。最后,使用 sys_open 调用 VFS。

    图 2. VFS 的分层架构

    VFS 的分层架构图示,它支持 POSIX API 和其他各种底层文件系统所需的抽象

    早期的VFS实现

    Linux 并不是第一个包含虚拟层以支持通用文件模型的操作系统。早期的 VFS 实现包括 Sun 的 VFS(SunOS version 2.0,大约出现在 1985 年),以及 IBM 和 Microsoft? 的 “Installable File System” for IBM OS/2。这些虚拟化文件系统层的方法为 Linux VFS 铺平了道路。

    VFS 提供抽象层,从而将 POSIX API 与特定文件系统如何实现该行为的细节分离开来。这里的关键之处是,不管底层文件系统是 ext3 还是 Btrfs,Open、Read、Write 或 Close API 系统调用都能正常工作。VFS 提供一个由底层文件系统(它们必须为各种 POSIX API 函数实现行为)继承的通用文件模型。另一个在 VFS 范围之外的深层抽象隐藏了底层物理设备(可能是磁盘、磁盘分区、网络储存实体、内存或其他能够储存信息的媒介 —— 即使是暂时性的)。

    除了从底层文件系统抽象文件操作的细节之外,VFS 还将底层块设备绑定到可用的文件系统。让我们看看 VFS 的内部结构及其工作原理。

    VFS的内部结构

    在查看 VFS 子系统的总体架构之前,我们先看看所使用的主要对象。这个小节探索了超块(superblock)、索引节点(或 inode)、目录条目(或 dentry)和文件对象。在这里,其他一些组成部分也很重要,比如缓存。不过我将在后面的总体架构中讨论它们。

    超块

    超块(superblock)是关于文件系统的高级元数据的容器。超块是存在于磁盘上(实际上位于磁盘的多个位置上,以提供冗余)的结构。它是处理磁盘上的文件系统的基础,因为它定义文件系统的管理参数(例如,块的总数、空闲块和根索引节点)。

    在磁盘上,超块向内核提供关于磁盘上的文件系统的结构的信息。在内存中,超块为管理活动的(已挂载)文件系统提供必要的信息和状态。因为 Linux 支持同时挂载多个并发文件系统,所以在一个列表中维护每个 super_block 结构(super_blocks 在 ./linux/fs/super.c 中定义,结构在 /linux/include/fs/fs.h 中定义)。

    图 3 提供了超块及其元素的简化视图。super_block 结构是指封装了其他信息的许多其他结构。例如,file_system_type 结构维护文件系统的名称(比如 ext3)以及各种锁和函数,以获取和删除 super_block。file_system_type 对象由常见的 register_file system 和 unregister_file system 函数管理(见 ./linux/fs/file systems.c)。super_operations 结构为读写节点和高级操作(比如重新挂载)定义大量函数。根目录条目(dentry)对象也缓存在这里,因为它是文件系统所在的块设备。最后,提供许多用于管理节点的列表,包括 s_inodes(列出所有节点的列表)、s_dirty(列出所有脏节点的列表)、s_io 和 s_more_io 以及 s_files(列出特定文件系统的所有打开文件的列表)。

    图 3. super_block 结构及其元素的简化视图

    super_block 结构及其元素的简化视图

    注意,在内核内部,另一个称为 vfsmount 的管理对象提供关于已挂载的文件系统的信息。这些对象的列表引用超块,并定义挂载点、文件系统所在的 /dev 设备的名称以及其他高级附加信息。

    索引节点(inode)

    Linux 通过一个称为 inode(index node 的缩写)的对象管理文件系统中的所有对象。一个 inode 可以引用一个文件、目录或另一个对象的符号链接。注意,因为文件用于表示其他类型的对象(比如设备或内存),所以也使用 inode 来表示它们。

    我在这里所指的 inode 是 VFS 层 inode(常驻 inode)。每个文件系统也包含一个位于磁盘上的 inode,并且提供关于特定文件系统的特定对象的细节。

    VFS inode 使用 slab 分配器进行分配(来自 inode_cache;参考资料 部分提供一个介绍 slab 分配器的链接)。inode 由描述 inode、inode 内容和可能在 inode 上发生的各种操作的数据和操作组成。图 4 简单展示了一个 VFS inode,该 inode 包含许多列表,其中一个列表指向引用该 inode 的 dentry。这里还包含对象级别的元数据,包括熟悉的操作时间(创建时间、访问时间和修改时间)和所有者和权限数据(组 ID、用户 ID 和权限)。inode 引用它所允许的文件操作,大部分这些操作直接映射到系统调用接口(例如,open、read、write 和 flush)。inode 还引用特定于 inode 的操作(create、lookup、link 和 mkdir 等等)。最后,对于由地址空间对象表示的对象的数据,有一个管理结构。地址空间对象 是为 inode 管理页缓存中的各种页的对象。地址空间对象用于为文件管理页,也用于将文件部分映射到独立的进程地址空间。地址空间对象有自己的操作集(writepage、readpage 和 releasepage 等等)。

    图 4. VFS inode 的简化图示

     VFS inode 的简化图示

    注意,可以在 ./linux/include/linux/fs.h 中找到所有这些信息。

    目录条目(dentry)

    文件系统的层次结构由 VFS 中的另一个称为 dentry 的对象管理。文件系统有一个根 dentry(在超块中引用),这是唯一没有父对象的 dentry。所有其他 dentry 都有父对象,并且一部分 dentry 有子对象。例如,如果打开一个由 /home/user/name 组成的文件,那么将创建 4 个 dentry 对象:一个针对根 /、一个针对根目录 home 的条目、一个针对 user 目录的 name 条目,以及一个针对 user 目录的 name 条目。通过这种方式,dentry 简洁地映射到现在使用的文件系统。

    dentry 对象由 dentry 结构定义(在 ./linux/include/fs/dcache.h 中)。它由许多元素组成,这些元素在文件系统和物理数据中跟踪条目之间的关系(比如文件名)。图 5 展示了 dentry 对象的简化图示。dentry 引用 super_block,super_block 定义包含该对象的特定文件系统实例。接下来是该对象的父 dentry(父目录),其后是包含在一个列表中的子 dentry(如果该对象刚好是一个目录的话)。然后,为 dentry 定义操作(比如 hash、compare、delete 和 release 等等)。接着定义对象的名称,在这里名称保存在 dentry 中而不是 inode 中。最后,提供一个到 VFS inode 的引用。

    图 5. dentry 对象的简化表示

    dentry 对象的简化表示

    注意,dentry 对象仅存在文件系统内存中,而不能储存在磁盘上。仅永久储存文件系统 inode,dentry 对象的目的是改善性能。您可以在 ./linux/include/dcache.h 中看到 dentry 的完整描述。

    文件对象

    在 Linux 系统中打开的每个文件都都存在一个 file 对象。这个对象为特定用户提供打开的实例的信息。图 6 提供了文件对象的简化视图。在图中可以看到,path 结构提供到 dentry 和 vfsmount 的引用。为每个文件定义了一组文件操作,常见的文件操作包括 open、close、read、write 和 flush 等。接着定义一组标志和权限(包括组和所有者)。最后,为特定文件实例定义状态数据,比如文件的当前偏移量。

    图 6. 文件对象的简化表示

    文件对象的简化表示

    对象关系

    我们已经查看了 VFS 层中的各种重要对象,现在我们通过一个图表展示它们之间的关系。到目前为止,我都是以一种自下而上的方式探索对象,现在我们采用自上而下方式,从用户透视图中考察对象(见 图 7)。

    在顶层是打开的 file 对象,它由进程的文件描述符列表引用。file 对象引用 dentry 对象,后者引用 inode。inode 和 dentry 对象都引用底层的 super_block 对象。可能有多个文件对象引用同一个 dentry(当两个用户共享同一个文件时)。注意,在图 7 中一个 dentry 对象还引用另一个 dentry 对象。在这里,目录引用文件,而文件反过来引用特定文件的 inode。

    图 7. VFS 中的主要对象之间的关系

    展示 VFS 中的主要对象之间的关系的图表

    VFS架构

    VFS 的内部架构由一个调度层(提供文件系统抽象)和许多缓存(用于改善文件系统操作的性能)组成。这个小节探索内部架构和主要对象之间的交互(见图 8)。

    图 8. VFS 层的高级视图

    VFS 层的高级视图

    在 VFS 中动态管理的两个主要对象是 dentry 和 inode 对象。缓存这两个对象,以改善访问底层文件系统的性能。当打开一个文件时,dentry 缓存将被表示目录级别(目录级别表示路径)的条目填充。此外,还为该对象创建一个表示文件的 inode。使用散列表创建 dentry 缓存,并且根据对象名分配缓存。dentry 缓存的条目从 dentry_cache slab 分配器分配,并且在缓存存在压力时使用最近不使用(least-recently-used,LRU)算法删除条目。您可以在 ./linux/fs/dcache.c 和 ./linux/include/linux/dcache.h 中找到与 dentry 缓存相关的函数。

    为了实现更快的查找速度,inode 缓存被实现为两个列表和一个散列表。第一个列表定义当前使用的 inode;第二个列表定义未使用的 inode。正在使用的 inode 还储存在散列表中。从 inode_cache slab 分配器分配单个 inode 缓存对象。您可以在 ./linux/fs/inode.c 和 ./linux/include/fs.h 中找到与 inode 缓存相关的函数。在现在的实现中,dentry 缓存支配着 inode 缓存。如果存在一个 dentry 对象,那么 inode 缓存中也将存在一个 inode 对象。查找是在 dentry 缓存中执行的,这将导致 inode 缓存中出现一个对象。

    结束语

    本文探索了 VFS 的基础概念,以及为访问不同文件系统提供统一接口的对象。Linux 具有很大的可伸缩性和灵活性,并且可以从子系统进行扩展。您可以从 参考资料 部分提供的链接更多地了解 VFS。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值