RFC-0238:VMO 大小 | |
---|---|
狀態 | 已接受 |
區域 |
|
說明 | 定義 VMO 的大小和串流大小。 |
Gerrit 變更 | |
作者 | |
審查人員 | |
提交日期 (年-月-日) | 2024-01-10 |
審查日期 (年-月-日) | 2024-01-10 |
摘要
先前,Zircon 會為每個 VMO 關聯兩種不同的大小:以頁面精細程度計算的 VMO 大小,以及以位元組精細程度計算的 VMO 內容大小。這份 RFC 會在 Zircon 系統介面中,合理化這兩種大小的處理方式。
提振精神
Zircon 系統介面中與 VMO 相關的部分,在處理 VMO 大小和內容大小時不一致。這種不一致性是這些介面演進方式的產品。在不同時間點,我們嘗試以位元組精細程度、頁面精細程度,以及在本 RFC 之前以位元組和頁面精細程度來表示每個 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 轉換為檔案描述符,但不會保留任何額外的背景資訊,而這些資訊應予以保留。不過,如果遷移用戶端可行,則可對公開介面進行小幅變更。
- 設計應盡可能簡單。虛擬記憶體管理器已是系統中複雜性的一大來源。每當我們在系統的這個部分增加複雜度時,都必須謹慎行事。
本文件中的關鍵字「MUST」、「MUST NOT」、「REQUIRED」、「SHALL」、「SHALL NOT」、「SHOULD」、「SHOULD NOT」、「RECOMMENDED」、「MAY」和「OPTIONAL」應依 IETF RFC 2119 所述進行解讀。
設計
就概念而言,VMO 是記憶體頁面的稀疏集合。在整個系統中,VMO 可用於許多用途,包括儲存和共用以位元組為單位的資料。視儲存在 VMO 中的記憶體類型而定,VMO 有各種類型。舉例來說,VMO 可能包含由分頁器或實體記憶體支援的虛擬記憶體,而這類記憶體與硬體資源的關係較為具體。
VMO 有大小,代表可儲存在 VMO 中的記憶體上限。這個大小一律是頁面大小的整數倍,因為 VMOs 會以頁面精細程度儲存資料。用戶端可以使用 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 的大小會初始化為大小,並捨入至最接近的頁面邊界。VMO 的串流大小會初始化為 size,且不會進行任何捨入。
本 RFC 針對 zx_vmo_create
和 zx_pager_create_vmo
引入 ZX_VMO_UNBOUNDED 選項,以便建立大型 VMO。如果用戶端提供這個選項,系統會將 VMO 的大小初始化為可能的最大值,而不是根據 size 引數初始化。ZX_VMO_UNBOUNDED 選項適用於 VMO 大小無法發揮實用功能的情況,例如用於 pager 支援檔案系統的 VMOs,以及用於支援 C 執行階段堆疊的 VMOs。因此,ZX_VMO_UNBOUNDED 無法與 ZX_VMO_RESIZABLE 選項搭配使用。
ZX_VM_FAULT_BEYOND_STREAM_SIZE
這份 RFC 為 zx_vmar_map
引進 ZX_VM_FAULT_BEYOND_STREAM_SIZE 選項。如果用戶端提供這個選項,則對應至 VMO 內含資料流的最後一個頁面以外的記憶體存取作業會失敗。對應項目仍可用於讀取及寫入資料流之外的記憶體,但只能讀取至下一個頁面邊界,因為記憶體對應項目的細緻度為頁面。當含有這類對應項目的 VMO 重新調整大小時,Zircon 會移除資料流以外頁面的頁面表項目,以便日後存取這些頁面時產生錯誤。
ZX_VM_FAULT_BEYOND_STREAM_SIZE 選項可用於實作與其他類似 POSIX 的作業系統 (例如 mmap
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_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 的大小會初始化為向上捨入至最近頁面邊界的大小。VMO 的串流大小會初始化為大小,且不會進行任何捨入。
如果用戶端提供 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 的大小會初始化為向上捨入至最近頁面邊界的大小。VMO 的串流大小會初始化為大小,且不會進行任何捨入。
如果用戶端提供 ZX_VMO_UNBOUNDED 選項,此作業會建立大小初始化為可能最大值的 VMO。
如果用戶端同時提供 ZX_VMO_UNBOUNDED 和 ZX_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
如果大小不是頁面大小的倍數,此作業會傳回 ZX_ERR_INVALID_ARGS。
VMO 的大小會初始化為 size。
VMO 的串流大小會初始化為零,無法修改。
vmo_create_physical
如果大小不是頁面大小的倍數,此作業會傳回 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 選項對應的資料串流重疊的頁面都會解除對應。
無論是擴增或縮減,這項作業都可能導致寫入時複製和使用者分頁器支援的 VMOs 提交與資料串流重疊的最後一個頁面,以便在該頁面中儲存零。
句柄必須具有 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 中繼資料的方式有關。我們已選擇每項作業的語義,以避免實作時發生效能問題。
人體工學
VMOs 的概念模型相當複雜,因為某些作業會將 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 的主要安全性風險是,資料可能會存在於 VMOs 中,而非 VMO 內的資料串流。許多開發人員可能會對這項行為感到意外,而意外總是會帶來安全風險。不過,這些作業的一般用途會導致資料一律為零,而其他廣泛使用的作業系統也存在這種風險。
隱私權注意事項
理論上,如果 VMO 中的資料串流以外儲存的資料意外洩漏,就可能會造成隱私權問題。不過,在正常運作期間,VMO 資料串流以外的資料都會歸零。
測試
我們會新增適當的 Zircon 核心測試,驗證對系統呼叫語義的所有變更。
說明文件
隨著這些異動實施,系統會更新系統呼叫說明文件,以反映新的語意。
缺點、替代方案和未知事項
我們在專案期間嘗試過各種替代方案,但都無法順利運作。理想情況下,VMOs 會根據位元組精細程度或頁面精細程度大小,建立一致的心理模型。不過,位元組精細程度有許多實用案例,因為業界已將八位元位元組標準化為資料大小精細程度,且有許多硬體限制,可用於處理頁面精細程度的記憶體。因此,這兩種極端做法都不是可行的設計方式。
實際上,這個 RFC 的主要替代方案是什麼都不做,並保留現有的 VMOs 雙大小模型。不過,我們認為這份 RFC 中較為溫和的變更,是對目前模式的改善。我們曾考慮對模型進行較大幅度的變更,但在每個案例中,我們都發現了使用情境或設計限制,因此將設計改回這個較為保守的方法。
既有技術與參考資料
這個領域有大量先前技術。與本發明最相關的先前技術是檔案與其他熱門作業系統中的記憶體對應方式。在我們研究的每個案例中,檔案都具有位元組精細度,而記憶體對應則具有頁面精細度。我們特別研究檔案最後一個頁面的記憶體對應方式 (例如延伸至檔案內容結尾) 如何運作。ZX_VM_FAULT_BEYOND_STREAM_SIZE 標記可讓我們複製在 Windows、macOS 和 Linux 中觀察到的語意。不過,我們並未將這些語意選為預設值,因為 zx_vmar_map
預設不允許錯誤。我們預期大部分跨平台程式碼會建構記憶體對應項目 (例如 mmap
) 會提供這個標記,以便在各作業系統中對齊語意。