RFC-0016:啟動頁面大小

RFC-0016:啟動時間頁面大小
狀態已接受
區域
  • 核心
說明

將 PAGE_SIZE 常數替換為 vdsocall。

Gerrit 變更
作者
審查人員
提交日期 (年-月-日)2020-12-02
審查日期 (年-月-日)2021-01-12

摘要

在需要最佳效能的情況下,以 Fuchsia 為基礎的系統應可利用較大的頁面大小。為了讓這個可行的頁面大小成為執行階段常數,而非編譯時間常數,這個常數應由核心在啟動期間以某種方式決定,然後透過 VDSO 提供,以便在執行階段對使用者進行查詢。

提振精神

為達到最佳效能,系統應能根據靜態資訊或啟動時查詢的資訊,選取頁面大小。此外,靜態決策應隨需求或條件變更而變更,最好是不會破壞 ABI 的方式。

不同頁面大小的效能取捨也不同。較大的頁面可增加有效的 TLB 涵蓋率,進而降低 CPU 額外負擔,並按比例改善以頁面精細程度運作的任何演算法或作業的效能,例如頁面配置、錯誤和掃描。雖然大型頁面可減少頁面表格的記憶體使用率,但也會導致過度配置,造成記憶體浪費,因此較小的頁面可提供更理想的記憶體用量。

這項效能與記憶體使用率的權衡取捨會因硬體和系統工作負載而異。在啟動期間告知使用者頁面大小,可在核心編譯期間靜態變更頁面大小,或在核心啟動期間動態變更頁面大小,而不會破壞與使用者層級元件的二進位相容性。

設計

方法是在 VDSO 中新增額外常數,並搭配 VDSO 呼叫 (zx_system_get_page_size) 擷取該常數。之後,您可以將任何現有編譯時間常數的用途遷移至使用 VDSO 呼叫,直到可以移除編譯時間常數為止。

您也應為每個平台宣告最小和最大網頁大小。這可讓使用者瞭解要連結的最大頁面大小,以便確保元件可攜性。

實作

導入程序分為三個階段。雖然使用 C/C++ 名稱,但需要在所有 Fuchsia 支援的語言中執行等效作業。

  1. 新增 zx_system_get_page_size VDSO 呼叫和相關的 VDSO 常數,以及 PAGE_MIN_SIZEPAGE_MAX_SIZE 定義。
  2. PAGE_SIZE (或同等語言) 的用法遷移至使用 VDSO 呼叫
  3. 未使用時,請移除 PAGE_SIZE (或等同於該語言的定義)。

第一和第三個階段都很簡單,因此會是單一的小型 CL。

遷移階段應簡單明瞭,但應完成許多以元件為範圍的 CL。

雖然這並非本 RFC 的必要條件,但如要實際變更特定產品的頁面大小,也必須執行下列操作:

  1. 支援較大的頁面,實作低層級核心。
  2. 使用者元件 (例如 BlobFS) 需要修改,才能支援非 4 KiB 的頁面。
  3. 需要增加 ELF 區段的對齊方式,以免頁面需要重疊的安全性權限。

成效

雖然這會將編譯時間常數遷移至執行階段查詢,但由於網頁大小計算並未位於任何熱門路徑,因此不應對效能造成任何可測量的影響。不過,在執行 VDSO 呼叫的遷移作業時,請記下任何未在初始化或測試程式碼中發現的用途,並評估受影響元件的效能。

安全性考量

隱私權注意事項

測試

現有的測試應足以找出遷移期間可能發生的任何愚蠢錯誤。遷移元件中的任何程式碼時,應檢查測試的程式碼涵蓋率。

說明文件

zx_system_get_page_size VDSO 呼叫需要記錄。說明文件應說明

  • 這是最小的分頁大小,也是所有分配作業的基本單位。
  • vdsocall 絕不會失敗。
  • 頁面大小保證為 2 的冪次。
  • 讀取後,頁面大小就會成為常數,並可由使用者快取。

現有的 VMOs 說明文件和其他與記憶體相關的系統呼叫和物件,都已抽象化,且一律會參照「系統頁面大小」。

平台說明文件應記載頁面的最小和最大大小,並反映 PAGE_MIN_SIZEPAGE_MAX_SIZE 常數。這些值如下:

  • ARM aarch64:最小 4 KiB,最大 64 KiB。
  • x86-64:最小 4 KiB,最大 2 MiB。

缺點、替代方案和未知事項

系統頁面大小與使用者正確執行 VMO 作業或與其他 Fuchsia 服務實作通訊協定有很大關係。因此,我們不清楚非 Fuchsia 原生程式碼何時需要知道頁面大小,或對頁面大小有依賴性,但如果發生這種情況,可能需要修改原始碼才能移植。

雖然從編譯時間常數執行遷移作業在概念上並不複雜,但會導致程式碼產生大量變動,且過程中很可能會引入錯誤。

不過,移除對編譯時間常數的參照,並不表示程式碼實際上能夠容許不同的頁面大小。演算法有許多機會可以將目前 4KiB 頁面大小的假設納入,或是簡單定義自己的頁面大小常數。如果編譯時間常數已變更,這些也會是問題,因此應視為不相關的錯誤。

主要替代方案是繼續使用編譯時間常數,但要為特定產品修正它,或為特定產品修正其中的某些組合。針對特定產品進行修正可能適用於某些受到嚴格控管的產品,但不適合長期運作的產品,因為這類產品需要在長時間內跨不同硬體版本維持二進位相容性。要求以不同網頁大小建構多個二進位檔版本,雖然可提供所需的彈性,但會大幅增加開發人員的時間和儲存空間成本。一般來說,使用編譯時間常數會帶來許多缺點,唯一可見的優點是避免遷移。

頁面大小可能會隨著時間變化,或在不同元件之間有所不同,而非啟動時間常數。雖然這可提供極大的彈性,但由於 VMOs 等物件具有與網頁大小相關聯的語意,因此可在元件之間任意共用,因此嘗試使用不同的網頁大小,會對使用者造成不合理的負擔,因為他們必須同時查詢及避免網頁大小變更的競爭狀態。當頁面大小與系統頁面大小不同,對特定子系統有益時,應開發一些獨立機制,明確選擇 VMOs,或以其他方式最佳化頁面大小。

應用程式也需要知道頁面大小以位元為單位,才能執行位移算術。為此,您可以新增 zx_system_get_page_shift,也可以新增 zx_system_get_page_size,或以 zx_system_get_page_shift 取代 zx_system_get_page_size。由於使用轉換是微最佳化,因此只有在應用程式將 vdsocall 的結果快取時,才可能有益。因此,使用者將頁面大小轉換為偏移量並快取,就等同於將頁面大小轉換為偏移量。因此,將兩個變化版本都提供做為 vdsocall 並無實際好處。

既有技術與參考資料

Unix 衍生產品會透過 sysconf(_SC_PAGE_SIZE) 回報網頁大小。

PAGE_SIZE 編譯時間常數會做為常數提供,用於核心程式碼內部,並由部分發行版提供,做為 <sys/user.h> 的一部分,但這不是標準或可移植的常數。

Windows 會透過 GetSystemInfo() 系統呼叫回報頁面大小。

MacOS 會透過 sysctl() 呼叫或 vm_page_size 變數回報網頁大小。