0.寫在前面
我一直以為現在的硬件輔助虛擬化技術不支持嵌套虛擬化,即物理機上只能有一層虛擬機,虛擬機裏面不能再創建虛擬機。但是最近接觸個事情,感覺這個知識點需要刷新。 虛擬機裏面一直跑容器,但是容器存在隔離性的安全問題,業界提出來的安全容器方案為給容器加一個linux內核,例如Kata Containers 和 Firecracker 這類安全容器,這樣不就是虛擬機裏面再跑虛擬機了嗎?也就是安全容器用到了嵌套虛擬化。 現在最新的技術已經解決了嵌套虛擬化的問題了。來看看怎麼實現的吧。
1.虛擬化需要解決什麼問題
參考:計算虛擬化基礎知識(kvm+qemu)https://blog.51cto.com/u_17355821/14245330
這篇文章裏提了兩個重要的問題,在嵌套虛擬化裏面同樣需要解決: (1)CPU虛擬化:CPU特權指令(敏感指令)的虛擬化 (2)內存虛擬化:地址在頁表中如何轉換 那麼這兩個問題在嵌套虛擬化中如何解決呢?
先講一下嵌套虛擬化的層級關係: L0 (Level 0): 物理主機及其 Hypervisor(如 ESXi、Hyper-V、KVM)。 L1 (Level 1): 在第一層 Hypervisor 上運行的虛擬機(Guest VM)。這台虛擬機“認為”自己是一台物理服務器。 L2 (Level 2): 在 L1 虛擬機內部,由 Kata 或 Firecracker 啓動的 輕量級虛擬機(MicroVM),容器進程就運行在其中。
2.嵌套虛擬化中的CPU特權指令處理
2.1 基本邏輯
特權指令的處理:虛擬的VMExit 在非嵌套環境中,當Guest OS執行一條特權指令(如CPUID),會直接導致一次VMExit,控制權從虛擬機交還給宿主的Hypervisor。<br> 在嵌套環境中,當L2 Guest OS執行特權指令時,會發生以下一系列精密的“接力”操作: L0的首次接管:硬件會首先產生一次VMExit。但此時,L0 Hypervisor發現這個退出事件實際上是由一個運行在L1 Guest VM中的Hypervisor(即L1 Hypervisor)所管理的L2虛擬機觸發的。 注入虛擬中斷:L0 Hypervisor並不會自己處理這個請求,而是將這個退出事件(例如,一條CPUID指令的執行)包裝成一個虛擬中斷,注入到L1 Guest VM中。 L1的接管處理:在L1 Guest VM看來,這個虛擬中斷就像一次真實的VMExit。於是,其內部的L1 Hypervisor被喚醒,它像在物理機上一樣,接管並模擬執行這條CPUID指令,然後將結果返回給L2 Guest OS。
2.2 常見問題
2.2.1 L2 guestos執行cpuid特權指令,硬件產生vmexit,問題是L0 hypervisor是如何知道這個cpuid是l2 guest os產生的而不是l1guestos 產生的
當 CPU 在執行 L2 的代碼時,當前活躍的 VMCS 指向的是 L2 的 VMCS。此時,如果 L2 中的代碼執行了一條特權指令(如 CPUID),CPU 的虛擬化硬件會立即觸發一次 VM-Exit。 關鍵在於這次退出的上下文信息。VM-Exit 發生時,硬件不僅會通知 L0 Hypervisor 發生了退出,還會在當前的 VMCS(即 L2 的 VMCS)中記錄詳細的退出原因、指令信息等。因此,L0 Hypervisor 在接管控制權時,通過檢查這些信息,就能明確知道:這次退出不是發生在 L1 虛擬機上下文中的,所以L0就直接把信息提供給L1讓L1處理。
3.嵌套虛擬化中的內存地址轉換
3.1 轉換過程的詳細分解
第一級轉換:L2虛擬地址 -> L2物理地址 轉換執行者:L2虛擬機內部的頁表(由L2 Guest OS管理)。 轉換機制:當L2虛擬機內的應用程序訪問一個虛擬地址時,CPU的MMU會像在物理機上一樣,使用L2的CR3寄存器指向的頁表來遍歷翻譯。這個階段完全在L2虛擬機內部完成,L0 Hypervisor不干預。 轉換結果:得到L2認為的“物理地址”,即GPA2。<br> 第二級轉換:L2物理地址 -> L1物理地址 轉換執行者:L1 Hypervisor為L2虛擬機設置的擴展頁表。 轉換機制:GPA2並不是真正的物理地址,它需要被再次轉換。這個轉換由硬件自動完成。CPU的MMU在完成第一級轉換得到GPA2後,會立刻使用L1 Hypervisor為L2配置的EPT,將GPA2翻譯為L1物理地址(GPA1)。 關鍵點:這次轉換由硬件直接處理,通常不需要L0 Hypervisor的介入,因此效率很高。<br> 第三級轉換:L1物理地址 -> L0物理地址 轉換執行者:L0 Hypervisor為L1虛擬機設置的擴展頁表。 轉換機制:GPA1同樣不是最終地址,它需要被再次轉換。CPU的MMU會再次利用EPT機制,這次是使用L0 Hypervisor為L1配置的EPT,將GPA1翻譯為最終的宿主機物理地址(HPA)。
3.2 常見問題
3.2.1 MMU什麼時候使用EPT繼續轉換,什麼時候只用CR3
虛擬機監控器(Hypervisor)在為每個虛擬機(VM)創建虛擬 CPU 時,會初始化一個名為 VMCS 的數據結構,它相當於這個 vCPU 的“配置文件”。其中一個至關重要的控制字段是 “Enable EPT”,虛擬機的VMCS中Enable EPT=1。 當 L0 Hypervisor 為 L1 Guest 啓動一個 vCPU 時,如果設置了 “Enable EPT = 1”,就意味着告訴硬件:“這個虛擬機擁有物理內存的幻覺,它看到的‘物理地址’需要你通過 EPT 再轉換一次才能得到真正的主機物理地址。” 這便開啓了兩級轉換的基礎。 在嵌套虛擬化中,當 L1 Guest 內部也運行一個 Hypervisor 去啓動 L2 Guest 時,L0 Hypervisor 會通過一種叫 “VMCS Shadowing” 的技術,將 L1 Hypervisor 希望為 L2 Guest 設置的 EPT 指針也納入管理。這時,硬件會知道,當前正在處理的是 L2 Guest 的內存訪問。
3.2.2 MMU是怎麼知道執行一次EPT轉換(普通虛擬化)還是兩次EPT轉換(嵌套虛擬化)
這裏就要理解EPT指針(位於其VMCS中)的作用了。 EPT指針:EPT翻譯的機制不是隻執行一次,而是類似於一個循環。虛擬機在創建時,L0在其VMCS的EPT指針中放的是EPT指針鏈表,包括其所有祖先虛擬機以及L0 Hypervisor的EPT表。EPT翻譯機制會按照EPT指針鏈表進行循環轉換,一直轉換到最終的物理地址上。