KVM 記憶體虛擬化及其實現
概述
KVM(Kernel Virtual Machine) , 作為開源的核心虛擬機器,越來越受到 IBM,Redhat,HP,Intel 等各大公司的大力支援,基於 KVM 的開源虛擬化生態系統也日益完善。而實現 KVM 虛擬化,使客戶機高效地、安全地使用宿主機的記憶體資源,就必須實現記憶體的虛擬化。
客戶機實體地址空間
為 了實現記憶體虛擬化,讓客戶機使用一個隔離的、從零開始且具有連續的記憶體空間,KVM 引入一層新的地址空間,即客戶機實體地址空間 (Guest Physical Address, GPA),這個地址空間並不是真正的實體地址空間,它只是宿主機虛擬地址空間在客戶機地址空間的一個對映。對客戶機來說,客戶機實體地址空間都是從零開始 的連續地址空間,但對於宿主機來說,客戶機的實體地址空間並不一定是連續的,客戶機實體地址空間有可能對映在若干個不連續的宿主機地址區間,如下圖 1 所示:
圖 1. 客戶機實體地址到宿主機虛擬地址的轉換
由 於客戶機實體地址不能直接用於宿主機物理 MMU 進行定址,所以需要把客戶機實體地址轉換成宿主機虛擬地址 (Host Virtual Address, HVA),為此,KVM 用一個 kvm_memory_slot 資料結構來記錄每一個地址區間的對映關係,此資料結構包含了對應此對映區間的起始客戶機頁幀號 (Guest Frame Number, GFN),對映的記憶體頁數目以及起始宿主機虛擬地址。於是 KVM 就可以實現對客戶機實體地址到宿主機虛擬地址之間的轉換,也即首先根據客戶機實體地址找到對應的對映區間,然後根據此客戶機實體地址在此對映區間的偏移量 就可以得到其對應的宿主機虛擬地址。進而再通過宿主機的頁表也可實現客戶機實體地址到宿主機實體地址之間的轉換,也即 GPA 到 HPA 的轉換。
實 現記憶體虛擬化,最主要的是實現客戶機虛擬地址 (Guest Virtual Address, GVA) 到宿主機實體地址之間的轉換。根據上述客戶機實體地址到宿主機實體地址之間的轉換以及客戶機頁表,即可實現客戶機虛擬地址空間到客戶機實體地址空間之間的 對映,也即 GVA 到 HPA 的轉換。顯然通過這種對映方式,客戶機的每次記憶體訪問都需要 KVM 介入,並由軟體進行多次地址轉換,其效率是非常低的。因此,為了提高 GVA 到 HPA 轉換的效率,KVM 提供了兩種實現方式來進行客戶機虛擬地址到宿主機實體地址之間的直接轉換。其一是基於純軟體的實現方式,也即通過影子頁表 (Shadow Page Table) 來實現客戶虛擬地址到宿主機實體地址之間的直接轉換。其二是基於硬體對虛擬化的支援,來實現兩者之間的轉換。下面就詳細闡述兩種方法在 KVM 上的具體實現。
影子頁表
由 於宿主機 MMU 不能直接裝載客戶機的頁表來進行記憶體訪問,所以當客戶機訪問宿主機實體記憶體時,需要經過多次地址轉換。也即首先根據客戶機頁表把客戶機虛擬地址轉傳成客戶 機實體地址,然後再通過客戶機實體地址到宿主機虛擬地址之間的對映轉換成宿主機虛擬地址,最後再根據宿主機頁表把宿主機虛擬地址轉換成宿主機實體地址。而 通過影子頁表,則可以實現客戶機虛擬地址到宿主機實體地址的直接轉換。如下圖 2 所示:
圖 2. 客戶機實體地址到宿主機實體地址的轉換
影 子頁表簡化了地址轉換過程,實現了客戶機虛擬地址空間到宿主機實體地址空間的直接對映。但是由於客戶機中每個程序都有自己的虛擬地址空間,所以 KVM 需要為客戶機中的每個程序頁表都要維護一套相應的影子頁表。在客戶機訪問記憶體時,真正被裝入宿主機 MMU 的是客戶機當前頁表所對應的影子頁表,從而實現了從客戶機虛擬地址到宿主機實體地址的直接轉換。而且,在 TLB 和 CPU 快取上快取的是來自影子頁表中客戶機虛擬地址和宿主機實體地址之間的對映,也因此提高了快取的效率。
在影子頁表中,每個頁表項指向的都是宿主機的實體地址。這些表項是隨著客戶機作業系統對客戶機頁表的修改而相應地建立的。客戶機中的每一個頁表項都有一個影子頁表項與之相對應。如下圖 3 所示:
圖 3. 客戶機頁表和影子頁表
為 了快速檢索客戶機頁表所對應的的影子頁表,KVM 為每個客戶機都維護了一個雜湊表,影子頁表和客戶機頁表通過此雜湊表進行對映。對於每一個客戶機來說,客戶機的頁目錄和頁表都有唯一的客戶機實體地址,通 過頁目錄 / 頁表的客戶機實體地址就可以在雜湊連結串列中快速地找到對應的影子頁目錄 / 頁表。在檢索雜湊表時,KVM 把客戶機頁目錄 / 頁表的客戶機實體地址低 10 位作為鍵值進行索引,根據其鍵值定位到對應的連結串列,然後遍歷此連結串列找到對應的影子頁目錄 / 頁表。當然,如果不能發現對應的影子頁目錄 / 頁表,說明 KVM 還沒有為其建立,於是 KVM 就為其分配新的物理頁並加入此連結串列,從而建立起客戶機頁目錄 / 頁表和對應的影子頁目錄 / 頁表之間的對映。當客戶機切換程序時,客戶機作業系統會把待切換程序的頁表基址載入 CR3,而 KVM 將會截獲這一特權指令,進行新的處理,也即在雜湊表中找到與此頁表基址對應的影子頁表基址,載入客戶機 CR3,使客戶機在恢復執行時 CR3 實際指向的是新切換程序對應的影子頁表。
影子頁表異常處理機制
在通過影子頁表進行定址的過程中,有兩種原因會引起影子頁表的 缺頁異常,一種是由客戶機本身所引起的缺頁異常,具體來說就是客戶機所訪問的客戶機頁表項存在位 (Present Bit) 為 0,或者寫一個只讀的客戶機物理頁,再者所訪問的客戶機虛擬地址無效等。另一種異常是由客戶機頁表和影子頁表不一致引起的異常。
當缺頁異常 發生時,KVM 首先截獲該異常,然後對發生異常的客戶機虛擬地址在客戶機頁表中所對應頁表項的訪問許可權進行檢查,並根據引起異常的錯誤碼,確定出此異常的原因,進行相應 的處理。如果該異常是由客戶機本身引起的,KVM 則直接把該異常交由客戶機的缺頁異常處理機制來進行處理。如果該異常是由客戶機頁表和影子頁表不一致引起的,KVM 則根據客戶機頁表同步影子頁表。為此,KVM 要建立起相應的影子頁表資料結構,填充宿主機實體地址到影子頁表的頁表項,還要根據客戶機頁表項的訪問許可權修改影子頁表對應頁表項的訪問許可權。
由 於影子頁表可被載入物理 MMU 為客戶機直接定址使用, 所以客戶機的大多數記憶體訪問都可以在沒有 KVM 介入的情況下正常執行,沒有額外的地址轉換開銷,也就大大提高了客戶機執行的效率。但是影子頁表的引入也意味著 KVM 需要為每個客戶機的每個程序的頁表都要維護一套相應的影子頁表,這會帶來較大記憶體上的額外開銷,此外,客戶機頁表和和影子頁表的同步也比較複雜。因 此,Intel 的 EPT(Extent Page Table) 技術和 AMD 的 NPT(Nest Page Table) 技術都對記憶體虛擬化提供了硬體支援。這兩種技術原理類似,都是在硬體層面上實現客戶機虛擬地址到宿主機實體地址之間的轉換。下面就以 EPT 為例分析一下 KVM 基於硬體輔助的記憶體虛擬化實現。
EPT 頁表
EPT 技術在原有客戶機頁表對客戶機虛擬地址到客戶機實體地址對映的基礎上,又引入了 EPT 頁表來實現客戶機實體地址到宿主機實體地址的另一次對映,這兩次地址對映都是由硬體自動完成。客戶機執行時,客戶機頁表被載入 CR3,而 EPT 頁表被載入專門的 EPT 頁表指標暫存器 EPTP。EPT 頁表對地址的對映機理與客戶機頁表對地址的對映機理相同,下圖 4 出示了一個頁面大小為 4K 的對映過程:
圖 4.EPT 頁錶轉換
在 客戶機實體地址到宿主機實體地址轉換的過程中,由於缺頁、寫許可權不足等原因也會導致客戶機退出,產生 EPT 異常。對於 EPT 缺頁異常,KVM 首先根據引起異常的客戶機實體地址,對映到對應的宿主機虛擬地址,然後為此虛擬地址分配新的物理頁,最後 KVM 再更新 EPT 頁表,建立起引起異常的客戶機實體地址到宿主機實體地址之間的對映。對 EPT 寫許可權引起的異常,KVM 則通過更新相應的 EPT 頁表來解決。
由此可以看出,EPT 頁表相對於前述的影子頁表,其實現方式大大簡化。而且,由於客戶機內部的缺頁異常也不會致使客戶機退出,因此提高了客戶機執行的效能。此外,KVM 只需為每個客戶機維護一套 EPT 頁表,也大大減少了記憶體的額外開銷。
結束語
本文主要介紹了兩種記憶體虛擬化方法在 KVM 上的具體實現。隨著虛擬化技術的發展以及硬體對記憶體虛擬化的進一步支援,新的記憶體虛擬化方法也會隨之出現,從而使記憶體虛擬化變的更加簡單、高效。