| RFC-0131:FIDL 線路格式的設計原則 | |
|---|---|
| 狀態 | 已接受 |
| 區域 |
|
| 說明 | 我們將說明各種設計原則,這些原則是 FIDL 線路格式的基礎。 |
| Gerrit 變更 | |
| 作者 | |
| 審查人員 | |
| 提交日期 (年-月-日) | 2021-06-15 |
| 審查日期 (年-月-日) | 2021-09-29 |
摘要
我們將說明目前 (2021 年 9 月) 的設計原則,這些原則是 FIDL 傳輸格式的基礎。
提振精神
FIDL 線路格式會指定訊息的編碼 (和解碼) 方式,以及傳輸層級中繼資料的格式,例如交易訊息標頭。線路格式規格隱含理論界限,最佳實作方式可達到此界限。就像資料結構會對作業隱含特定的大 O 範圍一樣,連線格式也是如此。
在 Fuchsia 中,程序間通訊 (至少是控制層) 無所不在,且是透過 FIDL 進行,或預計透過 FIDL 進行。因此,線路格式會對作業系統的整體目標效能造成重大影響。同樣地,線路格式在多層防禦隱私權和安全性的過程中,也扮演重要角色。
2017 年 3 月,「FIDL 2.0」的設計完成。與後續開發項目相比,FIDL 2.0 是更靜態的版本。如需更多歷史背景資訊,請參閱 RFC-0027:用多少付多少。
線路格式規格的目標如下1:
- 在程序之間有效率地傳輸訊息。
- 一般用途,適用於裝置驅動程式、高階服務和應用程式。
- 僅針對 Zircon IPC 進行最佳化,可攜性並非目標。(這項目標後來已放寬。)2
- 針對直接記憶體存取進行最佳化;機器間傳輸並非目標。
- 僅針對 64 位元環境進行最佳化,不適用於 32 位元環境。
- 使用主機位元組順序的未壓縮原生資料型別、元素的第一個合適封裝,以及正確對齊,支援訊息內容的就地存取。
- 與 C 結構體記憶體內配置相容 (具有適當的欄位排序和封裝註解)。
- 結構大小固定且內嵌;大小可變的資料則會儲存在行外。
- 結構不會自行描述,而是由 FIDL 檔案描述內容。
- 結構體沒有版本控管,但介面可透過新方法擴充,以利通訊協定演進。(這項目標後來已放寬。)[^2]
- 不需要計算偏移量,算術運算量極少,因此可能發生溢位。
- 支援快速單次編碼和驗證 (做為合併作業)。
- 支援快速單次解碼和驗證 (做為合併作業)。
雖然線路格式的持續演進遵循非常具體的設計原則 (如上所述),但這些原則不一定會連同基本原理一併寫下來。這份 RFC 嘗試清楚地寫下這些設計原則。
設計
我們將說明 FIDL 線路格式的各種設計原則。
先顯示低價位等級
如果為了支援低階程式設計而必須犧牲高階程式設計 (反之亦然),我們通常會選擇啟用低階程式設計。
FIDL 必須符合 Fuchsia 低階通訊協定的需求,有時會在啟動程序中使用,例如當 malloc 尚未提供時。如果 FIDL 無法滿足這些需求,替代做法是手動設計通訊協定。不過,在高階程式設計中,如果 FIDL 無法滿足需求,還有許多其他選項可供選擇 (例如 Protobuf、Cap'n Proto、JSON、Yaml 等)。
單次傳遞,且沒有堆積分配量
必須能夠在單一傳遞中編碼和解碼,且不得超出堆疊空間分配 (即不得動態分配堆積)。
這項原則某種程度上是為了避免過度專注於低層級用途,並確保系統上的任何軟體都能充分參與 FIDL 生態系統。
由於 FIDL 提供「解碼 + 驗證」,因此單次傳遞需求應與提供還原序列化和驗證的類似系統比較,這類系統通常會進行兩次傳遞 (驗證發生在解碼形式上)。
不配置記憶體的要求也表示編碼和解碼作業會在原地完成,也就是透過原地修改。
與手動建立的資料結構一樣有效率
必須能夠編寫線路格式的實作項目,且效率與手動捲動的資料結構相同。
這是「用多少付多少」原則的特例,也就是說,FIDL 旨在提供的便利性和人體工學設計,不得以效能為代價。在實務上,許多實作項目會選擇降低效率,以提供額外的人體工學,但線路格式不會規定這項選擇。
標準表示法
FIDL 值必須有單一明確的表示法,也就是說,FIDL 值只能有一個編碼表示法,也只能有一個解碼表示法。
強制使用單一表示法時,線路格式自然會更加嚴格,這表示實作項目必須預期輸入內容的變異較少,並遵循更直接的路徑。這有助於確保正確性,減少資料差異造成的意外。有了標準形式,您就能檢查兩個值是否相等,不必瞭解結構定義,也就是說,memcmp 足以做為值類型 (資源類型的情況稍微複雜)。
另請參閱標準形式的缺點。
指定每個位元組
編碼或解碼時,必須能夠在單一傳遞中遍歷訊息的每個位元組,且不會配置任何堆積。
為確保資料不會在傳送者不知情的情況下從一個程序洩漏到另一個程序,我們同時確保所有位元組都能有效率地遍歷,且所有位元組都有指定值 (例如填補必須為 0)。舉例來說,這有助於確保不會在無意間跨程序界線共用個人識別資訊 (PII),或避免洩漏可能含有指標值的未初始化記憶體,這些指標值可用於破解位址空間配置隨機化 (ASLR)。另一個例子是考慮「尾隨垃圾」,因為所有資料和控制代碼都必須列入考量,因此無效。
隨處驗證
為落實縱深防禦,我們希望 FIDL 線路格式在所有使用位置都能強制執行嚴格驗證 (例如邊界檢查、字串是否為格式正確的 UTF-8 程式碼單元序列、控制代碼是否為正確的類型和權限)。
嚴格驗證有助於確保平台安全,且可協助 API 作者在 API 結構定義中說明設計的假設和不變量,因此值得採用。根據我們的經驗,如果較低層級沒有驗證,應用程式往往會自行驗證不變量,導致程式碼不夠清楚、效率較低,且更容易發生錯誤。
由於嚴格驗證可能會導致效能成本過高,且 FIDL 的用途是低階層,因此這類驗證必須有效率地完成,並設計成單次傳遞。
沒有立即可用的反思功能
未明確選擇加入的情況下,不得允許同層級對通訊協定執行反映,無論是公開方法或公開型別。
舉例來說,如果對等互連端呼叫錯誤的 FIDL 方法,連線就會關閉,導致無法擷取對等互連端的任何資訊。建構這類功能似乎很方便,但可能會損害隱私權,而且難以復原 (使用者會開始根據這項功能建構負載功能)。
同樣地,缺乏自我描述格式的結構體也符合這項原則,目的是避免在互動同儕應互不信任的生態系統中,揭露超出必要的資訊。(避免使用自我描述格式也能大幅提升效能,這與低層級優先方法一致)。
由於我們已變更 FIDL 線路格式以利演進 (例如表格),因此必須謹慎地在禁止反映和新增足夠的項目之間取得平衡,以便在沒有結構定義的情況下處理。
實作
請保持冷靜,並遵循原則。如 RFC-0017 所示。
效能
FIDL 線路格式的大多數指導原則都以效能為目標,並專為低階用途而設計。效能是主要考量因素。
人體工學
人體工學設計不變。
回溯相容性
這裡列出的一些原則與 FIDL 的主要目標相衝突,也就是為穩定的 ABI 提供基礎,例如在沒有反射功能的狀況下,實作向後相容的通訊協定相當困難。FIDL 線路格式的設計在效能 (通常是彈性不足的結果) 和可演進性考量 (通常是彈性充足的結果) 之間取得平衡。如何平衡這些要素,就是遊戲的樂趣所在。
安全性考量
這份 RFC 說明瞭 FIDL 在 Fuchsia 多層式安全防護機制中扮演的角色。
隱私權注意事項
這份 RFC 說明瞭 FIDL 在 Fuchsia 多層式隱私權措施中的角色。
測試
測試方式不變。
說明文件
視需要進行修改:
缺點、替代方案和未知事項
如文字所述。
標準形式的缺點
要求使用標準化形式可能會限制尋找合適資料表示法的問題,甚至捨棄其他有趣或可追蹤的形式。
處理稀疏表格時,正規化是最難滿足的限制之一,且與格式效能需求直接衝突。舉例來說,我們可以在使用者提供的順序中寫入成員,不必進行第二次傳遞,重新排序這些成員來滿足正規化需求。
既有技術和參考資料
如文字所述。
-
作者:Jeff Brown jeffbrown@google.com。 ↩