| RFC-0238:VMO 大小 | |
|---|---|
| 狀態 | 已接受 |
| 區域 |
|
| 說明 | 定義 VMO 的大小和串流大小。 |
| Gerrit 變更 | |
| 作者 | |
| 審查人員 | |
| 提交日期 (年-月-日) | 2024-01-10 |
| 審查日期 (年-月-日) | 2024-01-10 |
摘要
先前,Zircon 會將兩種不同大小與每個 VMO 建立關聯:VMO 的大小 (以頁面為單位) 和 VMO 的內容大小 (以位元組為單位)。這項 RFC 會在 Zircon 系統介面中,合理化這兩種大小的處理方式。
提振精神
Zircon 系統介面中與 VMO 相關的部分,在處理 VMO 大小和內容大小時不一致。這種不一致性是這些介面演進的產品。我們曾嘗試以位元組和網頁為單位,表示每個 VMO 的大小,在本 RFC 之前,我們也曾以位元組和網頁為單位表示大小。這種不一致性會導致 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 轉換為檔案描述元,且不會提供任何額外的內容資訊,這類資訊「應」保留。不過,如果遷移用戶端是可行的,則允許對公開介面進行小幅變更。
- 設計應盡可能簡單。虛擬記憶體管理員已是系統中複雜度的重要來源。我們需要謹慎地為系統的這部分新增更多複雜性。
本文中的「MUST」、「MUST NOT」、「REQUIRED」、「SHALL」、「SHALL NOT」、「SHOULD」、「SHOULD NOT」、「RECOMMENDED」、「MAY」和「OPTIONAL」等關鍵字,應按照 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_create 和 zx_pager_create_vmo 導入 ZX_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_VM_FAULT_BEYOND_STREAM_SIZE 選項,適用於 zx_vmar_map。如果用戶端提供這個選項,則對 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_size 和 zx_vmo_set_stream_size。在可預見的未來,Zircon 將保留 ZX_PROP_VMO_CONTENT_SIZE 的舊版相容性。不過,除了 ZX_RIGHT_GET_PROPERTY 和 ZX_RIGHT_SET_PROPERTY 之外,取得和設定 ZX_PROP_VMO_CONTENT_SIZE 分別需要與 zx_vmo_get_stream_size 和 zx_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_UNBOUNDED 和 ZX_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_contiguous 或 vmo_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 的大小會初始化為 size,並向上取整至最接近的頁面邊界。VMO 的串流大小會初始化為 size,且不會四捨五入。
如果用戶端提供 ZX_VMO_UNBOUNDED 選項,這項作業會建立 VMO,其大小會初始化為可能的最大值。
如果用戶端同時提供 ZX_VMO_UNBOUNDED 和 ZX_VMO_RESIZABLE 選項,這項作業會傳回 ZX_ERR_INVALID_ARGS。
zx_vmo_create_child
這項作業的變更會因模式而異:
ZX_VMO_CHILD_SNAPSHOT - 如果「大小」不是頁面大小的倍數,這項作業會傳回 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 的大小會初始化為 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 引數,並將 VMO 中較小的 size 和先前串流大小之間的空間歸零。這與先前設定內容大小的行為不同,因為先前只有從新的 size 到 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_read 和 zx_vmo_write。合理的用戶端可能會假設這些作業會與資料串流互動,因為這些作業會讀取及寫入資料。不過,我們選擇讓這些作業反映透過對應記憶體上的 load 和 store 作業提供的語意,這表示這些作業會與 VMO 的頁面導向大小互動。
尤其是要疊代設計大量現有軟體的系統時,這種情況會過度受限。我們已盡力在設計限制下,找出最符合人體工學的解決方案。
回溯相容性
這項變更會造成一些回溯相容性風險。舉例來說,zx_vmo_create_child、zx_vmo_create_contiguous 和 zx_vmo_create_physical 現在需要與網頁對齊的大小,且設定 ZX_PROP_VMO_CONTENT_SIZE 需要額外權限,這兩項變更都會導致部分現有程式碼中斷。我們相信可以遷移該程式碼,以符合這些新限制,但如果無法完成遷移,可能就需要放寬這些限制。
這份 RFC 中的其他變更不太可能造成回溯相容性問題。在大多數情況下,現有語意都會保留,只是以不同用語說明。
安全性考量
這項 RFC 的主要安全風險在於,資料可能存在於 VMO 內,超出 VMO 內的資料串流。許多開發人員可能會對這種行為感到意外,而意外往往會造成安全風險。不過,這些作業的典型用法會導致資料一律為零,而其他廣泛使用的作業系統也存在這種風險。
隱私權注意事項
理論上,如果 VMO 內資料串流以外的資料意外揭露,可能會造成隱私權問題。不過,在正常運作情況下,VMO 資料串流以外的資料會是零。
測試
我們會新增適當的 Zircon 核心測試,驗證系統呼叫語意的所有變更。
說明文件
實作這些變更後,系統會更新系統呼叫說明文件,反映新的語意。
缺點、替代方案和未知事項
我們在專案期間嘗試過各種替代方案,但成效都不理想。理想情況下,VMO 應建立一致的心理模型,以位元精細程度或網頁精細程度的大小為基礎。不過,以位元組為單位進行精細度調整有許多實用案例,因為業界已將八位元位元組標準化為資料的大小精細度,而且以頁面精細度處理記憶體時,會受到許多硬體限制。因此,這兩個極端都不是可行的設計點。
實際上,這項 RFC 的主要替代方案是不採取任何行動,並保留 VMO 目前的雙尺寸模型。不過,我們認為這份 RFC 中的小幅變更可改善現有模型。我們曾考慮對模型進行大幅變更,但每次都會發現用途或設計限制,導致設計回歸這種較為保守的做法。
既有技術和參考資料
這個領域有大量先前技術。主要相關的先前技術是檔案在其他熱門作業系統中與記憶體對應的互動方式。在我們研究的每個案例中,檔案都具有位元組細微程度,而記憶體對應則具有頁面細微程度。我們特別研究了檔案最後一頁的記憶體對應 (例如超出檔案內容結尾的記憶體對應) 運作方式。ZX_VM_FAULT_BEYOND_STREAM_SIZE 標記的設計目的是為了讓我們複製在 Windows、macOS 和 Linux 中觀察到的語意。不過,我們並未選取這些語意做為預設值,因為 zx_vmar_map 預設不允許錯誤。我們預期大部分建構記憶體對應的跨平台程式碼 (例如 mmap) 會提供這個標記,以便在各個作業系統中對齊語意。