Fuchsia 接口定义语言

Fuchsia 接口定义语言 (FIDL) 是用于描述 Fuchsia 程序使用的进程间通信 (IPC) 协议的语言。FIDL 提供了简化的声明语法,供提供程序将接口定义为协议它们可以整理成更复杂的数组、向量、结构体、表和联合。

请考虑以下适用于 Echo 接口的 FIDL 协议示例:

library fuchsia.examples;

const MAX_STRING_LENGTH uint64 = 32;

@discoverable
closed protocol Echo {
    strict EchoString(struct {
        value string:MAX_STRING_LENGTH;
    }) -> (struct {
        response string:MAX_STRING_LENGTH;
    });
    strict SendString(struct {
        value string:MAX_STRING_LENGTH;
    });
    strict -> OnString(struct {
        response string:MAX_STRING_LENGTH;
    });
};

FIDL 协议描述了一组通过通道发送消息调用的方法。通道消息本质上是异步的,因此发送者和接收者彼此独立操作。FIDL 方法引入了更高级别的语义,支持在 FIDL 事务的客户端和服务器端进行更惯用的编程。

FIDL 支持以下方法类型:

  • 双向方法:一种典型的方法调用,它接受可选参数,且返回类型在 -> 运算符后面。双向方法会阻塞,直到收到响应。在 Echo 示例中,EchoString() 方法是双向方法。
  • 单向方法:立即返回而不等待响应的异步方法调用。未声明返回类型的方法会被视为从客户端单向调用的方法。在 Echo 示例中,SendString() 方法是单向方法。
  • 事件:必要时,服务器可能会向客户端发送未经请求的消息(称为“事件”)。事件会在 -> 运算符的返回端声明其方法名称。在 Echo 示例中,OnString() 方法是一个事件。

创建 FIDL 库

FIDL 库可将 FIDL 源文件分组在一起。库充当其包含的协议的命名空间,FIDL 源文件可以隐式访问同一库中的所有其他声明。FIDL 源文件必须从其他库 import 所有声明。

Fuchsia 构建系统提供 fidl() 构建目标,用于将 FIDL 源文件编译到库中。库目标的名称必须与每个源文件中的 library 声明一致。请参阅以下 fuchsia.examples 库的 BUILD.gn 示例:

# Import the fidl GN template.
import("//build/fidl/fidl.gni")

# Define a FIDL library target.
fidl("fuchsia.examples") {
  # FIDL source files contained in library
  sources = [
    "echo.fidl",
  ]
}

在构建时,FIDL 编译器 (fidlc) 前端工具会验证库源文件并将其编译成 JSON 中间表示 (IR)。此 JSON IR 格式是 FIDL 绑定的基础。

生成 FIDL 绑定

组件会通过名为 FIDL 绑定的生成代码使用 FIDL 协议。绑定会将请求和响应编码和解码为 FIDL 消息,并通过底层 IPC 通道传输它们。针对特定语言的绑定库提供了这些结构的封装容器,以便与熟悉的编程习惯用法保持一致。

客户端接口(有时称为代理)会在更高级别的函数调用与 FIDL 消息之间进行转换。在服务器端,绑定会处理传入的请求消息,并通过供组件实现的抽象接口传递这些消息。

此图展示了 FIDL 绑定如何提供生成的库代码,以将函数调用转换为 FIDL 消息,以便跨进程边界进行传输。

在构建时,fidlgen 后端工具会从 fidlc 生成的 JSON IR 库中为受支持的编程语言生成绑定。例如,fidlgen_rust 会从 JSON IR 生成 Rust 绑定。

fidl() 库目标会为每种受支持的语言创建单独的绑定目标。由于 GN 的性质,除非这些绑定作为依赖项包含在内,否则不会在构建时生成。

请参阅以下示例 BUILD.gn 代码段,其中包含为 fuchsia.examples 库生成的绑定目标:

Rust

deps = [
  "fidl/fuchsia.examples:fuchsia.examples_rust",
  ...
]

C++

deps = [
  "fidl/fuchsia.examples:fuchsia.examples",
  ...
]

练习:Echo FIDL 库

在本部分中,您将使用名为 Echo 的协议定义一个新的 FIDL 库,该协议包含一个可将字符串值返回给调用方的方法。

首先为 FIDL 库目标创建一个新目录:

mkdir -p vendor/fuchsia-codelab/echo-fidl

在新项目目录中创建以下文件和目录结构:

//vendor/fuchsia-codelab/echo-fidl
                        |- BUILD.gn
                        |- echo.fidl

添加一个名为 echo.fidl 的新 FIDL 接口文件,其中包含以下内容:

library fidl.examples.routing.echo;

const MAX_STRING_LENGTH uint64 = 64;

@discoverable
closed protocol Echo {
    /// Returns the input.
    strict EchoString(struct {
        value string:<MAX_STRING_LENGTH, optional>;
    }) -> (struct {
        response string:<MAX_STRING_LENGTH, optional>;
    });
};

EchoString 是一种双向方法,它接受可选的(可为 null)字符串值并返回相同的值。

添加包含以下内容的 BUILD.gn 文件,以声明库目标:

import("//build/fidl/fidl.gni")
fidl("echo") {
  name = "fidl.examples.routing.echo"

  sources = [ "echo.fidl" ]

  enable_hlcpp = true
}

将库目标添加到 build 配置:

Rust

fx set workstation_eng.x64 --with vendor/fuchsia-codelab/echo-fidl:echo_rust

C++

fx set workstation_eng.x64 --with vendor/fuchsia-codelab/echo-fidl:echo_hlcpp

检查 FIDL 绑定

fidl() GN 目标会编译 FIDL 接口并生成其他构建目标,以提供各种语言的绑定。如需检查绑定,您必须编译各个目标。

编译 fidl.examples.routing.echo 绑定:

Rust

fx build vendor/fuchsia-codelab/echo-fidl:echo_rust

C++

fx build vendor/fuchsia-codelab/echo-fidl:echo_hlcpp

使用 GN 定位系统生成的目标源文件,并在编辑器中打开这些文件:

Rust

fx gn desc out/default/ vendor/fuchsia-codelab/echo-fidl:echo_rust.actual sources

C++

fx gn desc out/default/ vendor/fuchsia-codelab/echo-fidl:echo_hlcpp sources

探索这些文件的内容。下面总结了生成的一些关键接口:

Rust

接口 说明
EchoMarker 用于打开代理并请求给定协议的流。
EchoProxy 异步客户端,可将协议方法转换为通过 IPC 通道发送的 FIDL 请求消息。
EchoSynchronousProxy 同步客户端,可将协议方法转换为通过 IPC 通道发送的 FIDL 请求消息。
EchoRequest 用于处理每种协议方法的传入请求的结构化类型。
EchoRequestStream 用于通过 IPC 通道处理传入 FIDL 请求消息的流。
EchoEchoStringResponder 用于以 FIDL 响应消息的形式将每个代理请求的返回值发送的回调。

C++

接口 说明
EchoPtr 异步客户端,可将协议方法转换为通过 IPC 通道发送的 FIDL 请求消息。
EchoSyncPtr 同步客户端,可将协议方法转换为通过 IPC 通道发送的 FIDL 请求消息。
Echo 用于替换和处理传入 FIDL 请求的服务器组件的抽象类。
EchoStringCallback 用于以 FIDL 响应消息的形式将每个请求的返回值发送的回调。