虛擬化技術實現 — QEMU-KVM

目錄

前文列表

虛擬化技術實現 — 虛擬化技術發展編年史網絡

KVM

KVM(Kernel-based Virtual Machine,基於內核的虛擬機)是一種用於 Linux 內核中的虛擬化基礎設施。本質是一個嵌入到 Linux 內核中的虛擬化功能模塊 kvm.ko(kvm-intel.ko/kvm-AMD.ko),該模塊在利用 Linux 內核所提供的部分操做系統能力(e.g. 任務調度、內存管理、硬件設備交互)的基礎上,再加入了處理器和內存虛擬化的能力,使得 Linux 內核具有了成爲 VMM 的條件。KVM 於 2007 年 2 月 5 日被集成到 Linux 2.6.20 內核中。使用 KVM 的前提是宿主機必須擁有支持硬件虛擬化拓展特性(Intel VT 或者 AMD-V)的處理器。數據結構

在這裏插入圖片描述

KVM 的功能清單多線程

  • 支持 CPU 和 Memory 超分(Overcommit)
  • 支持半虛擬化 I/O(virtio)
  • 支持熱插拔 (CPU、塊設備、網絡設備等)
  • 支持 SMP(Symmetric Multi-Processing,對稱多處理)處理器架構
  • 支持 NUMA (Non-Uniform Memory Access,非一致存儲訪問)處理器架構
  • 支持實時遷移(Live Migration)
  • 支持 PCI 設備直接分配(Pass-through)和單根 I/O 虛擬化 (SR-IOV)
  • 支持合併相同內存頁 (KSM )

以 Intel VT 爲例,當啓動 Linux 操做系統並加載 KVM 內核模塊時:架構

  1. 初始化 KVM 模塊內部的數據結構;
  2. KVM 模塊檢測當前的 CPU 體系結構,而後打開 CPU 控制器以及存取 CR4 寄存器的虛擬化模式開關,並經過執行 VMXON 指令將 Host OS/VMM(在 KVM 環境中,Host OS 便是 VMM)置於虛擬化模式的根模式(Root Mode);
  3. 最後,KVM 模塊建立特殊的接口設備文件 /dev/kvm 並等待來自用戶空間(QEMU)的指令。

但須要注意的是,KVM 是運行在內核態的且自己不能進行任何設備的模擬。因此,KVM 還必須藉助於一個運行在用戶態的應用程序來模擬出虛擬機所須要的虛擬設備(e.g. 網卡、顯卡、存儲控制器和硬盤)同時爲用戶提供操做入口。目前這個應用程序的最佳選擇就是 QEMU。性能

QEMU

QEMU(Quick Emulator)是一款免費的、開源的、純軟件實現的、可執行硬件虛擬化的 VMM。與 Bochs,PearPC 等模擬器相比,QEMU 具備高速(配合 KVM)以及跨平臺的特性。ui

在這裏插入圖片描述

事實上,QEMU 自己做爲一套完整的 VMM 實現,包括了處理器虛擬化,內存虛擬化,以及模擬各種虛擬設備的功能。QEMU 4.0.0 版本甚至幾乎能夠模擬任何硬件設備,但因爲這些模擬都是純軟件實現的,因此其性能低下。在 KVM 開發者在對 QEMU 進行稍加改造後,QEMU 能夠經過 KVM 對外暴露的 /dev/kvm 接口來對其進行調用。從 QEMU 角度來看,也能夠說是 QEMU 使用了 KVM 的處理器和內存虛擬化功能,爲本身的虛擬機提供了硬件輔助虛擬化加速。除此之外,虛擬機的配置和建立、虛擬機運行所依賴的虛擬設備、虛擬機運行時的用戶環境和用戶交互,以及一些虛擬機的特定技術,好比:動態遷移,都是交由 QEMU 來實現的。spa

在這裏插入圖片描述

總的來講,QEMU 具備如下幾種使用方式:操作系統

  1. 純軟件(二進制翻譯)實現的全虛擬化虛擬機
  2. 基於硬件輔助虛擬化(KVM)的全虛擬化虛擬機
  3. 仿真器:爲用戶空間的進程提供 CPU 仿真,讓在不一樣處理器結構體系上編譯的程序得以跨平臺運行。例如:讓 SPARC 架構上編譯的程序在 x86 架構上運行(藉由 VMM 的形式)。

QEMU-KVM

KVM 官方提供的軟件包下載包含了 KVM 內核模塊、QEMU、qemu-kvm 以及 virtio 四個文件。其中,qemu-kvm 本質是專門針對 KVM 的 QEMU 分支代碼包(一個特殊的 QEMU 版本)。.net

QEMU-KVM 相比原生 QEMU 的改動:線程

  • 原生的 QEMU 經過指令翻譯實現 CPU 的徹底虛擬化,可是修改後的 QEMU-KVM 會調用 ICOTL 命令來調用 KVM 模塊。
  • 原生的 QEMU 是單線程實現,QEMU-KVM 是多線程實現。

然而在 QEMU 1.3 版本以後二者又保持一致了,但咱們能仍習慣在 KVM 語境中將其稱之爲 QEMU-KVM。

NOTE:在 RHEL6/CentOS6 中,qemu-kvm 存放在 /usr/libexec 目錄下。不過 PATH 環境變量缺省是不包含此目錄的,因此用戶沒法直接使用 qemu-kvm,這樣作是爲了防止 QEMU 替代了 KVM 做爲 VMM 的角色。若是但願啓用 QEMU 做爲 VMM 的話,能夠經過將 /usr/libexec/qemu-kvm 連接爲 /usr/bin/qemu 來完成。

在這裏插入圖片描述
在 QEMU-KVM 中,KVM 運行在內核空間,提供 CPU 和內存的虛級化,以及 Guest OS 的 I/O 攔截。QEMU 運行在用戶空間,提供硬件 I/O 虛擬化,並經過 ioctl 調用 /dev/kvm 接口將 KVM 模塊相關的 CPU 指令傳遞到內核中執行。當 Guest OS 的 I/O 被 KVM 攔截後,就會將 I/O 請求交由 QEMU 處理。例如:

open("/dev/kvm", O_RDWR|O_LARGEFILE)    = 3
ioctl(3, KVM_GET_API_VERSION, 0)        = 12
ioctl(3, KVM_CHECK_EXTENSION, 0x19)     = 0
ioctl(3, KVM_CREATE_VM, 0)              = 4
ioctl(3, KVM_CHECK_EXTENSION, 0x4)      = 1
ioctl(3, KVM_CHECK_EXTENSION, 0x4)      = 1
ioctl(4, KVM_SET_TSS_ADDR, 0xfffbd000)  = 0
ioctl(3, KVM_CHECK_EXTENSION, 0x25)     = 0
ioctl(3, KVM_CHECK_EXTENSION, 0xb)      = 1
ioctl(4, KVM_CREATE_PIT, 0xb)           = 0
ioctl(3, KVM_CHECK_EXTENSION, 0xf)      = 2
ioctl(3, KVM_CHECK_EXTENSION, 0x3)      = 1
ioctl(3, KVM_CHECK_EXTENSION, 0)        = 1
ioctl(4, KVM_CREATE_IRQCHIP, 0)         = 0
ioctl(3, KVM_CHECK_EXTENSION, 0x1a)     = 0

QEMU-KVM 調用 KVM 內核模塊啓動虛擬機的流程概要

  1. 獲取 /dev/kvm fd(文件描述符)
kvmfd = open("/dev/kvm", O_RDWR);
  1. 建立虛擬機,獲取虛擬機的句柄。KVM_CREATE_VM 時,能夠理解成 KVM 爲虛擬機建立了對應的數據結構,而後,KVM 會返回一個文件句柄來表明該虛擬機。針對這個句柄執行 ioctl 調用便可完成對虛擬機執行相應的管理,好比:建立用戶空間虛擬地址(Virtual Address)、客戶機物理地址(Guest Physical Address)以及主機物理地址(Host Physical Address)之間的映射關係;
vmfd = ioctl(kvmfd, KVM_CREATE_VM, 0);
  1. 爲虛擬機映射內存和其餘的 PCI 設備,以及信號處理的初始化。
ioctl(kvmfd, KVM_SET_USER_MEMORY_REGION, &mem);
  1. 將虛擬機鏡像數據映射到內存,至關於物理機的 boot 過程,把操做系統內核映射到內存。

  2. 建立 vCPU,併爲 vCPU 分配內存空間。KVM_CREATE_VCPU 時,KVM 爲每個 vCPU 生成對應的文件句柄,對其執行相應的 ioctl 調用,就能夠對 vCPU 進行管理。

ioctl(kvmfd, KVM_CREATE_VCPU, vcpuid);
vcpu->kvm_run_mmap_size = ioctl(kvm->dev_fd, KVM_GET_VCPU_MMAP_SIZE, 0);
  1. 建立 vCPU 個數的線程並運行虛擬機。
ioctl(kvm->vcpus->vcpu_fd, KVM_RUN, 0);
  1. 線程進入循環,監聽並捕獲虛擬機退出緣由,作相應的處理。這裏的退出並不必定指的是虛擬機關機,虛擬機若是遇到 I/O 操做,訪問硬件設備,缺頁中斷等都會執行退出。執行退出能夠理解爲將 CPU 執行上下文返回到 QEMU。
open("/dev/kvm")

ioctl(KVM_CREATE_VM)
ioctl(KVM_CREATE_VCPU)

for (;;) {
     ioctl(KVM_RUN)
     switch (exit_reason) {  /* 分析退出緣由,並執行相應操做 */
     case KVM_EXIT_IO:  /* ... */
     case KVM_EXIT_HLT:  /* ... */
     }
}
相關文章
相關標籤/搜索