Starnix VFS 的架构

本页面介绍了 Starnix 虚拟文件系统 (VFS) 的关键概念和底层结构:

Starnix 自有虚拟文件系统的动机

Starnix 拥有自己的虚拟文件系统(与 Fuchsia 的 VFS 分开),原因如下:

  • 性能 – Starnix VFS 允许执行多种文件系统操作,以避免进行 FIDL 调用。Starnix VFS 通过缓存先前 FIDL 调用的结果并在进程中实现整个文件系统(类似于 tmpfsprocfs)来实现此目的。

  • 兼容性 - Starnix 的文件系统必须支持多种不受 Fuchsia 的 VFS 库支持的 Linux 文件系统操作,很多基于 Linux 的操作系统都依赖于这些操作。特别是,以下两个操作很难改造到 Fuchsia 的现有 VFS 设计中:

    • 装载目录 - Linux 文件系统允许将一个文件系统的目录装载到另一个文件系统的目录中。

    • 跟踪 open() 之后的文件路径 - Linux 文件系统可以使用文件描述符来确定调用 open() 所打开的文件的路径。如果该文件或其某个父目录发生移动或重命名,则该文件的路径必须考虑此项更改。

Starnix VFS 的组成要素

本部分将使用 Starnix VFS 的基本构建块构建一个简单的文件系统:

文件和目录

Starnix VFS 的核心是 FsNode 实例的集合。每个 FsNode 都包含有关文件或目录的信息(参见图 1)。文件系统操作(例如读取、写入和检索文件信息)对 FsNode 实例执行。

简单的文件系统

图 1. 包含目录和文件的简单文件系统。

DirEntry 用于为 FsNode 分配名称。DirEntryFsNode 的这种映射称为硬链接。多个 DirEntry 实例可映射到单个文件类型 FsNode(参见图 2)。这样即可将文件系统中的多个路径解析为同一个 FsNode。因此,多个路径可以解析为文件系统中的相同文件内容和属性。不过,虽然一个文件可以具有多个硬链接,但目录(即目录类型的 FsNode)只能有一个硬链接。

具有两个 DirEntry 实例的文件系统

图 2. 多个 DirEntry 实例可映射到同一个 FsNode

文件系统

文件系统会跟踪一组相关文件和目录的相关信息。在 Starnix VFS 中,文件系统(即 FileSystem 的单个实例)表示 DirEntry 实例的集合,每个 FileSystem 实例包含以下项:

  • 指向根目录的 DirEntry
  • FsNode 实例的缓存(表示根目录下的文件和目录)

文件系统

图 3. FileSystem 会跟踪 DirEntryFsNode 实例。

Starnix VFS 中的装载文件系统

通过装载,用户可以无缝地从一个文件系统转移到另一个文件系统。文件系统被装载到特定目录后,用户(或任务)就可以使用该系统及其内容了。该“装载点”目录通常是用户已有权访问的另一个文件系统的目录。例如,在图 4 中,用户可以使用父 FS 中的路径 /example/file1.txt 来访问示例 FS(文件系统)中的 file1.txt 文件。

装载的文件系统

图 4. Example FS 的根目录会装载到父 FS 的 example 目录中。

在 Starnix VFS 中装载文件系统并跟踪装载点涉及以下实例:

装载

Mount 使 FileSystem 可以在另一个 FileSystem 的装载点目录中访问。换句话说,Mount 用于链接以下两个目录:

  • 父级 FileSystem 中的装载点目录
  • 子级 FileSystem 的根目录

在图 4 中,父 FS 将 example 目录作为子目录,而示例 FS 装载在此 example 目录(即装载点目录)上。在此设置中,当您将工作目录更改为父 FS 中的 example 目录(例如,运行 cd example)时,您会“进入装载”,即您现在位于示例 FS 的根目录中。

已装载的文件系统

图 5. Mount 会跟踪已装载的 FileSystem 的根目录。

为了实现从父级 FileSystem 无缝移至子级 FileSystem(反之亦然)的功能,Mount 会跟踪指向已装载 FileSystem 的根目录的 DirEntry(见图 5)。Mount 还会使用 mountpoint 指针(请参阅 NamespaceNode)跟踪其父级 Mount,并使用 submounts 指针跟踪其子级 Mount 实例。(参见图 6)。

装载实例

图 6. Mountmountpointsubmounts 指针用于在装载的文件系统之间移动。

请务必注意,FileSystem 可以一次装载到多个不同的 FileSystem 实例。这样一来,就可以从多个父级 FileSystem 实例访问同一个 FileSystem,从而能够使用各种路径(或符号链接)访问已装载的 FileSystem 中的相同文件。

命名空间节点

Starnix 中的每个任务都包含一个 FsContext 实例。FsContext 存储有关任务与 Starnix VFS 关联的信息,文件描述符表除外。

重要的是,FsContext 包含一个 Namespace,可让任务识别此命名空间下装载的所有 FileSystem 实例。之所以能够这么做,是因为每个 Namespace 都包含一个用于跟踪命名空间中的“根装载”的 Mount(参见图 7)。使用根装载的 submounts 指针(该指针指向命名空间中所有 DirEntry 实例及其各自的 Mount 实例的映射),任务可以访问此命名空间下装载的所有其他 FileSystem 实例。

FsContext 和命名空间

图 7. FsContext 具有 Namespace,后者具有用于跟踪根装载的 Mount

NamespaceNode 对于遍历 Namespace 中的路径非常有用。每个 NamespaceNode 会跟踪一个 Mount 及其各自的 DirEntry

但是,由于同一 FileSystem 可在 Namespace 中多次装载(例如,在同一父级 FileSystem 中的不同装载点目录中装载),因此可能需要多个 NamespaceNode 实例才能跟踪这些指向同一底层 FileSystem 的不同 Mount 实例。

namespaceNodes 实例

图 8. FsContext 有两个用于跟踪特定目录的 NamespaceNode 实例:root 和 cwd。

例如,每个 FsContext 至少包含两个用于跟踪以下目录的 NamespaceNode 实例(参见图 8):

  • 根目录
  • 当前工作目录 (cwd)

两个 NamespaceNode 实例均用于查找此 Namespace 中的路径。系统会根据根 NamespaceNode 查找以 / 开头的路径(例如 /example/file1.txt),而使用 cwd NamespaceNode 查找没有起始 / 的路径(例如 file1.txt)。

简单的命名空间

图 9. 具有单个(根)Mount 的简单命名空间。

图 9 中的示意图显示了具有单个 Mount 的命名空间,其中当前工作目录 (cwd) 恰好是根目录。图 10 中的示意图显示,当前工作目录已更改为 example 目录(例如,运行 cd example),这会导致 cwd NamespaceNodeentry 指针更新为 example 目录。

cwd 命名空间节点

图 10. 当前工作目录将更改为命名空间中的 example 目录。

图 11 中的示意图显示了示例 FS 装载到父 FS 的命名空间。在这种情况下,当运行 cd example 时,cwd NamespaceNodemount 指针会更新为跟踪示例 FS 的根目录的新 Mount(标记为“示例装载”)。使用此新的 MountFsContext 中的任务可以在遍历路径时从父 FS 无缝移至示例 FS。

具有已装载文件系统的命名空间

图 11. 一个命名空间,其中示例 FS 会装载到父 FS 的 example 目录中。

遍历 NamespaceNode 实例时,系统会处理符号链接(或符号链接)。如果任务遇到符号链接并请求其路径,则系统会从 NamespaceNode 以递归方式解析此符号链接的路径。