本文档介绍了支持 Fuchsia 时通常使用的结构语言。
系统调用
Fuchsia 在语言中提供的最低级别支持可提供对 Zircon 系统调用的访问权限。通过公开这些系统调用,用该语言编写的程序可以与内核交互,并间接地与系统的其余部分交互。
程序无法直接发出系统调用。而是通过调用 vDSO 中的函数来进行系统调用,这些函数由其创建者加载到新创建的进程中。
vDSO 的公共入口点在 //zircon/vdso 中定义。此文件由 zither 工具处理。
异步
绝大多数 Fuchsia 程序都充当服务器。启动后,它们会在事件循环中等待接收消息,处理这些消息(可能会向其他进程发送消息),然后返回到事件循环中休眠。
Fuchsia 中事件循环的基本构建块是端口对象。线程可以使用 zx_port_wait 在端口中休眠。当内核唤醒线程时,内核会提供一个数据包,该数据包是一种描述内核唤醒线程的原因的数据结构。
通常,每个线程都有一个供其休眠的端口对象,用您的语言编写的大量代码都需要与该对象进行交互。语言维护者通常会提供一个库,该库对端口进行抽象化处理并提供异步等待操作,而不是直接公开端口。
大多数异步等待操作都会在 zx_object_wait_async 中触底。通常,port 和 key 参数由库提供,handle 和 signals 参数由客户端提供。在建立等待时,客户端通常还会提供一个上行调用(例如闭包),供库在等待完成时调用,此时库会使用 key 来恢复上行调用(例如,从哈希表中恢复)。
无需额外的内核对象即可从另一个线程唤醒一个线程。您只需使用 zx_port_queue 将用户数据包加入线程端口的队列,即可唤醒线程。
示例
- async(C 和 C++)
- fuchsia-async (Rust)
FIDL
Zircon 内核本身主要提供内存管理、调度和进程间通信。大部分系统接口实际上是通过进程间通信(通常使用通道)提供的,而不是由内核直接提供。用于进程间通信的协议在 Fuchsia 接口定义语言 (FIDL) 中定义。
对某种语言的 FIDL 支持通常涉及两个部分:
- FIDL 编译器的特定语言后端,用于生成目标语言的代码。
- 由 FIDL 编译器生成的代码使用的目标语言编写的支持库。
这些部分通常不内置于语言实现或运行时中。 相反,这些库是开发者程序的一部分,并且独立于语言运行时进行版本控制。程序和语言运行时之间的稳定接口应该是系统调用,而不是 FIDL 协议,以便开发者可以独立选择其 FIDL 协议的版本和语言运行时的版本。
在某些情况下,语言运行时可能需要在内部使用 FIDL。如果发生这种情况,最好在开发者的程序中隐藏此实现细节(如果您的语言支持这样做)。开发者可能希望使用相同 FIDL 协议的较新版本,而不会与语言运行时内部使用的版本发生冲突。
FIDL 编译器后端
FIDL 编译器有一个适用于所有语言的前端,以及多个支持各种语言的后端。前端会生成 JSON 中间格式,供特定于语言的后端使用。
您应该为您的语言创建 FIDL 编译器的新后端。后端可以使用您喜欢的任何语言编写。通常,语言维护者会选择 Go 或目标语言。官方后端名为 fidlgen_<lang>,可在 //tools/fidl 中找到。
生成的代码
生成的 FIDL 代码因语言而异。 生成的代码通常包含以下类型的代码:
- 表示在 FIDL 语言中定义的数据结构的数据结构定义。
- 一个编解码器,可将这些数据结构序列化为 FIDL 传输格式,以及将这些数据结构从 FIDL 传输格式反序列化。
- 表示 FIDL 协议服务器端的桩对象。通常,桩对象具有 dispatch 方法,该方法可反序列化从 Zircon 通道读取的消息,并间接跳转到消息的序数指定的方法的实现。
- 表示 FIDL 协议客户端端的代理对象。通常,对代理对象的方法调用会导致消息被序列化并通过 Zircon 通道发送。通常,代理对象具有用于事件消息的 dispatch,类似于用于请求消息的桩中的调度方法。
对于其中某些类型的生成代码,某些语言会提供多个选项。
例如,一种常见模式是同时提供同步和异步代理对象。同步代理使用 zx_channel_call 高效地写入消息、阻塞等待响应,然后读取响应,而异步代理使用 zx_channel_write、zx_object_wait_async 和 zx_channel_read 来避免在通道的远程端阻塞。
通常,我们更倾向于尽可能使用异步代码。许多 FIDL 协议都设计为以异步前馈模式使用。
支持库
在为您的语言设计生成的代码时,请特别注意二进制文件大小。复杂的程序通常会与大量 FIDL 协议交互,每个协议都可能定义许多数据结构和协议。
减小二进制文件大小的一项重要技术是将尽可能多的代码重构到 FIDL 支持库中。例如,C 绑定、所有序列化和反序列化逻辑均由支持库中的例程执行。生成的代码仅包含一个以紧凑形式描述传输格式的表。
通常,支持库位于异步库之上,而异步库本身不了解 FIDL。例如,大多数支持库都包含一个 reader 对象,用于管理通道上的异步等待和读取操作。然后,生成的代码可以限制为序列化、反序列化和分派。
POSIX 样式 IO
POSIX 样式的 IO 操作(例如 open、close、read 和 write)在 FIDL 之上分层。如果您的语言具有 C 互操作,则可以使用 FDIO 库,该库会将熟悉的 POSIX 操作转换为底层 fuchsia.io FIDL 协议。如果您的语言没有 C 互操作,则需要直接与 fuchsia.io 交互以提供 POSIX 样式的 IO。
您可以使用 lib/fdio/unsafe.h 恢复文件描述符的底层 Zircon 句柄。通常,语言有一个位于异步库之上的微型库,用于对文件描述符执行异步等待。此库通常提供一个不易出错的接口,用于抽象化这些“不安全”的 FDIO 函数。