| RFC-0222:引入 Fuchsia 控制器 | |
|---|---|
| 状态 | 已接受 |
| 领域 |
|
| 说明 | 为 Fuchsia 设备的宿主机端脚本编写和测试添加 Fuchsia 控制器 |
| 问题 |
|
| Gerrit 更改 | |
| 作者 | |
| 审核人 | |
| 提交日期(年-月-日) | 2022-11-17 |
| 审核日期(年-月-日) | 2023-06-13 |
摘要
FFX它使用 Fuchsia 接口定义语言 (FIDL) 调用各种服务进行 开发和测试。为了向该工具添加更多功能,FFX 支持子工具,这些子工具是用于扩展 FFX 的命令行界面和功能的单独二进制文件。该工具完全使用 Rust 编写。
如果用户希望访问 Fuchsia 设备上的 FIDL 协议,他们可以 为 FFX 编写插件、使用 Fuchsia 脚本编写层、 SL4F,或者提交在系统上编写组件。
这些选项都不适合轻量级实验、快速迭代或灵活性。FFX 插件需要在 Rust 中编写工具并进行编译。SL4F 不仅不受支持,还依赖于不安全的传输,并且与它进行交互需要为计划调用的每个 FIDL 接口编写一个 Rust 外观。
这些方法都不允许用户遵循“自带运行时”原则。
此 RFC 提出了一种通过 FFX 中的现有库访问设备的额外方法,称为 Fuchsia 控制器。它包含以下内容:
- 稳定的 ABI,用于通过 FIDL 句柄模拟与 Fuchsia 设备进行交互,而无需更改 FFX 本身。
- 至少对一种利用上述稳定 ABI 的常用脚本语言提供一流支持(当前选择为 Python)。
- 上述脚本语言中的更高级别库,针对编写关键测试用例进行了优化。
设计初衷
目前没有受支持的机制用于编写从宿主机设备到 Fuchsia 设备的交互脚本(通过 SDK、树内或其他方式)。
如果用户希望使用 FFX 与 Fuchsia 设备进行交互,则必须提供要编译到该工具中的服务定义。如果服务是在树外定义的,用户将无法访问该服务。如果服务已定义,则用户必须编写 FFX 插件并进行编译。
Fuchsia 控制器将允许用户以快速、高度迭代且易于自动化的方式与 Fuchsia 设备进行交互。支持稳定的 ABI 将允许用户自带运行时(如果他们选择实现该运行时),因为支持使用 C ABI 来扩展几乎所有常用编程语言。
利益相关方
辅导员:
待定
审核人:
jeremymanson@google.com(工具)mgnb@google.com(FFX)ianloic@google.com(FIDL)chok@google.com(lacewing)jzgriffin@google.com(SL4F)
咨询对象:
EngProd 团队 FFX 团队 FIDL 团队
社交化:
Fuchsia 控制器是工具、FFX、FIDL 和 EngProd 团队经过数月对话的成果。其他团队包括组件框架团队和测试架构团队。作者还使用实现部分中概述的 概念验证和高级演示编写并演示了 技术。
设计
概览
Fuchsia 控制器由三部分组成:
- FFX 客户端库。
- 特定于语言的扩展库。
- 使用扩展库的更高级别的一流库。
这些库都与 FFX 协同工作,以处理与 Fuchsia 设备的连接。

在继续之前,需要介绍一些必要事项。
FFX 的背景
FFX 是一种宿主机端工具,(在撰写本文时)它使用“守护程序”,在这种情况下,守护程序是其自身在后台运行的分支副本,当在网络上发现 Fuchsia 设备时,它会通过 SSH 主动连接到这些设备。发现设备的方法有很多,但这些方法不在本文档的讨论范围内。
FFX 守护程序允许 FFX 工具的用户在宿主机和 Fuchsia 设备之间打开模拟的 FIDL 信道。消息从 FFX 客户端调用路由,通过守护程序,然后路由到 Fuchsia 设备。
FFX 几乎完全使用 Rust 编写。除此之外,用于写入和读取宿主机端模拟 FIDL 渠道的库也使用 Rust 编写。
总而言之,FFX 允许用户通过 FIDL 与 Fuchsia 设备进行交互,使用与 FFX 实现紧密耦合的 Rust 编写的插件。
术语定义
以下部分的一些定义:
- “隔离”是指以某种方式使用 FFX,使其可能拥有一个单独的 FFX 守护进程实例,该实例自行运行,而不使用通用套接字位置或任何通用 FFX 配置值。FFX 守护进程的实例不会与 Fuchsia 设备通信隔离(除非明确调用以执行此操作)。
- “守护进程协议”是指 FFX 守护进程处理的宿主机端 FIDL 协议。这些协议各不相同,从软件包服务到检查网络上的 Fuchsia 设备,再到 Fuchsia 设备端口转发。守护程序本身可以被视为类似于公开其中几个守护程序协议的组件。
- 当提及渠道、句柄和套接字的“zircon”时,这些都是通过 Overnet 在宿主机端模拟的。不过,将来这些可能会在 Fuchsia 设备上用作实际的 zircon 对象。
FFX 客户端库
FFX 客户端库通过 C ABI 公开函数,以支持以下操作:
- 创建、读取、写入和关闭 zircon 渠道。
- 创建、读取、写入和关闭 zircon 套接字。
- 发出 zircon 句柄信号(主要用于事件对)。
- 将 zircon 渠道连接到 Fuchsia 设备上的组件。
- 将 zircon 信道连接到 FFX 守护程序协议。
此外,还将支持其他功能,例如:
- 创建 FFX 守护进程的隔离实例。
- 声明 FFX 配置键值对。
特定于语言的扩展
这涉及 FFX 客户端库之上的简单抽象,以共享库的形式实现,然后将在第一类脚本库中使用。
实现
如前所述,这涉及创建两个共享库。
- 与语言无关的“FFX 客户端”库,它将公开与 FFX 以及 zircon 句柄通信的函数。
- 语言绑定(在本例中为 Python)共享库。
此外,还将提供 Python 绑定,以实现符合人体工程学的 FIDL 用法。
实现不涉及 Fuchsia 源代码树中尚未包含的任何第三方依赖项。我们预计可以使用 FFX 客户端库实现对其他语言的支持,我们可能会支持这些语言(并且可能需要额外的第三方依赖项工作),但这超出了此 RFC 的范围。
将 Python 与 SDK 一起分发是一个单独的主题,超出了本文档的范围,并且可能会封装在自己的 RFC 中。
FFX 客户端库
本部分中的 C 定义旨在放置在将随 Fuchsia SDK 提供的头文件中。由于 C 中缺少命名空间,这些函数名称将相当冗长。函数名称也不一定旨在成为最终名称。
ABI 的模式旨在使大多数函数使用输出形参为调用方返回结构,返回值可以是 void 或错误。
如果可能,将使用 zx_status_t 和 zx_handle_t 等已建立的类型,以便与 Zircon 代码库中现有的 C 库保持一致。
用于读取渠道的示例函数可能如下所示(这并非旨在成为最终实现或文档来源):
extern zx_status_t ffx_channel_read(void* ctx,
zx_handle_t handle,
void* buf,
uint32_t buf_len,
zx_handle_t* handles,
uint32_t handles_len
uint32_t* actual_bytes_count,
uint32_t* actual_handles_count);
ctx 值可以指向一个 Rust 对象,该对象包含有关现有 FFX 环境的信息(运行目录、SDK 的位置、配置值以及我们与之通信的 Fuchsia 设备)。
这类似于现有的 zx_channel_read,只是 省略了 option 参数。ABI 不会导出与 Zircon 系统调用相同的符号,而是会显式写入头文件中,以便清楚地了解支持和不支持哪些功能(因为此代码最初可能仅针对宿主机编写,而不是针对 Fuchsia 设备编写,因此可能会有特殊注意事项)。
以下函数展示了如何获取句柄,该函数可用于通过使用 遥控器 FIDL 定义连接到 Fuchsia 设备上的几乎任何组件。
extern zx_status_t ffx_connect_proxy(void* ctx, const char* endpoint_path,
zx_handle_t* out);
这将向相关协议返回 FIDL 渠道。
endpoint_path 应包含必要的信息,以便通过组件框架直接连接到
设备上的特定 FIDL 协议(在撰写本文时,其格式为 "$moniker:expose:$protocol")。
通过支持套接字/渠道读取和写入以及事件信号,可以复制 FFX 和 Overnet 目前支持的所有 FIDL 功能,但使用 Python。
Python 语言绑定
Python 语言绑定将定义使用已建立的 create*/close*/destroy* 调用(使用 C++ 编写并提供 FFX 客户端库的精简封装容器)的对象。它还将使用 ffx_error_str() 导出错误类型。
您可以在此处找到此(略有不同)实现的示例 Fuchsia 控制器原型 here。
更高级别的绑定
对于 FIDL 协议的实际使用,将提供更高级别的库。
它将使用 importlib 挂钩到 FIDL 中间表示 (IR),导入分布式 .pyz(可在开发者的 PYTHONPATH 中找到)或手动提供的目录中提供的任何 IR。这些挂钩将创建在运行时与 FIDL 交互的类,类似于 C++ 或 Rust 执行编译时绑定时发生的情况。
示例可能如下所示:
import fidl.fuchsia_developer_ffx
from fuchsia_controller_py import Context
def main(ctx: Context):
h = ctx.connect_daemon_protocol(fidl.fuchsia_developer_ffx.Echo.Marker)
proxy = fidl.fuchsia_developer_ffx.Echo.Client(h)
print(proxy.echo_string(value = 'foobar'))
if __name__ == '__main__':
ctx = Context(None)
main(ctx)
从 fidl. 导入的内容将被库挂钩捕获,以在现有 FIDL IR 命名空间中搜索与 fuchsia.developer.ffx 匹配的内容,并根据其中定义的结构和协议生成类。
FIDL 线格式编码/解码
对于第一次传递,实际的编码和解码可能会使用 fidl_codec 库完成,然后稍后可以在 Python 中完全实现。
fidl_codec 包含用于根据 FIDL IR 定义读取/写入 FIDL 线格式的代码
,以及实用程序(例如可用于从
FIDL 消息构造对象的值访问器)。它在 fidlcat 工具中使用。可以使用访问器模式将 FIDL 结构体从 IR 生成的 Python 编码为线格式,从而创建一个缓冲区,然后可以将其写入 FIDL 句柄。
性能
FFX 客户端库 ABI
在跨越 FFX 客户端 ABI 边界时,即 Unix 域套接字读取/写入以及与 Fuchsia 设备之间的网络连接时,会遇到大部分性能开销。
除此之外,FFX 客户端库将完全复制 FIDL 读取两次,一次用于跨越 Rust ABI 边界,另一次用于将数据从 C++ 代码加载到 Python 对象中。如果将来出现问题,可以解决此问题。
平均用例将涉及较低开销的 FIDL 用法,即使用结构调用简单函数。在撰写本文时,宿主机上不支持更复杂的 FIDL(例如 VMO),因此不应成为性能方面的问题。
工效学设计
FFX 客户端 ABI
FFX 客户端二进制文件以尽可能小的配置文件实现,以防止在其他语言中实现绑定时出现过多的样板代码。
更高级别的 Python 绑定
更高级别的 Python 绑定将使用 FIDL IR 在运行时创建类绑定。这样不仅可以使用 Python 代码,使其行为方式与其他 FIDL 语言绑定(例如 Rust、Dart 或 C++)的行为方式一致,还可以在运行时试验 FIDL 协议,从而实现更具迭代性的开发。
如果 FIDL 服务是在树外定义的,这样还可以使用 SDK 访问该服务(首先生成 FIDL IR,然后将其导入 Fuchsia 控制器)。
安全注意事项
此 RFC 中的主要安全影响与 FFX 本身有关,这超出了本文档的讨论范围。
测试
FIDL 线格式
FIDL 团队有一套测试(GIDL 和 DynSuite),可以针对这些测试对编码和解码进行测试。这适用于以下情况:使用 fidl_codec(因为尚未针对这些套件进行测试),或者 Python 显式写入/读取线格式。
FFX 客户端库和 Python C 绑定
这些绑定足够轻量级,除了验证是否满足某些异常条件之外,无需进行太多单元测试。为了测试 FFX 客户端库的 ffx_error_str(),可以使用明确定义的错误,以防止依赖于 FFX 本身中的特定字符串。
这些代码段的大部分测试将采用集成测试和/或端到端测试。一个简单的示例(用于练习每个代码段)是使用 Echo 协议进行与非 Fuchsia 设备相关的测试。对于针对实际 Fuchsia 设备的测试,可以获取遥控器服务代理的实例并调用 IdentifyHost 函数。
文档
Fuchsia 控制器的文档将保留在 fuchsia.dev 网站上。
缺点、替代方案和未知事项
缺点
一流的 Python 支持
虽然 FFX 客户端库旨在支持其他语言的绑定,但 Python 不适合较大的生产项目。可以说,Python 允许快速编写代码并快速实现功能,但代价是维护和可读性较差。
替代方案
为什么不使用 Golang、Java、Rust 或 Dart?
在与端到端测试团队、面向产品的开发者和连接团队的初步讨论中,我们强烈倾向于使用提供互动性和易用性的脚本语言。这表明 Python 是第一个具有绑定的语言。
未知事项
FIDL IR
使用 SDK 将 FIDL IR 树外提供给用户很难正确实现,因为它必须生成。因此,可能需要为 Bazel 添加额外的 build 规则,以简化依赖项。
SDK 中的 FFX
上述使用 FFX 守护进程协议的示例在树外将无法正常运行,因为 FFX FIDL 协议尚未在 SDK 中实现。API 审核已部分完成,但仍需完成。很难估计这可能需要多长时间,因为 FFX 仍有广泛的 FIDL 界面需要涵盖,并且通常也是一个不断变化的目标。
FIDL 编解码器
在最终结果中可能根本不需要使用 fidl_codec。
使用它还需要向 Python 共享库添加更多界面,然后需要仔细逐步淘汰。
考虑的替代方案包括:
- 生成的代码(例如,在 C++ 和 Rust 中就是这样处理的)。
- 在 Python 中实现的
fidl_codec类似物。