RFC-0238:VMO 大小

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

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

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

摘要

先前,Zircon 與每個 VMO 建立關聯兩種大小:VMO 的大小、頁面精細程度,以及 VMO 的「內容大小」,位元組精細程度。這個 RFC 能合理化地在 Zircon 系統介面中處理這兩種大小。

提振精神

Zircon 系統介面中的 VMO 相關部分與 VMO size內容大小的處理方式不一致。這種一致性就是介面如何演變的產品。我們在多個時間點嘗試按位元組精細程度、頁面精細程度,以及之前的位元組與頁面精細程度表示每個 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 的大小。如果 VMO 是使用 ZX_VMO_RESIZABLE 選項建立,用戶端可以使用 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 的大小會初始化為 size,會無條件進位至最接近的頁面邊界。VMO 的串流大小會初始化為 size,不會經過任何捨入。

這個 RFC 導入了 zx_vmo_createzx_pager_create_vmoZX_VMO_UNBOUNDED 選項,可用來建立大型 VMO。如果用戶端提供此選項,則會將 VMO 的大小初始化為可能的上限值,而非根據 size 引數初始化。ZX_VMO_UNBOUNDED 選項適用於 VMO 大小無法提供實用用途的情況,例如由分頁式檔案系統使用的 VMO,以及支援 C 執行階段堆積的 VMO。因此,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 選項可用來實作與其他 POSIX 型作業系統 (例如mmapLinux 和 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 系統呼叫的詳細變更。

zx_pager_create_vmo

時至今日,VMO 的大小初始化為 size,會無條件進位至最接近的頁面邊界。VMO 的串流大小會初始化為 size,不會經過任何捨入。

如果用戶端提供 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 的 size

zx_vmo_create

時至今日,VMO 的大小初始化為 size,會無條件進位至最接近的頁面邊界。VMO 的串流大小會初始化為 size,不會經過任何捨入。

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

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

zx_vmo_create_child

這項作業僅適用於以下模式:

  • ZX_VMO_CHILD_SNAPSHOT - 如果「size」不是頁面大小的倍數,這項作業會傳回 ZX_VMO_CHILD_SNAPSHOT。這項作業先前支援未對齊的大小值,但這項行為可能非常危險,因為子項 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 的大小會初始化為 size

VMO 的串流大小已初始化為零,因此無法修改。

vmo_create_physical

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

VMO 的大小會初始化為 size

VMO 的串流大小已初始化為零,因此無法修改。

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 引數,並從較小 size 和先前的串流大小到 VMO 結尾的零。這與先前設定內容大小行為的做法不同,即只有新的「大小」發生至 VMO 結尾時才會零。這種差異表示串流大小增加新顯示的範圍時為零。

即使 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 核心測試,以驗證系統呼叫語意的所有變更。

說明文件

系統呼叫說明文件會隨著導入變更而更新,以反映新的語意。

缺點、替代項目和未知

我們在專案過程中嘗試了各種替代方案,但沒有任何缺點。在理想情況下,VMO 會擁有一致的心理模型,並以位元組精細度或分頁精細程度來建構。不過,位元組精細程度是相當不錯的用途,因為業界已經將 8 位元位元組的標準化處理為資料的大小精細程度,而且在處理頁面精細程度時,也設有嚴格的硬性限制。因此,任何極端方式都是自訂設計點。

在實際運作時,這個 RFC 的主要替代做法是不執行任何動作,而保留目前 VMO 的雙大小模型。不過,我們認為這個 RFC 中的些微變化,就能改善目前的模型。我們考慮到模型的大幅變更,但在每個案例中,都發現了有些用途或設計上的限制,使設計回歸到這種更適切的方法中。

優先藝術與參考資料

這個領域已有大量的舊藝術品。主要相關的發展原則是檔案與其他常用作業系統的記憶體對應互動的方式。根據我們研究的每個案例,檔案具有位元組精細程度,記憶體對應則是頁面精細程度。我們特別研究的是檔案最後一個頁面的記憶體對應方式 (例如延伸到檔案內容結尾以外的記憶體對應)。ZX_VM_FAULT_BEYOND_STREAM_SIZE 標記讓我們得以複製在 Windows、macOS 和 Linux 中觀察到的語意。不過,由於 zx_vmar_map 預設不允許使用錯誤,因此我們並未選取這些語意做為預設語意。我們預期大多數會建構記憶體對應的跨平台程式碼 (例如mmap) 會提供這個標記,讓不同作業系統的語意保持一致。