Mastering KVM Virtualization>:第二章 KVM內部原理
在本章中,我們將討論libvirt、QEMU和KVM的重要數據結構和內部實現。然後,我們將深入了解KVM下vCPU的執行流程。
在這一章,我們將討論:
- libvirt、QEMU和KVM的內部運作方式。
- libvirt、QEMU和KVM的重要數據結構和代碼路徑。
- vCPU的執行流程
- 所有這些組件如何通信以提供虛擬化
熟悉libvirt及其實現
上一章中提到,libvirt作為額外的管理層可以跟各種hypervisors(例如KVM/QEMU,LXC,OpenVZ,UML)進行通信。libvirt API是開源的。與此同時,它是一個守護進程和管理工具,用於管理前面提到的不同hypervisor。libvirt被各種虛擬化程序和平台廣泛採用,例如,圖形用戶界面工具GNOME boxes和virt-manager(http://virt manager.org/)。不要將libvirt與我們在第1章討論的VMM/hypervisor混淆起來。
libvirt的cli命令行介面叫做virsh。libvirt也被其他高級管理工具採用,如oVirt(www.ovirt.org)。
很多人以為libvirt只能在單個節點或者運行libvirt的本地節點上運行,這並不正確。libvirt庫支持遠程功能。因此,任何libvirt工具(例如virt-manager)都可以通過傳遞一個額外的--connect參數,遠程連接到網路上的libvirt守護進程。Fedora,CentOS等絕大多數發行版都安裝了libvirt的客戶端virsh(由libvirt -client package提供)。
正如前面所討論的,libvirt的目標是為管理在hypervisor上運行的VM提供一個通用的和穩定的抽象層。簡而言之,作為管理層,它負責提供執行管理任務的API,如虛擬機置備、創建、修改、監視、控制、遷移等。在Linux中,您會注意到一些進程成為了守護進程。libvirt進程也有一個守護進程,叫做libvirtd。與其他守護進程一樣,libvirtd為其client發起的請求提供服務。讓我們試著了解libvirt client(如virsh或virt-manager)向libvirtd請求服務時,到底發生了什麼。根據客戶端傳遞的連接URI(將在接下來的部分中討論),libvirtd建立起與hypervisor的連接。客戶端程序virsh或virt-manager就是通過這樣的方式使libvirtd與hypervisor建立通信的。在本書的範圍內,我們的目標是KVM虛擬化技術。因此,最好是用QEMU/KVM hypervisor而不是其他hypervisor為場景來考慮與libvirtd的通信。你可能會對使用QEMU/KVM而不是QEMU或KVM作為底層hypervisor的名稱有些困惑。但別擔心,一切都會在適當的時候變得明了。下面會討論QEMU和KVM之間的聯繫。目前你只需了解該hypervisor同時用到了QEMU和KVM技術。
讓我們回到通過libvirt客戶端virsh傳遞的連接URI。當我們將注意力集中在QEMU/KVM虛擬化時,從客戶端傳遞的連接URI包含「QEMU」字元串,或者傳遞給libvirt的連接URI是以下形式的:
- qemu://xxxx/system
- qemu://xxxx/session
前一個(qemu://xxxx/system)請求以root身份連接到本地管理的QEMU和KVM域,或者VM的守護進程。後一個(qemu://xxxx/session)請求以普通用戶身份連接到它自身的QEMU和KVM域。之前,我們提到libvirt也支持遠程連接;幸運的是要實現這個功能,只需在連接URI上做一個小小的更改。也就是說,可以通過在連接URI中更改某些字元串來建立遠程連接。例如,連接URI的通用格式如下:
driver[+transport]://[username@][hostname][:port]/[path][?extraparameters]
遠程連接的virsh簡單命令行示例如下:
$ virsh --connect qemu+ssh://root@remoteserver.yourdomain.com/system list --all
如virsh命令示例所示(qemu+ssh://root@remoteserver.yourdomain.com/system),遠程URI是由本地URI,主機名和/或transport name組成:
前面的圖顯示了遠程連接是如何與其他系統上的libvirt進行通信的。稍後將介紹驅動程序API或驅動程序實現的細節。 When using a URI scheme of "remote",it will tell the remote libvirtd server to probe for the optimal hypervisor driver。以下內容將提供一些關於 「remote driver」 的詳細信息。有關remote connection URI的更多選項,請參閱下面的URL以獲取更多細節:
- http://libvirt.org/remote.html#Remote_URI_reference
- http://libvirt.org/remote.html
要了解libvirt的工作原理,我們來看看代碼。本節包含一些面向開發者的細節,如果您一點都不想知道libvirt內部是如何工作的,您完全可以跳過這一部分。如果你有那麼一點想知道,那就來完成它吧。
libvirt的內部運作機制待補充
Time to think more about QEMU
Quick Emulator(QEMU)由Fabrice Bellard(FFmpeg的創作者)編寫,屬於自由軟體(free software),在GPL協議下許可開發。
QEMU是一個通用和開源的機器模擬和虛擬化軟體。當作為機器模擬使用時,QEMU可以在一種機器上(比如你的PC)運行為另一種機器(比如ARM開發板)編寫的操作系統和應用程序。通過使用動態轉譯技術,它的性能非常好(見www.QEMU.org)。
讓我重新解釋一下前面的段落,並給出一個更具體的解釋。QEMU實際上是一個執行硬體虛擬化的Hypervisor/VMM。你是不是感覺有些困惑?如果是的,別擔心。在本章末尾,您將獲得一個更好的了解,特別是當您瀏覽完每個相關的組件並關聯用於執行虛擬化的整個路徑。QEMU既可以充當模擬器(Emulator)也可以用作虛擬機(Virtualizer):
Qemu as an Emulator:
在第1章《理解Linux虛擬化》中,我們簡單討論了二進位轉譯。當QEMU作為一個模擬器運行時,它能夠在一種機器上運行為另外類型機器設計的OS/應用程序。它是如何做到的?這其中就用到了二進位轉譯的方法。這種模式下,QEMU通過動態二進位轉譯模擬CPU,並提供一組設備模型。因此,可以在不同的架構下運行多種不同的未修改的Guest OS。將Guest code運行在Host CPU上就需要用到二進位轉譯技術。完成這項工作的二進位轉譯程序稱為Tiny Code Generator(TCG);這是一個即時編譯器。它將為給定處理器編寫的二進位代碼轉換為運行在另一個處理器的二進位代碼(例如:ARM運行在X86上):
TCG旨在消除依賴於特定版本GCC或其他編譯器的這一缺點,而是將編譯器(代碼生成器)和運行時由QEMU執行的其他任務結合起來。整個轉譯任務由兩部分組成:target code(TBs)被重寫成TCG ops(一種獨立於機器的中間代碼),隨後由TCG根據主機的架構編譯此中間代碼。並根據需要進行相關優化。
TCG需要編寫專用代碼來支持它運行的每個架構。(TCG info from Wikipedia https://en.wikipedia.org/wiki/QEMU#Tiny_Code_Generator)
QEMU as virtualizer:
這種模式QEMU直接在主機CPU上執行Guest code,從而達到了原生性能。例如,當在Xen/KVM hypervisor下工作時,QEMU可以在這種模式下運行。如果KVM是底層hypervisor,那麼QEMU可以對嵌入式客戶機進行虛擬化,如Power PC、S390、x86等。簡而言之,QEMU可以在沒有KVM的情況下,以前文提到的二進位轉譯的方式運行。與啟用KVM的硬體輔助虛擬化相比,這種方式的執行速度會慢一些。無論在哪種模式下,QEMU不僅模擬處理器,還模擬了不同的外圍設備,如磁碟、網路、VGA、PCI、串口和並行埠、USB等。除了I/O設備的模擬,在使用KVM時,QEMU-KVM創建並初始化虛擬機。它還為每個vCPU(引用下圖)初始化不同的posix線程。此外,它還提供了一個框架,在QEMU-KVM用戶模式地址空間中,模擬虛擬機的物理地址空間:
為了在物理CPU中執行Guest code,QEMU使用了posix線程。也就是說,Guest虛擬CPU在主機內核中作為posix線程執行。這本身就帶來了許多好處,因為從上層來看它們只是主機內核的一些進程。從另一個角度看,KVM hypervisor的用戶空間部分由QEMU提供。QEMU通過KVM內核模塊運行客戶代碼。在使用KVM時,QEMU也執行I/O模擬、I/O設備設置、實時遷移等。
QEMU訪問KVM模塊創建的設備文件(/dev/kvm),並執行ioctls。請參閱KVM的下一節,了解這些ioctls。最後,KVM利用QEMU成為一個完整的hypervisor,而KVM是利用CPU提供的硬體虛擬化擴展(VMX或SVM)的加速器或促成器,與CPU架構緊密耦合。間接地,這表明VM也必須使用相同的架構以使用硬體虛擬化擴展/功能。一旦啟用KVM,它肯定會比其他技術(如二進位轉譯)提供更好的性能。
QEMU-KVM internals在開始研究QEMU內部之前,讓我們先git clone QEMU的倉庫:
#git clone git://git.qemu-project.org/qemu.git
一旦git clone完成,您就可以看到repo內部的文件層次結構,如下面的截圖所示:
一些重要的數據結構和ioctls構成了QEMU用戶空間和KVM內核空間。一些重要的數據結構是KVMState,CPU {X86} State,MachineState等等。在我們進一步探討內部構成之前,我想指出的是,對它們的詳細介紹超出了本書的範圍;然而,我依然將提供足夠的指引來理解在底層發生的事情,並為進一步的解釋提供額外的參考。
Data structures待補充
Threading models in QEMUQEMU-KVM是一個多線程的、事件驅動的(帶有一個大鎖)應用程序。重要的線程有:
- 主線程
- 後端虛擬磁碟I/O的工作線程
- 對應每個vCPU的線程
對應每個VM,都有一個QEMU進程在主機系統中運行。如果Guest關閉,這個過程將被銷毀/退出。除了vCPU線程之外,還有專門的iothreads運行一個select(2)事件循環來處理I/O,例如處理網路數據包和磁碟I/O。IO線程也由QEMU派生。簡而言之,情況將是這樣的:
在我們進一步討論之前,總有一個關於Guest物理內存的問題:它位於哪裡?情況是這樣的:如之前的圖片所示,Guest RAM是在QEMU進程的虛擬地址空間內分配的。也就是說,Guest的物理內存在QEMU進程地址空間內。
NOTE:關於線程的更多細節可以從線程模型中獲取:http://blog.vmsplice.net/2011/03/qemu-internals-overall-architecutre-and-html?m=1
事件循環線程也稱為iothread。事件循環用於計時器、文件描述符監控等。main_loop_wait是QEMU主事件循環線程,它的定義如下所示。這個main event loop thread負責main loop services:包括文件描述符回調、 bottom halves和計時器(在qemu-timer.h中定義)。Bottom halve類似於立即執行的計時器,但開銷要更低一些,調度它們是無等待的、線程安全的和信號安全的。
File:vl.c
static void main_loop(void) {
bool nonblocking;
int last_io = 0;
...
do {
nonblocking = !kvm_enabled && !xen_enabled && last_io > 0;
…...
last_io = main_loop_wait(nonblocking);
…...
} while (!main_loop_should_exit);
}
在我們離開QEMU代碼庫之前,我想指出,設備代碼主要有兩個部分。例如,目錄hw/block/包含host端的塊設備代碼,hw/block/包含設備模擬的代碼。
KVM實戰終於到了討論KVM的時間。KVM開發者遵循了和Linux kernel開發者的一樣的理念:不要重新發明輪子。也就是說,他們並沒有嘗試改變內核代碼來創建一個hypervisor;相反,代碼是圍繞硬體供應商的虛擬化(VMX和SVM)的新硬體支持,以可載入內核模塊的形式開發的。有一個通用的內核模塊kvm.ko和其他硬體相關的內核模塊,如kvm-intel.ko(基於Intel CPU系統)或kvm-amd.ko(基於AMD CPU系統)。相應的,KVM將載入kvm-intel.ko(如果存在vmx標誌)或kvm-amd.ko(如果存在svm標誌)模塊。這將Linux內核變成了一個hypervisor,從而實現了虛擬化。KVM是由qumranet開發的,自2.6.20後成為Linux內核主線的一部分。後來,qumranet被紅帽收購。
KVM暴露設備文件/dev/kvm以供應用程序調用ioctls。QEMU利用這個設備文件與KVM通信,並創建、初始化和管理VM的內核模式上下文。前面,我們提到QEMU-KVM用戶空間在QEMU-KVM的用戶模式地址空間內,提供了VM的物理地址空間,其中包括memory-mapped I/O。KVM幫助實現這一點。在KVM的協助下還實現了更多的事情。以下是其中一些:
- Emulation of certain I/O devices, for example (via "mmio") the per-CPU local APIC and the system-wide IOAPIC.
- Emulation of certain "privileged" (R/W of system registers CR0, CR3 and CR4) instructions.
- The facilitation to run guest code via VMENTRY and handling of "intercepted events" at VMEXIT.
- "Injection" of events such as virtual interrupts and page faults into the flow of execution of the virtual machine and so on are also achieved with the help of KVM.
再重複一遍,KVM不是hypervisor!是不是有些迷糊?好的,讓我重新解釋一下。KVM不是一個完整的hypervisor,但是藉助QEMU和模擬器的幫助(一個為I/O設備模擬和BIOS輕度修改的QEMU),它可以成為一個hypervisor。KVM需要支持硬體虛擬化的處理器才能運行。通過使用這些功能,KVM將標準Linux內核變成了一個hypervisor。當KVM運行虛擬機時,每個VM都是一個普通的Linux進程,很明顯,它可以在Host kernel的CPU上運行,就像在Host kernel中運行其他進程一樣。在第1章《理解Linux虛擬化》中,我們討論了不同的CPU運行模式。如果您還記得,主要有USER模式和Kernel/Supervisor模式。KVM是Linux內核中的一個虛擬化特性,它允許像QEMU這樣的程序直接在主機CPU上執行客戶代碼。只有當目標架構受到主機CPU的支持時,這才可以實現。
然而,KVM引入了一種名為 「guest mode「 的模式!簡而言之,guest模式是客戶系統代碼的執行。它可以運行Guest的user或者kernel代碼。在具備虛擬化特性的硬體支持下,KVM虛擬化了進程狀態、內存管理等。
通過其硬體虛擬化功能,處理器通過Virtual Machine Control Structure(VMCS)和Virtual Machine Control Block(VMCB)來管理host和guest os的處理器狀態,並代表虛擬機的OS管理I/O和中斷。也就是說,隨著這種類型的硬體的引入,諸如CPU指令攔截、寄存器讀/寫支持、內存管理支持(擴展頁表(EPT)和NPT)、中斷處理支持(APICv)、IOMMU等等,得到了支持。
KVM使用標準的Linux調度器、內存管理和其他服務。簡而言之,KVM所做的是幫助用戶空間程序利用硬體虛擬化功能。在這裡,您可以將QEMU作為一個用戶空間程序來對待,因為它對不同的用戶場景進行了良好的集成。當我們說 」硬體加速虛擬化「時,我們主要指的是Intel VT-x和ADM-Vs SVM。引入虛擬化支持的處理器帶來了額外的指令集,稱為Virtual Machine Extensions或VMX。
使用Intel的VT-x,VMM在「VMX根模式」中運行,而Guest(未修改的OSs)在「VMX非根模式」中運行。這個VMX給CPU帶來了額外的虛擬化指令,比如VMPTRLD、VMPTRST、VMCLEAR、VMREAD、VMWRITE、VMCALL、VMLAUNCH、VMRESUME、VMXOFF和VMXON。VMXON可以打開虛擬化模式(VMX),VMXOFF可以禁用。要執行客戶代碼,必須使用VMLAUNCH/VMRESUME指令,並使用VMEXIT離開。VMEXIT表示從非根操作切換到根操作。顯然,當我們進行這個切換時,需要保存一些信息,以便稍後可以獲取它。Intel提供了一種結構,以促進這種被稱為Virtual Machine Control Structure(VMCS)的切換;它將用來處理大部分虛擬化管理功能。例如在VMEXIT中,exit的原因將被記錄在這個結構中。那麼,我們如何從這個結構中讀取或寫入信息呢?VMREAD和VMWRITE指令可以用於讀取或寫入VMCS結構中的欄位。
最近Intel處理器也提供了一項功能,允許每個guest有自己的頁面表來跟蹤內存地址。如果沒有EPT,hypervisor必須退出虛擬機以執行地址轉換,性能就會降低。正如我們在Intel虛擬化CPU中觀察到的操作模式一樣,AMD的Secure Virtual Machine (SVM)也有一系列操作模式,稱為Host mode和Guest mode。正如您所猜測的那樣,hypervisor運行在Host mode下,Guest OS運行在Guest mode。顯然,在Guest mode下,一些指令可以導致VMEXIT,並以特定的方式處理Guest mode。AMD也有一個等效於VMCS的結構,它被稱為Virtual Machine Control Block(VMCB);正如前面所討論的,它記錄VMEXIT的原因。AMD增加了8種新的指令碼支持支持SVM。例如,VMRUN指令啟動Guest OS的操作,VMLOAD指令從VMCB載入處理器狀態,VMSAVE指令將處理器狀態保存到VMCB。此外,為了提高內存管理單元的性能,AMD引入了一種稱為NPT(嵌套分頁)的技術,類似於Intel的EPT。
KVM APIs如前所述,ioctl的主要類型有三種。
三套ioctls組成了KVM API。KVM API是一組用於控制虛擬機的各個方面的ioctls。這些ioctls分為這三類:
System ioctls:
它可以查詢並設置了全局屬性,影響整個KVM子系統。另外,系統ioctl用於創建虛擬機。VM ioctls:
這些查詢和設置的屬性將影響整個VM,例如內存分布。此外,VM ioctl用於創建虛擬CPU(vCPUs)。它們從創建VM的同一進程(地址空間)運行VM ioctls。vCPU ioctl:
這些查詢和設置的屬性控制單個vCPU的操作。它們從創建vCPU的同一線程上運行vCPU ioctls。
要進一步了解KVM對外發布的ioctl和屬於特定的fd組的ioctls,請參閱KVM.h:
/* ioctls for /dev/kvm fds: */
#define KVM_GET_API_VERSION _IO(KVMIO, 0x00)
#define KVM_CREATE_VM _IO(KVMIO, 0x01) /* returns a VM fd */
…..
/* ioctls for VM fds */
#define KVM_SET_MEMORY_REGION _IOW(KVMIO, 0x40, struct kvm_memory_region)
#define KVM_CREATE_VCPU _IO(KVMIO, 0x41)
…
/* ioctls for vcpu fds */
#define KVM_RUN _IO(KVMIO, 0x80)
#define KVM_GET_REGS _IOR(KVMIO, 0x81, struct kvm_regs)
#define KVM_SET_REGS _IOW(KVMIO, 0x82, struct kvm_regs)
Anonymous inodes and file structuresExecution flow of vCPU
待補充
概要在本章中,我們討論了定義libvirt、QEMU和KVM的內部實現的重要數據結構和函數。我們還討論了vCPU執行的生命周期以及QEMU和KVM如何在主機CPU上運行Guest OS。我們還討論了虛擬化的硬體支持、圍繞它的重要概念以及它如何在KVM虛擬化中發揮作用。有了這些概念和說明,我們可以更詳細地探索KVM虛擬化的細節。
在下一章中,我們將介紹如何使用libvirt管理工具建立一個獨立的KVM。
※ASP.NET Core 源碼學習之 Logging[4]:FileProvider
※HTML <img>標籤 創建圖像映射
※C 6.0 內插字元串 (Interpolated Strings)
※nginx實現請求的負載均衡 + keepalived實現nginx的高可用
TAG:科技優家 |
※如何在 Ubuntu Linux 上使用 KVM 雲鏡像
※Virsh KVM文件管理
※virmach聖何塞機房KVM架構VPS測評:大熱有道理,下載速度果然快
※MediaComm美凱憑藉光纖KVM坐席協作管理平台榮獲創新科技獎
※在 Ubuntu 18.04 LTS 上使用 KVM 配置無頭虛擬化伺服器
※如何使用 virsh 命令創建、還原和刪除 KVM 虛擬機快照
※在 KVM 中測試 IPv6 網路
※在 KVM 中測試 IPv6 網路:第 2 部分
※kvm命令行安裝
※深信服和英偉達共同發布:國內首款基於KVM技術的vGPU虛擬桌面解決方案
※一鍵查看KVM宿主機上虛擬機IP地址
※別鬧了,LED屏KVM上機櫃一人能輕鬆搞定?
※虛擬化技術KVM的搭建
※何謂4路工業級KVM切換器
※KVM管理時代已終結
※數字KVM 與延長器的完美結合運用方案
※KVM切換器是什麼,看懂這一篇就夠
※力登KVM解決方案助力蒙大拿公共電視網轉型
※輕鬆穿透內網,遠程控制的KVM硬體,向日葵控控A2體驗
※民族品牌光纖KVM坐席實現我軍隊指揮技術創新