OTA 更新

无线下载更新 (OTA) 是一种通过 紫红色。本文档详细介绍了 OTA 更新在 Fuchsia 上的工作原理。

更新过程分为以下阶段:

正在检查更新

操作系统更新流程的两个入口点分别是 omaha-client 以及 system-update-checker 组件。

omaha-clientsystem-update-checker 的用途相同 以了解是否有操作系统更新并开始更新。

通常,商品应该使用 omaha-client(如果它们想要 使用 Omaha 确定是否有可用更新。商品应使用 如果不想使用 Omaha,则需system-update-checker 想要直接从软件包代码库检查更新

在任意给定的 Fuchsia 系统上,只能有一个组件在运行:

使用 omaha-client 更新检查

在启动过程中,omaha-client 会启动并开始定期更新 检查。在这些检查期间,omaha-client 会轮询 Omaha 服务器以检查 更新。

使用 Omaha 的好处如下:

  • 它允许在一组操作系统中对系统更新小部分进行发布 Fuchsia 设备。例如,您可以将其配置为 设备组会进行更新。也就是说,只有 10% 的设备 在轮询奥马哈时会看到有可用更新。通过 其余 90% 的设备将看不到可用更新。
  • 它支持不同的更新渠道。例如,测试设备 通过测试渠道进行更新,并获取最新(可能不稳定) 软件开发应用。这样正式版设备便可从正式版中获取更新 并获取最稳定的软件。频道信息 连同产品和版本一起提供给 Omaha。

图:使用 omaha-client 检查更新

图 1. 使用 omaha-client 进行了更新检查流程的简化版本。还有 用于控制 omaha-client 能否检查更新或应用更新的政策。

omaha-client 从 Omaha 服务器获得更新软件包网址后,omaha-client 告知 system-updater 启动更新。

使用 system-update-checker 更新检查

未使用 omaha-client 的设备会使用 system-update-checker。取决于 配置方式后,system-update-checker 会定期轮询 更新软件包如果没有 auto_update,这些检查会默认处于停用状态 。

为了检查是否有可用更新,system-update-checker 会检查 以下条件:

  • 当前正在运行的系统映像(位于 /pkgfs/system/meta 中)的哈希值是否与 更新软件包中系统映像的哈希值(可在 packages.json 中找到)?
  • 如果系统映像没有变化,则当前在系统上运行的 vbmeta 是 与更新软件包的 vbmeta 不同?
  • 如果没有 vbmeta,系统当前运行的 ZBI 是否与 ZBI 不同 更新软件包的名称?

如果其中任何一个答案是肯定的,则 system-update-checker 知道 更新软件包已更改。系统更新检查工具发现更新 软件包发生更改时,system-update-checker 会触发 system-updater 使用默认更新包 (fuchsia-pkg://fuchsia.com/update) 启动更新。

图:使用 system-update-checker 检查更新

图 2. 使用 system-update-checker 进行了更新检查流程的简化版本。

如果不需要更新,更新检查工具会保存 更新在服务器上看到的软件包。在后续检查更新时 系统会将所提取的更新软件包的哈希值与最后一个 已知哈希如果最新更新软件包的哈希值 自上次检查以来发生了更改,因此更新检查工具会将 vbmeta 和 根据更新中的相应映像,运行系统上的 ZBI 软件包。如果正在运行的 vbmeta 或 ZBI 不同 映像和更新软件包时,检查工具会启动系统更新。

监控

如果客户想要监控更新进度和状态,他们可以实施 fuchsia.update.AttemptsMonitor 协议,并为客户端提供 fuchsia.update.Manager FIDL 的 MonitorAllUpdateChecks() 方法 协议。fuchsia.update.AttemptsMonitor 实例将只能收到 或其他方法启动更新时或正在进行更新时,系统将显示此消息。这个 不会触发新的更新。

fuchsia.update.AttemptsMonitor 实例将收到 OnStart 消息 其中包含 fuchsia.update.Monitor 协议的服务器端。这样, 客户端来接收和处理 OnState 消息,以通知更新状态变化。

另一种方法是实现 fuchsia.update.Monitor 并提供客户端 传递给 fuchsia.update.Manager 协议的 CheckNow() 方法。这将 开始检查更新。它只会监控 并将在更新完成后关闭句柄。

暂存更新

无论更新是否由 omaha-clientsystem-update-checker、 甚至强制更新检查,则需要将更新写入磁盘。

更新过程分为以下步骤:

图:启动状态图

图 3. 设备目前运行的是假设的操作系统版本 1(在插槽 A 上),并开始 更新到假设的操作系统版本 2(到槽 B)。警告:这可能并不是 在实践中进行分区。

提取更新软件包

system-updater 使用 提供的更新软件包网址。然后,动态索引会更新为 引用新的更新软件包。更新软件包示例可能如下所示:

/board
/epoch.json
/firmware
/fuchsia.vbmeta
/packages.json
/recovery.vbmeta
/version
/zbi.signed
/zedboot.signed
/meta/contents
/meta/package

如果由于空间不足而导致提取失败,system-updater 会触发垃圾回收 用于删除静态索引或动态索引或动态索引中未引用的所有 BLOB 保留的软件包设置垃圾回收后,system-updater 将重新尝试提取。如果 重试失败,system-updater替换保留的 仅包含其尝试提取的更新软件包设置的软件包(如果更新软件包网址 包含 hash,否则会被清除 保留的软件包集),然后再次触发垃圾回收并重新尝试更新软件包 提取。

图:提取更新软件包

图 4. system-updater 指示 pkg-resolver 解析版本 2 update 软件包。我们假设 system-updater 未能提取更新软件包,原因如下: 空间不足,会触发垃圾回收以逐出槽 B 引用的版本 0 blob; 然后重新尝试获取版本 2 更新软件包。

(可选)更新软件包可能包含 update-mode 文件。此文件 确定系统更新是在 Normal 还是 ForceRecovery(强制恢复模式)下进行 模式。如果更新模式文件不存在,system-updater 默认为普通模式。

当模式为 ForceRecovery 时,system-updater 会将映像写入恢复模式, 将槽位 A 和槽位 B 标记为不可启动,然后启动到恢复模式。如需更多信息 请参阅 ForceRecovery 实现

验证面板匹配情况

当前运行的系统在 /config/build-info/board 中有一个板级文件。 system-updater 用于验证系统上的板级文件是否与板级文件匹配 文件。

图:验证板级匹配

图 5. system-updater 用于验证更新软件包中的开发板是否与开发板匹配 广告位 A。

验证系统是否支持 Epoch

更新软件包包含一个周期文件 (epoch.json)。如果更新周期 软件包(目标周期)小于 system-updater 的周期 (源周期),OTA 会失败。如需更多背景信息 请参阅 RFC-0071

图:验证系统是否支持 Epoch

图 6. system-updater 会验证更新软件包中的周期是否受到 与当前操作系统的周期进行比较。

替换保留的软件包集

将保留的软件包集替换为当前的更新软件包 以及稍后将在 OTA 流程中提取的所有软件包。

保留的软件包集是一组免受垃圾回收保护的软件包(在 (静态和动态索引中的软件包)。它用于防止出现垃圾 从删除当前更新过程所需的 BLOB 中进行收集。例如,假设有一个设备 提取了更新所需的一些软件包,然后由于不相关的原因重新启动。时间 设备再次开始进行 OTA 更新,但在重新启动之前仍需要提取的软件包,但那些 动态索引包不受动态索引保护(动态索引与保留的内容包设置一样, 重新启动)。将这些软件包添加到保留的软件包集之后,system-updater 便可以 则会触发垃圾回收(例如,移除以前系统版本使用的 blob),而不会撤消 工作。

触发垃圾回收

会触发垃圾回收以删除旧系统专有的所有 BLOB。 此步骤可为任何新软件包释放更多空间。

图:垃圾回收

图 7. system-updater 指示 pkg-cache 对以下指定的所有 BLOB 进行垃圾回收: 旧系统。在此示例中,这表示 pkg-cache 将逐出 版本 1 更新软件包。

提取剩余的文件包

system-updater 会解析更新软件包中的 packages.json 文件。 packages.json 如下所示:

{
  "version": 1,
  "content": [
    "fuchsia-pkg://fuchsia.com/sshd-host/0?hash=123..abc",
    "fuchsia-pkg://fuchsia.com/system-image/0?hash=456..def"
    ...
  ]
}

system-updater 指示 pkg-resolver 解析所有软件包网址。解析时 软件包管理系统,那么软件包管理系统仅提取更新所需的 BLOB,即 只有那些不存在的 BLOB。软件包管理系统提取整个 BLOB, 而不是与系统中当前可能存在的任何差异。

提取完所有包后,会触发 BlobFS 同步以刷新 将 BLOB 复制到永久性存储空间。这个过程可确保所有必要的 BLOB 。

图:提取剩余的软件包

图 8. system-updater 指示 pkg-resolver 解析版本 2 packages.json 中引用的软件包。

将映像写入块设备

system-updater 用于确定需要将哪些图片写入块 设备。映像、资源和固件有两种。

然后,system-updater 会指示 paver 写入引导加载程序,并 固件。这些图片的最终位置并不取决于 设备支持 ABR ,了解所有最新动态。为了防止闪光灯磨损 只有当该映像与 块设备上已存在的映像

然后,system-updater 指示铺路机写入 Fuchsia ZBI 及其 vbmeta。这些图片的最终位置取决于 支持 ABR ,了解所有最新动态。如果设备支持 ABR ,摊铺机编写了 Fuchsia ZBI 将其 vbmeta 复制到当前未启动的槽位(备用槽位)。 否则,paver 会将这些文件同时写入 A 分区和 B 分区(如果 B 分区为 B), 分区)。

最后,system-updater 会指示 paver 写入恢复路径, ZBI 及其 vbmeta。与引导加载程序和固件一样 位置不取决于设备是否支持 ABR ,了解所有最新动态。

图:将映像写入块设备

图 9. system-updater 通过 paver 将版本 2 映像写入槽位 B。

将备用分区设为活动分区

如果设备支持 ABR,system-updater 会使用 paver 设置 备用分区处于活动状态。这样,设备就会启动到备用 与系统下次启动时相同的分区

您可以通过多种方式引用槽状态。例如,内部 paver 使用 Successful,而 FIDL 服务使用 Healthy,而其他 case 可以使用“Active”、“active”、“active”、“Bootable”、“Unbootable”、“Current”、“Alternate”等...

重要元数据是为每个内核存储的 3 条信息 。此信息有助于确定每个内核插槽的状态。对于 例如,在槽位 B 被标记为活动槽位之前,元数据可能如下所示:

元数据 广告位 A 广告位 B
优先级 15 0
剩余尝试次数 0 0
健康* 1 0

将槽 B 标记为活动槽后,元数据将如下所示:

元数据 广告位 A 广告位 B
优先级 14 15**
剩余尝试次数 0 7**
健康餐饮 1 0

如果设备不支持 ABR,则会跳过此检查,因为没有备用分区。相反, 每次更新都会写入一个活跃分区。

图:将备用分区设置为活动分区

图 10. system-updater 将槽位 B 设置为活跃槽位,以便设备启动进入槽位 B 会在下次启动时自动修复

重新启动

设备不一定会重新启动,具体取决于更新配置。设备后 重新启动,则设备会启动到新槽位。

图:重新启动

图 11. 设备重新启动到槽位 B,并开始运行版本 2。

验证更新

一旦系统验证了该更新,系统就会提交该更新。

系统通过以下方式验证更新:

正在重新启动进入更新后的版本

下次启动时,引导加载程序需要确定要启动到哪个槽位。 在此示例中,引导加载程序确定启动到槽位 B,因为 槽 B 的优先级较高,并且剩余的尝试次数已超过 0 次(请参阅 将备用分区设为活动分区)。然后, 引导加载程序验证 B 的 ZBI 是否与 B 的 vbmeta 匹配,最后启动到 广告位 B。

前期启动后,fshost 会使用新的系统映像软件包启动 pkgfs。 这是 packages.json 中引用的系统映像软件包 更新过程中会发生此错误系统映像软件包具有 static_packages 文件 其中列出了新系统的基础软件包。例如:

pkg-resolver/0 = new-version-hash-pkg-resolver
foo/0 = new-version-hash-foo
bar/0 = new-version-hash-bar
...
// Note the system-image package is not referenced in static_packages
// because it's impossible for it to refer to its own hash.

然后,pkgfs 会将所有这些软件包作为基础软件包加载。软件包出现在以下位置: /pkgfs/{packages, versions}:表示已安装软件包 或已启用。然后,系统启动 pkg-resolverpkg-cachenetstack等...

提交更新

system-update-committer 组件会运行各种检查,以验证 更新成功。例如,它指示 BlobF 任意 读取 1MiB 数据。如果系统已在启动时提交,则这些检查 已跳过。如果检查失败,并且根据系统的配置方式, system-update-committer 可能会触发重新启动。

验证更新后,系统会将当前分区(槽位 B)标记为 Healthy。使用 将备用分区设为活动分区,即启动 元数据现在可能如下所示:

元数据 广告位 A 广告位 B
优先级 14 15
剩余尝试次数 7 0
健康餐饮 0 1

然后,备用分区(槽位 A)会被标记为不可启动。现在, 启动元数据可能如下所示:

元数据 广告位 A 广告位 B
优先级 0 15
剩余尝试次数 0 0
健康餐饮 0 1

在此之后,更新会被视为已提交。这意味着:

  • 系统始终会启动到槽位 B,直到下一次系统更新。
  • 系统不再启动到槽位 A,直到下一次系统更新 覆盖广告位 A。
  • 槽 A 引用的 BLOB 现在能够进行垃圾回收。
  • 现在允许进行后续系统更新。更新检查工具 发现新的更新,整个更新过程都将重新开始。