Zircon 中的 C++

Zircon 樹狀結構中使用了一部分的 C++17 語言。其中包含核心程式碼和使用者空間程式碼。C++ 會在這兩個位置混合使用 C (以及部分組合)。請勿使用或禁止使用部分 C++ 語言功能。請務必使用 C++ 標準程式庫功能。

語言功能

  • 不允許
    • 例外狀況
    • RTTI 和 dynamic_cast
    • 運算子超載
    • 虛擬繼承
    • 靜態建構的物件
    • 結尾的傳回類型語法
      • 例外狀況:針對 lambda 搭配其他無法預測的傳回類型使用時
    • 初始化工具清單
    • 核心程式碼中的 thread_local
  • 已允許
    • 純介面繼承
    • Lambdas
    • constexpr
    • nullptr
    • enum class
    • template
    • 預設參數
      • 但請思考一下結尾處有一個選用的選用參數或許沒有問題,四個選用布林值引數,可能不是。
    • 素面舊課程
    • auto
    • 多項實作繼承
      • 但請保持禮貌。這廣泛使用,例如侵入式容器混合物。
  • 需要更多 TODO(cpu)
    • 全域建構函式
      • 目前我們提供的資料適用於全球資料結構。

待辦事項:要指向風格指南嗎?

C++ Standard 版

Zircon 程式碼使用 -std=c++17 建構而成,一般可以免費使用 C++ 17 語言和程式庫功能 (須遵守上述的樣式/功能限制,以及下方所述的程式庫使用規範)。在保持與 C++ 14 或舊版相容性方面,沒有任何「一般」疑慮。標準 C++ 17 功能是最簡潔的做法時,即可達到這個效果。

所有純 C 程式碼 (其使用的 .c 來源檔案和標頭) 都是 C 11。有些特殊的例外狀況,是允許樹狀結構外的系統啟動載入程式重複使用程式碼,內嵌程式碼會維持這種保守的 C 89 子集。

標準程式庫

C++ 標準程式庫 API 有許多介面各有不同的特性。我們根據每個特定介面的程式碼產生及使用機器和 OS 設備的可預測性和複雜度,將標準程式庫 API 細分為下列幾個類別。這些點可視為 API 的同心圓,從最基本的 C 類子集,到完整的 C++ 17 API 為止。

背景脈絡的重要性

本節提供如何思考在系統整體上使用特定標準 C++ 程式庫 API 的影響。除了核心 (請參閱下一節) 之外,沒有硬性和快速規則,除了實作限制之外,這些限制始終希望是暫時性的。

過度使用的規則可能遭到規避

  • 請評估您掌握時間和空間的複雜度、動態配置行為 (如有),以及您使用的每個 API 的失敗模式。

  • 接著,請考量使用特定「情境」,以及該背景資訊與各種疑慮的機密程度。

  • 特別要注意「受輸入」的行為,在使用未複雜的程式庫設施時,可能很快就難以預測。

如果您打算在驅動程式庫中編寫主要 I/O 邏輯,或是任何在某種系統服務中任何類型系統服務的熱路徑、處理量或可靠性的熱路徑,則在依賴哪些程式庫設施時應盡量保守。從技術層面來說,這些內容可在使用者空間中找到 (但核心較少,請參閱下一節)。但實際使用的應用方式並不多您可能不想使用許多會在背景提供精細動態分配的 std 容器。這些資料庫會讓您難以瞭解、預測及控管服務的儲存空間/記憶體用量、配置行為、效能和穩定性。

儘管如此,即使是驅動程式庫,還是可以啟動並剖析設定檔或引數等使用者空間程式。針對所有不屬於熱路徑的非必要或啟動函式,如果使用更複雜的程式庫設施,或許能簡化工作。不過請注意,請留意程式碼的整體指標,例如最低/總計/峰值執行階段記憶體用量、程式碼膨脹 (同時使用裝置儲存空間和執行階段記憶體),以及對非預期故障模式的復原能力。建議不要只使用花俏的設定剖析程式庫,而是將驅動程式的程式碼大小和記憶體佔用空間加倍。

核心中沒有 std

C++ std 命名空間不能用於核心程式碼 (包含系統啟動載入程式)。部分未涉及 std:: API 的 C++ 標準程式庫標頭仍可直接使用。請參閱下一節。

核心程式碼中不應使用其他 C++ 標準標頭。值得一提的是,任何值得在核心中使用的程式庫設施 (例如 std::move) 都是透過核心專用 API (例如 ktl::move) 提供。實際上,這些 API 的核心實作方式可能仰賴提供核心 API 名稱的 std:: 實作的工具鍊標頭。不過,只有這些 API 實作,以及特定程式庫標頭中的特殊情況,才能使用 std:: 內建於核心的原始碼。

通用標頭

這些標頭 API 可以安全地在任何位置使用,即使在核心中也是如此。

這些程式庫在核心支援的標準 C 介面子集中加入 C++ 包裝函式:

這些標頭中的 C 程式庫 API 的 std 命名空間別名不應用於核心程式碼。

核心中也可以使用一個純 C++ 標頭:

保守的使用者空間

這些標頭 API 可以安全地使用。核心不允許在核心內,因為它們完全位於 std 命名空間中。但如果在核心程式碼中使用這類 API,則這些 API 的子集可能適合取得核心 API 別名。

這些都是純標頭類型和範本。不會自行進行動態配置每個函式的時間和空間複雜度應從說明中清楚明瞭。

這牽涉部分動態分配功能,但僅限明確說明:

  • <any>
  • <memory>

    「請勿」使用 std::shared_ptrstd::weak_ptrstd::auto_ptr API。請改用 std::unique_ptrfbl::RefPtr

僅限使用者空間

這些功能並非完全可用,或由核心中的類似 API 或名稱所取得。但在使用者空間中 基本上一般是無害的不會涉及動態分配。

廚房水槽

這牽涉到較難預測的動態分配,而且通常讓您無法掌控。我們通常很難判斷確切的執行階段行為和記憶體需求。在確保可靠性或效能的重要路徑中使用這些介面之前,或是為了精簡和空間效率的任何元件使用這類介面之前,請審慎考慮。

FBL

FBL 是 Fuchsia Base Library,可在核心和使用者空間之間共用。因此,FBL 具有非常嚴格的依附元件。舉例來說,FBL 無法依附 syscall 介面,因為核心中沒有系統呼叫介面。同樣地,FBL 不得依附於核心中無法使用的 C 程式庫功能。

  1. system/ulib/fbl,可從核心和使用者空間使用。
  2. kernel/lib/fbl,您只能從核心使用該 API。

FBL 提供:

FBL 對記憶體配置設有嚴格控制。記憶體配置應較為明確,並使用 AllocChecker 讓用戶端從配置失敗復原。在某些情況下,允許隱含記憶體配置,但隱式配置記憶體的函式必須 #ifdef' 無法在核心中使用。

FBL 不適用於平台來源樹狀結構。

ZX

ZX 包含適用於 Zircon 物件系統呼叫的 C++ 包裝函式。這些包裝函式可提供類型安全,並移動控點的語意,但除了 syscalls.abigen 中的外,沒有任何意見。未來在某些情況下,我們可能會從 syscalls.abigen 自動產生 ZX,就像使用其他語言自動產生系統呼叫包裝函式的方式類似。

ZX 是 Fuchsia SDK 的一部分,

FZL

FZL 是 Fuchsia Zircon 程式庫,這個程式庫可為核心物件相關的常見作業提供附加價值,而且您可以自由針對如何與 Zircon 系統呼叫互動。假使程式碼沒有依附於 Zircon 系統呼叫,程式碼應改為在 FBL 中運作。

平台來源樹狀結構外無法使用 FZL。

密封 C++

建議您在 Fusia 中使用 C++ 而非 C 做為實作語言。然而,在許多情況下,我們都需要狹窄的 ABI 瓶頸,以便簡化預防、追蹤或適應 ABI 偏移的問題。如要簡化 ABI,第一個關鍵方法是以純 C API 為基礎 (可從 C++ 直接使用,或透過來自許多其他語言的外函式介面) 建立,而非 C++ API。當我們使用純 C 外部 API 和 ABI 將程式碼主體連結至模組時,但會在內部使用 C++ 進行實作,稱為「themetic C++」

  • 據說,核心本身即可實作在密封的 C++ 中。
  • vDSO 是在密封 C++ 中實作的共用程式庫。
  • Fuchsia 的標準 C 程式庫,大部分在 C 中實作,也會在實作中使用密封的 C++。
  • 大多數的 Fuchsia 裝置驅動程式已在密封的 C++ 中實作。

對於從 Fuchsia 公開 SDK 匯出的二進位檔而言,共用程式庫必須具備純 C API 和 ABI,這是困難又快速的規則。這類程式庫也應在實作中使用 C++ (而非 C),而且可以將其他靜態連結的程式庫與 C++ API 搭配使用,前提是這些內部 C++ API 的 ABI 部分不會洩露到共用程式庫的公開 ABI。

「可載入模組」(有時稱為「外掛程式」模組) 與共用程式庫非常類似,與純 C ABI 瓶頸相關的規則也適用於可載入的模組 ABI。Fuchsia 裝置驅動程式就是這類可載入的模組,且必須滿足驅動程式庫 (純 C) ABI。因此,在 C++ 中實作的每個驅動程式庫都必須使用密封 C++。

Fuchsia C++ 工具鍊提供使用 libc++ 實作的完整 C++17 標準程式庫。在 C++ 執行檔 (以及具有 C++ ABI 的共用程式庫) 中,這通常會以動態方式連結,這也是編譯器的預設行為。工具鍊也會透過 -static-libstdc++ 切換至編譯器 (clang++),進行密封靜態連結libc++。在 Zircon GN 建構系統中,連結目標 (例如 executable()test()library() (含 shared = true)) 會使用以下這一行來要求密封 C++ 標準程式庫:

    configs += [ "//zircon/public/gn/config:static-libc++" ]

在透過 sdk = "shared"二進位格式匯出公開 IDK 的每個 library() 中,此為必要元素。

每個 driver() 會自動使用密封的 C++,因此不需要這一行。(駕駛人不能依賴自己的共用資料庫,只有驅動程式庫 ABI 提供的動態連結環境)。

對執行檔和非匯出的共用程式庫而言,這是一種判斷呼叫,不論使用的是標準 C++ 程式庫的靜態連結或動態連結。在 Fuchsia 套件部署模型中,與其他系統中一樣,使用共用程式庫沒有特定的可更新性改善措施。主要的取捨是指從多個已儲存套件的記憶體和儲存空間節省量,以及使用個別套件完全相同的共用資料庫二進位檔和密集度 (有時是效能) 執行系統執行程序之間所節省的費用。由於系統建構作業中的許多套件都會使用相同的共用 libc++ 程式庫,因此除非有特殊情況,否則這是應該採取的做法。這是編譯器和建構系統中的預設選項。