CSP 課堂筆記之 Hypervisor
Background
CSP是上海交通大學軟體學院的研究生課程,因為之前大四上的時候沒好好上,特意重修了一遍,這裡是重修時候課上的一些記錄。
正文
虛擬化是一個非常有趣的技術,對於不同的人,聽到這個詞可能都會有不同的想法。因為虛擬化的概念實在是太常見了。在這裡我們只討論對操作系統的虛擬化,通俗來講就是如何在一台機器上跑多個系統的那種虛擬化。這樣的虛擬化在 wiki 上被稱作 Hardware Virtulization,或者是叫做 Platform Virtualization。
這是一篇半科普的文章,並不是很嚴謹,有用詞不慎還請指正。
虛擬化的分類
就算是把重點放在 Hardware Virtulization 上,也存在好多種實現的方式,這些方式都是從不同角度來進行虛擬化的。如果想要了解這些方法,首先要介紹下一個操作系統的體系結構。
上面的圖比較形象地介紹了從底層硬體到操作系統的整體架構。操作系統和硬體之間的那一層叫做 Instruction Set Architecture (ISA)。這裡就是指底層的那些指令,比如 load, store, call, return 等。其中有一些,是特權指令,只有運行在 ring 0 模式下的應用可以使用這些指令,也就是內核。這樣的指令包括 hlt, load cr3 等。這樣的指令,就是圖中 3 所代表的 System ISA。而用戶可以使用的指令,比如 call, return 這些,就是 4 所代表的 User ISA。兩者共同組成了 ISA。
再上一層,是 ABI,也就是操作系統和庫之間的那一層。它包括系統調用,和之前提到的 User ISA,也包括一些調用協定,比如調用方和被調用方各自保存哪些寄存器的值這樣的。這一層對編譯器的實現有很大的影響。
再往上一層就是廣為人知的 API,作為一個只會使用 API 的程序員,了解下面的東西還真花了一些功夫,但 API 的概念想必早就已經深入人心了,這裡就不再去介紹了。
那如果要實現虛擬化,可以從 ABI 和 API 兩個層次來實現,這兩種實現都要跟 ISA 打交道。上圖說明了兩種實現的結構,Process VM 可以實現在 Host OS 上運行其他 ISA 或 OS specific 的程序,這可以被當做是在 API 層次上實現了虛擬化。通常來說,Process VM 會有一個圖中所示的 Runtime,Runtime 跟 Guest 代碼是在一個進程里的,所有 Guest 代碼的執行都會在其中。
這張圖可以看出,其實 Process VM 的實現還是挺複雜的,其中最關鍵的是 Evaluation Engine,它會負責做代碼的解釋,或者是二進位的翻譯。因此其實 Process VM 可以算是在 ABI 或者是 API 上的虛擬化,並不是純粹只是在 API 層次上的虛擬化。除了 Evaluation Engine 之外,還有倆 emulator,分別來捕獲和模擬 Host OS 的異常和系統調用。可以看到,Process VM 跟 Container 還是有比較大的差距的,最開始以為這兩個是一個東西,但是現在看來不是這樣的。起碼這個 Process VM 比 Container 技術上要複雜一些的樣子。 Process VM 的例子有 IA-32 EL, FX!32, DynamoRIO 等等,都是沒有摸過的東西。
再回去看看 System VM,被大家熟知的 Xen 和 kvm 就是 System VM 範疇的。它主要的部分是 VMM,VMM 負責管理和保護硬體,同時讓運行在它上面的操作系統以為自己在跟硬體打交道。這裡還可以繼續分為兩類,一類是 Classic VM,一類是 Hosted VM。
兩者的區別在於,Hosted VM 中,VMM 是運行在 OS 之上的,而 Classic VM 中的 VMM 則是直接運行在硬體上的。Xen 是典型的 Classic VM,而 kvm 因為是在 Linux Kernel 中的,因此是 Hosted VM。
介紹完虛擬化的分類,那已經知道了它們大概的邏輯架構是什麼樣的,那接下來會分析下,如果要實現一個虛擬化,都需要對哪些資源進行虛擬化,以及可以採取的方式都有哪些。
在虛擬化中,最主要的虛擬化是三個:CPU, Memory 和 IO,這也是最經典的三個概念。這裡會先介紹 Xen,後面 kvm 相比 Xen 其實差不多,只是利用了硬體的支持使得實現更加簡單了。
CPU 虛擬化
提到 CPU,首先就是特權級的問題,在沒有虛擬化的時候,Kernel 運行在 ring 0 里,應用程序運行在 ring 3 里,但其實不止有這樣兩個特權級,因此 Xen 的做法是把 VMM 放在 ring 0,而 Guest OS 運行在 ring 1,應用還是在 ring 3。這樣就可以使得只有 VMM 才能運行很多特權指令,而 Guest OS 很多指令在運行的時候會失敗。
軟體方式
不過失敗會有很多種,有的會觸發一個 fault,然後可以被 trap 到 VMM 中,而有的指令在不同特權級別下有不同的邏輯,所以會按照低特權級的邏輯執行,也有的指令會 silent fail,也就是毫無聲息地就掛了。對於可以 trap 的指令,完全可以被 VMM 處理,而對於後面兩者,沒有很好的辦法,這樣的指令一共有17條。Xen 的方法是修改操作系統,對於不能被 trap 的指令用 hypercall 的方式 trap 到 VMM,而還有一種二進位翻譯的技術,就是用可以被合理 trap 的指令來模擬這17條指令,這樣的二進位翻譯可以做代碼級別的拼接,因此在處理循環等等的時候甚至比上面那種方式要快。VMWare 就是用這樣的黑科技獲得了很大的市場,也是它的核心技術之一。
Xen 提出了一種優化,就是能夠在 Guest OS 里處理一部分系統調用,這樣就不需要再在每次系統調用的時候 trap 到 VMM 處理。
硬體輔助
既然軟體實現這麼麻煩,還要修改 Guest OS,那為什麼不讓硬體來輔助進行虛擬化呢,Intel VT-x 就是干這個的。
VT-x 提出了兩個 mode,就是 root mode 和 non-root mode。其中每個 mode 都有跟之前一樣的特權級。這樣 VM 都是放在 non-root mode 下的,而 VMM 是放在 root mode 下的。每當 Guest OS 觸發了一個異常等等,都會由硬體來保證會被 trap 到 在 root mode 下的 VMM 中。這樣就大大降低了虛擬化的實現難度,kvm 就是採用了 VT-x 的輔助,來進行的,因此如果 CPU 沒有這個 feature,就不能用 kvm 來完成虛擬化。
Memory 虛擬化
內存虛擬化很關鍵。因為引入了虛擬機,所以原本由 Virtual Address(VA) 到 Physical Address(PA) 的轉換,變成了 Guest Virtual Address(GVA) 到 Guest Physical Address(GPA),再到 Host Physical Address (HPA) 的轉換。
那問題來了,cr3 寄存器只有一個,那就是說頁表只能有一個啊,那怎麼能有兩個轉換呢,於是就有了 Shadow Page Table(SPT) 來解決這樣的問題。
Shadow Page Table
既然 cr3 寄存器只有一個,而轉換有兩個,那做一個折衷的選擇,讓 VMM 維護一個從 GVA 直接到 HPA 的頁表,這樣,在虛擬機中的進程執行的時候,只需要這一張頁表就 OK 了。所以這樣的設計意味著對於每一個 VM 中的每一個進程,都需要有一個 SPT 維護在 VMM 中。
這樣的設想是可以解決問題的,但是會在實現上遇到一些坑。比方說,Guest OS 因為是不知道有 SPT 這樣一個東西的,因此在它只會更新自己的頁表,而管不到 VMM 中維護的 SPT,那怎麼樣才能使 VMM 能夠知道 VM 在修改自己的頁表呢,那方法就是 Guest Page Table(GPT) 會被置為可讀不可寫的,那這樣每次寫都會 trap 到 VMM 中,這個時候 VMM 再去更新 SPT。
而整個的地址翻譯過程如圖所示。首先,GVA 如果在 TLB 里 MISS 了,就會去 SPT 中找地址,如果還是沒有,就會觸發 Page Fault,然後會去 GPT 中,根據 GVA 先找到 GPA,然後再通過維護在 VMM 中的 PMAP,也就是從 GPA 到 HPA 的一張表,找到真正的 HPA,再放到 SPT 中。這只是簡單的介紹,真正的比這個要複雜一些。
SPT 是可以解決地址轉換的問題,但是有不少的缺點。首先就是在 SPT 找不到的時候,會遍歷一次 GPT,很傷,還有就是切換進程,在載入 cr3 寄存器的時候會 Flush TLB,這樣才能正確地 trap 到 SPT 或者被 VMM 中的邏輯處理。簡單來說就是性能不好。
EPT/NPT
既然軟體實現 GVA 到 GPA 再到 HPA 的轉換這麼麻煩,能不能讓硬體來做這件事呢,那 EPT/NPT 就是做這件事的。EPT/NPT 暫時可以被理解為多了一個 cr3,因此 CPU 可以維護兩個頁表,一個用來做從 GVA 到 GPA 的轉換,一個用來做 GPA 到 HPA 的轉換,這樣會少很多跟 VMM 的交互。不過在實際情況中,GPT 和 EPT 都有可能是多層的,每遍歷 GPT 的一層都可能要遍歷一次 EPT,如果 GPT 是 啊層,EPT 是 b 層,遍歷是 O(mn) 的。
IO 虛擬化
軟體方式
IO 是最後一個比較重要的概念。最簡單的 IO 虛擬化方案,是在每次 IO 操作的時候,都 trap 到 VMM 中處理。但是這樣的效率太低了。Xen 採取的方法是引入一個特權 VM: Domain 0。這裡也存在兩種實現,一種是使用 qemu,VM 中的 IO 請求都會通過一個驅動轉發給 Domain 0 的 qemu,qemu 會負責具體的邏輯。
還有一種是把驅動分成前端後端兩種,前端在 Guest VM 中,後端在 Domain 0 里,IO 請求在 Guest OS 中的前端驅動里,通過 IO Ring 的方式共享給後端,後端會發給真正的硬體驅動來完成 IO 請求。
SR-IOV & IOMMU 硬體支持
SR-IOV 是硬體支持的設備虛擬化,對它的了解並不深,最早支持 SR-IOV 的設備是網卡,它會把硬體分為多個 Virtual Function(VF),VF 可以被分給不同 VM 來使用。
IOMMU 還不懂,之後再來補充- -
Reference
- Smith, James E., and Ravi Nair. "The architecture of virtual machines." Computer 38.5 (2005): 32-38.
- Process VM - NYU Computer Science
License
- This article is licensed under [CC BY-NC-SA 3.0](https://creativecommons。org/licenses/by-nc-sa/3.0/).
- Please contact me for commercial use.
推薦閱讀:
※會飛的貓咪很危險,區塊鏈虛擬寵物是場騙局嗎?
※廣東觀音山:竭力守護綠水青山 勵志打造生態文明
※乾貨,幾維安全代碼虛擬化技術原理與實現