RFC-0235:组件字典 | |
---|---|
状态 | 已接受 |
领域 |
|
说明 | 一种将字典类型引入组件框架以实现功能捆绑的提案。 |
问题 | |
Gerrit 更改 | |
作者 | |
审核人 | |
提交日期(年-月-日) | 2023-10-16 |
摘要
设计初衷
目前,组件框架仅支持“点对点”的路由 它定义的功能:为了将功能 C 从组件 A 路由到 组件 B 中,因此要建立路线的每个中间路段中都必须有一段路线 C 放在相邻组件之间。
大量用例,通过路由 功能组合作为单个逻辑单元。如果没有这项功能 客户不得不采用成本高昂、缺乏灵活性和 以下是这些用例的示例:
- 诊断功能,例如
LogSink
、InspectSink
和跟踪记录 几乎所有组件都会用到提供程序,这会简化 那么如果我们能将它们作为一个捆绑包进行路由 fuchsia.debugdata.Publisher
等 Profiler 功能与 诊断功能,但只有在 特定版本目前,我们没有适当的方法让 build 配置 添加需要跨越整个拓扑的功能。这是 性能分析器目前仅在测试领域中启用的原因。- 测试领域代理会向测试用例公开一个接口,以供测试 运行被测组件。目前,这是一个自定义界面 与组件连接 API 非常相似。此界面 可以替换为测试领域代理 转移到测试这样,测试领域代理就不必 定义自己的抽象层,并让任何组件框架 功能。
- 任何位置的功能均通过多层路由,
通过捆绑来简化:
<ph type="x-smartling-placeholder">
- </ph>
session_manager
:从core
路由到session_manager
需要由session_manager.cml
重新路由。这是 包含大量功能,因此难以维护。 非平台功能泄露到session_manager.cml
。chromium
:Chromium Cml 文件包含大量重复内容,因此 可以极大地简化功能, 单个名称。https://fxbug.dev/42072339)
- 组件框架环境是 功能使跑步者和 解析器可配置为 并隐式路由至整个子树。如果我们可以使用一个通用的 但利用捆绑 API 来实现这一目标,这种做法会更和谐 组件框架路由 API 的其余部分, 基于环境的隐式路由的最小权限问题。
以下用例也是激励功能捆绑的动力应用场景,但它们 有一些特别考虑事项,需要采取后续的设计工作, 该 RFC 规范中的提案。
- 一些板专用驱动程序希望提供
为平台定义的所有指标这些服务应通过
bootstrap
Realm,因为所有驱动程序组件都存在于其中,但不存在 在平台拓扑中明确命名这些组件有意义。周三 如果 Cml 有办法将这些服务绑定在一起, 就能解决这个问题 - 使用
driver_test_realm
的测试也会出现类似问题。这些测试 希望将不同的服务从驱动程序路由到测试。在这些测试中 驱动程序测试领域组件位于驱动程序和测试之间, 我们希望能够在这些测试中重复使用驱动程序测试领域 而无需进行修改
最后,已经有几个现有的组件框架 API 涉及分组功能但是,它们是独立的,仅适用于 特定情况下的预测。示例如下:
- 命名空间是所有
传送给程序的功能(其
use
声明中的功能)。 - “公开目录”是 的功能,即组件路由到其父项的功能, 界面。
- 服务功能是一种分组方式, 而服务功能本身可分为一组 形成“汇总”的服务功能
事实上,我们拥有这么多 API 提示, 一种通用抽象,允许他们定义自己的字典和路由 。
利益相关方
根据用例,以下团队已被确定为利益相关方 如上所示:
- 架构
- 诊断
- 测试
- 工具链
- 驱动程序框架
- 安全
教员:hjfreyer@
审核者:
- abarth@(架构)
- crjohns@(测试)
- Markdittmer@(安全)
- miguelfrde@(诊断)
- surajmalhotra@(驱动程序)
- ypomortsev@(组件框架)
已咨询:
- phosek@
- kjharland@
- 安米塔尔@
- wittrock@
- novinc@
社交化:
此 RFC 之前有两个内部文档:用例和要求文档;以及 核心设计文档。这些文档已获得以下组织的非正式批准: 相关方。
这些文档中的信息已并入此 RFC,其中 相关性。
要求
这些是必须支持的操作字典,由我们通过 分析用例并从现有分组 API 进行泛化:
- 一级:字典是 CF API 中的一级概念。 应表示为一项功能。
- 汇总:其中包含一个“汇总”用于构建字典的操作 。
- 提取:存在一次“提取”操作来提取单个 功能可以像其他任何 API 一样进行路由和使用, 功能。这大致与聚合相反。
- 委托:可以在组件之间传递字典。
- 嵌套:由于字典是一项功能,因此字典可以包含 其他字典。
- 结构:字典使用元数据标记,可精确指明 其中包含哪些功能
- 扩展:有一个用于构造新字典
B'
的操作, 会继承B
的内容,并会添加其他功能。 - 可变性:字典的内容可能会随时间而变化。不过, 可能存在更高层级的政策,它会对 特定字典。
设计
定义
字典定义为键值对,其中键是
功能名称(例如fuchsia.example.Echo
),值为 CF
功能。
功能名称是一个或多个功能的序列,
字符集 [A-Za-z0-9_-.]
,大小为 1 到 N(目前 N = 100,但是
我们将来可能会扩展该 API)。
在运行时使用字典
我们将介绍一个公共 FIDL 协议,该协议提供 字典。如 FIDL 伪代码:
library fuchsia.component;
type DictionaryEntry = resource struct {
key DictionaryKey;
value Capability;
};
protocol Dictionary {
Insert(DictionaryEntry) -> ();
Remove(DictionaryKey) -> (Capability);
Lookup(DictionaryKey) -> (Capability);
Enumerate() -> Iterator<DictionaryKey>;
Clone();
};
我们还将引入一个公开的 FIDL 协议, 来创建一个空字典。
后续的设计工作将确定
Capability
。
组件声明中的字典
我们先介绍一些规范化,以帮助您定义 操作。我们将定义四个与每个 组件:组件输入字典、组件输出字典、 程序输入字典和程序输出字典。这些共同体 根字典。
组件输入字典是包含所有功能的字典。
由其父项通过 offer
添加到组件;也就是说
表示组件可以路由 from parent
。
组件输出字典是包含所有功能的字典。
expose
由组件映射到其父级;也就是
父级可以路由 from #component
。
程序输入字典是包含所有功能的字典。
use
。
程序输出字典是包含组件的所有
capability
声明。
我们可以根据以下定义来表达功能路由操作:
use
、offer
、expose
和capabilities
是路由操作, 在字典之间传送功能。use
将功能从根字典路由到程序输入 字典。expose
将功能从根字典路由到组件 输出字典。offer
将 capability 从根字典路由到子项的 组件输入字典。capabilities
在程序输出字典中使功能 以便进行路由
在此设计中,我们将对其进行泛化,以允许路由操作使用 任意字典作为来源,而不仅仅是根字典:
use
将功能从字典路由到程序输入 字典。expose
将功能从字典路由至组件输出 字典。offer
将功能从字典路由到子项的组件 输入字典 或其他字典。
声明
要定义全新的空字典,请执行以下操作:
capabilities: [
{
dictionary: "diagnostics-bundle",
},
],
要定义一个内容继承自现有字典的字典,请执行以下操作:
使用 extends
提供该字典的路径(请参阅
扩展程序):
{
dictionary: "diagnostics-bundle",
extends: "parent/logging-bundle/sys",
},
汇总
要将功能聚合到字典中,请使用
offer
中的 to
关键字。由于字典是由
组件,则包含它的根字典为 self
。所有相同的关键字
作为常规 offer
提供。例如,您可以将
使用 as
关键字在目标字典中功能的名称。
capabilities: [
{
dictionary: "diagnostics-bundle",
},
],
offer: [
{
protocol: "fuchsia.logger.LogSink",
from: "#archivist",
to: "self/diagnostics-bundle",
},
{
protocol: "fuchsia.inspect.InspectSink",
from: "#archivist",
to: "self/diagnostics-bundle",
},
{
directory: "publisher",
from: "#debugdata",
to: "self/diagnostics-bundle",
rights: [ "r*" ],
as: "coverage",
},
],
委托
委托意味着传送字典:
offer: [
{
dictionary: "diagnostics-bundle",
from: "parent",
to: "#session",
},
],
与其他功能路由一样,您可以使用
as
关键字。
offer: [
{
dictionary: "logging-bundle",
from: "parent",
to: "#session",
as: "diagnostics-bundle",
},
],
等效的运行时 API 适用于 动态优惠。
嵌套
可以使字典包含其他字典,方法是将这些字典 另一个使用 aggregation 语法的字典:
capabilities: [
{
dictionary: "session-bundle",
},
],
offer: [
{
dictionary: "driver-services-bundle",
from: "parent",
to: "self/session-bundle",
},
],
提取
根据正式规定,我们将扩展 from
关键字不仅可接受根字典,还可接受嵌套在
一个根字典。
如需从字典中提取功能,请在 from
中为该字典命名。这个
字典相对于根字典(parent
、#child
等)
offer: [
{
protocol: "fuchsia.ui.composition.Flatland",
from: "parent/session-bundle",
to: "#window_manager",
},
],
这也适用于 use
:
use: [
{
protocol: "fuchsia.ui.composition.Flatland",
from: "parent/session-bundle",
},
],
当字典嵌套在其他字典中时,提取也有效:
use: [
{
protocol: "fuchsia.ui.composition.Flatland",
from: "parent/session-bundle/gfx",
},
],
扩展程序
在字典定义中使用 extends
选项可继承另一个
字典:
capabilities: [
{
dictionary: "session-bundle",
// `session-bundle` is initialized with the dictionary the parent
// offered to this component, also called `session-bundle`.
extends: "parent/session-bundle",
},
],
offer: [
{
dictionary: "session-bundle",
from: "self",
to: "#session-manager",
},
{
protocol: "fuchsia.ui.composition.Flatland",
from: "#ui",
to: "self/session-bundle",
},
],
可变性
以声明方式构建的字典是不可变的 安全属性。为了让字典可变 必须在运行时创建。
字典中功能的元数据
当功能添加到字典中时,它们将保留其所有类型 这些信息和元数据,这些信息独立于任何与 字典本身的内容
例如,如果一个 capability 的 optional
可用性
通过组件 A
添加到字典中,组件 B
提取
功能,则在提取时,可用性为 optional
,
即使字典本身的可用性是 required
。
我们可能会对汇总时的可用性施加某些限制。
例如,合理的做法是禁止将 required
capability
optional
字典,因为它违反了通常的不变性,
从目标路由到来源时,可用性不会变弱。
运行时和声明式字典之间的互操作性
在运行时创建的字典必须与 组件声明。否则,就会强制用户 即只能选择其中一种, 组合的基础不够广泛,无法同时满足这两种用途。 以相似的方式进行分析
我们将在后续跟进时讨论互操作性的设计细节 提案。
此功能的主要已知用例是驱动程序框架,用于路由 服务包。
实现
导入 XML 中的字典将遵循常规管道引入新的
cml 功能。首先,我们将字典添加到 cml 和 component.decl 中
架构。然后,我们会更新 cmc
、cm_fidl_validator
和 cm_rust
,
realm_builder
,用于编译、验证和表示字典。审查
也进行了更新,以识别字典,并且能够验证
字典路由。
我们正在将字典(作为一种 Rust 类型)集成到 组件模型和路由引擎。字典 API 的实现 应该在此工作的基础上,将这些字典用作 以及作为路由字典功能的传输工具。
性能
没有特殊的性能注意事项。路由字典应为 与推选成员的能力 。
工效学设计
改进建筑组件拓扑的工效学是 此设计。
虽然引入新功能自然会增加 API 的复杂性, 我们认为,复杂性的降低所带来的效果将远远抵消 将字典纳入拓扑。
向后兼容性
cmc 中还没有对组件清单功能进行版本控制的支持,因此
必须小心,以避免破坏与预构建清单的兼容性。
幸运的是,为字典引入的所有新语法均能
旧语法,让这项工作更加轻松。例如,当前名称
语法会成为新路径语法的特例。from
如果功能路由的一部分通过字典,则任何安全政策 与该功能有关的内容仍适用。
安全注意事项
当功能通过字典进行路由时,会失去部分透明度, 字典中各项功能的身份将对 路径中的中间组件。不过,仍然可以通过 沿着路线传回提供商。这是有意为之 以便实现字典所能实现的灵活性和功能。
以声明方式构建的字典是不可变的。对于这些字典 您可以执行 对字典从目标到目标的汇总路由进行深度优先搜索 来源。
当字典替换环境时,将会增强安全性 系统的状态,因为与环境不同, 并且采用与其他功能相同的方式。
隐私注意事项
此方案对隐私权没有任何影响。
测试
我们将像对大多数组件管理器功能一样对此进行测试,
component_manager
和 cmc
,以及集成测试
component_manager/tests
。我们还将添加集成测试
练习字典和应用于包含字典的路线的政策。
文档
我们将在 //tools/lib/cml
中更新 rustdoc。
向 //docs/concepts/components
添加一个页面来解释字典。
向 //examples/components
添加一个示例。
缺点、替代方案和未知问题
替代方案 1:为字典使用 in
和 into
关键字
声明
字典功能是一种 cml/component.decl 功能类型,用于 访问字典。
您可以像声明任何其他组件框架功能一样声明字典。是两个
字典创建的变体,由是否存在 extends
决定
关键字。
首先,您可以定义一个全新的空字典:
capabilities: [
{
dictionary: "diagnostics-bundle",
},
],
或者,您也可以定义一个字典,其内容继承自现有
在 extends
中指定字典的路径,在
from
(请参阅扩展程序):
{
dictionary: "diagnostics-bundle",
extends: "logging-bundle/sys",
from: "parent",
},
汇总
要将功能汇总到字典中,请使用
into
个关键字:
capabilities: [
{
dictionary: "diagnostics-bundle",
},
],
offer: [
{
protocol: "fuchsia.logger.LogSink",
from: "#archivist",
into: "diagnostics-bundle",
},
{
protocol: "fuchsia.inspect.InspectSink",
from: "#archivist",
into: "diagnostics-bundle",
},
{
protocol: "fuchsia.debugdata.Publisher",
from: "#debugdata",
into: "diagnostics-bundle",
},
],
委托
与主设计相同。
嵌套
可以让字典包含其他字典 转换为字典:
capabilities: [
{
dictionary: "session-bundle",
},
],
offer: [
{
dictionary: "driver-services-bundle",
from: "parent",
into: "session-bundle",
},
],
提取
我们引入了一个新的 in
关键字,用于指定一个字典来提取
功能。
如果存在 in
,则功能关键字(protocol
等)是指
功能,而不是由
在 from
中命名的组件。
in
可以是名称或路径(其中 name 可视为退化形式)。如果
它是一个名称,表示 from
提供的字典。如果它是一条路径,
路径的第一段表示 from
提供的字典,而
路径的其余部分指定了指向嵌套在此字典中的字典的路径。
use
、offer
和 expose
支持 in
。此字段始终是可选的。
offer: [
{
protocol: "fuchsia.ui.composition.Flatland",
from: "parent",
in: "session-bundle/gfx",
to: "#window_manager",
},
],
expose: [
{
protocol: "fuchsia.ui.composition.Flatland",
from: "#scenic",
in: "gfx-bundle",
},
],
use: [
{
protocol: "fuchsia.ui.composition.Flatland",
in: "session-bundle/gfx",
},
],
扩展程序
在字典定义中使用 extends
关键字以从另一个关键字继承
字典:
capabilities: [
{
dictionary: "diagnostics-bundle",
extends: "parent/logging-bundle/sys",
},
],
offer: [
{
dictionary: "diagnostics-bundle",
from: "self",
to: "#session-manager",
},
{
protocol: "fuchsia.tracing.provider.Registry",
from: "#trace_manager",
into: "diagnostics-bundle",
},
],
替代方案 2:功能标识符中的路径
无需在 from
中指定字典的路径,而是应改为
功能标识符的一部分(protocol
、directory
等)
汇总
与主设计相同。
委托
与主设计相同。
嵌套
与主设计相同。
提取
要从字典中提取功能,请指定功能的路径
功能标识符(protocol
、directory
、
等)。
offer: [
{
protocol: "session-bundle/fuchsia.ui.composition.Flatland",
from: "parent",
to: "#window_manager",
},
],
默认情况下,目标中的名称将是最后一个路径元素或“dirname”
(fuchsia.ui.composition.Flatland
)。您也可以使用 as
重命名:
offer: [
{
protocol: "session-bundle/fuchsia.ui.composition.Flatland",
from: "parent",
to: "#window_manager",
as: "fuchsia.ui.composition.Flatland-windows",
},
],
这也适用于 use
,它会使字典中的功能可用
加入该计划与其他 use
声明一样,默认目标路径
根据 /svc
对名称(最后一个路径元素)执行 rebase 操作:
use: [
{
protocol: "session-bundle/fuchsia.ui.composition.Flatland",
path: "/svc/fuchsia.ui.composition.Flatland", // default
},
],
路径语法也适用于嵌套在其他字典中的字典:
use: [
{ protocol: "session-bundle/gfx/fuchsia.ui.composition.Flatland" },
],
扩展程序
在字典定义中使用 origin: #...
选项可继承另一个
字典:
capabilities: [
{
dictionary: "session-bundle",
// Source of the dictionary to extend (in this case, the one named
// "session-bundle" from the parent)
origin: "#session",
from: "parent",
},
],
offer: [
{
dictionary: "session-bundle",
from: "self",
to: "#session-manager",
},
{
protocol: "fuchsia.ui.composition.Flatland",
from: "#ui",
into: "session-bundle",
},
],
替代方案 3:功能标识符成为路径
名称 ->路径
正式地说,cml 中的功能标识符是名称,本身没有 嵌套结构。例如:
offer: [
{
protocol: "fuchsia.fonts.Provider",
from: "#font_provider",
to: "#session-manager",
},
],
不过,功能标识符会映射到 capabilities
中的路径
和use
部分对于协议,这通常是隐式的:如果没有路径
cmc 会填充默认路径 /svc/${capability-name}
。例如:
use: [
{
protocol: "fuchsia.fonts.Provider",
// path in namespace
path: "/svc/fuchsia.fonts.Provider",
},
],
capabilities: [
{
protocol: "fuchsia.fonts.Provider",
// path in outgoing directory
path: "/svc/fuchsia.fonts.Provider",
},
],
此替代方案将支持功能标识符中的路径。更正式地说:
- 功能标识符是指一个或多个 names(来自
字符集“
[A-Za-z0-9_-.]
”,包含 1 到 100 个字符, 按/
个字符。不得以/
开头。- 或者使用正则表达式语法:
[A-Za-z0-9_-]{1,100}(/[A-Za-z0-9_-]{1,100})*
- 现有功能标识符向前兼容新的功能 语法。
- 或者使用正则表达式语法:
在下面,我们将了解此语法如何自然地为捆绑销售奠定基础。
汇总
如需将功能聚合到字典中,请使用相同的路径传送这些功能 前缀:
offer: [
{
protocol: "fuchsia.logger.LogSink",
from: "#archivist",
to: "all",
as: "diagnostics/fuchsia.logger.LogSink",
},
{
protocol: "fuchsia.inspect.InspectSink",
from: "#archivist",
to: "all",
as: "diagnostics/fuchsia.inspect.InspectSink",
},
{
protocol: "fuchsia.debugdata.Publisher",
from: "#debugdata",
to: "all",
as: "diagnostics/fuchsia.debugdata.Publisher",
},
],
委托
委托只是按如下方式传送字典:
offer: [
{
dictionary: "diagnostics",
from: "parent",
to: "#session",
},
],
嵌套
可以让字典包含其他字典,方法是 字典的路径 - 嵌套字典的前缀:
offer: [
{
dictionary: "driver-services-bundle",
from: "parent",
to: "#session-manager",
as: "session/driver-services",
},
],
提取
只需在您想要从中提取数据的功能在该字典中命名即可:
offer: [
{
protocol: "session-bundle/fuchsia.ui.composition.Flatland",
from: "parent",
to: "#window_manager",
as: "fuchsia.ui.composition.Flatland",
},
],
这也适用于 use
:
use: [
{
protocol: "session-bundle/fuchsia.ui.composition.Flatland",
path: "/svc/fuchsia.ui.composition.Flatland",
},
],
当字典嵌套在其他字典中时,提取也有效(TODO: 示例)
扩展程序
重命名功能,以使路径前缀与字典一致:
offer: [
{
protocol: "session-bundle",
from: "parent",
to: "#session-manager",
},
{
protocol: "fuchsia.ui.composition.Flatland",
as: "session-bundle/fuchsia.ui.composition.Flatland",
from: "#ui",
to: "#session-manager",
},
],
为什么使用字典而不是目录?
我们无需引入字典,而是可以使用 fuchsia.io
目录作为
包的基本类型。从某个方面来说,这种方式很有吸引力:目录
以及提供自己的分层捆绑形式。不过,
使用目录时有很多反对意见:
- VFS 类型系统携带的信息与 CF 类型系统不同; 例如,服务、目录和存储将全部映射到 即使它们在 CF 中属于不同的类型,也会如此。
- 目录界面比 来支持功能捆绑NODE_REFERENCE、链接、 标志、属性、数据文件等与捆绑用例无关。
- 对某些应用来说,VFS 库过大,尤其是
。正是因为这个原因,
“
//sdk/lib/component/outgoing
”分享的库链接 库 shim (//sdk/lib/svc
),而不是 VFS 库 而代价是功能和透明度。 - 不存在一种 VFS 实现,而是有两种单独的 C++ 实现 和一个 Rust 实现。这些实现与 和功能差距。这对字典没有问题 字典的单一实现。
- 目录会增加编写代码生成绑定的难度, 将程序提供或使用的各项功能表示为独立的 语言元素。
- 目录并不能自然地支持“汇总”或“扩展程序” 操作。必须通过提供一个新目录来模拟 节点会重定向到旧节点,这种做法并不容易,而且容易实现 出错。
未来工作
我们将针对驱动程序用例单独建议配套设计, 仅凭此提案中的功能无法彻底解决这个问题。
此设计为功能路由开启了一个更经济的语法。
我们不再将 capability 名称和 from
设为不同的属性,
可以将它们组合成一条路径,从概念上来讲,该路径的根是字典
包含所有根字典的文件例如:
offer: [
{
protocol: "#ui/fuchsia.ui.composition.Flatland",
to: "#session-manager",
},
],
此语法有一个很好的属性:可以自然地泛化为 路由整个根字典:
offer: [
{
// Plumb all capabilities from parent to child #session-manager
dictionary: "parent",
to: "#session-manager",
},
],
另外值得一提的是更通用的版本, 它将语法 更多:
route: [
{
// Path to source capability in dictionary
src: "#ui/fuchsia.ui.composition.Flatland",
// Path of target capability in dictionary
dst: "#session-manager/fuchsia.ui.composition.Flatland",
},
],
route: [
{
src: "parent",
dst: "#session-manager/parent",
},
],
先验技术和参考资料
功能包是一种过时的想法。内部前身文档有很多 提出类似想法的人。