一文看懂影子頁表和擴展頁表
我是cloud3,前段時間有虛擬機出現內存問題,今天借著這個話題給大家介紹一下內存虛擬化,也就是MMU虛擬化。
我們知道從intel的80386引入了保護模式后,內存空間分為虛擬地址空間和物理地址空間。后引入頁表機制,把虛擬地址送往MMU,MMU查TLB不中的情況下,依次查頁表就可以找到對應的物理地址。
(關于MMU的原理可以先看我的文章-圖解MMU)
在引入虛擬化技術后,內存地址空間就變得復雜了,客戶機(Guest)和宿主機(Host)都有自己的地址空間。GuestOS本身有虛擬地址和物理地址。HostOS也有虛擬機地址和物理地址。那虛擬機如何訪問到物理機上的物理地址呢?這就是今天我們要討論的內存虛擬化技術。
首先標記幾個概念:
- HPA:Host Physical Address
- HVA:Host Virtual Address
- GPA:Guest Physical Address
- GVA:Guest Virtual Address
- PDBR:頁目錄表物理基地址寄存器,X86上叫CR3
- EPT:擴展頁表
ptr:這里用來描述指向某個頁表的寄存器
一.內存虛擬化要解決的問題
內存虛擬化實際實現就是MMU虛擬化,要實現GVA -> GPA -> HVA -> HPA,而傳統MMU只能實現VA->PA的轉換。所以在虛擬化場景下要解決虛擬機里面的進程如何訪問物理機上的內存這一問題,也就是GVA->HPA的映射問題。
在硬件輔助內存虛擬化出現之前,這個過程是通過軟件實現的,即通過VMM來實現的。最典型的實現方式就是影子頁表技術。
二.影子頁表
(Shadow page table)
影子頁表我用一句話來描述就是:VMM把Guest和Host中的頁表合并成一個頁表,稱為影子頁表,來實現GVA->HPA映射。
變為:
影子頁表需要實現 GVA -> HPA的轉換。如何實現呢?有下面幾步:
1,GVA->GPA,VMM層的軟件會將guest Page Table本身使用的物理頁面設為write protected的,Guest在進行GVA->GPA 時,由于是只讀的,導致 VM exit, traps to VMM。(關于VM exit的過程我們在CPU虛擬化時再詳解)。
2, GPA -> HVA,這一過程由VMM軟件實現的,這個很容易理解,就是通用的malloc。
3, HVA->HPA,這一過程就是我們已知的使用物理MMU完成VMM進程的虛擬內存到物理內存的轉換。
4, 把GVA -> HPA,這一路的映射關系記錄到頁表中,這個頁表就是影子頁表。
虛擬機頁表和影子頁表通過一個哈希表建立關聯(當然也有其他的關聯方式),客戶機操作系統把當前進程的頁表基址載入PDBR時而VMM將會截獲這一特權指令,將進程的影子頁表基址載入客戶機PDBR,使客戶機在恢復運行時PDBR實際指向的是進程對應的影子頁表。這樣通過影子頁表就可以實現真正的內存訪問。
影子頁表實現非常復雜,需要為每個Guest中的每個進程的Guest PT都維護一個對應的Shadow PT。page fault和vm-exit的數量,也加重了CPU的負擔。為了提高效率,各個CPU廠家推出了硬件輔助MMU虛擬化的技術。
三.擴展頁表技術/EPT
嵌套頁表技術/NPT
從Intel的Nehalem架構開始,EPT(Extended Page Tables)就作為CPU的一個特性加入到CPU硬件中去了。AMD也提供的類似技術叫做NPT,即Nested Page Tables。
硬件層面引入EPTP寄存器,來指向EPT頁表基地址。Guest運行時,Guest頁表被載入PDBR,而 EPT 頁表被載入專門的EPT 頁表指針寄存器 EPTP。
GVA->GPA的轉換依然是通過查找原來頁表完成,而GPA->HPA的轉換則通過查找EPT來實現,每個guest VM有一個由VMM維護的EPT。
具體過程
當Guest中進程訪問GVA時,CPU首先就要通過PDBR寄存器去找頁目錄,但是PDBR中存儲的地址是GPA,所以要到EPT中進行GPA->HPA的轉換,這個轉換過程和物理MMU的工作流程相同。
找到了頁目錄的HPA基地址,再通過GVA中的Directory offset段,就找到頁表的VGA了,這個頁表VGA再去EPT中進行GPA->HPA的轉換,就找到頁表VGA的HPA了。
重復上述過程,依次查找各級頁表,最終獲得該GVA對應的HPA。如果是三級或者四級頁表,也是同樣的原理。
page fault處理
上面的查表過程是最理想的處處命中情況,那如果有page fault的情況如何處理呢?
如果Guest的頁表中沒有命中可直接由guest OS處理,不會產生vm-exit。如果在EPT中沒有命中,則產生EPT violation異常,這是Host中VMM層的page fault,不需要vm exit,只需要按照Host中的page fault處理就可以了。所以說EPT/NPT MMU解耦了GVA->GPA轉換和GPA->HPA轉換之間的依賴關系。并且一個VM只需要一套EPT頁表,減少了內存開銷,維護也比較簡單。
四.看圖總結
最后我們直觀的看看引入虛擬化之后MMU的變化情況:
沒有虛擬化:
影子頁表:
EPT/NPT:
通過上面的對比圖,我們應該能清楚的看到MMU虛擬化的整個設計思路。