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 SDK 提供了 fuchsia_fidl_library() build 目标,用于将 FIDL 源文件编译为库。库目标的名称必须与每个源文件中的 library 声明一致。请参阅 fuchsia.examples 库的以下 BUILD.bazel 示例:

# Import the fidl template.
load("fuchsia_fidl_library")

# Define a FIDL library target.
fuchsia_fidl_library(
    name = "fuchsia.examples",
    srcs = [
        "echo.fidl",
    ],
    library = ""fuchsia.examples"",
    visibility = ["//visibility:public"],
)

在构建时,FIDL 编译器 (fidlc) 前端工具会验证和编译 将库源文件转换为 JSON 中间表示法 (IR)。这种 JSON IR 格式是 FIDL 绑定的依据。

生成 FIDL 绑定

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

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

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

在构建时,fidlgen 后端工具会为支持的 fidlc 生成的 JSON IR 库中提供的各种编程语言。 Fuchsia SDK 提供了构建模板,用于为每个受支持的语言生成绑定。请参阅以下 BUILD.bazel 示例,为 fuchsia.examples 库生成 HLCPP 绑定:

fuchsia_fidl_hlcpp_library(
    name = "fuchsia.examples.fidl_cc",
    library = ":fuchsia.examples",
    visibility = ["//visibility:public"],
    deps = [
        "@fuchsia_sdk//pkg/fidl_cpp",
    ],
)

使用此库的组件可以将绑定目标用作依赖项。

练习:Echo FIDL 库

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

创建 FIDL 库

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

mkdir -p fuchsia-codelab/echo-fidl

完成本部分后,项目应具有以下目录结构:

//fuchsia-codelab/echo-fidl
                  |- BUILD.bazel
                  |- echo.fidl

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

echo-fidl/echo.fidl

library examples.routing.echo;

const MAX_STRING_LENGTH uint64 = 32;

@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.bazel 文件,以声明库 目标:

echo-fidl/BUILD.bazel

load(
    "@fuchsia_sdk//fuchsia:defs.bzl",
    "fuchsia_fidl_library",
    "fuchsia_fidl_llcpp_library",
)

package(default_visibility = ["//visibility:public"])

fuchsia_fidl_library(
    name = "examples.routing.echo",
    srcs = [
        "echo.fidl",
    ],
    library = "examples.routing.echo",
)

fuchsia_fidl_llcpp_library(
    name = "examples.routing.echo.fidl_cc",
    library = ":examples.routing.echo",
    deps = [
        "@fuchsia_sdk//pkg/fidl_cpp_v2",
    ],
)

运行 bazel build 并验证构建是否成功完成:

bazel build --config=fuchsia_x64 //fuchsia-codelab/echo-fidl:examples.routing.echo.fidl_cc

检查 FIDL 绑定

FIDL 绑定目标会编译 FIDL 接口,并生成 bazel-bin/ 目录中特定于语言的绑定:

bazel-bin/fuchsia-codelab/echo-fidl/_virtual_includes/

找到并打开上述目录中生成的头文件:

find bazel-bin/fuchsia-codelab/echo-fidl/_virtual_includes -name *.h

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

接口 说明
Echo 表示 FIDL 协议,并充当类型和 类。
EchoString FIDL 协议方法的表示形式,供客户端和服务器用来 提供输入的请求和响应
fidl::Server<Echo> 服务器组件的抽象类,用于替换和处理传入的 FIDL 请求。
fidl::internal::NaturalClientImpl<Echo> 异步客户端,用于将协议方法转换为通过 IPC 通道发送的 FIDL 请求消息。为此协议创建 fidl::Client 时提供。
fidl::internal::NaturalSyncClientImpl<Echo> 同步客户端,用于将协议方法转换为通过 IPC 通道发送的 FIDL 请求消息。为此协议创建 fidl::SyncClient 时提供。