无线下载更新 (OTA) 是一种通过 紫红色。本文档详细介绍了 OTA 更新在 Fuchsia 上的工作原理。
更新过程分为以下阶段:
正在检查更新
操作系统更新流程的两个入口点分别是 omaha-client
以及 system-update-checker
组件。
omaha-client
和 system-update-checker
的用途相同
以了解是否有操作系统更新并开始更新。
通常,商品应该使用 omaha-client
(如果它们想要
使用 Omaha 确定是否有可用更新。商品应使用
如果不想使用 Omaha,则需system-update-checker
想要直接从软件包代码库检查更新
在任意给定的 Fuchsia 系统上,只能有一个组件在运行:
使用 omaha-client 更新检查
在启动过程中,omaha-client
会启动并开始定期更新
检查。在这些检查期间,omaha-client
会轮询 Omaha 服务器以检查
更新。
使用 Omaha 的好处如下:
- 它允许在一组操作系统中对系统更新小部分进行发布 Fuchsia 设备。例如,您可以将其配置为 设备组会进行更新。也就是说,只有 10% 的设备 在轮询奥马哈时会看到有可用更新。通过 其余 90% 的设备将看不到可用更新。
- 它支持不同的更新渠道。例如,测试设备 通过测试渠道进行更新,并获取最新(可能不稳定) 软件开发应用。这样正式版设备便可从正式版中获取更新 并获取最稳定的软件。频道信息 连同产品和版本一起提供给 Omaha。
图 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) 启动更新。
图 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-client
、system-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。
图 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-resolver
、pkg-cache
。
netstack
等...
提交更新
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 现在能够进行垃圾回收。
- 现在允许进行后续系统更新。更新检查工具 发现新的更新,整个更新过程都将重新开始。