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 SDK 提供了 fuchsia_fidl_library() 构建目标,用于将 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 提供了构建模板,用于为每种受支持的语言生成绑定。如需为 fuchsia.examples 库生成 HLCPP 绑定,请参阅以下 BUILD.bazel 示例:

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

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

练习:Echo FIDL 库

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

创建 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
protocol Echo {
    /// Returns the input.
    EchoString(struct {
        value string:<MAX_STRING_LENGTH, optional>;
    }) -> (struct {
        response string:<MAX_STRING_LENGTH, optional>;
    });
};

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

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

echo-fidl/BUILD.bazel:

load(
    "@rules_fuchsia//fuchsia:defs.bzl",
    "fuchsia_fidl_llcpp_library",
    "fuchsia_fidl_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 时提供。