Skip to content

Latest commit

 

History

History
773 lines (609 loc) · 60.5 KB

File metadata and controls

773 lines (609 loc) · 60.5 KB

五、文件系统和文件 I/O

到目前为止,我们已经遍历了内核的基本资源,例如地址空间、处理器时间和物理内存。 我们已经建立了对进程管理CPU 调度、内存管理以及它们提供的关键抽象的经验理解。 在本章中,我们将继续通过查看内核提供的另一个关键抽象(文件 I/O 体系结构)来建立我们的理解。 我们将详细介绍以下方面:

  • 文件系统实施
  • 文件 I/O
  • VFS
  • VFS 数据结构
  • 特殊文件系统

计算系统存在的唯一目的是处理数据。 大多数算法的设计和编程都是为了从采集的数据中提取所需的信息。 为这一过程提供动力的数据必须永久存储以供连续访问,从而要求设计的存储系统能够在更长时间内安全地包含信息。 然而,对于用户来说,从这些存储设备获取数据并使其可供处理的是操作系统。 内核的文件系统就是服务于此目的的组件。

文件系统-高级视图

Filessystems从用户抽象存储设备的物理视图,并通过称为文件和目录的抽象容器为系统的每个有效用户虚拟化磁盘上的存储区域。 文件充当用户数据的容器,而目录充当一组用户文件的容器。 简而言之,操作系统将每个用户的存储设备视图虚拟化为一组目录和文件。 文件系统服务实现创建、组织、存储和检索文件的例程,用户应用通过适当的系统调用接口调用这些操作。

我们将从查看用于管理标准磁盘的简单文件系统的布局开始讨论。 此讨论将帮助我们从总体上理解与磁盘管理相关的关键术语和概念。 然而,典型的文件系统实现涉及描述磁盘上文件数据的组织的适当数据结构,以及使应用能够执行文件 I/O 的操作。

诠释数据

存储磁盘通常由大小相同的物理块组成,称为个扇区;扇区的大小通常为 512 字节或倍数,具体取决于存储类型和容量。 扇区是磁盘上 I/O 的最小单位。 当磁盘呈现给文件系统进行管理时,它将存储区域视为固定大小的个块的阵列,其中每个块与一个扇区或扇区大小的倍数相同。 典型的默认数据块大小为 1024 字节,可能会因磁盘容量和文件系统类型而异。 文件系统将数据块大小视为 I/O 的最小单位:

索引节点(索引节点)

文件系统需要维护元数据来标识和跟踪用户创建的每个文件和目录的各种属性。 描述文件的元数据有几个元素,例如文件名、文件类型、上次访问时间戳、所有者、访问权限、上次修改时间戳、创建时间、文件数据大小以及对包含文件数据的磁盘块的引用。 通常,文件系统定义一个称为 inode 的结构来包含文件的所有元数据。 Inode 中包含的信息的大小和类型是特定于文件系统的,并且可能会因其支持的功能而大不相同。 每个 inode 由称为索引的唯一编号标识,该编号被视为文件的低级名称:

文件系统保留几个磁盘块用于存储 inode 实例,其余的用于存储相应的文件数据。 为存储信息节点保留的数据块数量取决于磁盘的存储容量。 索引节点块中保存的节点的磁盘列表称为索引节点表。 文件系统需要跟踪索引节点和数据块的状态以识别空闲块。 这通常是通过位图实现的,一个用于跟踪空闲索引节点的位图和另一个用于跟踪空闲数据块的位图。 下图显示了具有位图、信息节点和数据块的典型布局:

数据块映射

如前所述,每个索引节点都应该记录存储相应文件数据的数据块的位置。 根据文件数据的长度,每个文件可能占用n个数据块。 有多种方法用于跟踪 inode 中的数据块细节;最简单的方法是直接引用,它涉及包含指向文件数据块的个直接指针的 inode。 这样的个直接指针的数量取决于文件系统设计,并且大多数实现选择使用较少的字节来使用这样的指针。 此方法对于跨几个数据块(通常为<16k)的小文件非常有效,但缺乏对跨多个数据块的大文件的支持:

为了支持大文件,文件系统采用一种称为多级索引的替代方法,该方法涉及间接指针。 最简单的实现是在 inode 结构中有一个间接指针和几个直接指针。 间接指针指的是包含指向文件的数据块的直接指针的块。 当文件变得太大而无法通过索引节点的直接指针引用时,空闲数据块与直接指针接合,索引节点的间接指针引用它。 由间接指针引用的数据块被称为间接块。 间接块中的直接指针数可以通过块大小除以块地址的大小来确定;例如,在具有 4 字节(32 位)宽块地址和 1024 个块大小的 32 位文件系统上,每个间接块最多可以包含 256 个条目,而在具有 8 字节(64 位)宽块地址的 64 位文件系统中,每个间接块最多可以包含 128 个直接指针:

通过使用双间接指针,可以进一步支持更大的文件,指的是包含间接指针的块,每个条目指的是包含直接指针的块**。** 假设 64 位文件系统具有 1024 个块大小,每个块容纳 128 个条目,则每个块将有 128 个间接指针,每个间接指针指向包含 128 个直接指针的块;因此,使用此技术,文件系统可以支持最多可跨越 16384(128x128)个数据块(16MB)的文件。

此外,可以使用三重间接指针扩展该技术,从而使文件系统可以管理更多的元数据。 然而,尽管有多级索引**,**增加文件系统块大小并减少块地址大小是支持更大文件的最推荐和最有效的解决方案。 用户在使用文件系统初始化磁盘时需要选择适当的块大小,以确保正确支持较大的文件。

一些文件系统使用另一种称为扩展区的方法在 inode 中存储数据块信息。 范围是一个指针,它引用具有附加长度位的起始数据块(类似于直接指针),该长度位指定存储文件数据的连续块的计数。 根据文件大小和磁盘碎片级别的不同,单个扩展区可能不足以引用文件的所有数据块,并且要处理此类事件,文件系统构建扩展区列表,每个扩展区引用磁盘上一个连续数据块区域的起始地址和长度。

扩展区方法减少了文件系统存储大量数据块映射所需管理的元数据,但这是以牺牲文件系统操作的灵活性为代价的。 例如,考虑在大文件的特定文件位置执行读取操作:要定位指定文件偏移量位置的数据块,文件系统必须从第一个盘区开始扫描列表,直到找到覆盖所需文件偏移量的盘区。

目录 / 名录

文件系统将目录视为特殊文件。 它们表示具有磁盘索引节点的目录或文件夹。 它们通过标记为目录类型字段与普通文件索引节点区分开来。 每个目录都分配了数据块,其中包含有关其包含的文件和子目录的信息。 目录维护文件记录,每个记录都包括文件名(不超过文件系统命名策略定义的特定长度的名称字符串)和与文件关联的信息节点号。 为实现高效管理,文件系统实现通过适当的数据结构(如二叉树、列表、基数树和哈希表)定义目录中包含的文件记录的布局:

超级块

除了存储捕获单个文件的元数据的 inode 外,文件系统还需要维护与整个磁盘卷相关的元数据,例如卷的大小、总块数、文件系统的当前状态、inode 块的计数、inode 的计数、数据块的计数、起始 inode 块号和标识的文件系统签名(幻数)。 这些细节被捕获在名为超级块的数据结构中。 在磁盘卷上的文件系统初始化期间,超级块是在磁盘存储开始时组织的。 下图说明了带超级数据块的磁盘存储的完整布局:

运营

虽然数据结构是文件系统设计的基本组成部分,但对这些数据结构进行可能的操作以呈现文件访问和操作操作构成了核心功能集。 支持的操作数量和功能类型因文件系统实施而异。 以下是大多数文件系统提供的几个常见操作的一般描述。

装载和卸载操作

mount是将磁盘上的超级块和元数据枚举到内存中以供文件系统使用的操作。 该过程创建描述文件元数据的内存中数据结构,并向主机操作系统呈现卷中的目录和文件布局的视图。 执行装载操作是为了检查磁盘卷的一致性。 如前所述,超级块包含文件系统的状态;它指示卷是一致还是。 如果卷是干净的或一致的,装载操作将会成功,如果卷被标记为脏的或不一致的,它将返回相应的失败状态。

突然关机会导致文件系统状态变脏*,,并且需要进行一致性检查,然后才能将其标记为再次使用。* 一致性检查所采用的机制既复杂又耗时;此类操作是特定于文件系统实现的,大多数简单的操作提供了特定的一致性和检查工具,而其他现代实现则使用日志记录。

unmount是将文件系统数据结构的内存状态刷新回磁盘的操作。 此操作将导致所有元数据和文件缓存与磁盘块同步。 卸载将超级块中的文件系统状态标记为一致,表示正常关闭。 换句话说,在执行卸载之前,磁盘上的超级数据块状态一直是脏的。

文件创建和删除操作

创建文件是需要实例化具有适当属性的新索引节点的操作。 用户程序使用选定的属性(如文件名、要创建文件的目录、各种用户的访问权限和文件模式)调用文件创建例程。 此例程还初始化 inode 的其他特定字段,如创建时间戳和文件所有权信息。 此操作将一个新的文件记录写入目录块,描述文件名和信息节点号。

当用户应用对有效文件启动delete操作时,文件系统会从目录中删除相应的文件记录,并检查该文件的引用计数以确定当前使用该文件的进程数。 从目录中删除文件记录可防止其他进程打开标记为删除的文件。 当关闭对文件的所有当前引用时,通过将文件的数据块返回到空闲数据块列表,并将索引节点返回到空闲索引节点列表,释放分配给该文件的所有资源。

文件打开和关闭操作

当用户进程试图打开文件时,它会使用适当的参数(包括文件的路径和名称)调用文件系统的open操作。 文件系统遍历路径中指定的目录,直到到达包含所请求文件记录的直接父目录。 查找文件记录会生成指定文件的 inode 编号。 然而,查找操作的特定逻辑和效率取决于特定文件系统实现为组织目录块中的文件记录而选择的数据结构。

一旦文件系统检索到文件的相关 inode 号,它就会启动适当的健全性检查,以对调用上下文实施访问控制验证。 如果清除了调用方进程的文件访问权限,则文件系统会实例化名为文件描述符的内存中结构,以维护文件访问状态和属性。 成功完成后,open 操作将文件描述符结构的引用返回给调用者进程,该引用充当调用者进程启动其他文件操作(如readwriteclose)的文件句柄。

在启动close操作时,文件描述符结构被销毁,文件的引用计数递减。 调用方进程将不再能够启动任何其他文件操作,直到它可以重新打开该文件。

文件读写操作

当用户应用使用适当的参数在文件上启动read时,将调用底层文件系统的read例程。 操作从查找文件的数据块映射开始,以定位要读取的适当数据磁盘扇区;然后,它从页面缓存分配一页并调度磁盘 I/O。在 I/O 传输完成后,文件系统将请求的数据移动到应用的缓冲区中,并更新调用方的文件描述符结构中的文件偏移量位置。

类似地,文件系统的write操作检索从用户缓冲区传递的数据,并将其写入页面缓存中文件缓冲区的适当偏移量,并使用PG*_*dirty标志标记页面。 但是,当调用write操作在文件末尾追加数据时,文件可能需要新的数据块才能增长。 在继续写入之前,文件系统在磁盘上查找空闲数据块,并为该文件分配它们。 分配新数据块需要更改 inode 结构的数据块映射,并从映射到分配的新数据块的页面缓存分配新页面。

附加特征

尽管文件系统的基本组件仍然相似,但数据的组织方式和访问数据的启发式方法取决于实现。 设计者考虑诸如可靠性安全性类型存储卷容量以及I/O 效率等因素来识别和支持增强文件系统能力的功能。 以下是现代文件系统支持的几个扩展功能。

扩展文件属性

由文件系统实现跟踪的一般文件属性在索引节点中维护,并由适当的操作解释。 扩展文件属性是一种功能,使用户能够定义文件的自定义元数据,文件系统不会对其进行解释。 这些属性通常用于存储取决于文件包含的数据类型的各种类型的信息。 例如,文档文件可以定义作者姓名和联系方式,Web 文件可以指定文件的 URL 和其他与安全相关的属性,如数字证书和加密散列密钥。 与普通属性类似,每个扩展属性由名称标识。 理想情况下,*、*大多数文件系统不会对此类扩展属性的数量施加限制。

一些文件系统还提供了索引属性的功能,这有助于快速查找所需类型的数据,而不必导航文件层次结构。 例如,假设文件被分配有称为关键字*、*的扩展属性,该扩展属性记录描述文件数据的关键字值。 有了索引,用户可以发出查询,通过适当的脚本查找与特定关键字匹配的文件列表,而不考虑文件的位置。 因此,索引为文件系统提供了一个强大的替代接口。

文件系统一致性和崩溃恢复

磁盘映像的一致性对于文件系统的可靠运行至关重要。 当文件系统处于更新其磁盘结构的过程中时,极有可能发生灾难性错误(断电、操作系统崩溃等),从而导致部分提交的关键更新中断。 这会导致磁盘结构损坏,并使文件系统处于不一致的状态。 通过采用有效的崩溃恢复策略来处理此类事件,是大多数文件系统设计人员面临的主要挑战之一。

一些文件系统通过专门设计的文件系统一致性检查工具(如fsck(广泛使用的 Unix 工具))处理崩溃恢复。 它在挂载前的系统引导时运行,扫描磁盘上的文件系统结构以查找不一致之处,并在发现时进行修复。 一旦完成,磁盘上的文件系统状态将恢复到一致状态,系统继续执行mount操作,从而使用户可以访问磁盘。 该工具分多个阶段执行其操作,在每个阶段密切检查每个磁盘结构(如超级块、索引节点块、空闲块)的一致性,检查各个索引节点的有效状态、目录检查和坏块检查。 虽然它提供了急需的崩溃恢复,但它也有其缺点:在大型磁盘卷上完成这种分阶段操作可能会消耗大量时间,这直接影响系统的引导时间。

日志记录是大多数现代文件系统实现用于快速可靠的崩溃恢复的另一种技术。 通过为崩溃恢复编程适当的文件系统操作来强制执行此方法。 其想法是准备一个日志(注),列出要提交给文件系统的磁盘映像的更改,并在开始实际的更新操作之前,将日志写入一个称为日志块的特殊磁盘块。 这确保了在实际更新期间发生崩溃时,文件系统可以很容易地检测到不一致,并通过查看日志中记录的信息来修复它们*。* 因此,通过略微扩展在更新期间完成的工作,日志文件系统的实现消除了对乏味且昂贵的磁盘扫描任务的需要。

访问控制列表(ACL)

为所有者、所有者所属的组和其他用户指定访问权限的默认文件和目录访问权限在某些情况下不提供所需的细粒度控制。 ACL 是一种功能,它使扩展机制能够为各种进程和用户指定文件访问权限。 此功能将所有文件和目录视为对象,并允许系统管理员为每个文件和目录定义访问权限列表。 ACL 包括对具有访问权限的对象的有效操作,以及对指定对象的每个用户和系统进程的限制。

Linux 内核中的文件系统

既然我们已经熟悉了与文件系统实现相关的基本概念,我们将探索 Linux 系统支持的文件系统服务。 内核的文件系统分支实现了许多文件系统服务,这些服务支持多种文件类型。 根据它们管理的文件类型,内核的文件系统可以大致分类为:

  1. 存储文件系统
  2. 特殊文件系统
  3. 分布式文件系统或网络文件系统

我们将在本章的后面部分讨论特殊的文件系统。

  • 存储文件系统:内核支持各种持久存储文件系统,根据它们要管理的存储设备类型,这些文件系统可以大致分为不同的组。
  • 磁盘文件系统:这一类别包括内核支持的各种标准存储磁盘文件系统,其中包括 Linux 原生 EXT 系列磁盘文件系统,如 ext2、ext3、ext4、ReiserFS 和 Btrfs;Unix 变体,如 sysv 文件系统、UFS 和 Minix 文件系统;Microsoft 文件系统,如 MS-DOS、VFAT 和 NTFS;其他专有文件系统,如 IBM 的 OS/2(。 以及像 IBM 的 JFS 和 SGI 的 XFS 这样的日志文件系统。
  • 可移动媒体文件系统:这一类别包括为 CD、DVD 和其他可移动存储媒体设备设计的文件系统,例如 ISO9660CD-ROM 文件系统和通用磁盘格式(UDF)DVD 文件系统,以及 Linux 发行版的 live CD 映像中使用的 squashfs。
  • 半导体存储文件系统:这一类别包括为原始闪存和其他需要支持损耗均衡和擦除操作的半导体存储设备设计和实现的文件系统。 当前支持的文件系统集包括 UBIFS、JFFS2、CRAMFS 等。

我们将简要讨论内核中的几个本机磁盘文件系统,这些文件系统默认用于 Linux 的各种发行版。

EXT 系列文件系统

Linux 内核的初始版本使用 Minix 作为默认的本机文件系统,该文件系统是为在 Minix 内核中用于教育目的而设计的,因此具有许多使用限制。 随着内核的成熟,内核开发人员构建了一个新的用于磁盘管理的本机文件系统,称为扩展文件系统*。* ext的设计深受标准 Unix 文件系统 UFS 的影响。 由于各种实现限制和效率低下,原来的 ext 很短暂,很快就被名为第二个扩展文件系统(ext2)的改进、稳定和高效的版本所取代。 ext2 文件系统在相当长一段时间内一直是默认的原生文件系统(直到 2001 年,Linux 内核发布了 2.4.15 版)。

后来,磁盘存储技术的快速发展导致存储容量和存储硬件效率的大幅提高。 为了利用存储硬件提供的特性,内核社区对ext2的分支进行了适当的设计改进,并添加了最适合特定存储类别的特性。 当前版本的 Linux 内核包含三个版本的扩展文件系统,分别称为 ext2、ext3 和 ext4。

Ext2

Ext2 文件系统最早是在内核版本 0.99.7(1993)中引入的。 它保留了具有回写缓存的经典 UFS(Unix 文件系统)的核心设计,从而实现了更短的周转时间和更高的性能。 尽管它的实施支持 2 TB 到 32 TB 的磁盘卷和 16 GB 到 2 TB 的文件大小,但由于 2.4 内核中的块设备和应用施加了限制,它的使用被限制为最多 4 TB 的磁盘卷和 2 GB 的最大文件大小。 它还包括通过一致性检查工具 fsck 支持 ACL、文件内存映射和崩溃恢复。 Ext2 将物理磁盘扇区划分为固定大小的块组。 为每个块组构建文件系统布局,每个块组具有完整的超级块、空闲块位图、索引节点位图、索引节点和数据块。 因此,每个数据块组看起来都像一个微型文件系统。 此设计帮助fsck在大型磁盘上进行更快的一致性检查。

Ext3

也称为Third Extended FileSystem,它使用日志记录扩展了 ext2 的功能。 它保留了具有块组的 ext2 的整个结构,从而实现了 ext2 分区到 ext3 类型的无缝转换。 如前所述,日志记录会导致文件系统将更新操作的详细信息记录到称为日志块的特定磁盘区域中;这些日志有助于加快崩溃恢复,并确保文件系统的一致性和可靠性。 然而,在日志文件系统上,磁盘更新操作可能会变得昂贵,因为写操作速度较慢或时间可变(由于日志日志),这将直接影响常规文件 I/O 的性能。作为一种解决方案,ext3 提供了日志配置选项,系统管理员或用户可以通过这些选项选择要记录到日志中的特定类型的信息。 这些配置选项称为日志记录模式

  1. 日志模式:此模式使文件系统将文件数据和元数据更改记录到日志中。 这会增加磁盘访问,从而最大限度地提高文件系统一致性,从而导致更新速度变慢。 此模式会导致日志消耗额外的磁盘块,并且是最慢的 ext3 日志记录模式。

  2. 有序模式:该模式仅将文件系统元数据记录到日志中,但它保证在将相关元数据提交到日志块之前将相关文件数据写入磁盘。 这可确保文件数据有效;如果在执行文件写入时发生崩溃,日志将指示附加的数据尚未提交,从而导致清理过程对此类数据执行清除操作。 这是 ext3 的默认日志记录模式。

  3. 回写模式:这类似于仅有元数据日志记录的有序模式,不同之处在于相关文件内容可能会在元数据提交到日志之前或之后写入磁盘。 这可能会导致文件数据损坏。 例如,假设要附加到的文件在实际文件写入之前可能在日志中标记为COMMITTED:如果在文件附加操作过程中发生崩溃,则日志会建议该文件大于其实际大小。 此模式速度最快,但会将文件数据的可靠性降至最低。 许多其他日志记录文件系统(如 JFS)都使用这种日志记录模式,但请确保在重新引导时将由于未写入数据而导致的任何垃圾清零。

所有这些模式在元数据的一致性方面都有类似的效果,但在文件和目录数据的一致性方面有所不同,日志模式可确保最大的安全性,但文件数据损坏的可能性最小,而回写模式提供的安全性最低,但损坏的风险很高。 管理员或用户可以在 ext3 卷的挂载操作期间调优适当的模式。

Ext4

Ext4 作为具有增强功能的 ext3 的替代品实现,首次出现在内核 2.6.28(2008)中。 它完全向后兼容 ext2 和 ext3,任何一种类型的卷都可以作为 ext4 挂载。 这是大多数当前 Linux 发行版上的默认 EXT 文件系统。 它使用个日志校验和扩展了 ext3 的日志记录功能,从而提高了可靠性。 它还为文件系统元数据添加校验和,并支持透明加密,从而增强了文件系统的完整性和安全性。 其他功能包括支持有助于减少碎片的扩展区*、*、永久预分配磁盘块(允许为媒体文件分配连续块)、支持存储容量高达 1 艾字节(EIB)的磁盘卷和大小高达 16 字节(TiB)的文件。

通用文件系统接口

不同文件系统和存储分区的存在导致每个文件系统维护其不同于其他文件系统的文件和数据结构树。 在挂载时,每个文件系统都需要将其内存中的文件树与其他文件系统隔离管理,从而导致系统用户和应用的文件树视图不一致。 这使得内核对各种文件操作(如打开、读取、写入、复制和移动)的支持变得复杂。 作为解决方案,Linux 内核(与许多其他 Unix 系统一样)采用了一个称为**虚拟文件系统(VFS)**的抽象层,该抽象层使用一个公共接口隐藏所有文件系统实现。

VFS 层构建一个名为rootfs的公共文件树,在该树下,所有文件系统都可以枚举它们的目录和文件。 这使得具有不同磁盘表示的所有特定于文件系统的子树能够统一并作为单个文件系统呈现。 系统用户和应用对文件树具有一致、同构的视图,这使得内核可以灵活地定义一组简化的通用系统调用,应用可以使用这些调用来执行文件 I/O,而不考虑底层文件系统及其表示。 由于 API 有限且灵活,此模型确保了应用设计的简单性,并实现了文件从一个磁盘分区或文件系统树到另一个磁盘分区或文件系统树的无缝复制或移动,而无需考虑底层的不同之处。

下图描述了虚拟文件系统:

VFS 定义了两组函数:第一,一组与文件系统无关的通用例程,用作所有文件访问和操作操作的公共入口函数;第二,一组特定于文件系统的抽象操作接口。 每个文件系统定义其操作(根据其文件和目录的概念),并将它们映射到提供的抽象接口,并且对于虚拟文件系统,这使得 VFS 能够通过动态切换到底层文件系统特定的功能来处理文件 I/O 请求。

VFS 结构和操作

破译 VFS 的关键对象和数据结构可以让我们更清楚地了解 VFS 如何在内部与文件系统协同工作,并实现至关重要的抽象。 以下是编织整个抽象网络的四个基本数据结构:

  • struct super_block--包含有关已挂载的特定文件系统的信息
  • struct inode--表示特定文件
  • struct dentry--表示目录条目
  • struct file--表示已打开并链接到进程的文件

所有这些数据结构都绑定到文件系统定义的适当抽象操作接口。

结构超级块

VFS 通过该结构定义了超级块的通用布局。 每个文件系统都需要实例化此结构的一个对象,以在挂载期间填充其超级块详细信息。 换句话说,这种结构将特定于文件系统的超级块从内核的其余部分中抽象出来,并帮助 VFS 通过struct super_block列表跟踪所有挂载的文件系统。 不具有持久超块结构的伪文件系统将动态生成超块。 超级块结构(struct super_block)在<linux/fs.h>中定义:

struct super_block {
         struct list_head        s_list;   /* Keep this first */
         dev_t                   s_dev;    /* search index; _not_ kdev_t */
         unsigned char           s_blocksize_bits;
         unsigned long           s_blocksize;
         loff_t                  s_maxbytes;  /* Max file size */
         struct file_system_type *s_type;
         const struct super_operations   *s_op;
         const struct dquot_operations   *dq_op;
         const struct quotactl_ops       *s_qcop;
         const struct export_operations *s_export_op;
         unsigned long           s_flags;
         unsigned long           s_iflags; /* internal SB_I_* flags */
         unsigned long           s_magic;
         struct dentry           *s_root;
         struct rw_semaphore     s_umount;
         int                     s_count;
         atomic_t                s_active;
 #ifdef CONFIG_SECURITY
         void                    *s_security;
 #endif
         const struct xattr_handler **s_xattr;
         const struct fscrypt_operations *s_cop;
         struct hlist_bl_head    s_anon; 
         struct list_head        s_mounts;/*list of mounts;_not_for fs use*/ 
         struct block_device     *s_bdev;
         struct backing_dev_info *s_bdi;
         struct mtd_info         *s_mtd;
         struct hlist_node       s_instances;
         unsigned int   s_quota_types; /*Bitmask of supported quota types */
         struct quota_info  s_dquot;   /* Diskquota specific options */
         struct sb_writers       s_writers;
         char s_id[32];                          /* Informational name */
         u8 s_uuid[16];                          /* UUID */
         void                    *s_fs_info;   /* Filesystem private info */
         unsigned int            s_max_links;
         fmode_t                 s_mode;

         /* Granularity of c/m/atime in ns.
            Cannot be worse than a second */
         u32                s_time_gran;

         struct mutex s_vfs_rename_mutex;        /* Kludge */

         /*
          * Filesystem subtype.  If non-empty the filesystem type field
          * in /proc/mounts will be "type.subtype"
          */
         char *s_subtype;

         /*
          * Saved mount options for lazy filesystems using
          * generic_show_options()
          */
         char __rcu *s_options;
         const struct dentry_operations *s_d_op; /*default op for dentries*/
         /*
          * Saved pool identifier for cleancache (-1 means none)
          */
         int cleancache_poolid;

         struct shrinker s_shrink;       /* per-sb shrinker handle */

         /* Number of inodes with nlink == 0 but still referenced */
         atomic_long_t s_remove_count;

         /* Being remounted read-only */
         int s_readonly_remount;

         /* AIO completions deferred from interrupt context */
         struct workqueue_struct *s_dio_done_wq;
         struct hlist_head s_pins;

         /*
          * Owning user namespace and default context in which to
          * interpret filesystem uids, gids, quotas, device nodes,
          * xattrs and security labels.
          */
         struct user_namespace *s_user_ns;

         struct list_lru         s_dentry_lru ____cacheline_aligned_in_smp;
         struct list_lru         s_inode_lru ____cacheline_aligned_in_smp;
         struct rcu_head         rcu;
         struct work_struct      destroy_work;

         struct mutex            s_sync_lock;  /* sync serialisation lock */

         /*
          * Indicates how deep in a filesystem stack this SB is
          */
         int s_stack_depth;

         /* s_inode_list_lock protects s_inodes */
         spinlock_t              s_inode_list_lock ____cacheline_aligned_in_smp;
         struct list_head        s_inodes;       /* all inodes */

         spinlock_t              s_inode_wblist_lock;
         struct list_head        s_inodes_wb;    /* writeback inodes */
 };

超级块结构包含定义和扩展超级块的信息和功能的其他结构。 以下是super_block的一些元素:

  • s_list的类型为struct list_head,并包含指向已安装的超级块列表的指针
  • s_dev是设备标识符
  • s_maxbytes包含最大文件大小
  • s_typestruct file_system_type类型的指针,它描述文件系统类型
  • s_opstruct super_operations类型的指针,包含对超级块的操作
  • s_export_op的类型为struct export_operations,有助于文件系统可导出,以便远程系统使用网络文件系统进行访问
  • s_rootstruct dentry类型的指针,指向文件系统根目录的 dentry 对象

每个枚举的超级块实例都包含一个指向函数指针抽象结构的指针,函数指针定义了超级块操作的接口。 文件系统将需要实现它们的超级块操作,并将它们分配给适当的函数指针。 这有助于每个文件系统按照其磁盘上超级块的布局实现超级块操作,并将该逻辑隐藏在公共接口下。 Struct super_operations<linux/fs.h>中定义:

struct super_operations {
         struct inode *(*alloc_inode)(struct super_block *sb);
         void (*destroy_inode)(struct inode *);

         void (*dirty_inode) (struct inode *, int flags);
         int (*write_inode) (struct inode *, struct writeback_control *wbc);
         int (*drop_inode) (struct inode *);
         void (*evict_inode) (struct inode *);
         void (*put_super) (struct super_block *);
         int (*sync_fs)(struct super_block *sb, int wait);
         int (*freeze_super) (struct super_block *);
         int (*freeze_fs) (struct super_block *);
         int (*thaw_super) (struct super_block *);
         int (*unfreeze_fs) (struct super_block *);
         int (*statfs) (struct dentry *, struct kstatfs *);
         int (*remount_fs) (struct super_block *, int *, char *);
         void (*umount_begin) (struct super_block *);

         int (*show_options)(struct seq_file *, struct dentry *);
         int (*show_devname)(struct seq_file *, struct dentry *);
         int (*show_path)(struct seq_file *, struct dentry *);
         int (*show_stats)(struct seq_file *, struct dentry *);
 #ifdef CONFIG_QUOTA
         ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);
         ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
         struct dquot **(*get_dquots)(struct inode *);
 #endif
         int (*bdev_try_to_free_page)(struct super_block*, struct page*, gfp_t);
         long (*nr_cached_objects)(struct super_block *,
                                   struct shrink_control *);
         long (*free_cached_objects)(struct super_block *,
                                     struct shrink_control *);
 };

此结构中的所有元素都指向在超块对象上操作的函数。 除非指定,否则所有这些操作都只能从进程上下文调用,并且不会持有任何锁。 下面我们来看几个重要的例子:

  • alloc_inode:此方法用于为新的 inode 对象创建和分配空间,并在超级块下对其进行初始化。
  • destroy_inode:这会销毁给定的 inode 对象,并释放分配给 inode 的资源。 仅当定义了alloc_inode时才使用此选项。
  • dirty_inode:这由 VFS 调用以标记脏 inode(当 inode 被修改时)。
  • write_inode:VFS 在需要将索引节点写入磁盘时调用此方法。 第二个参数指向struct writeback_control,这是一个告诉写回代码要做什么的结构。
  • put_super:当 VFS 需要释放超级块时,调用此函数。
  • sync_fs:调用此函数是为了将文件系统数据与底层块设备的文件系统数据同步。
  • statfs:调用以获取 VFS 的文件系统统计信息。
  • remount_fs:需要重新挂载文件系统时调用。
  • umount_begin:在 VFS 卸载文件系统时调用。
  • show_options:由 VFS 调用以显示装载选项。
  • quota_read:由 VFS 调用以读取文件系统配额文件。

结构索引节点

struct inode的每个实例表示rootfs中的一个文件。 VFS 将此结构定义为特定于文件系统的 inode 的抽象。 不管 inode 结构的类型及其在磁盘上的表示形式如何,每个文件系统都需要将其文件作为struct inode枚举到rootfs中,以获得通用的文件视图。 此结构在<linux/fs.h>中定义:

struct inode {
      umode_t                 i_mode;
   unsigned short          i_opflags;
        kuid_t                  i_uid;
    kgid_t                  i_gid;
    unsigned int            i_flags;
#ifdef CONFIG_FS_POSIX_ACL
  struct posix_acl        *i_acl;
   struct posix_acl        *i_default_acl;
#endif
       const struct inode_operations   *i_op;
    struct super_block      *i_sb;
    struct address_space    *i_mapping;
#ifdef CONFIG_SECURITY
   void                    *i_security;
#endif
  /* Stat data, not accessed from path walking */
   unsigned long           i_ino;
    /*
         * Filesystems may only read i_nlink directly.  They shall use the
         * following functions for modification:
   *
         *    (set|clear|inc|drop)_nlink
   *    inode_(inc|dec)_link_count
   */
       union {
           const unsigned int i_nlink;
               unsigned int __i_nlink;
   };
        dev_t                   i_rdev;
   loff_t                  i_size;
   struct timespec         i_atime;
  struct timespec         i_mtime;
  struct timespec         i_ctime;
  spinlock_t              i_lock; /*i_blocks, i_bytes, maybe i_size*/
       unsigned short          i_bytes;
  unsigned int            i_blkbits;
        blkcnt_t                i_blocks;
#ifdef __NEED_I_SIZE_ORDERED
       seqcount_t              i_size_seqcount;
#endif
      /* Misc */
        unsigned long           i_state;
  struct rw_semaphore     i_rwsem;

    unsigned long           dirtied_when;/*jiffies of first dirtying */
       unsigned long           dirtied_time_when;

  struct hlist_node       i_hash;
   struct list_head        i_io_list;/* backing dev IO list */
#ifdef CONFIG_CGROUP_WRITEBACK
   struct bdi_writeback    *i_wb;  /* the associated cgroup wb */

      /* foreign inode detection, see wbc_detach_inode() */
     int                     i_wb_frn_winner;
  u16                     i_wb_frn_avg_time;
        u16                     i_wb_frn_history;
#endif
     struct list_head        i_lru;  /* inode LRU list */
      struct list_head        i_sb_list;
        struct list_head        i_wb_list;/* backing dev writeback list */
        union {
           struct hlist_head       i_dentry;
         struct rcu_head         i_rcu;
    };
        u64                     i_version;
        atomic_t                i_count;
  atomic_t                i_dio_count;
      atomic_t                i_writecount;
#ifdef CONFIG_IMA
      atomic_t                i_readcount; /* struct files open RO */
#endif
/* former->i_op >default_file_ops */
       const struct file_operations  *i_fop; 
       struct file_lock_context *i_flctx; 
       struct address_space i_data; 
       struct list_head i_devices; 
       union { 
          struct pipe_inode_info *i_pipe; 
          struct block_device *i_bdev; 
          struct cdev *i_cdev; 
          char *i_link; 
          unsigned i_dir_seq; 
       }; 
      __u32 i_generation; 
 #ifdef CONFIG_FSNOTIFY __u32 i_fsnotify_mask; /* all events this inode cares about */ 
     struct hlist_head i_fsnotify_marks; 
#endif 
#if IS_ENABLED(CONFIG_FS_ENCRYPTION) 
    struct fscrypt_info *i_crypt_info; 
#endif 
    void *i_private; /* fs or device private pointer */ 
};

请注意,并非所有字段都是必填的,并且适用于所有文件系统;它们可以自由地初始化与其索引节点定义相关的适当字段。 每个 inode 都绑定到底层文件系统定义的两个重要操作组:第一,管理 inode 数据的一组操作。 这些由 inode 的i_op指针引用的struct inode_operations类型的实例表示。 第二个是一组操作,用于访问和操作 inode 表示的底层文件数据;这些操作封装在类型为struct file_operations的实例中,并绑定到 inode 实例的i_fop指针。

换句话说,每个 inode 都绑定到由类型 structinode_operations的实例表示的元数据操作,以及由类型struct file_operations的实例表示的文件数据操作。 但是,用户模式应用从为表示调用者进程的打开文件而创建的有效file对象访问文件数据操作(我们将在下一节详细讨论文件对象):

struct inode_operations {
 struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
 const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *);
 int (*permission) (struct inode *, int);
 struct posix_acl * (*get_acl)(struct inode *, int);
 int (*readlink) (struct dentry *, char __user *,int);
 int (*create) (struct inode *,struct dentry *, umode_t, bool);
 int (*link) (struct dentry *,struct inode *,struct dentry *);
 int (*unlink) (struct inode *,struct dentry *);
 int (*symlink) (struct inode *,struct dentry *,const char *);
 int (*mkdir) (struct inode *,struct dentry *,umode_t);
 int (*rmdir) (struct inode *,struct dentry *);
 int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t);
 int (*rename) (struct inode *, struct dentry *,
 struct inode *, struct dentry *, unsigned int);
 int (*setattr) (struct dentry *, struct iattr *);
 int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
 ssize_t (*listxattr) (struct dentry *, char *, size_t);
 int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
 u64 len);
 int (*update_time)(struct inode *, struct timespec *, int);
 int (*atomic_open)(struct inode *, struct dentry *,
 struct file *, unsigned open_flag,
 umode_t create_mode, int *opened);
 int (*tmpfile) (struct inode *, struct dentry *, umode_t);
 int (*set_acl)(struct inode *, struct posix_acl *, int);
} ____cacheline_aligned

以下是几个重要操作的简要说明:

  • lookup:用于定位指定文件的 inode 实例;此操作返回 Dentry 实例。
  • create:此例程由 VFS 调用,以构造指定为参数的 dentry 的索引节点对象。
  • link:用于支持硬链接。 由link(2)系统调用。
  • unlink:用于支持删除 inode。 由unlink(2)系统调用。
  • mkdir:用于支持创建子目录。 由mkdir(2)系统调用。
  • mknod:由mknod(2)系统调用调用以创建命名管道、索引节点或套接字的设备。
  • listxattr:由 VFS 调用以列出文件的所有扩展属性。
  • update_time:由 VFS 调用以更新索引节点的特定时间或i_version

下面是 VFS 定义的struct file_operations,它封装了文件系统定义的对底层文件数据的操作。 因为它被声明为所有文件系统的公共接口,所以它包含适合于支持对具有不同文件数据定义的各种类型的文件系统的操作的函数指针接口。 底层文件系统可以自由选择适当的接口,其余接口则根据它们对文件和文件数据的概念而定:

struct file_operations {
 struct module *owner;
 loff_t (*llseek) (struct file *, loff_t, int);
 ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
 ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
 ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
 ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
 int (*iterate) (struct file *, struct dir_context *);
 int (*iterate_shared) (struct file *, struct dir_context *);
 unsigned int (*poll) (struct file *, struct poll_table_struct *);
 long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
 long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
 int (*mmap) (struct file *, struct vm_area_struct *);
 int (*open) (struct inode *, struct file *);
 int (*flush) (struct file *, fl_owner_t id);
 int (*release) (struct inode *, struct file *);
 int (*fsync) (struct file *, loff_t, loff_t, int datasync);
 int (*fasync) (int, struct file *, int);
 int (*lock) (struct file *, int, struct file_lock *);
 ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
 unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
 int (*check_flags)(int);
 int (*flock) (struct file *, int, struct file_lock *);
 ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
 ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
 int (*setlease)(struct file *, long, struct file_lock **, void **);
 long (*fallocate)(struct file *file, int mode, loff_t offset,
 loff_t len);
 void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
 unsigned (*mmap_capabilities)(struct file *);
#endif
 ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
 loff_t, size_t, unsigned int);
 int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t,
 u64);
 ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *,
 u64);
};

以下是几个重要操作的简要说明:

  • llseek:当 VFS 需要移动文件位置索引时调用。
  • read:由read(2)和其他相关系统调用调用。
  • write:由write(2)和其他相关系统调用调用。
  • iterate:当 VFS 需要读取目录内容时调用。
  • poll:当进程需要检查文件上的活动时,这由 VFS 调用。 由select(2)poll(2)系统调用。
  • unlocked_ioctl:当用户模式进程调用文件描述符上的ioctl(2)系统调用时,将调用分配给此指针的操作。 此功能用于支持特殊操作。 设备驱动程序使用此接口支持目标设备上的配置操作。
  • compat_ioctl:类似于 ioctl,不同之处在于它用于将从 32 位进程传递的参数转换为与 64 位内核一起使用。
  • mmap:当用户模式进程调用mmap(2)系统调用时,调用分配给此指针的例程。 此函数支持的功能取决于底层文件系统。 对于常规持久文件,此函数用于将文件的调用方指定的数据区域映射到调用方进程的虚拟地址空间。 对于支持mmap的设备文件,此例程将底层设备地址空间映射到调用方的虚拟地址空间。
  • open:当用户模式进程启动open(2)系统调用以创建文件描述符时,分配给此接口的函数由 VFS 调用。
  • flush:由close(2)系统调用调用以刷新文件。
  • release:当用户模式进程执行 Close(2)系统调用以销毁文件描述符时,VFS 会调用分配给此接口的函数。
  • fasync:当为文件启用异步模式时,由fcntl(2)系统调用调用。
  • splice_write:由 VFS 调用以将数据从管道拼接到文件。
  • setlease:由 VFS 调用以设置或释放文件锁租用。
  • fallocate:由 VFS 调用以预分配块。

结构数据项

在前面的讨论中,我们了解了典型的磁盘文件系统如何通过inode结构表示每个目录,以及磁盘上的目录块如何表示该目录下的文件信息。 当用户模式应用使用完整路径(如/root/test/abc)启动文件访问操作(如open())时,VFS 将需要执行目录查找操作来解码和验证路径中指定的每个组件。

为了高效地查找和转换文件路径中的组件,VFS 枚举了一种称为dentry的特殊数据结构。 Dentry 对象包含文件或目录的字符串name、指向其inode的指针和指向父文件或目录的指针dentry。 为文件查找路径中的每个组件生成一个 Dentry 实例;例如,在/root/test/abc的情况下,为root枚举一个 Dentry 实例,为test*、*枚举另一个 Dentry 实例,最后为文件abc枚举一个 Dentry 实例。

struct dentry在内核头</linux/dcache.h>中定义:

struct dentry {
 /* RCU lookup touched fields */
   unsigned int d_flags;           /* protected by d_lock */
 seqcount_t d_seq;               /* per dentry seqlock */
  struct hlist_bl_node d_hash;    /* lookup hash list */
    struct dentry *d_parent;        /* parent directory */
    struct qstr d_name;
       struct inode *d_inode; /* Where the name -NULL is negative */
     unsigned char d_iname[DNAME_INLINE_LEN];        /* small names */

   /* Ref lookup also touches following */
   struct lockref d_lockref;       /* per-dentry lock and refcount */
        const struct dentry_operations *d_op;
     struct super_block *d_sb;       /* The root of the dentry tree */
 unsigned long d_time;           /* used by d_revalidate */
        void *d_fsdata;                 /* fs-specific data */

      union {
           struct list_head d_lru;         /* LRU list */
            wait_queue_head_t *d_wait;      /* in-lookup ones only */
 };
        struct list_head d_child;       /* child of parent list */
        struct list_head d_subdirs;     /* our children */
        /*
         * d_alias and d_rcu can share memory
      */
       union {
           struct hlist_node d_alias;      /* inode alias list */
            struct hlist_bl_node d_in_lookup_hash;  
          struct rcu_head d_rcu;
    } d_u;
};
  • d_parent是指向父 Dentry 实例的指针。
  • d_name保存文件的名称。
  • d_inode是指向文件的 inode 实例的指针。
  • d_flags包含在<include/linux/dcache.h>.中定义的几个标志
  • d_op指向包含指向 Dentry 对象各种操作的函数指针的结构。

现在让我们看一下struct dentry_operations,它描述了文件系统如何重载标准 Dentry 操作:

struct dentry_operations {
 int (*d_revalidate)(struct dentry *, unsigned int);
       int (*d_weak_revalidate)(struct dentry *, unsigned int);
  int (*d_hash)(const struct dentry *, struct qstr *);
      int (*d_compare)(const struct dentry *,
                   unsigned int, const char *, const struct qstr *);
 int (*d_delete)(const struct dentry *);
   int (*d_init)(struct dentry *);
   void (*d_release)(struct dentry *);
       void (*d_prune)(struct dentry *);
 void (*d_iput)(struct dentry *, struct inode *);
  char *(*d_dname)(struct dentry *, char *, int);
   struct vfsmount *(*d_automount)(struct path *);
   int (*d_manage)(const struct path *, bool);
       struct dentry *(*d_real)(struct dentry *, const struct inode *,
                            unsigned int);

} ____ca

以下是几个重要的 Dentry 操作的简要说明:

  • d_revalidate:当 VFS 需要重新验证 Dentry 时调用。 每当名称查找返回 dcache 中的 dentry 时,都会调用此函数。
  • d_weak_revalidate:当 VFS 需要重新验证跳转的 Dentry 时调用。 如果路径遍历在父目录的查找中未找到的 dentry 处结束,则会调用此函数。
  • d_hash:当 VFS 将 dentry 添加到哈希表时调用。
  • d_compare:调用以比较两个 Dentry 实例的文件名。 它将 dentry 名称与给定名称进行比较。
  • d_delete:在删除对 Dentry 的最后一个引用时调用。
  • d_init:在分配 Dentry 时调用。
  • d_release:在释放 Dentry 时调用。
  • d_iput:在从 dentry 释放 inode 时调用。
  • d_dname:在必须生成 dentry 的路径名时调用。 特殊文件系统可以方便地延迟路径名生成(无论何时需要路径)。

结构文件

struct fil*e*的实例表示打开的文件。 此结构是在用户进程成功打开文件时创建的,它包含调用方应用的文件访问属性,如文件数据的偏移量、访问模式和特殊标志等。 此对象映射到调用方的文件描述符表,并充当调用方应用对该文件的句柄。 此结构是进程的本地结构,并由进程保留,直到关闭相关文件。 对文件描述符的close操作将销毁file实例。

struct file {
       union {
           struct llist_node       fu_llist;
         struct rcu_head         fu_rcuhead;
       } f_u;
    struct path             f_path;
   struct inode            *f_inode;       /* cached value */
        const struct file_operations    *f_op;

      /*
         * Protects f_ep_links, f_flags.
   * Must not be taken from IRQ context.
     */
       spinlock_t              f_lock;
   atomic_long_t           f_count;
  unsigned int            f_flags;
  fmode_t                 f_mode;
   struct mutex            f_pos_lock;
       loff_t                  f_pos;
    struct fown_struct      f_owner;
  const struct cred       *f_cred;
  struct file_ra_state    f_ra;

       u64                     f_version;
#ifdef CONFIG_SECURITY
    void                    *f_security;
#endif
  /* needed for tty driver, and maybe others */
     void                    *private_data;

#ifdef CONFIG_EPOLL
     /* Used by fs/eventpoll.c to link all the hooks to this file */
   struct list_head        f_ep_links;
       struct list_head        f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
      struct address_space    *f_mapping;
} __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */

f_inode指针指向文件的 inode 实例。 当文件对象由 VFS 构造时,f_op指针使用与文件的 inode 相关联的struct file_operations地址进行初始化,如前所述。

特殊文件系统

与常规文件系统不同,常规文件系统旨在管理备份到存储设备上的持久文件数据,内核实现了各种特殊的文件系统,这些文件系统管理特定类别的内核内核数据结构。 由于这些文件系统不处理持久性数据,因此它们不消耗磁盘块,并且整个文件系统结构都在核心中维护。 存在这样的文件系统可以简化应用开发、调试和更容易的错误检测。 这一类别中有许多文件系统,每个文件系统都是专门为特定目的而设计和实现的。 以下是几个重要项目的简要说明。

Procfs 和 procfs

Procfs是一种特殊的文件系统,它将内核数据结构枚举为文件。 该文件系统作为内核程序员的调试资源,因为它允许用户通过虚拟文件接口查看数据结构的状态。 将 Procfs 挂载到 rootfs 的/proc目录(挂载点)。

Procfs 文件中的数据不是持久的,总是在运行时构造的;每个文件都是一个接口,用户可以通过它触发相关操作。 例如,对 proc 文件的读取操作调用绑定到文件条目的相关读取回调函数,该函数被实现为用适当的数据填充用户缓冲区。

枚举的文件数量取决于为其构建内核的配置和体系结构。 以下是几个重要文件的列表,这些文件在/proc下列举了有用的数据:

| 加入时间:清华大学 2007 年 01 月 25 日下午 3:33 | 说明 | | /proc/cpuinfo | 提供低级 CPU 详细信息,如供应商、型号、时钟速度、高速缓存大小、同级数量、核心、CPU 标志和虚假信息。 | | /proc/meminfo | 提供物理内存状态的摘要视图。 | | /proc/ioports | 提供有关 x86 类计算机支持的端口 I/O 地址空间的当前使用情况的详细信息。 此文件在其他体系结构上不存在。 | | /proc/iomem | 显示了描述内存地址空间当前使用情况的详细布局。 | | /proc/interrupts | 显示 IRQ 描述符表的视图,该表包含绑定到每个 IRQ 行和中断处理程序的详细信息。 | | /proc/slabinfo | 显示片高速缓存及其当前状态的详细列表。 | | /proc/buddyinfo | 显示由好友系统管理的好友列表的当前状态。 | | /proc/vmstat | 显示虚拟内存管理统计信息。 | | /proc/zoneinfo | 显示每个节点的内存区统计信息。 | | /proc/cmdline | 显示传递给内核的引导参数。 | | /proc/timer_list | 显示活动挂起计时器的列表,以及时钟源的详细信息。 | | /proc/timer_stats | 提供有关活动计时器的详细统计信息,用于跟踪计时器的使用和调试。 | | /proc/filesystems | 显示当前处于活动状态的文件系统服务的列表。 | | /proc/mounts | 显示当前装载的设备及其装载点。 | | /proc/partitions | 显示使用关联的/dev 文件枚举检测到的当前存储分区的详细信息。 | | /proc/swaps | 列出活动交换分区以及状态详细信息。 | | /proc/modules | 列出当前部署的内核模块的名称和状态。 | | /proc/uptime | 显示内核自启动以来一直运行并处于空闲模式的时间长度。 | | /proc/kmsg | 显示内核消息日志缓冲区的内容。 | | /proc/kallsyms | 给出了内核符号表。 | | /proc/devices | 显示已注册的块和字符设备及其主编号的列表。 | | /proc/misc | 显示通过 misc 接口注册的设备列表及其 misc 标识符。 | | /proc/stat | 显示系统统计信息。 | | /proc/net | 包含各种与网络堆栈相关的伪文件的目录。 | | /proc/sysvipc | 包含伪文件的子目录,这些伪文件显示 system V IPC 对象、消息队列、信号量和共享内存的状态。 |

/proc还列出了许多子目录,这些子目录提供了工艺 PCB 或任务结构中元素的详细视图。 这些文件夹按它们所代表的进程的 PID 命名。 以下是显示流程相关信息的重要文件列表:

| 文件名文件名 | 描述 / 描写 / 形容 / 类别 | | /proc/pid/cmdline | 进程的命令行名称。 | | /proc/pid/exe | 指向可执行文件的符号链接。 | | /proc/pid/environ | 列出进程可访问的环境变量。 | | /proc/pid/cwd | 指向进程的当前工作目录的符号链接。 | | /proc/pid/mem | 显示进程虚拟内存的二进制映像。 | | /proc/pid/maps | 列出进程的虚拟内存映射。 | | /proc/pid/fdinfo | 列出打开的文件描述符的当前状态和标志的目录。 | | /proc/pid/fd | 包含指向打开的文件描述符的符号链接的目录。 | | /proc/pid/status | 列出进程的当前状态,包括其内存使用情况。 | | /proc/pid/sched | 列出计划统计信息。 | | /proc/pid/cpuset | 列出了此进程的 CPU 关联掩码。 | | /proc/pid/cgroup | 显示进程的 cgroup 详细信息。 | | /proc/pid/stack | 显示进程拥有的内核堆栈的回溯。 | | /proc/pid/smaps | 显示每个映射到其地址空间所占用的内存。 | | /proc/pid/pagemap | 显示进程的每个虚拟页的物理映射状态。 | | /proc/pid/syscall | 公开进程当前正在执行的系统调用的系统调用号和参数。 | | /proc/pid/task | 包含子进程/线程详细信息的目录。 |

These listings were drawn up to familiarize you with proc files and their use. You are advised to visit the manual page of procfs for a detailed description of each of these files.

到目前为止,我们列出的所有文件都是只读的;procfs 还包含一个包含读写文件的分支/proc/sys,这些文件被称为内核参数。 /proc/sys下的文件根据它们适用的子系统进一步分类。 列出所有这些文件超出了范围。

系统文件系统

sysfs是另一个伪文件系统,用于将统一硬件和驱动程序信息导出到用户模式。 它通过虚拟文件将有关设备和相关设备驱动程序的信息从内核的设备模型角度枚举到用户空间。 Sysfs 挂载到rootfs的/sys 目录(挂载点)。 与 procfs 类似,可以通过 sysfs 的虚拟文件接口为电源管理和其他功能配置底层驱动程序和内核子系统。 Sysfs 还通过适当的守护进程(如udev)启用 Linux 发行版的热插拔事件管理,该守护进程被配置为侦听和响应热插拔事件。

以下是 sysfs 的重要子目录的简要说明:

  • Devices:引入 sysfs 背后的目标之一是提供当前由各个驱动程序子系统枚举和管理的设备的统一列表。 设备目录包含全局设备层次结构,其中包含驱动程序子系统发现并注册到内核的每个物理和虚拟设备的信息。
  • Bus:该目录包含一个子目录列表,每个子目录代表内核中注册了支持的物理总线类型。 每个总线类型目录包含两个子目录:devicesdriversdevices目录包含当前发现或绑定到该总线类型的设备列表。 列表中的每个文件都是一个符号链接,指向全局设备树中设备目录中的设备文件。 drivers目录包含描述向总线管理器注册的每个设备驱动程序的目录。 每个驱动程序目录都列出了显示驱动程序参数的当前配置(可以修改)的属性,以及指向驱动程序绑定到的物理设备目录的符号链接。
  • class目录包含当前向内核注册的设备类的表示。 设备类别描述了一种功能类型的设备。 每个设备类目录都包含表示当前在此类下分配和注册的设备的子目录。 对于大多数类设备对象,它们的目录包含指向全局设备层次结构和总线层次结构中与该类对象相关联的设备和驱动程序目录的符号链接。
  • Firmwarefirmware目录包含用于查看和操作开机/重置期间运行的特定于平台的固件的界面,例如 x86 上的 BIOS 或 UEFI 以及 PPC 平台上的 OpenFirmware。
  • 模块:该目录包含表示当前部署的每个内核模块的子目录。 每个目录都使用它所代表的模块的名称进行枚举。 每个模块目录都包含有关模块的信息,如 refcount、modparams 及其核心大小。

调试

与通过虚拟文件接口提供特定信息的 procfs 和 sysfs 不同,debugfs是一个通用内存文件系统,它允许内核开发人员导出任何被认为对调试有用的任意信息。 Debugfs 提供用于枚举虚拟文件的函数接口,通常挂载到/sys/debug目录。 诸如 ftrace 之类的跟踪机制使用 Debugfs 来显示函数和中断跟踪。

还有许多其他特殊的文件系统,如 pipefs、mqueue 和 sockfs;我们将在后面的章节中讨论其中的几个。

简略的 / 概括的 / 简易判罪的 / 简易的

通过本章,我们对典型的文件系统、其结构和设计以及是什么使其成为操作系统的基本部分有了一般性的了解。 本章还强调了抽象的重要性和优雅,使用内核全面吸收的通用的、分层的体系结构设计。 我们还扩展了对 VFS 及其通用文件接口的理解,该接口促进了通用文件 API 及其内部结构。 在下一章中,我们将探讨内存管理的另一个方面,称为虚拟内存管理器,它处理进程虚拟地址空间和页表。