如何构建针对某种语言的 Fuchsia 支持

本文档介绍了支持紫红色时通常会使用的结构语言。

系统调用

每种语言中最低级别的 Fuchsia 支持提供对 Zircon 系统调用的访问权限。公开这些系统调用可让使用相应语言编写的程序与内核以及以传递方式与系统的其余部分进行交互。

程序无法直接发出系统调用。而是通过调用 vDSO 中的函数进行系统调用,vDSO 由其创建者加载到新创建的进程中。

vDSO 的公共入口点在 //zircon/vdso 中定义。 此文件由 zither 工具处理。

异步

绝大多数 Fuchsia 程序都充当服务器。启动后,它们会在事件循环中等待接收消息,处理这些消息(可能是通过向其他进程发送消息),然后在事件循环中返回休眠状态。

Fuchsia 中事件循环的基本构建块是 port 对象。线程可以使用 zx_port_wait 在端口中休眠。当内核唤醒线程时,内核会提供一个数据包,它是一种数据结构,用于说明内核唤醒线程的原因。

通常,每个线程都有一个休眠的端口对象,使用该语言编写的大量代码需要与之交互。语言维护人员通常不是直接公开端口,而是提供一个通过端口进行抽象化的库,并提供异步等待操作。

大多数异步等待操作会在 zx_object_wait_async 中底部出现。通常,portkey 参数由库提供,handlesignals 参数由客户端提供。建立等待时,客户端通常还会提供一个在等待完成时供库调用的向上调用(例如,一个闭包),此时库会使用 key 恢复向上调用(例如,从哈希表中恢复)。

无需额外的内核对象即可从其他线程唤醒线程。 您只需使用 zx_port_queue 将用户数据包加入线程端口队列,即可唤醒线程。

示例

FIDL

Zircon 内核本身主要提供内存管理、调度和进程间通信。实际上,大部分系统接口不是由内核提供,而是通过进程间通信(通常使用通道)提供。用于进程间通信的协议在 Fuchsia 接口定义语言 (FIDL) 中定义。

对某种语言的 FIDL 支持通常涉及两个部分:

  1. FIDL 编译器的特定语言的后端,用于以目标语言生成代码。
  2. 以目标语言编写的支持库,由 FIDL 编译器生成的代码使用。

这些代码段通常不会内置到语言实现或运行时中。相反,这些库是开发者程序的一部分,并独立于语言运行时进行版本控制。程序和语言运行时之间的稳定接口应该是系统调用而不是 FIDL 协议,以便开发者能够独立选择其 FIDL 协议的版本和语言运行时的版本。

在某些情况下,语言运行时可能需要在内部使用 FIDL。因此,建议您尽可能以您的语言向开发者的程序隐藏此实现细节。开发者可能希望使用相同 FIDL 协议的较新版本,而不与语言运行时在内部使用的版本冲突。

FIDL 编译器后端

FIDL 编译器包含一个可用于所有语言的前端,以及多个支持各种语言的后端。前端会生成一种 JSON 中间格式,供特定语言的后端使用。

您应为所用语言的 FIDL 编译器创建一个新的后端。您可以使用自己偏好的任何语言编写后端。通常,语言维护者会选择 Go 或目标语言。官方后端的名称为 fidlgen_<lang>,可以在 //tools/fidl 中找到。

生成的代码

所生成的 FIDL 代码因语言而异。通常,生成的代码将包含以下类型的代码:

  • 数据结构定义,表示使用 FIDL 语言定义的数据结构。
  • 可将这些数据结构序列化和反序列化为 FIDL 有线格式的编解码器。
  • 表示 FIDL 协议服务器端的存根对象。通常,桩对象具有 dispatch 方法,该方法会对从 Zircon 通道读取的消息进行反序列化,并间接跳转到由消息序数指定的方法的实现。
  • 代表 FIDL 协议客户端端的代理对象。通常,对代理对象进行方法调用会导致消息序列化并通过 Zircon 通道发送。通常,代理对象有一个针对事件消息的 dispatch,类似于在请求消息桩中找到的调度方法。

某些语言为其中一些类型的生成代码提供了多个选项。例如,一种常见的模式是同时提供同步异步代理对象。同步代理利用 zx_channel_call 高效地写入消息、阻止等待响应,然后读取响应;而异步代理则使用 zx_channel_writezx_object_wait_asynczx_channel_read 来避免在通道的远程端发生阻塞。

通常,我们会尽可能使用异步代码。许多 FIDL 协议都可以在异步前馈模式中使用。

支持库

在为您的语言设计生成的代码时,请特别注意二进制文件的大小。复杂程序通常会与大量 FIDL 协议进行交互,其中每个协议可能会定义许多数据结构和协议。

缩减二进制文件大小的一项重要方法是将尽可能多的代码分解到 FIDL 支持库中。例如,对于 C 绑定,所有序列化和反序列化逻辑都由支持库中的例程执行。生成代码仅包含一个以紧凑形式描述传输格式的表。

通常情况下,支持库会叠加在异步库之上,而异步库本身并不知道 FIDL。例如,大多数支持库都包含一个 reader 对象,用于管理通道的异步等待和读取操作。然后,生成的代码可以仅限于序列化、反序列化和调度。

POSIX 式 IO

POSIX 样式的 IO 操作(例如,openclosereadwrite)位于 FIDL 之上。如果您的语言具有 C 互操作性,您可以使用 FDIO 库,该库可将熟悉的 POSIX 操作转换为底层 fuchsia.io FIDL 协议。如果您的语言没有 C 互操作性,则需要直接与 fuchsia.io 连接以提供 POSIX 式 IO。

您可以使用 lib/fdio/unsafe.h 恢复文件描述符的底层 Zircon 句柄。通常,语言有一个微小的库,该库叠加在异步库之上,对文件描述符执行异步等待。该库通常提供一个不容易出错的接口,可以将这些“不安全”的 FDIO 函数抽象化。