RFC-0110:重新启动以终止关键组件 | |
---|---|
状态 | 已接受 |
领域 |
|
说明 | v2 组件功能,提供与 v1 keyword_components 相当的功能 |
Gerrit 更改 | |
作者 | |
审核人 | |
提交日期(年-月-日) | 2021-05-26 |
审核日期(年-月-日) | 2021-07-21 |
摘要
引入“终止时重新启动”的提案组件选项
清单的子声明,其作用等同于 sysmgr 的
critical_components
功能。
设计初衷
在组件 v1 中,sysmgr 支持
名为 critical_components
的功能,
系统服务组件会将其自身标记为“关键”组件。这意味着,如果
组件因任何原因(包括正常退出)终止,sysmgr 将会触发
重新启动系统。此次重新启动是一次安全重新启动
由 power_manager
驱动,这会使组件拓扑
有序关停。优雅重新启动以一致的方式破坏系统
并给组件彻底关闭的机会
和文件系统来彻底关闭
如果客户端没有信心,通常会在其组件上设置此选项 如果组件出现故障,正常的系统行为可以继续进行。 不出所料,此选项往往设置在服务运行 在系统运行中发挥核心作用,例如:
netstack
wlanstack
omaha-client-service
system-update-checker
除了
由 critical_components
实现的相对简单函数。这种设计是
应该专注于解决相应应用场景。崩溃恢复范围超出
critical_components
提供的不在范围内(但请参阅
未来的工作)。
要求
主要要求是提供与 critical_components
相当的功能。
这意味着,core
下的组件或
core
的子领域,以选择在其组件时触发安全重新启动
终止。
为何此时推荐?
动机中提到的下列使用
critical_components
被阻止迁移到组件 v2,直到
可用的功能
设计
我们会将 on_terminate
枚举添加到 ChildDecl
(等效
添加到组件清单的 children
部分),并提供
等效于 critical_component
的语义说明。有两个选项:none
(默认)或 reboot
。如果包含 on_terminate: reboot
的子组件
因任何原因(包括正常退出)终止,component_manager
将
从以下位置调用 Admin/Reboot
方法:
fuchsia.hardware.power.statecontrol.Admin
协议公开了
power_manager
触发系统的安全重新启动。
这就需要 component_manager
和
power_manager
。不过,这两者都在 ZBI 中,因此并没有明显
分层问题。在任何情况下,都要避免
一定程度的依赖
反转,因为重新启动会导致设备的电源状态发生变化,
驾驶员的责任
如果对 Admin/Reboot
的调用失败,component_manager
将回退到
恐慌,触发了不正常的重新启动。
这是一项敏感功能;我们不希望任意组件单方面
在终止时触发重新启动。因此,其使用将受到限制
component_manager
安全政策中的许可名单(将接受检查)
会在组件启动时自动触发此外,我们可以使用
restricted_features
GN 许可名单,用于生成
如果
未授权使用该功能的领域中的孩子。
实现
第 on_terminate
个选项
我们需要将 on_terminate
选项添加到清单的
child
部分。这需要对 cmc
、cmc_fidl_validator
和
cm_rust
来连接选项。由于这是一项特殊功能
允许在 ComponentDecl
中将其设置为 None
(当然,默认为
on_terminate: none
)。
我们会为 on_terminate
的 cmc
添加新的 restricted_feature
。仅 CML
此许可名单中的文件将能够on_terminate: reboot
子女。首先,此许可名单将由 core
和 network
组成
领域。
此外,我们还会在 component_manager
的reboot_on_terminate_enabled
配置,因此可以针对组件管理器的非根实例(针对
例如,测试中的嵌套实例)。
检测“在终止时重新启动”组件的终止
必须将逻辑添加到 component_manager
以检测何时重新启动(在终止时重启)
组件终止。在 Stop
操作期间,component_manager
可以检查
on_terminate
选项。如果已设置该属性,并且组件未关闭,
component_manager
调用 Admin/Reboot
。关机意味着
组件停止运行并且永远无法再启动
以下情况:
- 系统关机期间(关机现象本身由
Admin/Reboot
协议。在这种情况下,系统已关机, 因此没有必要再次触发关闭 - 当组件被销毁时。这可以通过以下任一方法实现:(a)
对
DestroyChild
的调用;(b)transient
的父级 收集停止,或 (c)single-run
集合中的一个组件 正在退出。对于 (a) 和 (b),不触发重新启动似乎是 因为这是该组件外部的操作, 从导致其停止的组件中终止。案例 (c) 中,如果 谨慎实施这一功能,即触发销毁程序 系统只在该组件终止后才开始播放
调用 fuchsia.hardware.power.statecontrol.Admin
协议
为了触发安全重新启动,需要连接到协议
fuchsia.hardware.power.statecontrol.Admin
和调用
Admin/Reboot
。此协议由 power_manager
组件实现。
(由于历史原因,它实际上由 shutdown_shim
代理。)由于
协议是由组件实现的,component_manager
如何获取访问权限
?为此,我们可以让 root
从
将 #bootstrap
映射到其父级。这意味着,根会将该协议公开给
位于根节点上方的节点,即 component_manager
。如需了解更多详情,请参阅设计
更多关于这种反转的说明。
原型
如需查看原型,请点击此处。
性能
此设计无需考虑性能。component_manager
只会
如果fuchsia.hardware.power.statecontrol.Admin
on_terminate: reboot
组件实际终止。
工效学设计
这种设计采用了简单的工效学设计:只需设置 组件的 restart-on-terminate 是执行以下操作:
- 在父级的
ChildDecl
(children
) 中设置on_terminate: reboot
(以 CML 格式声明)。 - 如果不存在,请将父级的 CML 添加到
cmc
on_terminate: reboot
的restricted_features
许可名单。 - 将该组件的名称添加到政策许可名单中,以启动终止时重新启动。
由于 on_terminate
选项由父项(而非组件)设置
本身,可以在生产环境中利用应该触发重新启动的组件,
而无需修改 CML此外,这还使您可以
将组件添加到不同的产品配置中,以设置
而不必更改组件。
向后兼容性
此更改不会破坏兼容性。客户必须明确选择启用 “remind-on-terminate”
安全注意事项
假设用户通过将组件标记为
次重新启动,以不当方式触发重新启动。
不过,由于使用受到安全政策许可名单的限制,新用途
必须获得明确批准。请注意,不受信任的
该组件会诱使 component_manager
向其授予重新启动权限,具体方法是:
嵌入已列入许可名单的组件,因为该组件已根据其
名称(拓扑路径),而不是网址。
隐私注意事项
此方案没有引入新的隐私保护注意事项。
测试
我们可以通过模拟
fuchsia.hardware.power.statecontrol.Admin
协议。我们应该记得
例如协议缺失或失败。
理想情况下,E2E 测试覆盖范围应涵盖“在终止时重新启动”组件, 以验证其终止是否确实会触发正常重新启动。
文档
必须进行以下文档更改:
- 添加有关
on_terminate
选项的文档,以便并行执行critical components
。 - 更新迁移指南以说明如何迁移
critical_component
秒
缺点、替代方案和未知问题
优点和缺点
益处
- 配置非常简单。
- 与 v1 直接对等,使迁移轻松简单。
- 由于该功能完全位于
component_manager
中, 易于实现,且不具有太多故障模式风险 比如丢失的事件 - 可以允许我们将
main_process_critical
的某些用途替换为on_terminate: reboot
,这绝对是优越的。 - 允许客户端利用设置了
on_terminate: reboot
的组件 无需修改即可制作。
缺点
- 不基于功能,这与正统的框架模型有所不同。
- 直接在
component_manager
中对一些崩溃恢复政策进行编码。虽然 一般情况下,我们并建议不要这样做,在本例中, 政策很简单,所以虽然费用不为零,但似乎很小。 - 通过
component_manager
引入对power_manager
的反转依赖项。 不过,这两家公司都在 ZBI 内,因此这并不是严重的分层违规行为。 - 由于涉及到 CML 架构更改,因此需要了解此选项
cmc
、cm_fidl_validator
、cm_rust
和客户端 但cm_rust
替代方案:在 program
上使用 system_critical
位
我们无需将该选项添加到 ChildDecl
中,而是将其添加到
组件清单的 program
部分中定义。主要区别在于
那就是在组件本身上设置选项,
与父声明中的子声明相比。
将位放入 program
的好处是可以保留一项专门的功能
共 ComponentDecl
份。自 program
起,来自 ComponentDecl
的
具有自由格式的语法,因此无需更改 cmc
、验证器,
或 Rust 绑定
来解释新选项我们只需在
component_manager
检索选项的 program
,
组件停止运行(以确定是否需要重新启动)。
不过,这种方法有一个显著的缺点:如果 system_critical
组件在测试中使用,则必须更改其 CML 以移除
system_critical
位(因为不允许在测试中设置该位)
且不希望测试触发系统重新启动)。这会增加
编写集成测试来利用
组件。
替代方案:使用 main_process_critical
ELF 运行程序支持名为
main_process_critical
,这会导致
component_manager 的根作业在组件退出并显示
非零状态或被终止。这会导致
重新启动。由于重新启动很不正常,这会导致系统关闭
使系统无法持续进行诊断或
指标。
main_process_critical
仅应用于正常触发
无法重新启动。例如,power_manager
本身会被标记为
main_process_critical
。由于任何关键组件都不是这种情况
此选项并不是可行的替代方案,
完整性。
备选:主管
我们不用在 component_manager
中管理崩溃恢复,只需执行以下操作即可:
core
领域。此替代方案包括两部分。首先,介绍
"component-scoped"可让使用者监控事件(在
Started
和 Stopped
事件)的范围限定为单个组件
实例。其次,引入一个名为监督器的组件,该组件使用
监控异常终止或启动和重新启动失败
进行响应
组件级范围的事件
组件框架团队已经讨论过一种想法,即: 将事件功能的范围限定为单个组件实例的方式, 而非整个领域此设计提供了一个具体的应用场景, 这个想法监控器只需监控特定组件, 特别是,接收这些组件的事件 整个领域。
为了提高速度,我们提议在 CML 上引入尽可能小的更改
才能启用此功能未来,我们可能会使更多
以不同方式指定事件范围的大量语法修订
(请参阅组件事件 RFC)。我们将添加一个 scope
,
字段添加到 offer event
声明中,可以指定 #child
或 realm
(默认)。
// core.cml
offer: [
{
event: "started",
from: "framework",
scope: "#wlanstack",
to: "#supervisor",
as: "started-wlanstack",
},
{
event: "stopped",
from: "framework",
scope: "#wlanstack",
to: "#supervisor",
as: "stopped-wlanstack",
},
],
鉴于将来可能会修改语法,我们可以使用 cmc
将 scope
功能列入许可名单到 core.cml
和集成测试。
组件级范围的事件不会包含 组件(例如名称或网址)。一般来说, 其载荷中含有敏感信息,如组件名称、 网址,我们仅在有必要知道的情况下才会公开这些网址。由于 主管不需要这些信息, 组件级范围的事件不会提供相关信息 生成事件的组件的身份信息。 负载中的信息是时间戳和终止状态 敏感性。
主管
监督器本身很简单。它是 core
下的一个组件,负责
以下:
- 使用包含
Started
和Stopped
事件列表的静态event_stream
。 - 如果通过此事件_stream,它会收到包含
Started
错误,或者负载为“不正常”状态的Stopped
事件触发, 通过调用 fuchsia.hardware.power.statecontrol/Admin.Reboot.
这是 critical_components
功能的简单实现目标。在
监督器可能会不断完善,以支持更多用例,
多位主管 -- 请参阅未来工作。
将事件转送给监督者
组件级范围的事件必须从每个关键组件路由到
主管。对于作为 core
的子项的关键组件,这需要
两项更改:
- 对 core.cml 进行了修改,以便从 组件添加到 Supervisor(请参阅 组件级范围的事件)
- 修改了监管者的 CML,以便在静态图片中使用事件 事件流。
如果关键组件嵌套在 core
的子领域下,则另一个步骤是
要求:
- 修改每个中间组件,以将子项的事件公开给 其父级。
例如,Netstack 正是如此,
让netstack
居住在core
下的network
子领域。
以下是主管的 CML 的示例:
// supervisor.cml
use: [
{
events: [
"netstack-started",
"netstack-stopped",
"wlan-started",
"wlan-stopped",
],
},
// The supervisor will trigger reboot under the following conditions:
// - It receives a `started` event with an error.
// - It receives a `stopped` event with a non-ok status.
{
event_stream: "EventStream",
subscriptions: [
{
event: [
"netstack-started",
"netstack-stopped",
"wlan-started",
"wlan-stopped",
],
on_receive: "start",
},
],
},
],
...
请注意,无需修改正在监控的组件。这是 有意:监督被认为是领域管理其数据的一种功能 而不是组件本身也就是说, 组件负责决定是否监管或如何监管。
启动 Supervisor
我们需要确保主管始终及时启动,以接收事件。
为此,我们建议您为 event_stream
订阅添加一个选项
名为 on_receive: "start"
。on_receive: "start"
导致component_manager
以便在收到该事件时自动启动组件这样,
component_manager
可保证活动永不丢失。默认选项是
"dispatch_if_started"
:仅当事件为
已在运行(默认行为)。
这需要对事件调度系统进行更改。具体来说,当
事件分派后,component_manager
必须在所有路由的事件之后
以防被静态事件流使用。否则,
组件可能会错过事件,即使其将事件标记为 on_receive: "start"
,
尚未解决。
可能存在将 on_receive: "start"
设为默认行为的参数
但这超出了此方案的范围。
优点和缺点
益处
- 避免在
component_manager
中对崩溃恢复政策进行编码。这会将 可以更好地分离关注点,因为根据一般规则, 深入了解什么是崩溃恢复政策 足够普遍component_manager
。 - 此方法比
recovery
选项的适应性更强。在 basemgr 中和 sessionmgr,有必要实施崩溃恢复政策 与重新启动恢复不同
缺点
- 需要需要构建的事件系统的支持。这会将
事件系统的复杂性,可能需要更多的时间和精力
而不是直接在
component_manager
中实现解决方案。 - 需要比
recovery
更多的样板文件。每个关键事件对应的事件数 必须从每个关键组件以路由方式发送到 Supervisor。 - 我们最终需要解决
basemgr
/sessionmgr
中的类似问题。 如果我们推迟设计一个更通用的解决方案, 从而更好地了解问题空间。
未来工作
basemgr
和 sessionmgr
会实施自己的崩溃恢复策略,
我们可以采取
supervisor 替代方法。
fshost
和archivist
目前使用main_process_critical
。有可能
而是可以使用“terminate-on-reboot”这样,我们就可以
对重新启动过程中涉及的组件执行 main_process_critical
操作
(driver_manager
和 power_manager
)。
某些路径仍会触发异常重新启动:
- 此设计创建了
component_manager
power_manager
和间接driver_manager
。因此,这些 组件无法使用 terminate-on-reboot,因此会将其标记为main_process_critical
,这意味着如果发生上述任一情况 组件就会触发异常重新启动。 - 如果
Reboot
调用本身失败,component_manager
panic,这也会 会触发非正常重新启动。
我们可以在上述区域
情况;例如,component_manager 可以执行常规的
关闭,然后退出。另一方面,由于 power_manager
和
driver_manager
对系统运行至关重要,我们可能不希望让
即使发生崩溃,系统也会持续运行任意时长。
我们可能会重新审视电力管理职责
分发;例如,component_manager
或许能够
驱动重新启动本身(仍需要依靠 driver_manager
来设置
电源状态)。
系统完全迁移到组件 v2 后, 组件管理器,以便利用 其对依赖关系图的了解。
先验技术和参考资料
存在适用于 critical_components
功能和
。