开放流程的生命周期

本文档详细介绍了在 Fuchsia 上访问文件系统的全过程, 深入探究执行某些操作时所使用的每个层的详细信息, 就像打开文件一样简单需要注意的是,所有这些层 userspace;即使与文件系统服务器和驱动程序交互,内核 仅用于在组件之间传递消息。

可调用:

open(“foobar”);

这项要求会转到何处?

标准库:其中的“open”已定义

“open”调用是由标准库提供的函数。对于 C/C++ 程序,这通常在 unistd.h 中进行声明,后者具有 libfdio 中的支持定义。 对于 Go 程序,在 Go 中有一个等效(但不同)的实现 标准库。对于每种语言和运行时,开发者可以选择使用 自己对“开放”的定义

在单体式内核中,open 是系统周围的轻量级 shim 调用中,内核可能会处理路径解析、重定向等。在该调用中, 则内核将需要根据外部表层来调解对资源的访问 。但是,Zircon 内核有意 此类系统调用。相反,客户端通过“通道”访问文件系统,-- 初始化进程时,系统会为其提供一个命名空间, 这是一个“绝对路径”表格->“handle”映射。已访问所有路径 来打开该命名空间 将请求定向到此命名空间 映射。

不过,在此示例中,包含打开“foobar”的请求, 因此来电可以通过代表 当前工作目录(其本身表示为绝对路径) 和标识名)。

标准库负责接受一个(或多个)句柄 并使其看起来像文件描述符。因此,“文件 描述符表”这一概念存在于客户端进程中(如果客户端 选择使用自定义运行时,他们可以完全以 句柄 --“文件描述符”封装是可选的)。

然而,这样就提出了一个问题:给定一个针对文件、套接字和 那么标准库是怎么做才能使所有这些资源 在功能上是否相同?客户端如何确定要发送哪些消息 这些标识名?

Fdio

名为 fdio 的库 负责提供一个统一界面以访问各种资源 包括文件、套接字、服务、管道等。该图层定义了一组 读取、写入、打开、关闭、跳转等 由各种协议支持的文件描述符每种支持的协议 负责提供客户端代码来解释 互动例如,套接字为客户端提供了多个句柄;一 另一个充当控制平面相比之下,文件 通常只使用一个渠道进行控制和数据(除非有额外的工作 请求内存映射)。尽管套接字和文件 收到对 openwrite 的调用时,它们需要解读这些命令 。

在本文档中,我们将重点介绍 文件系统客户端使用:FIDL

FIDL

调用 open("foo") 的程序将调用标准库, 找到了与当前工作目录对应的“fdio”对象, 因此需要向远程服务器发送请求以“please open foo”。如何做到这一点 达成了什么目标?该程序具有以下工具:

  • 一个或多个表示与 CWD 的连接的句柄
  • zx_channel_write: 可以(通过通道)发送字节和句柄的系统调用
  • zx_channel_read: 可以(通过通道)接收字节和句柄的系统调用
  • zx_object_wait_one: 可以等待句柄可读 / 可写的系统调用

借助这些基元,客户端可以向文件系统服务器写入消息 对 CWD 句柄进行处理,服务器可以读取该句柄,然后使用 回复给客户端时显示“成功”或“失败消息”。而服务器 客户需要处理大量工作,以确定实际打开什么,客户可能会或有可能 不要选择等待后再尝试阅读状态信息。

务必要让客户端和服务器就这些准则的解释达成一致 当传输或接收消息时,将使用 N 个字节和 N 个句柄:如果存在 分歧,消息可能会被丢弃(甚至更糟糕的是, 意外行为)。此外,如果该协议允许客户端 可以任意控制服务器,那么这个通信层已经很成熟 进行漏洞利用

FIDL IO 协议 说明了这些字节和句柄的实际含义的传输格式 。该协议描述了 “预期句柄数量”“枚举操作”和“数据”。在本例中, open("foo") 会创建 Open 消息,并设置 FIDL 的“data”字段 向字符串“foo”发送该消息。此外,如果将任何标志传递到 open(例如 O_RDONLY, O_RDWR, O_CREAT 等),这些标记会放置在 FIDL 结构的“arg”字段。但是,如果操作已更改 (例如,设置为 write),则此消息的解释为 。

在这一层,精确的字节协议至关重要, 完全不同的运行时之间产生的差异:理解 FIDL 的进程 在 C、C++、Go、Rust、Dart 程序(及其他程序)之间轻松通信 透明度。

libfidl 包含用于 C/C++ 的客户端和服务器端代码 FIDL 的实现,并负责自动验证输入 两端的输出。

对于 open 操作,FIDL 协议要求 客户端将创建一个通道,并将一端(作为句柄)传递给服务器。一次 交易完成时,此渠道可用作 与打开的文件进行通信,就像之前 与“CWD”句柄进行通信

通过将协议设计为让 FIDL 客户端(而不是服务器)提供句柄, 因此更适合通过管道传输。可以通过以下方式访问 FIDL 对象: 异步;对 FIDL 对象的请求可以在该对象之前传输 打开文件这种行为对于 功能 托管方: 组件 - 组件(默认情况下)延迟启动, 当请求功能时,该操作通过 open 调用完成。而不是 在组件完成启动过程并开始 此模式允许客户端开始发送请求 然后在组件准备就绪后做出响应请参阅 开放协议的生命周期 行为适用于功能路由。

总而言之,“公开”的通话已经经历了标准库, “CWD”fdio 对象,该对象用于将请求转换为 FIDL 消息, 使用 zx_channel_write 系统调用发送到服务器。客户端可以 (可选)使用 zx_object_wait_one 等待服务器响应,或继续 异步处理无论采用哪种方式,都会创建一个频道 一端与客户端相连,另一端传输至 “server”

文件系统:服务器端

调度

一旦消息从通道的客户端传输过, 位于频道的服务器端,等待读取。服务器 由“持有频道另一端标识名的任何人”进行标识: 可能与客户端处于同一(或不同)的进程中,请使用相同(或 不同的运行时,并且使用相同(或不同)编写, 比客户端更方便。通过使用商定的传输格式, 进程间依赖关系在单薄的通信层存在瓶颈, 都发生在频道中

将来,您需要用到 CWD 句柄的服务器端一端, 读取客户端传输的消息。此过程不是自动的,-- 服务器将需要有意地等待 接收句柄,在本示例中是指“当前工作目录” 标识名。当打开服务器对象(文件、目录、服务等)时, 其句柄已在服务器端的 Zircon 端口中注册,该端口会等待 其底层句柄是否可读取(表示邮件已送达)closed(表示用户永远不会再收到邮件)。该对象将 将传入的请求分派给适当的句柄(称为调度程序)。 它负责将传入的消息重定向到回调函数, 以及之前提供的一些表示打开连接的“iostate”。

对于使用 libfs 的 C++ 文件系统,此回调函数称为 vfs_handler,并且会收到一些关键信息:

  • FIDL 消息,由客户端提供(或人为构建) (如果句柄已关闭)
  • 表示当前与句柄的连接的 I/O 状态(作为 “iostate”字段)。

vfs_handler 可以解释 I/O 状态以推断其他信息:

  • 文件内(如果使用 readdir,则位于目录内)的定位指针
  • 用于打开底层资源的标志
  • Vnode:代表底层对象 多个客户端或多个文件描述符)

这个处理程序函数提供了这些信息, “switch/case”表,将 FIDL 消息重定向到相应的函数 具体取决于客户提供的“operation”字段在未结案例中, 您会发现 Open 序号是该操作,因此 (1) 需要一个句柄;以及 (2)“data”字段(“foo”)被解释为路径。

VFS 层

在 Fuchsia 中,“VFS 层”是一个独立于文件系统的代码库, 可分派和解释服务器端消息,并调用 底层文件系统值得注意的是,这一层 可选 - 如果文件系统服务器不想链接到该库, 他们没有义务使用的使用权要成为文件系统服务器,必须有一个进程 仅了解 FIDL 传输格式。因此 一种语言中任意数量的“VFS”实现目前有这些 实现:

  • 树内 C++ VFS:由 Fuchsia 的“main”使用文件系统 minfs 和 blobfs它具有所有 VFS 实现中的最多功能,但也可能是最 难以使用。
  • 树内 Rust VFS:供某些 Rust 文件系统使用,包括 fat32 实现它比 C++ 实施。
  • SDK C++ VFS:树内”功能的简化版本C++ 版本 SDK 用户专用这最常用于较简单的用途,例如服务发现。

VFS 层定义了可以路由到 包括:

  • 对 Vnode 执行读写操作
  • 通过父节点查找/创建/取消关联 Vnode(按名称)
  • 按名称重命名/关联 Vnode
  • 等等

实现文件系统(假设开发者想要使用共享 VFS) 层),只需定义一个用来实现此接口和链接的 Vnode 即可 基于 VFS 层。这将提供“路径步行”等功能 只需极少的工作量即可“装载文件系统”,并且几乎没有重复的代码。在 与文件系统无关,因此 VFS 层没有 文件系统使用的底层存储空间:文件系统可能需要访问权限 块存储设备、网络,或者仅仅是内存来存储数据。 仅处理作用于路径、数据字节数组和 vnode 的接口。

小径徒步

为了打开服务器端资源,需要为服务器提供一个起点 (由调用的句柄表示)和一个字符串路径。此路径分为 每个部分都按“/”字符进行“查找” 回调。如果查询成功返回 vnode 且检测到另一个“/”段,则该过程会继续,直到 (1) lookup 找不到组件;(2) 路径处理到达最后一个 组件,或者 (3) lookup 找到 mountpoint vnode,该 vnode 是 具有附加的“远程”句柄的 vnode。目前,我们将忽略装载点 请参阅关于文件系统的文件系统 装载

假设 lookup 已成功找到“foo” Vnode。文件系统服务器 将继续调用 VFS 接口“Open”,确认所请求的 在调用“GetHandles”之前,可以使用提供的标志访问资源。 询问底层文件系统是否需要额外的句柄 与 Vnode 交互假设客户端要求“foo”对象 同步(默认 POSIX open 调用暗含其中),任何 与“foo”交互所需的额外句柄已打包到一个小型 FIDL 中, 描述对象并将其传递回客户端。或者,如果“foo”有 无法打开,系统仍会返回 FIDL 说明对象,但 “status”字段设置为错误代码,表示失败。我们假设“foo” 已成功打开服务器将继续创建一个“iostate”对象, “foo”,并向调度程序注册它。这样一来,以后对“foo”的调用 由服务器处理“Foo”已打开,客户端现在已准备好发送 额外请求

从客户的角度来看,在“Open”通话开始时,路径和 句柄组合已通过 CWD 句柄传输到远程文件系统 服务器。由于调用是同步的,因此客户端会继续等待 响应。在正确找到、打开并初始化服务器后 此文件的 I/O 状态,它会发回“成功”FIDL 说明对象。这个 对象将读取到对象,从而识别调用已完成 成功。此时,客户端可以创建一个 fdio 对象 表示“foo”的句柄,请通过文件中的一个条目来引用该句柄 描述符表,并将 fd 返回给调用原始“open”的 函数。此外,如果客户端想要发送任何其他请求 (例如“read”或“write”)写入“foo”,即可直接 来连接文件系统服务器,因此系统 因此在以后的请求中,需要通过“CWD”进行路由。

Open 的生命周期:图表

+----------------+
| Client Program |
+----------------+
|   fd: x    |   fd: y    |
| Fdio (FIDL)| Fdio (FIDL)|
+-------------------------+
| '/' Handle | CWD Handle |
+-------------------------+
      ^            ^
      |            |
Zircon Channels, speaking FIDL                   State BEFORE open(‘foo’)
      |            |
      v            v
+-------------------------+
| '/' Handle | CWD Handle |
+-------------------------+
|  I/O State |  I/O State |
+-------------------------+
|   Vnode A  |   Vnode B  |
+-------------------------+
| Filesystem Server |
+-------------------+


+----------------+
| Client Program |
+-------------------------+
|   fd: x    |   fd: y    |
| Fdio (FIDL)| Fdio (FIDL)|
+-------------------------+
| '/' Handle | CWD Handle |   **foo Handle x2**
+-------------------------+
      ^            ^
      |            |
Zircon Channels, speaking FIDL                   Client Creates Channel
      |            |
      v            v
+-------------------------+
| '/' Handle | CWD Handle |
+-------------------------+
|  I/O State |  I/O State |
+-------------------------+
|   Vnode A  |   Vnode B  |
+-------------------------+
| Filesystem Server |
+-------------------+


+----------------+
| Client Program |
+-------------------------+
|   fd: x    |   fd: y    |
| Fdio (FIDL)| Fdio (FIDL)|
+-------------------------+--------------+
| '/' Handle | CWD Handle | ‘foo’ Handle |
+-------------------------+--------------+
      ^            ^
      |            |
Zircon Channels, speaking FIDL                  Client Sends FIDL message to Server
      |            |                            Message includes a ‘foo’ handle
      v            v                            (and waits for response)
+-------------------------+
| '/' Handle | CWD Handle |
+-------------------------+
|  I/O State |  I/O State |
+-------------------------+
|   Vnode A  |   Vnode B  |
+-------------------------+
| Filesystem Server |
+-------------------+


+----------------+
| Client Program |
+-------------------------+
|   fd: x    |   fd: y    |
| Fdio (FIDL)| Fdio (FIDL)|
+-------------------------+--------------+
| '/' Handle | CWD Handle | ‘foo’ Handle |
+-------------------------+--------------+
      ^            ^
      |            |
Zircon Channels, speaking FIDL                  Server dispatches message to I/O State,
      |            |                            Interprets as ‘open’
      v            v                            Finds or Creates ‘foo’
+-------------------------+
| '/' Handle | CWD Handle |
+-------------------------+
|  I/O State |  I/O State |
+-------------------------+-------------+
|   Vnode A  |   Vnode B  |   Vnode C   |
+------------------------------+--------+
| Filesystem Server |
+-------------------+


+----------------+
| Client Program |
+-------------------------+
|   fd: x    |   fd: y    |
| Fdio (FIDL)| Fdio (FIDL)|
+-------------------------+--------------+
| '/' Handle | CWD Handle | ‘foo’ Handle |
+-------------------------+--------------+
      ^            ^          ^
      |            |          |
Zircon Channels, FIDL         |                   Server allocates I/O state for Vnode
      |            |          |                   Responds to client-provided handle
      v            v          v
+-------------------------+--------------+
| '/' Handle | CWD Handle | ‘foo’ Handle |
+-------------------------+--------------+
|  I/O State |  I/O State |  I/O State   |
+-------------------------+--------------+
|   Vnode A  |   Vnode B  |    Vnode C   |
+------------------------------+---------+
| Filesystem Server |
+-------------------+


+----------------+
| Client Program |
+-----------------------------+----------+
|   fd: x    |   fd: y    |    fd: z     |
| Fdio (FIDL)| Fdio (FIDL)|  Fdio (FIDL) |
+-------------------------+--------------+
| '/' Handle | CWD Handle | ‘foo’ Handle |
+-------------------------+--------------+
      ^            ^           ^
      |            |           |
Zircon Channels, speaking FIDL |                  Client recognizes that ‘foo’ was opened
      |            |           |                  Allocated Fdio + fd, ‘open’ succeeds.
      v            v           v
+-------------------------+--------------+
| '/' Handle | CWD Handle | ‘foo’ Handle |
+-------------------------+--------------+
|  I/O State |  I/O State |  I/O State   |
+-------------------------+--------------+
|   Vnode A  |   Vnode B  |    Vnode C   |
+------------------------------+---------+
| Filesystem Server |
+-------------------+