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 源文件必须导入来自其他库的所有声明。

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 接口并生成额外的 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 响应消息发送。