协议开放生命周期

本文档介绍了组件尝试连接到其命名空间中的协议时发生的步骤。

这些步骤适用于在组件管理器下运行的以下模型:

概括来讲,以下步骤包括:

构建组件的命名空间

命名空间是指在组件启动时向其提供的一组目录。每个目录都与一个文件系统相关联 组件可通过该路径访问 组件。

这些目录采用通道句柄形式,组件可以通过这些通道使用 fuchsia.io.Directory FIDL 协议

例如,所有组件都将收到软件包内容的句柄 它们的创建来源为 /pkg。也就是说,组件可以查看 通过读取 /pkg/bin 的内容,可在其软件包中获得二进制文件。

组件清单中的 use 声明 确定命名空间的填充方式。使用协议功能时...

use: [
    {
        protocol: "fuchsia.example.Foo",
    },
]

...组件管理器会在组件命名空间中添加一个 协议的父级目录。在此示例中,该协议的命名空间路径为 /svc/fuchsia.example.Foo(默认路径分配),这意味着组件管理器将向命名空间添加 /svc 的句柄。

/svc 目录由组件管理器本身提供,而组件 经理会响应此目录的协议请求, 该组件的生命周期

命名空间中显示的内容的确切语义因功能而异 类型。例如,如果使用目录功能而不是协议 功能...

use: [
    {
        directory: "example-data",
        rights: [ "r*" ],
        path: "/example/data",
    },
]

...命名空间中显示的是目录本身的句柄,而不是父目录的句柄。在此示例中,这表示 /example/data 将出现在命名空间中,而如果将该路径用于 协议功能 /example 将出现在命名空间中。

组件用于打开协议

当某个组件想要打开某个协议时,它会创建一个新的通道对, 通过 Open 请求通过其 命名空间。例如,如果组件想要打开与 /svc/fuchsia.example.Foo 的连接,则新通道对的一端将通过其命名空间中的 /svc 句柄发送。然后,组件可能会调用 通过通道使用 fuchsia.example.Foo 协议。

由于包含协议的目录 (/svc) 由组件提供 那么,它是组件管理器,将接收新 通过组件发送的 Open 请求来发送和处理。然后,组件管理器必须确定通过此通道提供协议的组件。

Open 触发功能路由

要确定在通道上提供协议的组件, 组件管理器必须遍历组件树,遵循 offerexpose 声明来查找 capability 的来源。此过程称为 功能路由

从触发功能路由的组件的父级开始, 组件管理器将检查每个组件的清单,以查找 offer 其目的地与子项匹配的声明。优惠将指定一个来源 parentself 或子女名称。如果此优惠是由 就会继续在树中向上移动,如果邀约来自 将从组件的某个子组件中沿着树向下走到该子节点。

当路线开始沿着树木向下走时,它会寻找expose 声明,这会指定 self 的来源或 子节点。如果功能来自子项,则组件管理器将继续运行 沿着这棵树走下去

找到源为 selfofferexpose 声明后,组件管理器便可将通道交给该组件。

如果链条在任何步骤中无效,组件管理器将记录错误并关闭从 Open 调用收到的通道。如果发生这种情况, 例如:

  • 组件 C 提供了 parent 中的 capability,但其父级 R 未向 C 提供该 capability。
  • 组件 C 通过其子级 D 提供了一项功能,但子级 D 提供了一项功能 不向 C 公开该功能。

例如,请考虑以下组件树及其清单 (为简洁起见,省略了 program 块和运行程序设置):

    C
   / \
  B   D
 /
A

A.cml:
{
    // ...
    capabilities: [
        {
            protocol: "fuchsia.example.Foo",
        },
    ],
    expose: [
        {
            protocol: "fuchsia.example.Foo",
            from: "self",
        },
    ],
}

B.cml:
{
    // ...
    expose: [
        {
            protocol: "fuchsia.example.Foo",
            from: "#A",
        },
    ],
    children: [
        {
            name: "A",
            url: "fuchsia-pkg://fuchsia.com/a#meta/a.cm",
        },
    ]
}

C.cml:
{
    // ...
    offer: [
        {
            protocol: "fuchsia.example.Foo",
            from: "#B",
            to: [ "#D" ],
        },
    ]
    children: [
        {
            name: "B",
            url: "fuchsia-pkg://fuchsia.com/b#meta/b.cm",
        },
        {
            name: "D",
            url: "fuchsia-pkg://fuchsia.com/d#meta/d.cm",
        },
    ]
}

D.cml:
{
    // ...
    use: [
        {
            protocol: "fuchsia.example.Foo",
        },
    ],
}

D 对其命名空间中的 /svc/fuchsia.example.Foo 调用 Open 时,组件 管理器将遍历树,以查找应提供此 协议。它将从 D 的父级 C 开始,然后:

  • 查找 fuchsia.example.FooDoffer 声明,并确认它来自子 B
  • B 中查找 fuchsia.example.Fooexpose 声明, 您会看到来自 A
  • A 中查找 fuchsia.example.Fooexpose 声明,并确认它来自 self。也就是说,A 是组件 提供 D 正在尝试使用的功能。

现在,已找到提供程序组件,组件管理器可以尝试 通过 Open 请求转交收到的频道。

绑定到组件并发送协议通道

找到提供程序后,客户端组件现已绑定到提供程序。这个 会导致组件当前停止运行,然后开始运行。

每个组件在启动后都会在其句柄表中收到指向出站目录的服务器句柄。 绑定一个组件后,组件管理器会将 协议通道发送到提供组件的传出目录(位于 来源路径:位于提供内容的组件的 offerexpose 声明中。

在上面的示例中,组件管理器将通过Open 组件 A/svc/fuchsia.example.Foo 的传出目录句柄 路径,从而提供在组件 D 收到的频道句柄。 名为 Open 传递给组件管理器。

然后,组件 A 会接收此请求,并开始通过所提供的通道响应消息。

由于组件管理器会直接将协议通道的服务器端转发到提供方组件的传出目录,因此它不会参与消息代理,并且在完成 capability 路由后完全不在视野范围内。一旦与另一个组件建立连接,它们就会通过 相互之间直接进行,中间没有仲裁者

注意事项

运行时不可预测性

由于功能路由的运行时性质以及 组件提供功能,就无法知道 可以先从其命名空间中成功访问某功能,然后再尝试 即使存在针对相应功能的有效优惠/公开链,软件包 更新可能会在运行时破坏此链,并且完全有可能 声称在其清单中提供某项功能的组件无法做到这一点 。

提供的功能与 Ambient 权能

有些功能由组件框架本身提供, 直接使用(或将隐式提供给)没有 提供这些功能的父级发布商。目前,这些国家/地区包括:

  • /pkg:用于创建组件的软件包的句柄。
  • /svc/fuchsia.component.Realm:一种协议,组件可以使用该协议来管理自己的领域。