無線更新 (OTA) 是 Fuchsia 上的作業系統更新機制。本文件詳細說明 Fuchsia 中 OTA 更新的運作方式。
更新程序分為以下幾個階段:
正在檢查更新
作業系統更新程序的兩個進入點是 omaha-client
和 system-update-checker
元件。
omaha-client
和 system-update-checker
用途相同,可確認是否有作業系統更新並啟動更新。
一般來說,如果產品想使用 Omaha 來判斷更新的供應情形,則應使用 omaha-client
。如果產品不想使用 Omaha,而想直接從套件存放區檢查更新,則應使用 system-update-checker
。
在任何指定的 Fuchsia 系統中,只能執行下列其中一項元件:
使用 omaha-client 更新檢查
在啟動過程中,omaha-client
會啟動並開始定期更新檢查。在這些檢查期間,omaha-client
會輪詢 Omaha 伺服器來檢查更新。
使用 Omaha 的好處如下:
- 可讓在 Fauchsia 裝置上進行小規模的系統更新發布作業。例如,您可以設定只讓 10% 的裝置更新。這表示在輪詢 Omaha 時,只有 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-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:裝置目前正在執行假設的 OS 版本 1 (在運算單元 A 上),並開始更新至假設 OS 版本 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
將僅用要擷取的更新套件「替換」所設的保留套件。如果更新套件網址包含雜湊,就會清除保留的套件集,然後再觸發垃圾收集,然後重新擷取更新套件。
圖 4 system-updater
會指示 pkg-resolver
解析第 2 版更新套件。我們假設 system-updater
因空間不足而無法擷取更新套件,觸發了垃圾收集來剔除運算單元 B 參照的版本 0 blob,然後重試以成功擷取版本 2 更新套件。
如有需要,更新套件可能包含 update-mode
檔案。這個檔案用於決定系統更新是「一般」或「強制復原」模式。如果沒有更新模式檔案,system-updater
將預設為一般模式。
模式為強制復原時,system-updater
會將映像檔寫入復原作業,將運算單元 A 和 B 標示為無法啟動,然後啟動復原。詳情請參閱強制復原的實作。
驗證主面板相符
目前的執行系統中有一個主面板檔案位於 /config/build-info/board
。system-updater
會驗證系統中的主面板檔案與更新套件中的主面板檔案是否相符。
圖 5:system-updater
會驗證更新套件中的主面板與插槽 A 上的主機板是否相符。
確認支援週期
更新套件包含 Epoch 紀元檔案 (epoch.json
)。如果更新套件的週期 (目標週期) 低於 system-updater
(來源訓練週期) 的週期,則 OTA 會失敗。如要進一步瞭解相關資訊,請參閱 RFC-0071。
圖 6:system-updater
會將更新套件與當前 OS 的週期進行比較,藉此驗證更新套件中的週期。
取代保留的檔案組合
替換以目前的更新套件和之後在 OTA 程序中擷取的所有套件。
保留的套件組合是一組保護套件,它不會進行垃圾收集 (除了靜態和動態索引中的套件以外)。用於防止垃圾收集,刪除目前更新程序所需的 BLOB。舉例來說,假設某部裝置已擷取更新所需的某些套件,然後因不相關原因而重新啟動。裝置再次啟動 OTA 時,系統仍會要求裝置在重新啟動前擷取所擷取的套件,但這些套件不會受到動態索引保護 (例如保留的套件組合會在重新啟動時清除)。將這些套件新增至保留的套件集後,system-updater
就能觸發垃圾收集 (例如移除先前系統版本使用的 blob),不必復原先前的工作。
觸發垃圾收集作業
系統會觸發垃圾收集,刪除舊系統專用的所有 BLOB。 這個步驟可以釋出額外空間,供任何新包裹使用。
圖 7. system-updater
會指示 pkg-cache
將舊系統專屬的所有 BLOB 收集為垃圾。在這個範例中,這表示 pkg-cache
會剔除版本 1 更新套件只參照的 BLOB。
擷取其餘套件
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 清除至永久儲存空間。這個程序可確保 BlobFS 提供系統更新的所有必要的 BLOB。
圖 8. system-updater
會指示 pkg-Resolver 解析 packages.json
中參照的版本 2 套件。
撰寫圖片以封鎖裝置
system-updater
會決定要將哪些圖片寫入區塊裝置。映像檔、資產和韌體有兩種。
接著,system-updater
會指示分區映像檔安裝工具編寫系統啟動載入程式和韌體。
為避免閃光燈佩戴,只有在映像檔與區塊裝置上既有映像檔不同時,圖片才會寫入分區。
接著,system-updater
會指示分區映像檔安裝工具寫入 Fuchsia ZBI 及其 vbmeta。
否則,分區映像檔安裝工具體會同時寫入 A 和 B 分區 (如果 B 分區存在)。
最後,system-updater
會指示分區映像檔安裝工具寫入復原 ZBI 及其 vbmeta。
圖 9. system-updater
會透過分區映像檔安裝工具將第 2 版圖片寫入 B 槽。
將替代分區設為啟用
如果裝置支援 ABR,system-updater
會使用分區映像檔安裝工具將替代分區設為啟用。這樣一來,裝置就會在下次啟動時啟動至替代分區。
您可以透過幾種方式參照運算單元狀態。例如,內部分區映像檔安裝工具使用 Successful
,而 FIDL 服務使用 Healthy
,而其他情況則可能使用「Active」、「Active」、「Bootable」、「Unbootable」、「Current」、「Alternative」等...
重要的中繼資料是每個核心運算單元儲存的 3 項資訊。這項資訊有助於判斷每個核心運算單元的狀態。舉例來說,在運算單元 B 標示為有效前,中繼資料可能如下所示:
中繼資料 | 版位 A | 版位 B |
---|---|---|
優先順序 | 15 | 0 |
剩餘點數 | 0 | 0 |
健康* | 1 | 0 |
運算單元 B 標示為有效後,中繼資料看起來會像這樣:
中繼資料 | 版位 A | 版位 B |
---|---|---|
優先順序 | 14 | 15** |
剩餘點數 | 0 | 7** |
健康料理 | 1 | 0 |
如果裝置不支援 ABR,由於沒有替代分割區,因此系統會略過這個檢查。而是會為每個更新寫入有效的分區。
圖 10. system-updater
會將運算單元 B 設為「Active」,以便在下次啟動時啟動,進入插槽 B。
Reboot
視更新設定而定,裝置不一定能重新啟動。裝置重新啟動後,裝置會啟動至新的插槽。
圖 11. 裝置會重新啟動進入運算單元 B,並開始執行版本 2。
驗證更新
一旦系統驗證更新,系統就會提交更新。
系統會透過下列方式驗證更新:
正在重新啟動,以使用更新版本
下次啟動時,系統啟動載入程式必須決定要啟動的運算單元。 在這個範例中,系統啟動載入程式會判定運算單元 B 的優先順序較高,而且剩餘的嘗試次數超過 0 次,因此系統啟動載入程式會判斷要啟動至運算單元 B (請參閱「將替代分區設為啟用」)。接著,系統啟動載入程式會驗證 B 的 ZBI 與 B 的 vbmeta,最後再啟動到運算單元 B。
提早啟動後,fshost
會使用新的系統映像檔套件啟動 pkgfs
。這是暫存更新時 packages.json
參照的系統映像檔套件。System-映像檔套件內含 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 現在可以進行垃圾收集作業。
- 目前已允許後續的系統更新作業。當更新檢查工具發現新的更新時,整個更新程序就會再次啟動。