RFC-0238:VMO 大小

RFC-0238:VMO 大小
狀態已接受
區域
  • 核心
說明

定義 VMO 的大小和串流大小。

變更
  • 891370
作者
審查人員
提交日期 (年/月)2024-01-10
審查日期 (年/月)2024-01-10

摘要

Zircon 會針對每個 VMO 與兩種不同大小建立關聯:VMO 的「大小」(頁面精細程度),以及 VMO 的「內容大小」 (以位元組精細程度為準)。此 RFC 可合理地在 Zircon 系統介面中處理這兩種大小。

提振精神

Zircon 系統介面上 VMO 相關部分的處理 VMO 大小內容大小處理方式不一致。這種不一致的問題是這些介面的演進方式。我們有時會嘗試以位元組精細程度的方式,以位元組精細程度來表示每個 VMO 的大小,以前則是以位元組和頁面精細程度為準。這種不一致會導致 Zircon 系統介面難以改進,例如添加更精細的分頁介面。

相關人員

誰擔心是否接受這個 RFC?(本節為選填,但建議填寫)。

講師:

  • hjfreyer@google.com

審查者:

  • cpu@google.com
  • csuter@google.com
  • jamesr@google.com
  • rashaeqbal@google.com

顧問:

  • adanis@google.com
  • dworsham@google.com
  • sagebarreda@google.com

社群媒體化:

本 RFC 中的提案源自本機儲存空間、虛擬記憶體管理員和架構團隊之間的一系列討論。思考過各種做法後,這個群組決定對 Zircon 系統介面進行一些合理的變更,如下所述。

相關規定

  • 完成變更後,系統「必須」能繼續運作。舉例來說,檔案「必須」具有位元組精細程度,因為大量的程式碼取決於位元組精細程度的檔案長度。
  • 在核心和硬體中實作的設計「必須」高效率。舉例來說,記憶體對應「必須」以頁面分解為精細,因為這是硬體支援。
  • 這項設計「應」盡量減少對核心或 SDK 程式庫對任何公用介面所做的變更。舉例來說,FDIO 的公用介面可將 VMO 轉換為檔案描述元,但不包含任何其他背景資訊,「應該」保留下來。然而,如果遷移用戶端不行,則允許小幅變更公開介面。
  • 設計應盡可能簡單。虛擬記憶體管理員已是系統中複雜程度的一大來源。每當系統的這個部分更加複雜時,都必須謹慎行事。

本文件中的重要字詞「必須」、「不得」、「必要」、「應」、「不應」、「應該」、「不應該」、「建議」、「可能」和「選用」均採用 IETF RFC 2119 說明。

設計

就概念上來說,VMO 是大量記憶體頁面的稀疏集合。VMO 用於整個系統中的多種用途,包括儲存及共用位元組精細程度的資料。視儲存在 VMO 中的記憶體類型而定,VMO 有多種類型。例如,VMO 可能包含受呼叫器或實體記憶體支援的虛擬記憶體,與硬體資源具有更具體的關係。

VMO 具有「大小」,代表可在 VMO 中儲存的記憶體數量上限。這個大小一律為頁面大小的倍數,因為 VMO 會在頁面精細程度儲存資料。用戶端可以使用 zx_vmo_get_size 函式瞭解 VMO 的大小。如果使用 ZX_VMO_RESIZABLE 選項建立 VMO,用戶端可以使用 zx_vmo_set_size 函式變更 VMO 的大小,並假設其擁有 ZX_RIGHT_RESIZE 控制代碼。大多數的 VMO 作業都會與這個大小互動。

VMO 也有「串流大小」,代表儲存在 VMO 內儲存的資料串流中的位元組數 (如有)。串流大小會在建立 VMO 時初始化,通常是依據建立 VMO 之函式的引數,而且不一定是頁面大小的倍數。串流大小絕不會大於 VMO 大小,因為儲存在 VMO 中的資料串流不能大於 VMO 中可儲存的記憶體上限。用戶端可使用 zx_vmo_get_steam_size 函式瞭解 VMO 的串流大小。如果 VMO 適合儲存資料串流,用戶端可以使用 zx_vmo_set_stream_size 函式變更 VMO 的串流大小,假設其具有 ZX_RIGHT_WRITE 的控制代碼。zx_vmo_set_size 函式不會修改串流大小,只是會強制執行串流大小絕不會大於 VMO 本身大小的不變性。用戶端通常會在與 VMO 相關聯的串流物件上使用 zx_stream_* 函式與串流大小互動。與相同 VMO 相關聯的所有串流物件會共用串流大小。

ZX_VMO_UNBOUNDED

用戶端使用 zx_vmo_create 建立 VMO 時,系統會使用 size 引數初始化 VMO 的大小和串流大小。VMO 的大小會初始化為「大小」,四捨五入至最接近的頁面邊界。VMO 的串流大小會初始化為「size」,沒有任何四捨五入。

這個 RFC 引入 zx_vmo_createzx_pager_create_vmoZX_VMO_UNBOUNDED 選項,用於建立大型的 VMO。如果用戶端提供此選項,則 VMO 的大小會初始化為可能的值上限,而不是根據 size 引數初始化。如果 VMO 大小無法提供實用用途,例如用於分頁式輔助檔案系統使用的 VMO,以及支援 C 執行階段堆積的 VMO,就適合使用「ZX_VMO_UNBOUNDED」ZX_VMO_UNBOUNDED選項。因此,ZX_VMO_UNBOUNDED 無法與 ZX_VMO_RESIZABLE 選項搭配使用。

ZX_VM_FAULT_BEYOND_STREAM_SIZE

這個 RFC 引入 zx_vmar_mapZX_VM_FAULT_BEYOND_STREAM_SIZE 選項。如果用戶端提供此選項,除了最後一個含有資料串流的 VMO 內頁面外,對應記憶體存取權也會出錯。對應關係仍可用於在資料串流外讀取及寫入記憶體,但最多只會使用下一頁邊界,因為記憶體對應是以頁面的精細程度為準。當具有這類對應類型的 VMO 調整大小時,Zircon 會移除資料串流以外頁面的頁面表格項目,日後存取這些網頁時就會產生錯誤。

ZX_VM_FAULT_BEYOND_STREAM_SIZE 選項適用於實作 mmap 語意,而這些語意與其他類似 POSIX 的作業系統 (例如Linux 和 OpenBSD)。在這類作業系統中,如果存取的記憶體超出最後一頁,而存取記憶體對應檔案的內容,就會產生錯誤。

骯髒範圍

zx_pager_query_dirty_ranges 日後的 RFC 可能會新增在 VMO 資料串流中查詢骯髒範圍的選項,而非目前的預設設定,可用來查詢整個 VMO 中的骯髒範圍。

ZX_PROP_VMO_CONTENT_SIZE

此 RFC 已淘汰 ZX_PROP_VMO_CONTENT_SIZE。用戶端應改用 zx_vmo_get_stream_sizezx_vmo_set_stream_size。Zircon 將會保留 ZX_PROP_VMO_CONTENT_SIZE 的舊版相容性,可預見日後會派上用場。不過,除了 ZX_RIGHT_GET_PROPERTYZX_RIGHT_SET_PROPERTY 外,取得並設定 ZX_PROP_VMO_CONTENT_SIZE 之外,也需要與 zx_vmo_get_stream_sizezx_vmo_set_stream_size 相同的權限 (即ZX_RIGHT_WRITE 用於設定 ZX_PROP_VMO_CONTENT_SIZE)。

實作

本節說明每個 Zircon syscall 的詳細變更。

zx_pager_create_vmo

如目前為止,VMO 的大小會初始化為「大小」,四捨五入至最接近的頁面邊界。VMO 的串流大小會初始化為「大小」,沒有任何四捨五入。

如果用戶端提供 ZX_VMO_UNBOUNDED 選項,此作業會建立 VMO,其大小會初始化為可能的最大值。

如果用戶端同時提供 ZX_VMO_UNBOUNDEDZX_VMO_RESIZABLE 選項,則這項作業會傳回 ZX_ERR_INVALID_ARGS

zx_pager_query_vmo_stats

沒有變更。不過,如果 VMO 包含資料串流以外的修改,這項作業可能會傳回非預期的結果。在日後的 RFC 中,我們預計會新增 zx_pager_query_dirty_ranges 選項,將查詢限制為資料串流。

zx_stream_create

如果 vmo 引數參照了以 zx_vmo_create_contiguousvmo_create_physical 建立的 VMO 物件,這項作業會傳回 ZX_ERR_WRONG_TYPE

zx_stream_writev

如果這項作業嘗試寫入資料流的結尾超過資料流,這項作業就會增加資料串流的大小,類似於 zx_vmo_set_stream_size 變更資料串流的大小。如果資料串流無法增加,例如因為新的串流大小超出 VMO 的「大小」,調整大小作業就會失敗。zx_stream_writev 說明文件會明確定義這種情況,但是大致來說,如果作業能夠寫入任何資料到 VMO 中並傳播錯誤,則 writev 作業大致上會因為部分寫入作業成功執行。

這些語意與先前作業的語意不同。在在此 RFC 之前,如有所需的「串流大小」會超出 VMO 的「大小」zx_stream_writev 會嘗試變更 VMO 的「大小」。經過這個 RFC 後,VMO 的大小就一律不會因串流作業而改變。

zx_vmo_create

如目前為止,VMO 的大小會初始化為「大小」,四捨五入至最接近的頁面邊界。VMO 的串流大小會初始化為「大小」,沒有任何四捨五入。

如果用戶端提供 ZX_VMO_UNBOUNDED 選項,此作業會建立 VMO,其大小會初始化為可能的最大值。

如果用戶端同時提供 ZX_VMO_UNBOUNDEDZX_VMO_RESIZABLE 選項,則這項作業會傳回 ZX_ERR_INVALID_ARGS

zx_vmo_create_child

這項作業僅對這個模式進行的變更:

  • ZX_VMO_CHILD_SNAPSHOT - 如果 size 不是頁面大小的倍數,這項作業會傳回 ZX_ERR_INVALID_ARGS。先前,這項作業支援未對齊的大小值,但這個行為可能會存在危險,因為子項 VMO 實際上可存取父項 VMO 中的整數數量。

    子項 VMO 的大小和串流大小都會初始化為 size 引數。如有需要,您可以單獨變更子項的串流大小。

  • ZX_VMO_CHILD_SNAPSHOT_AT_LEAST_ON_WRITE - 與 ZX_VMO_CHILD_SNAPSHOT 相同的行為類似。

  • ZX_VMO_CHILD_SLICE:與 ZX_VMO_CHILD_SNAPSHOT 相同的行為。

  • ZX_VMO_CHILD_REFERENCE:子項的串流大小會初始化為父項的「串流大小」

zx_vmo_create_contiguous

如果 size 不是頁面大小的倍數,這項作業會傳回 ZX_ERR_INVALID_ARGS

VMO 的大小會初始化為「大小」

VMO 的串流大小已初始化為 0,且無法修改。

vmo_create_physical

如果 size 不是頁面大小的倍數,這項作業會傳回 ZX_ERR_INVALID_ARGS

VMO 的大小會初始化為「大小」

VMO 的串流大小已初始化為 0,且無法修改。

zx_vmo_get_size

沒有變更。

zx_vmo_set_size

如果 size 引數不是頁面大小的倍數,這項作業會傳回 ZX_ERR_INVALID_ARGS

將 VMO 的大小覆寫為 size 引數和零 (即捨棄) 這個時間點之後的任何頁面。如果串流大小大於新的 VMO 大小,請配合新 VMO 大小截斷串流大小。

此行為與先前這項作業的行為不同,後者可用於增加 VMO 的內容大小。如果 VMO 數量超過目前大小,請先使用 zx_vmo_set_size 增加 VMO 的大小,然後使用 zx_vmo_set_stream_size 增加串流大小。

zx_vmo_get_stream_size

傳回 VMO 的串流大小

zx_vmo_set_stream_size

如果 size 引數大於 VMO 的大小,這項作業會傳回 ZX_ERR_OUT_OF_RANGE

將 VMO 的串流大小覆寫為 size 引數,並將 VMO 的其餘部分覆寫為零。此行為與先前設定內容大小的行為相符。

如果這項作業會縮減資料串流,只要頁面不再與對應 ZX_VM_FAULT_BEYOND_STREAM_SIZE 選項的資料串流,就不會對應。

在持續成長和縮減的情況下,這項作業可能會導致寫入時複製和使用者呼叫 VMO 的最後一個與資料串流重疊的頁面,以便在該頁面中儲存零。

帳號代碼必須包含 ZX_RIGHT_WRITE

zx_vmo_op_range

沒有變更。

zx_vmo_read

沒有變更。

這項作業會與 VMO 的「大小」(而非「串流大小」) 互動,這表示這項作業可以讀取資料串流以外的資料。

zx_vmo_write

沒有變更。

這項作業會與 VMO 的「大小」(而非「串流大小」) 互動,這表示此作業可將資料寫入資料串流以外的資料。

zx_vmar_map

如果用戶端提供 ZX_VM_FAULT_BEYOND_STREAM_SIZE 選項,則在 VMO 中,除了最後一頁包含資料串流的對應作業的記憶體存取錯誤。這個選項需要使用 ZX_VM_ALLOW_FAULTS 選項。

效能

這項 RFC 應該不會對效能造成影響。主要變更與 Zircon 回報 VMO 中繼資料的方式有關。每項作業的語意都經過選擇,可避免實作過程發生效能問題。

人體工學

VMO 的概念模型很複雜,因為某些作業會將 VMO 視為記憶體頁面的集合處理,而其他作業會與 VMO 中儲存的資料串流互動。頁面導向作業會以頁面精細程度運作,而串流導向的作業則會以位元組精細程度運作。這種複雜度之所以會產生複雜情況,是因為商用 CPU 中的虛擬記憶體硬體是根據頁面設計,但用戶端儲存在記憶體中的資料是以位元組為設計單位。

這個 RFC 會更明確地區隔以網頁為基礎的作業和串流式作業,藉此區分這些概念。例如,zx_vmo_set_size 是一種網頁式作業,因此需要頁面對齊的大小,而 zx_vmo_set_stream_size 屬於串流型作業,因此支援位元組對齊大小。

有些情況十分細微,例如 zx_vmo_readzx_vmo_write。合理的用戶端可能會認為這些作業與資料串流互動,因為他們會讀取及寫入資料。不過,我們選擇讓這些作業鏡像到對應記憶體上的 loadstore 作業可用,這表示這兩個作業會與 VMO 的頁面導向大小互動。

這種情況非常受限,尤其是在設計具備大量現有軟體的系統設計時,因路徑而異。我們已在設計上的限制下,盡可能找出最緊急的解決方案。

回溯相容性

這項變更會造成一些回溯相容性風險。舉例來說,zx_vmo_create_childzx_vmo_create_contiguouszx_vmo_create_physical 現在需要頁面對齊大小,而設定 ZX_PROP_VMO_CONTENT_SIZE 時,也需要額外的權限,這兩者都會破壞部分現有程式碼。我們相信我們可以遷移該程式碼,遵循這些新的限制,但可能無法執行這些遷移作業時,可能還需要放寬這些限制。

這項 RFC 中的其他變更不太可能導致回溯相容性問題。在大部分情況下,系統會保留現有的語意,僅以不同的字詞說明。

安全性考量

這項 RFC 中的主要安全風險是,除了 VMO 的資料串流外,資料可能還存在於 VMO 中。許多開發人員可能對這類行為感到驚訝,而意外回報往往是安全性風險。不過,在一般情況下使用這些作業會導致資料一律為零,其他廣泛使用的作業系統中也會出現這個風險。

隱私權注意事項

理論上,如果儲存在 VMO 內資料串流以外的資料,就可能是隱私權問題,導致資料意外揭露。但在正常作業中,VMO 資料串流以外的資料將會是零。

測試

我們會新增適當的 Zircon 核心測試,驗證系統呼叫語意的所有變更。

說明文件

在實作這些變更時,Syscall 說明文件會更新以反映新的語意。

缺點、替代方案和未知

我們已在專案期間嘗試了各種替代方案,但這些方法都效果不彰。在理想情況下,VMO 將擁有依據位元組精細程度或頁面精細程度建構的一致心理模型。然而,位元組精細程度的用途依然是強而有力的用途,因為業界已將 8 位元位元組標準化,做為資料的大小精細程度,而且在頁面精細程度上就有強烈的硬式限制來處理記憶體。因此,並非極端設計點。

實際上,這個 RFC 的主要替代方案是不執行任何操作,並保持 VMO 目前的雙大小模型。不過,我們認為這項 RFC 中的微幅變化能夠改善目前的模型。我們考慮過對模型進行較大的變更,但在每個案例中,我們都發現了用途或設計限制,導致設計回歸到這個較適宜的方法。

先前的圖片和參考資料

這個領域已有大量的先進技術。主要相關的技術是檔案與其他熱門作業系統中記憶體對應的互動方式。在我們研究的每個案例中,檔案的位元組精細程度,而記憶體對應則設有頁面精細程度。我們特別研究了檔案最後一個頁面的記憶體對應 (例如延伸到檔案內容結尾) 的記憶體對應情形。ZX_VM_FAULT_BEYOND_STREAM_SIZE 標記旨在讓我們複製我們在 Windows、macOS 和 Linux 中觀察到的語意。不過,由於 zx_vmar_map 預設不允許錯誤,因此我們並未選擇這些語意做為預設語意。我們預期大部分會建構記憶體對應的跨平台程式碼 (例如mmap) 會提供此旗標,讓不同作業系統之間的語意保持一致。