摘要
在應用程式和周邊硬體之間轉移大量資料時,盡可能減少資料複製的數量十分重要。舉例來說,假設應用程式想要讀取元件永久儲存空間中的檔案,為此,應用程式會向檔案系統發出讀取檔案的要求,然後系統可能因此必須向區塊裝置傳送要求。視區塊分區拓撲而定,要求可能會在最終接觸可執行讀取作業的驅動程式庫之前,傳遞好幾層的驅動程式。
執行上述的一般方法可能會導致應用程式與硬體之間的各個層透過 Zircon 管道傳送 FIDL 訊息,進而產生許多資料副本。由於這種做法的效率不彰,因此 Google 不予採納。業界發現成功的模式後,我們將訊息分成兩層:控制層和資料層。透過控制層傳送的訊息非常小且費用低廉,而資料層中的訊息含有大量資料,複製費用會高昂。透過控制層傳送的訊息通常會使用以 Zircon 管道建構的 FIDL 通訊協定。資料層中的訊息是透過共用記憶體原始 Zircon VMO 傳送。
因此,簡易實作可能會選擇為每筆交易建立新的 VMO,以便透過控制層轉移,直到達到核發 DMA 的驅動程式庫為止,在 VMO 和最終驅動程式之間,就能達成所需目標,達成所需目標:在 VMO 與最終驅動程式之間。但是,成效可能不夠出色,原因如下:
- 為了發出 DMA 要求,您必須先固定記憶體,這需要呼叫核心,並選擇性地在 IOMMU 中設定頁面對應。
- 如果最終驅動程式庫需要將要求複製到特殊緩衝區 (因為並非所有硬體都支援指定行銷區域),您必須將 VMO 對應至其程序或呼叫核心,才能複製記憶體。
這兩者都非常高昂,因此我們需要更明智的做法:使用預先註冊的 VMO。方法是讓應用程式傳送一次性控制訊息,以便向堆疊中的最終驅動程式庫註冊 VMO。這則訊息的回應會傳回 ID,日後可能會用於參照 VMO。控制訊息應直接參照這個 ID,而非附加 VMO 控制代碼。登錄後,堆疊中的最終驅動程式庫可以執行一次昂貴的固定或對應作業,並快取結果。
VMO ID 注意事項
為了確保我們不會落入混淆代理攻擊,由於核心處理的是 VMO ID,我們必須維持相同的 VMO ID 相關不變。為了達到這個目的,各層用戶端的 VMO ID 不得重複,且每一層都必須驗證 ID 是否有效。更確切來說,使用 koid 做為 ID,仍然需要伺服器檢查含有該 koid 的 VMO 是否已經由用戶端註冊。
為了減少來回行程的次數,您可以允許用戶端在註冊 API 中將 VMO ID 命名,提高一次性 VMO 用量。或者,通訊協定可以指出一律將 VMO 的 Koid 當做 ID。
鋯石
為了進一步改善效能,部分通訊協定也可能會選擇將 FIFO 用於控制層。FIFO 降低了複雜度,得以降低成本。其中一項限制是他們無法轉移帳號代碼。因此,如要使用 FIFO,就必須使用 VMO 註冊模式。(請注意,仍須使用管道才能報名)。
程式庫
這個模式可能會增加驅動程式庫的複雜度,並維護 VMO 和 ID 之間的對應關係。我們建立了程式庫來協助實作,並存放在 //src/lib/vmo_store 中。如需使用範例,請參閱 //src/connectivity/network/drivers/network-device/device。
模式的缺點
在低處理量的情況下,這個模式顯然較為複雜,因此應該避免使用。
註冊 VMO 會導致一次性作業變成兩次來回。如果很常見,則 FIDL 通訊協定應繼續允許除了預先註冊的 VMO 外,還可使用一次性 VMO。您也可以透過允許用戶端在註冊期間提供 VMO 的 ID 來減輕負擔。
預先註冊的 VMO 可能會導致用戶端持續註冊 VMO,卻忘記取消註冊,導致記憶體「外洩」。此外,如果伺服器沒有謹慎管理用戶端,可能會忘記清除屬於用戶端 (可能會中斷與伺服器連線) 的已註冊 VMO。
以驅動程式庫預先註冊的 VMO,會導致支援 VMO 的頁面無法再分頁。
驅動因素
由於部分驅動程式位於相同的驅動程式代管程序程序中,而且我們採用迷你驅動程式庫模式,並將通用邏輯提升到「核心」驅動程式庫中,因此顯然是在核心驅動程式中執行 VMO 登錄作業,而不是裝置專用驅動程式庫。不過,基於以下原因,我們不建議這麼做:
- 核心驅動程式需要知道是要由裝置專屬驅動程式庫執行固定或對應作業。
- 釘選功能需要存取平台匯流排或 pci 驅動程式提供的公車交易啟動器 (BTI) 控制代碼。傳遞 BTI 處理驅動程式庫堆疊是一種反模式。
- 如果需要對應,這表示原始緩衝區會透過 FIDL 傳遞。這是一種反模式,因為在日後進行程序內跨驅動程式庫通訊時,可能就無法再複製資料。
- 無論是非同步進行或非同步作業時,核心驅動程式都會有責任確保 VMO 在運作期間不會取消固定/取消對應。這種做法在關閉及暫停等測試不準確的情況下,尤其容易發生問題。
- 在區塊堆疊等情況下,核心驅動程式會遞迴地在相同的驅動程式代管程序中多次繫結。您需要瞭解核心驅動程式是否會直接繫結至與硬體或篩選器層通訊的驅動程式庫。