RFC-0259:单调时钟暂停和启动时间轴 | |
---|---|
状态 | 已接受 |
区域 |
|
说明 | 详细介绍了如何在 S2Idle 期间暂停单调时钟,并介绍了启动时间轴。 |
问题 | |
Gerrit 更改 | |
作者 | |
审核人 | |
提交日期(年-月-日) | 2024-08-16 |
审核日期(年-月-日) | 2024-09-11 |
问题陈述
“挂起到空闲状态”RFC 规定,在挂起到空闲状态期间,应暂停单调时钟。不过,在 Fuchsia 上运行的某些程序可能需要了解暂停期间经过的时间。如果我们暂停时钟,但未为这些程序提供其他时间跟踪方式,这些程序将会中断。
摘要
本文档通过提议添加一个新时间轴(以下简称“启动时间轴”),为上述问题提供了解决方案。该时间轴将在从暂停到空闲期间继续推进。我们还将介绍与此时间轴互动的时钟、计时器和其他相关对象。
在暂停单调时钟之前,需要了解暂停期间经过的时间的现有代码需要修改为使用启动时间轴。
利益相关方
主持人:jamesr@google.com
审核者:
- adamperry@google.com
- alizhang@google.com
- brunodalbo@google.com
- crjohns@google.com
- fmil@google.com
- gmtr@google.com
- johngro@google.com
- maniscalco@google.com
- miguelfrde@google.com
- tgales@google.com
- tmandry@google.com
咨询了:
我们就此 RFC 的内容咨询了 Fuchsia 中的许多团队。这包括但不限于以下团队的人员:
- 取证
- 诊断
- 内核
- 性能
- 安全
- Starnix
- 计时
社交:
此 RFC 的内容通过多个单独的文档进行共享,这些文档已发送给包含 Fuchsia 组织大多数成员的各种邮寄名单。
要求
此 RFC 对现有的单调时间轴和新的启动时间轴提出了几项要求:
- 引入启动时间轴并在暂停期间暂停单调时钟不得阻止应用实现其现有功能。
- 启动时钟必须报告自系统启动以来经过的总时间,包括在暂停到空闲状态期间所花费的任何时间。
- 单调时钟不得包含在挂起到空闲状态所花费的时间。在 Suspend-To-Idle RFC 之前,系统挂起期间单调时钟的行为未定义,因此我们在此处明确定义了该行为。
- 单调时钟和启动时钟必须单调递增。
- 当内核初始化计时器硬件时,必须将单调时钟和启动时钟都设置为零。
- 反过来,计时器硬件初始化必须在用户空间启动之前完成。
设计
此提案介绍了新启动时间轴的行为以及单调时间轴行为的变化。它还简要介绍了 Fuchsia 中可能需要使用启动时间轴来适应暂停单调时钟的部分。我们将在未来的 RFC 中更详细地介绍所需的 API 更改。
命名注意事项
虽然每个主要操作系统都有一个在系统暂停时不会暂停的时间轴,但每个操作系统对该时间轴的命名都不同。下表显示了 Fuchsia 中的两个时间轴,以及它们在其他操作系统中的名称:
紫红色 | Linux | BSD | Windows | OS X | Android |
---|---|---|---|---|---|
单调时间 | CLOCK_MONOTONIC_RAW | CLOCK_UPTIME | 无偏时间 | 绝对时间 | 正常运行时间 |
启动时间 | CLOCK_BOOTTIME | CLOCK_BOOTTIME | 实时或时间 | 连续时间 | ElapsedRealtime |
之所以选择“启动时间轴”这个名称,是为了与 Linux 和 BSD 使用的现有名称保持一致,尤其是因为 POSIX 兼容性(通过 Starnix)是我们向 Zircon 内核引入这一新时间轴的主要原因之一。
时间轴语义
在挂起到空闲期间,启动时间轴保证会推进。与之相反,正例时间轴在暂停期间保证会暂停,如 RFC-230 所述。不过,当系统未暂停时,这两个时间轴会以相同的速率滴答。当内核首次初始化计时器硬件(目前在内核启动初期进行)时,这两个时间轴也会设为零。
Fuchsia 的受影响部分
为了适应暂停单调时钟,Fuchsia 的以下部分需要更改。本文档的目标是概要介绍需要更改的方面。未来的 RFC 和设计文档将明确说明这些更改的具体内容和实施方式。
请注意,此处列出的每个领域还应审核其提供的 FIDL 服务,以确保所有时间戳字段都记录了其操作的时间表。时间戳类型本身不会发生变化,但注释和变量名称应明确指明其所采用的时间轴。这些类型未来可能会发生变化,如需了解详情,请参阅“未来工作”部分。
- 内核:许多内核 API 都会接受或返回单调时间戳。其中一些 API 可能需要修改才能同时支持这两个时间轴,或者完全改用启动时间。
- Timekeeper:计算 UTC 时间应改为使用启动时间。
- 诊断:诊断堆栈在许多位置(归档程序、日志、检查等)使用时间戳。必须对每种用法进行审核,以确保其正确无误。
- 轨迹:轨迹目前使用单调时间戳对采样进行注解,因此在暂停期间将无法正常运行。在这些时间间隔内保留轨迹有非常实用的用例,因此我们需要一种方法来重建这些轨迹。
- Starnix:Starnix 中对
CLOCK_BOOTTIME
的所有用法都需要改用启动时间轴。 - 法医:崩溃报告和 Cobalt 都包含单调时间戳;这些时间戳可能需要改为使用启动时间戳,也可能不需要。
- 开发者工具:FFX 可能需要添加功能来获取当前启动时间,并且可能需要在检索日志时审核其对单调时间的使用情况。
- 系统库:某些特定于语言的功能(例如标准库中的异步执行器或时间 API)可能需要考虑新的时间表。
- 软件分发:Omaha 客户端(以及 SWD 堆栈中的其他组件)依赖于单调时间,并且可能需要切换时间轴。
- Netstack:Netstack3 使用了可能需要或不需要切换到使用启动时间轴的计时器。
实现
实施将分几个阶段进行。
第 1 阶段:对启动时间的内核支持
启动时间轴将在 Zircon 内核中引入,内部结构将被修改为适应新时间轴。然后,启动时间轴将公开给用户空间。这需要进行一些 API 更改,因此需要另一个 RFC 来记录这些更改。
第 2 阶段:Starnix 集成
在 Zircon 中支持启动时间轴后,Starnix 便可使用它来支持其实现 CLOCK_BOOTTIME
和所有相关的下游功能。由于 Starnix 使用 @next vDSO,因此可以先与 Starnix 集成,然后再与系统的其他部分集成,这可能允许在获得第 1 阶段中提及的 RFC 批准后进行并行处理。
第 3 阶段:在启用了暂停功能的设备上维护功能
单调时钟仅会在系统暂停期间暂停。因此,我们必须确保在启用了暂停功能的设备上运行的所有软件都改用第 1 阶段中介绍的启动时间轴。这包括系统库、诊断、跟踪、取证和计时器的更改。
此阶段可尽可能与第 2 阶段并行进行。
第 4 阶段:在挂起到空闲期间暂停单调时钟
修复了在支持暂停的设备上运行的所有代码后,我们可以在暂停期间暂停单调时钟。这可能会突出显示其他问题(如需了解详情,请参阅“未知”部分)。如果这些问题会破坏关键用户体验历程,我们会还原暂停计时的 CL,并解决相应问题。如果关键用户体验历程未中断,则计时器将保持暂停状态,并且问题将在后续修复。
第 5 阶段:在已停用挂起功能的设备上维护功能
然后,应修改仅在已停用暂停功能的设备上运行且必须知道暂停时长的时间软件,以便在必要时使用启动时间。由于这些设备对挂起到空闲状态的支持优先级较低,因此时间压力较小。此阶段的变更包括软件交付和开发者工具。
Netstack3 也将在此阶段更新;即使它在支持暂停的设备上运行,当单调时钟暂停时,其对计时器的使用也不会中断,因此我们无需在此处阻塞。
后续工作
唤醒矢量
我们可能希望支持在启动时间轴上的给定时间点恢复系统。为此,我们需要在系统中对唤醒向量进行形式化定义,并建立基于时间的唤醒向量创建的语义。这将留待未来的 RFC 中进行说明。
时间轴感知型 FIDL 时间戳
FIDL 目前使用 zx.Time 表示所有时间戳,该时间戳的别名为 int64
。最终,此类型将受益于:
- 存储或以其他方式考虑其参考时间轴,或者
- 将被替换为每个参考时间轴的不同时间类型。
我们将在未来的 RFC 中决定采用哪种方法,因为发布新时间表并了解常见的使用模式有助于我们做出更明智的决策。
性能
引入第二个时间轴并暂停单调时间轴本身不会影响性能。不过,未来 RFC 中就各种 API 如何与启动时间交互做出的设计决策可能会对性能产生影响。相关 RFC 和设计文档中应讨论这些影响。
工效学设计
这项更改会使在 Fuchsia 中推理时间变得更加复杂。代码作者需要考虑在使用时间 API 之前,是否需要让其代码了解挂起间隔时间。不过,大多数程序应该能够使用单调时间轴并忽略暂停期,因为在此期间不会运行任何用户空间线程。
向后兼容性
如“设计和实现”部分所述,目前有一些代码使用单调时钟,并且在挂起到空闲期间该时钟暂停后,这些代码可能无法正常运行。在暂停时钟之前,此代码将迁移到启动时间轴,如“实现”部分中所述。
安全注意事项
有几项安全关键操作需要可信的时间源。这些步骤包括节流、验证令牌和验证证书。目前,节流和令牌验证需要可信执行环境 (TEE) 和/或云,因此对于高级操作系统 Fuchsia 而言,这并不是一个主要问题。
证书验证在 Fuchsia 中用于验证 HTTPS、SSH、Omaha、TUF 等的安全传输 (TLS)。这需要时间源具有很高的可信性,即时间源必须在几分钟的误差范围内反映时间的实际情况。目前,Timekeeper 可满足此需求,它会专门调整客户端获取时间所依据的 UTC 时钟。因此,如 RFC 中所述,Timekeeper 必须改用启动时间。请注意,在可信性和精确性得到确认之前,Timekeeper 会阻止客户端检索时间,因此无需执行任何额外工作即可确保此操作的安全性。
请注意,在 Timekeeper 及其客户端之外使用启动时间的任何用法均不得处理关键安全用例;这些用例应通过 TEE 或使用 Timekeeper 提供的 UTC 时钟。
隐私注意事项
此提案不会影响系统隐私。
测试
必须测试两类不同的代码:
- 我们必须验证使用单调时间轴的代码不会受到暂停期间的暂停影响。我们打算使用 CQ 和自动暂停功能进行广泛的本地测试来验证这一点。
- 我们必须验证需要切换到启动时间轴的所有代码区域是否正常运行。这些各个领域的测试计划将由相应的代码所有者负责,并将在未来的文档和/或 RFC 中指定。
文档
概括来说,我们将在 fuchsia.dev 中添加一个页面,介绍新的启动时间表及其行为方式。我们还将修改现有的单调文档,以反映它会在挂起到空闲期间暂停。
然后,设计部分中提及的每个代码区域都负责根据自己的判断更新文档。
缺点、替代方案和未知情况
替代方案
我们可以设想其他解决方案,即在暂停期间不暂停时钟,从而无需新的启动时间表。很抱歉,这不符合我们的平台要求:
- Starnix 中的预期:Linux 定义 CLOCK_MONOTONIC 为“不计入系统处于暂停状态的时间”。因此,Starnix 必须提供相同的保证。我们可以使用用户空间时钟来实现此功能,但这会带来很大的复杂性,因为 Starnix 和 Fuchsia 的时钟行为会有所不同,这意味着涉及时间和时间戳的任何 IPC 或操作都必须考虑到这种差异。
- 对某些现有代码的预期:编写某些代码时,假定单调时间不会暂停。有些内容在编写时就假定会这样。在挂起到空闲状态 RFC 中,我们决定暂停单调时钟是两害相权取其轻的做法。
- 避免计时器“唤醒风暴”:假设系统长时间处于暂停状态,单调时钟在此期间继续运行。系统中由程序设置的任意数量的计时器都可能会达到其截止期限,但在从暂停状态恢复之前,系统不会执行其回调。这可能会导致在恢复时花费大量时间运行计时器回调。
未知
几乎所有在 Fuchsia 上运行的程序都使用单调时间轴。如前一部分所述,编写此部分代码时,我们假定单调时钟会在挂起期间继续运行。一旦我们暂停时钟,这种情况就会中断,而且中断的方式可能很细微。
我们已尽最大努力识别和枚举需要改用新时间轴的所有代码区域。为此,我们不仅查看了代码,还与组织中的许多利益相关方进行了广泛的讨论。我们有信心找到了大多数可能的断点。
不过,我们可能错过了一些调用点,或者错误地假定使用单调时间轴即可。遗憾的是,没有简单的方法来识别这些问题;它们是未知的未知问题,只有在我们在挂起到空闲期间暂停时钟并对系统挂起进行广泛测试后,才会显现出来。
在先技术和参考文档
RFC-0230:挂起到空闲状态 RFC