RFC-0260:核心啟動時間支援

RFC-0260:支援核心啟動時間
狀態已接受
區域
  • Kernel
說明

列舉支援啟動時間軸所需的 Zircon API 變更。

問題
Gerrit 變更
作者
審查人員
提交日期 (年-月-日)2024-06-14
審查日期 (年-月-日)2024-09-25

問題陳述

單調時鐘暫停和啟動時間軸 RFC 指出,Fuchsia 必須提供啟動時間軸,回報自啟動以來經過的時間量,包括暫停至閒置狀態所花費的時間。該 RFC 提到需要進行核心變更,但未明確指出變更內容。

摘要

本 RFC 說明支援 Zircon 核心啟動時間軸所需的系統呼叫和 API 變更。

利害關係人

講師:jamesr@google.com

審查者:

  • adamperry@google.com
  • andresoportus@google.com
  • cja@google.com
  • costan@google.com
  • fmil@google.com
  • gkalsi@google.com
  • harshanv@google.com
  • johngro@google.com
  • maniscalco@google.com
  • mcgrathr@google.com
  • miguelfrde@google.com
  • surajmalhotra@google.com
  • tgales@google.com
  • tmandry@google.com

社交:

這項 RFC 是一組文件,每份文件都討論設計的各個部分。這些文件已傳送給核心團隊和整個機構的各種人員,並經過多次迭代。

需求條件

核心必須支援:

  1. 讀取啟動時間軸的目前值。
  2. 以開機時間軸為參考時間軸,建立自訂時鐘。
  3. 在啟動時間軸上建立計時器,但可喚醒系統的計時器會明確留待日後的 RFC。
  4. 在啟動時間軸中,回報記錄訊息和當機記錄的時間戳記。

請注意,我們不打算在啟動時間軸上支援其他與時間相關的系統呼叫 (例如物件或通訊埠等待),因為我們尚未找出這樣做的動機。如果出現相關用途,我們會在日後的 RFC 中重新考慮這項決定。

設計

Zircon API 推出新的時間型別別名

Zircon 目前提供多種時間表示類型:

  1. zx_time_t:代表以奈秒為單位的時間點。一般來說,這個時間點是從單調時間軸取樣,但並非一律如此;舉例來說,zx_clock_read 也會傳回 zx_time_t,但不在單調時間軸上。
  2. zx_duration_t:代表時間長度或兩點之間的時間距離。這個數量與時間軸沒有具體關聯,但一律以奈秒為單位。
  3. zx_ticks_t:代表平台特定刻度中的時間點或時間長度。

這些都是 int64_t 的別名,表示編譯器不會對任何這些實體提供型別檢查。這些標記僅用於提升程式碼的可讀性。如果將這些型別用於啟動時間軸,會大幅降低處理時間戳記的程式碼可讀性,因為開發人員無法輕易區分不同時間軸上的時間戳記。

因此,我們將推出新的型別別名,結構如下:

zx_<kind>_<timeline>_[units_]t

其中:

  • kindinstantduration
  • timelinemonoboot
  • units 可以是 ticks 或省略,在這種情況下,系統會將單位解讀為奈秒。絕大多數的程式碼都會使用奈秒時間,因此省略這個部分可讓常見情況更容易輸入。

現有的大部分 zx_time_t 用途都會遷移至 zx_instant_mono_t,現有的大部分 zx_duration_t 用途都會遷移至 zx_duration_mono_t。我們會逐一評估並遷移 zx_ticks_t 的用途。

現有別名不會移除,且會在需要模稜兩可的型別時使用 (例如,zx_clock_read 仍會傳回 zx_time_t)。從長遠來看,核心團隊的目標是移除這些模稜兩可的型別,但具體做法不在本文範圍內,將留待日後的 RFC。

時間的使用者空間型別

雖然前一節所述的時間新別名可提升 Zircon API 的程式碼清晰度和可讀性,但無法提供編譯器強制執行的型別安全。這類型安全機制將改由 Zircon 的 Rust 繫結C++ libzx 程式庫中的使用者空間系統呼叫包裝函式程式庫提供,這兩者都需要使用本 RFC 導入和修改的系統呼叫進行擴充。使用者空間程式碼應盡可能使用這些程式庫。

如要為強型別提供適當的 API,必須在每種語言中仔細設計,才能符合慣例並易於實際使用。這項作業不會倉促進行,且完成每種語言的翻譯並非必要條件,您仍可完成前一節詳述的 C API 類型遷移作業。

讀取目前的開機時間

我們將導入兩個系統呼叫,讓使用者模式讀取開機時間軸的目前值:

zx_instant_boot_t zx_clock_get_boot(void);
zx_instant_boot_ticks_t zx_ticks_get_boot(void);

第一個會以奈秒為單位回報時間,第二個則會以平台刻度為單位回報時間。這會反映單調時間軸上的對應系統呼叫。

請注意,單調時間軸和開機時間軸的刻度與秒數比率相同,也就是說,zx_ticks_per_second 的結果可用於將 zx_ticks_getzx_ticks_get_boot 的結果轉換為秒數。

建立自訂錶面

使用者模式時鐘是透過 zx_clock_create 系統呼叫建立,系統呼叫已接受 options 參數,因此我們不需要變更函式簽章。

而是會導入名為 ZX_CLOCK_OPT_BOOT 的新選項標記,指示新建立的時鐘使用開機時間軸做為參考時間軸。

時鐘的時間軸屬性 (單調和啟動) 將無法變更,也就是說,時鐘建立後就無法切換時間軸。

重新命名 zx_clock_details 中的欄位

zx_clock_details_v1_t 結構體包含兩個欄位,用於儲存從單調時間軸到時鐘合成時間軸的轉換:

typedef struct zx_clock_details_v1 {
  // other fields
  zx_clock_transformation_t ticks_to_synthetic;
  zx_clock_transformation_t mono_to_synthetic;
  // other fields
} zx_clock_details_v1_t;

這些欄位會重新命名,以強調現在會儲存從參照時間軸到時鐘合成時間軸的轉換:

typedef struct zx_clock_details_v1 {
  // other fields
  zx_clock_transformation_t reference_ticks_to_synthetic;
  zx_clock_transformation_t reference_to_synthetic;
  // other fields
} zx_clock_details_v1_t;

快速掃描程式碼庫後,發現使用這些欄位的使用者非常少,而且這些使用者全都在 fuchsia.git 以外。因此,就地重新命名應該相對簡單。

建立開機計時器

使用者模式計時器是使用 zx_timer_create 系統呼叫建立。這個系統呼叫會接受 clock_id 參數,方便您指定計時器應運作的時間軸。

因此,系統會導入名為 ZX_CLOCK_BOOT 的新 clock_id 值,指示新建立的計時器在啟動時間軸上運作。

與時鐘類似,計時器的時間軸屬性是不可變更的,也就是說,計時器建立後就無法切換時間軸。

請注意,系統會重複使用 zx_timer_set 設定啟動計時器。由於這個系統呼叫會將單調和啟動截止期限做為輸入內容,因此其參數型別仍為 zx_time_t

clock_id 新增至 zx_info_timer_t

使用者可以呼叫 zx_object_get_info,並使用 ZX_INFO_TIMER 主題取得計時器控制代碼的相關資訊。產生的 zx_info_timer_t 結構體會經過修改,納入建立計時器時使用的 clock_id

一般來說,這需要進行一些結構演變,但在這個情況下,結構已經有 4 個位元組的填補,這剛好足夠容納我們的 clock_id,後者會儲存為 4 個位元組的 zx_clock_t

更新 Zircon 結構中的時間戳記

Zircon 結構中目前有幾個時間戳記會回報單調時間,但應改為使用開機時間 (如有)。

請注意,如果時間戳記持續回報單調時間,功能上不會有任何變更,但類型會更新為 zx_instant_mono_t,如上方的型別別名一節所述。

zx_log_record_t 時間戳記

zx_log_record_t 是 Zircon 公開的結構,用於說明 debuglog 中訊息的結構。這個結構體包含 timestamp 欄位,該欄位是 zx_time_t,也是 ZX_CLOCK_MONOTONIC 上的點。這會更新為 zx_instant_boot_t,因此會是 ZX_CLOCK_BOOT 上的點。

當機記錄正常運作時間戳記

crashlog目前會回報使用單調時間的 uptime 欄位。由於單調時間不包含暫停期間,因此應修改為使用開機時間,且類型應更新為 zx_instant_boot_t

中斷 API 變更

中斷系統呼叫

Zircon 中斷 API 包含兩個使用時間戳記的系統呼叫:zx_interrupt_waitzx_interrupt_trigger。目前這些系統呼叫使用單調時間軸。不過,由於中斷可能會在暫停至閒置期間觸發,因此預設時間戳記行為會改用啟動時間軸。這有助於確保中斷時間戳記是唯一的,即使在暫停/繼續週期也是如此,但唯一性仍受時鐘解析度限制。使用單調時鐘會導致在暫停至閒置期間觸發的所有中斷,都具有相同時間戳記,也就是系統暫停的時間。

為維持與可能依賴單調時間戳記 (例如螢幕驅動程式) 的現有驅動程式相容性,我們會在 zx_interrupt_create 系統呼叫中新增名為 ZX_INTERRUPT_TIMESTAMP_MONO 的新標記。設定這個旗標後,相關聯的中斷物件會繼續使用單調時間戳記,用於 zx_interrupt_waitzx_interrupt_trigger 和中斷埠封包 (如下所述)。

新增「zx_object_get_info」主題

由於中斷物件會在建立時設定時間軸,因此我們必須在執行階段為使用者空間提供查詢特定中斷控制代碼時間軸的方法。為此,系統會在 zx_object_get_info 中新增 ZX_INFO_INTERRUPT 主題。傳遞這個主題會傳回下列結構體: typedef struct zx_info_interrupt { // The options used to create the interrupt object. uint32_t options; } zx_info_interrupt_t; 呼叫端隨後可以檢查 options 欄位中是否有 ZX_INTERRUPT_TIMESTAMP_MONO

libzx 包裝函式

libzx 程式庫提供 zx_interrupt_waitzx_interrupt_trigger 系統呼叫的 C++ 包裝函式。這些包裝函式必須更新,才能同時使用單調和啟動時間戳記。

通訊埠封包變更

有幾個包含時間戳記的連接埠封包需要修改。

zx_packet_interrupt_t

zx_interrupt_bind 系統呼叫可將中斷物件繫結至埠。繫結中斷觸發時,系統會在通訊埠上將 ZX_PKT_TYPE_INTERRUPT 封包排入佇列。這個封包包含 timestamp 欄位,指出中斷發生的時間。

為與更新後的 Interrupt API 保持一致,這些封包中的 timestamp 欄位也會預設改用啟動時間軸。這可確保所有中斷時間戳記 (無論是透過系統呼叫或中斷封包取得),都能在暫停/恢復週期中,提供一致的中斷時間檢視畫面。

不過,如果是使用 ZX_INTERRUPT_TIMESTAMP_MONO 標記建立的中斷,對應封包中的 timestamp 欄位會繼續使用單調時間戳記。這樣可確保與依賴這項行為的驅動程式相容。

zx_packet_signal_t

這個封包會排入繫結至物件的埠,方法是使用 zx_object_wait_async 系統呼叫,其簽名如下:

zx_status_t zx_object_wait_async(zx_handle_t handle,
                                 zx_handle_t port,
                                 uint64_t key,
                                 zx_signals_t signals,
                                 uint32_t options);

handle 集合中的信號在 handle 上斷言,且呼叫端在 options 欄位中傳遞 ZX_WAIT_ASYNC_TIMESTAMP 時,產生的封包會排入佇列至連接埠:signals

typedef struct zx_packet_signal {
  // ... other fields ...
  uint64_t timestamp;
  // ... reserved fields ...
} zx_packet_signal_t;

timestamp 欄位會記錄信號的判斷時間。視等待的物件類型而定,時間戳記會出現在不同的時間軸上。舉例來說,等待啟動計時器時,我們需要啟動時間戳記。

因此,我們建議採取下列做法:

  1. zx_object_wait_async 系統呼叫中新增 ZX_WAIT_ASYNC_BOOT_TIMESTAMP 旗標,表示 timestamp 欄位應使用啟動時間軸。
  2. timestamp 的型別切換為多型別別名 zx_time_t

我們會稽核 ZX_WAIT_ASYNC_TIMESTAMP 標記的所有現有用法,並確保在暫停單調時鐘前,所有需要開機時間戳記的呼叫端都已遷移。

實作

系統會先實作核心 API 的變更,並將各類變更 (讀取時間、時鐘、計時器等) 分成不同的 CL。

這些變更 (應該相對較小) 之後,我們會進行稍大的變更,在 Rust 和 C++ 系統呼叫包裝函式庫中新增啟動時間支援。這些異動將分兩階段進行:

  1. 第 1 階段會新增對這項 RFC 新增或修改的系統呼叫支援。 這需要修改所用的型別,但這個階段主要著重於功能變更。
  2. 第 2 階段會針對時間類型加入更嚴格的類型強制執行。這項工作會在第 1 階段後完成,因為釐清每種語言所需的確切型別結構可能需要較長時間。

效能

支援啟動時間所需的系統呼叫修改完全是附加性質,因此不應改變系統的任何現有效能特徵。

同樣地,在 Zircon 中更新各種結構的時間戳記欄位以使用啟動時間軸,應該不會對效能造成影響,因為計算啟動時間的成本與計算單調時間的成本相同。

人體工學

時間系統呼叫和型別的使用者可能必須注意,核心現在支援多個時間軸。不過,大多數程式應該只要使用單調時間即可。

回溯相容性

除了記錄時間戳記的時間軸語意變更外,這些變更都與來源和二進位檔回溯相容。

不過,許多現有程式碼可能需要從使用單調時間軸切換至開機時間軸,才能維持正確性。如要進一步瞭解我們如何處理這項問題,請參閱「單調時鐘暫停和啟動時間軸 RFC」。

安全性考量

我們預期這項提案不會造成任何安全疑慮。如要進一步瞭解原因,請參閱「Monotonic Clock Suspension and the Boot Timeline RFC」的安全性章節。

隱私權注意事項

這項提案不會影響系統隱私權,因為在核心中新增開機時間支援功能,不會涉及任何資料收集作業。

測試

核心測試將會修改,以執行新的系統呼叫和現有系統呼叫的修改。

說明文件

上述所有 Zircon API 修改內容都必須記錄下來。 具體來說,這表示:

  1. 記錄所有新的型別別名,並更新現有型別別名的文件。
  2. 記錄新加入的系統呼叫。
  3. 記錄新增至現有系統呼叫的新旗標。
  4. 記錄更新的各種時間戳記時間軸。

此外,我們也應更新 fuchsia.dev 上的時鐘說明文件,強調 zx_time_t 並未繫結至特定時間軸。

既有技術和參考資料