本文档详细介绍了在 Fuchsia 上访问文件系统的全过程, 深入探究执行某些操作时所使用的每个层的详细信息, 就像打开文件一样简单需要注意的是,所有这些层 userspace;即使与文件系统服务器和驱动程序交互,内核 仅用于在组件之间传递消息。
可调用:
open(“foobar”);
这项要求会转到何处?
标准库:其中的“open”已定义
“open”调用是由标准库提供的函数。对于
C/C++ 程序,这通常在 unistd.h
中进行声明,后者具有
libfdio 中的支持定义。
对于 Go 程序,在 Go 中有一个等效(但不同)的实现
标准库。对于每种语言和运行时,开发者可以选择使用
自己对“开放”的定义
在单体式内核中,open
是系统周围的轻量级 shim
调用中,内核可能会处理路径解析、重定向等。在该调用中,
则内核将需要根据外部表层来调解对资源的访问
。但是,Zircon 内核有意
此类系统调用。相反,客户端通过“通道”访问文件系统,--
初始化进程时,系统会为其提供一个命名空间,
这是一个“绝对路径”表格->“handle”映射。已访问所有路径
来打开该命名空间
将请求定向到此命名空间
映射。
不过,在此示例中,包含打开“foobar”的请求, 因此来电可以通过代表 当前工作目录(其本身表示为绝对路径) 和标识名)。
标准库负责接受一个(或多个)句柄 并使其看起来像文件描述符。因此,“文件 描述符表”这一概念存在于客户端进程中(如果客户端 选择使用自定义运行时,他们可以完全以 句柄 --“文件描述符”封装是可选的)。
然而,这样就提出了一个问题:给定一个针对文件、套接字和 那么标准库是怎么做才能使所有这些资源 在功能上是否相同?客户端如何确定要发送哪些消息 这些标识名?
Fdio
名为 fdio 的库
负责提供一个统一界面以访问各种资源
包括文件、套接字、服务、管道等。该图层定义了一组
读取、写入、打开、关闭、跳转等
由各种协议支持的文件描述符每种支持的协议
负责提供客户端代码来解释
互动例如,套接字为客户端提供了多个句柄;一
另一个充当控制平面相比之下,文件
通常只使用一个渠道进行控制和数据(除非有额外的工作
请求内存映射)。尽管套接字和文件
收到对 open
或 write
的调用时,它们需要解读这些命令
。
在本文档中,我们将重点介绍 文件系统客户端使用: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 |
+-------------------+