UTC 架构

简介

Fuchsia 的时间同步必须灵活:在 Fuchsia 上构建的不同产品必须能够使用不同的时间源,并且这些来源必须能够随着产品的演变而重新配置或替换。

本页面定义了我们用于提供这种灵活性的基本架构:相关的组件、这些组件的角色和职责,以及它们之间的交互。

架构

下图说明了时间同步中涉及的组件以及这些组件之间的基本关系。

此图显示了 Fuchsia UTC 架构。

内核

内核定义了可用于跟踪时间进度的时钟对象的概念。每个时钟对象都是时钟单调参考时间轴的一维仿射转换,可以由用户空间组件(“时钟维护者”)进行调整,并可由许多其他用户空间组件(“客户端”)观察。

组件管理器

组件管理器负责创建 UTC 时钟,并将其分发给其他用户空间组件。

组件管理器通过读取构建流程的输出,创建一个采用世界协调时间 (UTC) 的内核时钟对象,从而将“往返时间”设置为 build 中包含的最后一个 CL 的时间。请注意,组件管理器不会启动此 UTC 时钟。

组件管理器将世界协调时间 (UTC) 时钟的只读句柄传递给它启动的组件实例,并公开 fuchsia.time.Maintenance 服务以分发 UTC 时钟的读/写句柄。在生产系统中,Timekeeper 应该是唯一有权访问此服务的组件。

时间客户端

时间客户端是 Fuchsia 设备上采用世界协调时间 (UTC) 的用户。Fuchsia 的 libc 实现使用组件管理器提供的时钟句柄来读取世界协调时间 (UTC),因此任何组件实例都可以使用其运行时提供的标准时间 API 充当时间客户端。如需了解详情,请参阅语言支持

需要更深入地了解时间同步状态的组件可以获取 UTC 时钟句柄,并使用它来调用 zx_clock_get_details 或等待 ZX_CLOCK_STARTED 信号。

计时仪

Timekeeper 负责根据产品定义的来源和政策启动和维护 UTC 时钟。

Timekeeper 连接到 fuchsia.time.Maintenance 服务,以在启动时获取 UTC 时钟的可写句柄。它会启动并连接到产品配置的时间源组件,并连接到 fuchsia.time.external.PushSourcefuchsia.time.external.PullSource,以便从每个源接收时间同步信息。注意:自 2020 年第 4 季度起,时间源在 Timekeeper 中处于硬编码状态,尚不能按产品配置。目前尚不支持 PullSource。

针对特定产品,每个时间来源都配置为以下四种不同角色之一:

  1. 主要来源:主要来源用于维护 UTC 时钟(如果 UTC 时钟可用且与任何门控来源保持一致,即主要来源和门控来源报告的时间均在特定上限内)。
  2. 回退:后备来源用于在主来源不可用或不一致时保持 UTC 时钟,前提是回退来源可用且与任何门控来源保持一致。
  3. 门控:门控来源用于对另一个(通常更准确,但不太可信)时间源进行有效性检查。门控来源用于确定应使用主要来源还是后备来源。当主要来源和后备来源都不可用(或者它们可用但不一致)时,可以使用门控来源来维护 UTC 时钟。
  4. 监控:监控来源绝不会用于维护 UTC 时钟。系统会记录相关指标,以便评估监视器来源的性能,使监视器能够安全地测试或“暗发布”新算法或经过修改的算法。

虽然这是一个可以支持许多源的灵活系统,但我们预计大多数产品不会需要两个不同的时间源,其中“主要”“主要 + 备用”、“主要 + 门控”和“主要 + 监控”是常见配置。注意:从 2020 年第 4 季度起,仅支持主要来源和监控来源

Timekeeper 可自行决定是否将时间源中的信息应用于 UTC 时钟。它可能会舍弃具有严重不确定性或看似离群值的更新。如果需要应用明显的时钟校正,Timekeeper 必须平衡以下 3 个相互冲突的需求:

  1. 应尽可能避免步骤变更的时间。
  2. 用于通过偏差应用校正的频率调整不应过多。
  3. 通过调整来应用更正所需的时间不应过长。

Timekeeper 是控制设备上硬件振荡器性能的核心授权方。它会跟踪观察到的振荡器错误,并应用世界协调时间 (UTC) 更正来适应此错误。Timekeeper 使用 stash 存储重启期间的振荡器误差。注意:截至 2020 年第 4 季度,全频率校正算法尚未实现。

在具有实时时钟 (RTC) 的设备上,Timekeeper 负责在初始化期间读取 RTC,并定期更新 RTC 以反映 UTC 时钟。

时间源

每个时间源组件负责根据时间源与之通信的一个或多个远程源提供可用于同步 UTC 的信息。每个时间来源组件都会公开 fuchsia.time.external.PushSource 和/或 fuchsia.time.external.PullSource 服务。

许多时间源使用某种能够时间同步的协议(如 NTP、Roughtime 或 HTTPS)通过网络与服务器通信(请参阅 HTTPSDate 时间源)。某些时间源可以改为与本地硬件(如 GPS 接收器或 VLF 接收器)进行通信。

时间源组件封装了用于与远程源交互的协议的知识,并负责遵循此协议的规则。这意味着,当多个远程源使用相同的协议时,单个时间源组件实例通常负责与所有这些源组件进行通信,并实现协议中的任何跨远程源要求。

每个时间源都应独立于其他时间源,这样可以灵活选择时间源的安装方式,并避免多个时间源之间的交互导致的复杂故障模式。这意味着时间源绝不应在其实现中直接使用系统 UTC(因为系统 UTC 可能包含来自其他时间源的输入)。相反,所有时间源都使用系统单调时钟作为其参考时间。

接口

时间源到计时工具

时间源向 Timekeeper 提供时间更新。

每次更新都表示为更新有效时间的单调时钟与时间源确定的世界协调时间 (UTC) 之间的对应对。以对应对而非绝对时间发送更新意味着时间源和 Timekeeper 之间的 FIDL 延迟不会直接影响准确性,并且会清晰地传达更新最有效的单调时间。除了对应对之外,时间源还会发送标准偏差,以传达与更新相关的不确定性。

时间源可能支持两种不同的操作模式:

  • 推送模式下,时间源会确定应何时生成时间更新,并自动将这些更新发送给 Timekeeper。
  • 拉取模式下,Timekeeper 会确定应何时生成时间更新,并从时间源请求这些更新。

几乎在所有情况下都首选推送模式,因为时间源更透彻地了解协议的时间限制、远程资源利用率和任何依赖项的可用性,以便做出更合适的决策。拉取模式可能适用于微不足道的时间源,或者很少使用时间源(例如,每隔几小时)作为门控源的情况。使用拉取模式时,时间源可能会拒绝时间更新请求,例如,如果请求会违反协议中的最大速率限制。对于同时支持这两种模式的时间源,Timekeeper 会确定要使用哪种模式并连接到相应的服务。当没有打开这些服务的连接时,时间源可以选择生成更新的频率较低或完全不生成。

在推送模式下,Timekeeper 不知道何时更新,因此无法根据未成功更新推断失败:在推送时间模式下,时间源还会提供其整体运行状况,使 Timekeeper 能够更好地选择何时切换时间源。

时间源的计时器

Timekeeper 可提供可能有助于时间源生成时间更新的全局信息,包括:

  • 振荡器的频率公差。
  • 观察到的振荡器频率。

提供这些数据可确保时间源不需要重复 Timekeeper 功能。截至 2020 年第 4 季度,没有时间源需要这些数据,因此尚未定义此 API

Timekeeper 不会提供有关系统的全局同步状态的信息,也不会提供有关来自特定来源的更新是否实际用于维护系统时钟的信息。这种刻意的决策可避免在时间源之间形成反馈环,并可确保时间源在不同角色之间保持一致的行为。