Starnix 核心

Starnix 核心會在 Fuchsia 上實作 Linux 使用者空間 API (UAPI)。Starnix 核心會攔截 Linux 程序中的系統呼叫,並管理執行 Linux 程式所需的語意。本文將說明 Starnix 核心的內部結構。

愜意

Starnix 的目標是執行未修改的 Linux 二進位檔。如要直接執行這些二進位檔,Starnix 會維持與 Linux 核心的錯誤相容性。這項高保真互通性仰賴廣泛的測試。

如要瞭解 Linux UAPI 語意,測試會探查介面的邊緣和極端情況。這些測試在 Linux 核心上通過後,就會在 Fuchsia 的持續整合基礎架構中執行,一開始會標示為在 Starnix 上失敗。隨著 Starnix 改善,這些測試最終會通過,因此可以標示為通過。

單元測試與使用者空間測試

在大多數情況下,Starnix 的測試是編譯為 Linux 二進位檔的使用者空間程式。這種做法可讓相同的測試針對 Starnix 和 Linux 核心執行,確保行為一致。

在某些情況下,單元測試會在 Starnix 核心中執行。這些測試取決於未透過 UAPI 公開的實作詳情。雖然這樣可以測試內部邏輯,但無法保證行為與 Linux 核心相符。此外,隨著實作方式演進,這類測試通常需要更多維護作業。不過,如果情境較容易測試,且可存取核心內部項目,這個方法就相當實用。

在其他情況下,整合測試會執行使用者空間程式,並在 Fuchsia 端進行斷言。這些測試會驗證使用者空間不易存取的不變量。

track_stub! 巨集

Linux UAPI 語意非常廣泛。個別系統呼叫或虛擬檔案的功能可能比目前實作的功能更多。track_stub! 巨集會追蹤未實作的語意、記錄缺少的程式碼路徑,並將這些路徑連結至問題追蹤器錯誤。此外,這項工具也會檢測 Starnix 核心二進位檔,觀察 Linux 程式何時會觸發這些路徑。

從概念上來說,原始的 Starnix 核心實作項目是 syscall 編號的 match 陳述式,其中每個 syscall 都會呼叫 track_stub! 巨集。隨著對更精細的 Linux 程式支援度提高,實際實作取代了這些巨集。對於有多個選項或模式的系統呼叫,track_stub! 會推送至函式內,以「實作」缺少的選項。

內部資訊主頁會追蹤 track_stub! 巨集執行的頻率和情境統計資料。隨著 Starnix 功能擴充,track_stub! 巨集會持續追蹤進度。

結構

Starnix 核心由多個 Rust Crate 組成。Starnix 核心本身就是一個 Crate,其中包含 main.rs 這個主要進入點。核心機制位於依附元件圖的中央,也就是 starnix_core Crate。

流程模型

Starnix 核心會以工作內的一組 Fuchsia 程序執行。每個 Linux 位址空間 (概念上是 Linux 程序) 都會對應一個 Fuchsia 程序,此外還有一個額外的初始 Fuchsia 程序。這個額外程序會載入 Starnix 核心二進位檔,並開始執行。這個程序包含 Starnix 共用位址空間,但不包含受限位址空間

初始程序的執行緒會執行標準 Fuchsia 非同步執行器,處理 FIDL 要求,例如執行 Starnix 容器的要求。這個程序也包含背景執行緒 (稱為 kthread),可執行核心背景工作。在初始程序中執行這些執行緒,可確保執行緒的存續時間比可能觸發建立執行緒的使用者空間程序更長。

starnix_syscall_loop crate

建立後,使用者空間執行緒會進入主要的 syscall 迴圈,由 starnix_syscall_loop Crate 實作。在這個迴圈中,執行緒會進入使用者模式 (即受限模式),並處於特定機器狀態。當 Linux 程式結束使用者模式時,執行緒的控制權會返回 Starnix 核心,通常是因為系統呼叫、例外狀況或踢回核心模式。

Linux 程式發出系統呼叫時,starnix_syscall_loop Crate 中的 dispatch_syscall 函式會解碼系統呼叫,並叫用對應的系統呼叫實作函式。將 dispatch_syscall 與系統呼叫實作項目分開,可讓實作項目分散到多個 Crate。目前大部分的實作項目都位於 starnix_core,但有計畫要將這些項目移出,以簡化作業。

模組

許多 Starnix 核心功能都是以模組的形式導入。 在初始化期間,Starnix 核心只會初始化透過對應功能標記啟用的模組。模組通常會向 starnix_core 註冊。舉例來說,裝置模組會向 DeviceRegistry 註冊為特定裝置號碼的處理常式,而檔案系統模組則會向 FsRegistry 註冊。

starnix_core 不會直接呼叫模組。而是實作與所提供抽象概念對應的特徵。舉例來說,裝置模組會實作 DeviceOps 特徵,而檔案系統模組則會實作 FileSystemOps 特徵。這些特徵通常會傳回實作其他特徵的物件,例如 FileOpsFsNodeOps

需要核心全域狀態的模組應使用 kernel.expando 物件,而非在 Kernel 結構體上定義欄位。kernel.expando 會以 Rust 型別做為鍵,因此模組可以定義不重複的儲存空間位置,不會有衝突風險。這項機制也能避免為未使用的模組分配資源。

starnix_core crate

starnix_core Crate 包含 Starnix 核心的基礎機制,包括工作、記憶體管理、裝置註冊和虛擬檔案系統 (VFS)。這些緊密相關的子系統有許多循環依附元件。starnix_core 的大部分設計都記錄在原始碼的 rustdoc 註解中。

程式庫

starnix_core、模組或其他元件所需的程式碼,但獨立於 starnix_core,應位於 //src/starnix/lib 目錄中的個別 Crate。獨立 Crate 可釐清程式碼的依附元件關係圖,並縮短漸進式建構時間,因為對 starnix_core 的變更不會觸發這些獨立程式庫的重建作業。

UAPI

starnix_uapi Crate 位於 Starnix 核心依附元件圖表底部。它會為 Linux UAPI 概念定義符合人體工學的 Rust 型別。舉例來說,如果 Linux UAPI 定義了具有特定位元語意的 u32starnix_uapi 可能會使用 bitflags! 巨集定義對應的 Rust 型別。

starnix_uapi Crate 也定義了 UserAddress 型別,代表使用者空間位址。為避免意外取消參照,Starnix 核心會使用 UserAddress,而非 Rust 指標,做為使用者空間記憶體。這可降低兩項危害:

  • 使用者空間提供核心位址,以操控核心記憶體。
  • 存取使用者空間位址時,如果未使用安全 usercopy機制,核心就會恐慌。

starnix_uapi Crate 依附於 linux_uapi Crate,後者會使用 bindgen,根據 Linux 程式使用的 Linux UAPI C 定義自動產生。