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 库

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

首先,为 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 接口并生成其他 build 目标,以提供各种语言的绑定。如需检查绑定,您必须编译各个目标。

编译 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 响应消息发送。