RFC-0108:组件 binder 协议 | |
---|---|
状态 | 已接受 |
区域 |
|
说明 | 框架提供的协议,允许组件启动其他组件。 |
Gerrit 更改 | |
作者 | |
审核人 | |
提交日期(年-月-日) | 2021-05-21 |
审核日期(年-月-日) | 2021-06-30 |
摘要
此 RFC 引入了框架提供的新协议 fuchsia.component.Binder
,该协议允许组件启动公开该协议的其他组件。
设计初衷
fuchsia.sys2.Realm 协议 是框架提供的 API,可让组件在运行时操控其 realm。借助此协议,组件可以创建子组件并绑定到其公开的功能,而这些决策由运行时决策而非静态声明驱动。组件可以通过调用 BindChild
方法绑定到子级功能。此方法在成功执行后,会启动所提供的子组件(如果尚未启动),并打开与子组件公开目录支持的 fuchsia.io.Directory
协议实例的连接。子项的公开目录包含子项在其清单中公开的所有功能。
这种方法有两个缺点。首先,由于它同时满足两个用例,因此目前处于过载状态。它允许组件绑定到其子组件的功能,并且还允许组件启动子组件。其次,它与组件框架的 capability 模型不一致。大多数组件都是为了满足对某项功能的请求而启动的。换句话说,组件会绑定到功能,而不是提供这些功能的组件。这是封装的一个重要特性,因为如果组件与 capability 交互(而不是直接与组件交互),则实现组件可以换出,而无需对客户端进行任何更改。
因此,BindChild
将被废弃。fuchsia.sys2.Realm
协议中将添加替换方法 OpenExposedDir
,以允许父级绑定到其子级的功能。此方法与 BindChild
之间的显著区别在于,在新方法中,只有当父级绑定到其某项 capability 时,子级才会启动。这种语义上的更改更符合组件框架的设计原则。此迁移工作已开始,您可以通过 fxr/531142 进行跟踪。
不过,仅仅将 BindChild
替换为 OpenExposedDir
是不够的。有许多依赖于 BindChild
的自动启动行为的用例,这些用例对平台来说数量众多且非常重要。在这些情况下,父组件不会绑定到子组件公开的任何功能,或者父组件会启动不公开任何功能的组件。在某些集成测试、驱动程序和会话元素中,可以观察到这种模式。对于此用例,组件框架团队必须为客户提供启动组件的解决方案。
设计
组件框架将引入框架提供的新协议 fuchsia.component.Binder
。借助此功能,作者可以将组件声明为可直接绑定。然后,希望启动其他组件的组件会像使用任何其他功能一样使用该协议。组件管理器将位于此协议的服务器端,一旦建立连接,它将启动公开此功能的组件。您可以通过观察连接的客户端上的 ZX_CHANNEL_PEER_CLOSED
来捕获目标组件的终止。
library fuchsia.component;
/// A framework-provided protocol that allows components that use it to start the
/// component that exposes it.
///
/// Note: The component doesn't need to serve this protocol, it is implemented
/// by the framework.
[Discoverable]
protocol Binder {};
组件作者只需公开协议:
{
expose: [
{
protocol: "fuchsia.component.Binder",
from: "framework", // Note that this is implemented by the framework on the component's behalf.
},
],
}
将启动此类组件的组件将通过绑定到公开的 capability 来启动这些组件:
{
use: [
{
protocol: "fuchsia.component.Binder",
from: "parent",
},
],
}
此提案的主要优势在于,直接启动将成为组件 API 的一部分。可直接启动的组件(尤其是不公开功能的组件)可以在清单文件中进行审核。此外,启动其他组件并不局限于父级组件启动直接子级。不过,fuchsia.component.Binder
协议可以像其他功能一样路由到任何位置。
实现
实现此设计不需要进行太多更改。引入此协议应该需要一两次 Gerrit 更改。
此功能发布后,BindChild
用户将根据其用例迁移到使用 OpenExposedDir
或建议的 fuchsia.component.Binder
协议。
迁移完 BindChild
的所有用例后,该方法将从 fuchsia.sys2.Realm
协议中移除。
性能
此协议会导致性能回归。目前,父级组件可以调用 BindChild
来启动子级组件。在此提案生效后,父级组件必须调用 OpenExposedDir
,然后打开 fuchsia.component.Bind
才能实现相同的效果。不过,这种回归应该只是名义上的,因为这些事件很少见。
安全注意事项
此协议不应引发安全问题。此协议将可路由,因此可以通过检查清单文件来审核哪些组件启动了哪些其他组件。
隐私注意事项
此协议不应引发隐私问题,因为它只是允许一种机制启动其他组件。
测试
此功能将通过单元测试和集成测试进行测试。
此外,借助此功能,组件作者可以更轻松地测试其他组件的副作用。例如,在“诊断”中,有多个集成测试会断言 Inspect VMO 的状态。在这种情况下,驱动程序组件会启动用于操控 VMO 的木偶组件,然后驱动程序组件会对 VMO 的内容进行断言。所提议的功能将允许驱动程序组件启动木偶组件,而无需额外的 FIDL 协议。
文档
我们将编写 FIDL API 文档,并在组件框架 Realm 文档中更详细地介绍此功能。
缺点、替代方案和未知情况
单次运行组件
我们最近接受了一项相关的 RFC,该 RFC 还允许组件启动子组件。该提案将能够满足 BindChild
填充的部分用例,但并非全部。值得注意的是,通过建议的机制启动的组件必须位于集合中,而 fuchsia.component.Binder
适用于所有组件。
fuchsia.sys2.Realm/StartChild
另一种方法是通过添加用于启动子项的方法来扩展 fuchsia.sys2.Realm
协议。此方法 StartChild
将接受一个参数 ChildRef
,该参数将是指向静态声明的子项或动态创建的子项(集合)的引用。这与 BindChild
接收的参数相同,也是 OpenExposedDir
将接受的参数。此方法将返回一个 Zircon 事件对象,该对象将在子组件停止时发出信号。子组件的生命周期将与父组件相关联。如果父级组件被停止,子级组件也会被停止。
library fuchsia.sys2;
[Discoverable]
protocol Realm {
/// Start child component instance, if it isn't already. Returns a Zircon
/// Event object that clients can use to observe when the child component stops.
/// When the child component stops, Component Manager will set this object's
/// ZX_EVENT_SIGNALED bit.
StartChild(ChildRef child) -> (zx.handle:EVENT event) error fuchsia.component.Error;
};
最终,这种替代方案并不理想,因为它允许任何组件直接启动。仅当组件声明了直接启动(而不是作为 capability 绑定的副作用启动)时,才应允许直接启动。由于 StartChild
会接受 ChildRef
,因此它允许启动任何组件。对于集合,只有在运行时才能观察到启动的组件。
绑定方法
最后,您可以使用某种方法触发启动,而不是引入上述建议的空 Binder 协议。
library fuchsia.component;
[Discoverable]
protocol Binder {
/// Start the associated component instance.
/// This method is reentrant and safe for concurrency.
/// Calling `Bind` on an already-binded component is a no-op.
/// When the child component stops, the `ZX_EVENTPAIR_PEER_CLOSED` signal
/// will be asserted on the `event` object.
Bind(zx.handle:EVENTPAIR event) -> () error fuchsia.component.Error;
};
虽然此选项会使启动触发器更明确(即作为对方法调用的响应,而不是连接到协议),但它不如提案可行,因为它会给客户端带来额外的障碍。必须先设置事件对对象,然后才能调用该方法,而不会获得任何额外的功能。
Binder Capability
组件框架还可以引入由提议的 fuchsia.component.Binder
协议支持的新功能。
// a.cml
{
capabilities: [
{
binder: "a",
},
],
expose: [
{
binder: "a",
from: "self",
},
],
}
新功能具有许多优势,例如:
- 使用独特的类型有助于更好地区分启动功能。例如,我们可以推广与协议不同的 binder 功能的命名惯例。
- 与其他功能在转换为绑定方面的一致性(连接 == 绑定)
- 不会忘记
framework
关键字(不过我们可以为此添加 cmc 检查)。
不过,尽管有这些好处,引入新功能也会增加概念开销,即开发者需要学习和了解新事物,CF 也需要提供支持。框架提供的协议更简单,更易于理解。