我們在前面的文章中提到了虛擬化技術的大致分類情況,即分為全虛擬化、半虛擬化和硬件輔助虛擬化3大類。而我們虛擬化技術最主要的虛擬主體就是我們的硬件CPU、內存和IO,那么我們的CPU在全虛擬化模式下如何工作,在半虛擬化下如何工作,在硬件輔助虛擬化模式下如何工作?或着說細分下來,我們又可以分為:
- CPU的全虛擬化技術、半虛擬化技術和硬件輔助虛擬化技術,
- 內存的全虛擬化技術、半虛擬化技術和硬件輔助虛擬化技術
- IO設備的全虛擬化技術、半虛擬化技術和硬件輔助虛擬化技術。
本次我們就來說說CPU的全虛擬化技術、半虛擬化技術和硬件輔助虛擬化技術。
不支持硬件輔助虛擬化技術的X86架構下的CPU有4個特權級(ring0~ring3),操作系統是處於最高級別的ring0,應用程序處於最低級別的ring3。
在這種架構下實現CPU的全虛擬化是極其困難的,為什么困難?
- 原先的OS運行在ring0層,擁有對所有硬件的全部特權級;
- 虛擬化之后將OS運行在ring1層,OS就沒有權限執行一些特權指令,怎么保證這些特權指令執行;
- 在保證該OS虛擬機的特權指令執行的情況下,保證其他運行的OS虛擬機的安全;
1、模擬仿真技術
最先實現這種CPU全虛擬化技術的是Trap-and-emulation技術,即陷入模式和模擬仿真技術。這種技術通過將OS需求的特權指令通過VMM自動捕獲的方式運行后返回去OS。當OS有特權指令產生時,VMM將其自動捕獲,將OS所請求的特權指令進行截獲,然后通過VMM運行之后將結果返回給OS層。VMM會使用模擬仿真將特權指令模擬仿真的方式執行一遍。
在虛擬化模式下,就存在着2中特殊的指令:特權指令和敏感指令。那么什么是特權指令?什么是敏感指令?
特權指令:系統中有一些操作和管理關鍵系統資源的指令,這些指令只有在最高特權級上能夠正確運行。如果在非最高特權級上運行,特權指令會引發一個異常,處理器會陷入到最高特權級,交由系統軟件處理了。
敏感指令:操作特權資源的指令,包括修改虛擬機的運行模式或者下面物理機的狀態;讀寫時鍾、中斷等寄存器;訪問存儲保護系統、地址重定位系統及所有的I/O指令。
根據Popek和Goldberg的定義,指令集支持虛擬化的前提是:所有敏感指令都是特權指令。很可惜x86指令集不能滿足這個要求。
虛擬化場景下,要求將GuestOS內核的特權解除,從原來的0降低到1或者3。這部分特權指令在Guest OS中發生的時候,就會產生Trap,被VMM捕獲,從而由VMM完成。這就是虛擬的本質方法,特權解除和陷入模擬(Privilege deprivileging/Trap-and-Emulation)。虛擬化場景中敏感指令必須被VMM捕獲並完成。對於一般 RISC 處理器,如 MIPS,PowerPC 以及SPARC,敏感指令肯定是特權指令,但是x86 例外,x86絕大多數的敏感指令是特權指令,但是由於部分敏感指令不是特權指令,執行這些指令的時候不會自動trap被VMM捕獲。

2、二進制翻譯技術
采用模擬仿真的方式模擬和虛擬化x86架構的CPU,但是由於x86架構的CPU中,不是所有的敏感指令都是特權指令,所以並不能完全的解決掉那些不是特權指令的敏感指令的模擬仿真問題。例如SGDT, SLDT, SIDT …
由於模擬仿真技術固有的缺陷,導致對CPU的虛擬化並不完整。所以也導致了基於x86的虛擬化難以和其他CPU架構一樣實現虛擬化。比如IBM的Power CPU架構就很早具備了虛擬化的技術並使用於實踐。
這個現象在1999年得到改善,VMware通過二進制翻譯技術完成了對x86 CPU架構的完全虛擬化。
其主要采用優先級壓縮技術(Ring Compression)和二進制代碼翻譯技術(Binary Translation)。優先級壓縮技術讓VMM和Guest運行在不同的特權級下。對x86架構而言,即VMM運行在最高特權級別Ring 0下,Guest OS運行在Ring 1下,用戶應用運行在Ring 3下。因此,Guest OS的核心指令無法直接下達到計算機系統硬件執行,而是需要經過VMM的捕獲和模擬執行(部分難以虛擬化的指令需要通過二進制翻譯【Binary Translation】技術進行轉換)。如下圖所示。
特權級我想在這里就不用多說,大家都比較清楚,說說大家可能不清楚的二進制代碼翻譯技術。二進制翻譯技術簡稱BT,是一種直接翻譯可執行二進制程序的技術,能夠把一種處理器上的二進制程序翻譯到另外一種處理器上執行。二進制翻譯技術將機器代碼從源機器平台映射(翻譯)至目標機器平台,包括指令語義與硬件資源的映射,使源機器平台上的代碼“適應”目標平台。因此翻譯后的代碼更適應目標機器,具有更高的運行時效率。二進制翻譯系統是位於應用程序和計算機硬件之間的一個軟件層,它很好地降低了應用程序和底層硬件之間的耦合度,使得二者可以相對獨立地發展和變化。二進制翻譯也是一種編譯技術,它與傳統編譯的差別在於其編譯處理對象不同。傳統編譯處理的對象是某一種高級語言,經過編譯處理生成某種機器的目標代碼;二進制翻譯處理的對象是某種機器的二進制代碼,該二進制代碼是通過傳統編譯過程生成的,經過二進制翻譯處理后生成另一種機器的二進制代碼。
根據不同的實現方式,二進制翻譯技術可分為三大類:解釋執行,靜態翻譯和動態翻譯。
代碼解釋執行
解釋執行(Interpretation)過程對源機器代碼中的每條指令實時解釋執行,系統不保存且不緩存解釋過的指令,不需要用戶干涉,也不進行任何優化。解釋器相對容易開發,比較容易與老的體系結構高度兼容,但效率很差。
靜態二進制翻譯
在靜態二進制翻譯(SBT,Static BinaryTranslation)中,代碼在運行之前被離線翻譯,根據目標機器的指令結構生成一個新的程序,然后直接執行這個翻譯后生成的程序。靜態翻譯器的離線翻譯過程不會給程序運行帶來額外開銷,因此可以充分采用各種優化措施生產高質量代碼,大大提高運行時效率。
動態二進制翻譯
動態二進制翻譯(DBT,Dynamic BinaryTranslation)則在程序運行時對執行到的代碼片段進行翻譯,克服了靜態翻譯所無法解決的一些困難,如運行時動態信息收集,代碼挖掘,自修改代碼和精確中斷問題。而且動態翻譯器對用戶完全透明,無需用戶干預。雖然動態翻譯有上述諸多優點,翻譯過程卻由於受到動態執行的限制而不能像靜態翻譯那樣進行完全細致的優化,使得翻譯生成的代碼效率比靜態翻譯器差。
三種二進制翻譯技術的比較
解釋執行是最易實現的一種翻譯技術,但是其繁瑣的實現方式大大降低了翻譯系統的執行效率。靜態翻譯雖然能提供高效的運行時性能,但由於無法在靜態環境下覆蓋所有代碼,無法脫離對解釋器的依賴。與上述兩種相比,動態翻譯很好的解決了代碼覆蓋、自修改代碼和精確中斷等諸多問題,同時也能提供可接受的執行效率。因此VMware基於動態二進制翻譯技術實現了x86架構的CPU的虛擬化。
典型動態二進制翻譯系統結構所示,被翻譯的代碼稱為源機器代碼,在宿主機上運行的代碼稱為目標機器代碼,一個典型的動態二進制翻譯器主要包括兩個模塊:翻譯引擎和執行引擎。其中翻譯器引擎負責將源機器代碼翻譯代碼翻譯成目標機器代碼;執行引擎負責准備目標機器代碼運行的上下文環境(Execution Context)然后從目標機器代碼緩存中找到源機器代碼對應的目標代碼並執行。
其基本運行流程如下:
查找(Lookup)階段
這個階段查詢目標代碼塊是否存在於目標代碼緩存中,如果存在則返回目標塊入口地址,如果不存在則進入翻譯階段。
上下文切換(Context Switch)階段
當一個目標代碼塊被查詢到或者翻譯模塊生成的時候,二進制翻譯系統會執行一次控制權轉移。系統會把控制權交給執行模塊去運行該目標代碼塊,目標代碼塊運行完畢后系統需要恢復執行引擎的控制權。一次控制權轉移需要保存程序的上下文環境。
翻譯(Translation)階段
完成從源機器二進制代碼到目標機器二進制代碼的翻譯。包括解碼、中間代碼優化、編碼三個子階段。
執行和鏈接(Executing & Linking)階段
當基本塊被翻譯生成目標代碼塊之后,依照源代碼的控制流完成目標代碼塊之間的直接以及間接跳轉的鏈接,並依次運行目標代碼塊。
3、總結
在沒有CPU硬件輔助虛擬化技術之前,對於X86架構的CPU就采用模擬和二進制翻譯的技術對CPU進行虛擬化實現,但是模擬的方式存在固有缺陷,並不完全虛擬化了x86的CPU架構。而二進制翻譯技術則采用完全不同的思路實現了x86架構的CPU虛擬化。其實對於x86的CPU虛擬化,其難點就在於對其特權指令和敏感指令的虛擬化實現,當然,在實現了CPU的指令這一難題之后,還有一個難題在等着我們!那就是x86架構的CPU調度問題?
在虛擬化環境下,x86架構的CPU有什么調度問題?
1、 虛擬CPU和物理CPU之間的對應關系?
2、 虛擬CPU和物理CPU之間的資源分配?
3、 虛擬CPU和虛擬CPU之間的優先級?
4、 多核虛擬CPU架構vSMP和vNUMA與物理多核CPU架構SMP和NUMA之間的調度和負載均衡?





