VT,全称是,Virtualization Technology,即是虚拟化技术,虚拟化技术可以扩大硬件的容量,简化软件的重新配置过程。CPU 的虚拟化技术可以单 CPU 模拟多 CPU 并行,允许一个平台同时运行多个操作系统,并且应用程序都可以在相互独立的空间内运行而互不影响,从而显著提高计算机的工作效率。如果不开启 VT 技术支持,模拟器、虚拟机只能是单核心 CPU。

英特尔(Intel)和 AMD 的大部分 CPU 均支持此技术,名称分别为 VT-x、AMD-V。VT 开启之后对虚拟机,比如 iTools 安卓模拟器的性能有非常大的提高。

区分一下,CPU 是 Intel(英特尔)的话,虚拟化技术即 VT;CPU 是 AMD 的话,虚拟化技术即 SVM。SVM,全称是 Secure Virtual Machine mode。

KVM

KVM(Kernel-based Virtual Machine,基于内核的虚拟机)是一种 TYPE1 Hypervisor(裸金属类型)虚拟化技术,VMM 和 HostOS 一体化,直接运行 Host Hardware 之上,实现硬件和虚拟机完全管控。具有以下 3 个典型特点是:

  1. 依赖 CPU 硬件辅助的虚拟化技术(e.g. Intel VT-x / AMD-V);
  2. VMM 和 HostOS 一体化;
  3. 运行效率高。

所以,KVM 的本质就是一个 Linux Kernel Module,命名为 kvm.ko(kvm-intel.ko / kvm-AMD.ko),在利用了 Kernel 所提供的部分操作系统能力(e.g. 内存管理、进程管理、硬件设备管理)的基础之上,再加入了 CPU 和 Memory 虚拟化的能力,使得 Linux Kernel 得以具备成为一个完备 VMM 的 3 个条件:

  1. 资源控制(Resource Control):VMM 必须能够管理所有的系统资源。
  2. 等价性(Equivalence):在 VMM 管理下运行的 HostOS 和 GuestOS,除了 CPU 时序和硬件资源可用性之外的行为应该完全保持一致。
  3. 效率性(Efficiency):绝大多数的 GuestOS 指令应该由 Host Hardware 直接执行而无需 VMM 的参与(二进制翻译)。

KVM 最早于 2007 年 2 月 5 日被集成到 Linux Kernel 2.6.20 中,现在最新的 KVM 已经具备以下功能清单:

  • 支持 SMP(Symmetric Multi-Processing,对称多处理)多核处理器架构。
  • 支持 NUMA (Non-Uniform Memory Access,非一致存储访问)多核处理器架构。
  • 支持 CPU 亲和性。
  • 支持 CPU 和 Memory 超分(Overcommit)。
  • 支持 VirtIO 设备和驱动。
  • 支持 PCI 设备直通(Pass-through)和 SR-IOV(单根 I/O 虚拟化)。
  • 支持热插拔 CPU、Disk、NIC 等设备。
  • 支持 Live Migration(实时迁移)。
  • 支持 KSM (Kernel Shared Memory,内核内存共享技术)。

当启动 Linux 操作系统并加载 kvm.ko 时,会完成以下工作:

  1. 首先,初始化 kvm.ko 的数据结构;
  2. 然后,kvm.ko 检测当前的 CPU 体系结构,读写 CR4 寄存器的虚拟化模式开关,再执行 Intel VT-x 的 VMXON 指令,将 VMM 设置为运行在 Root Mode 之上;
  3. 最后,kvm.ko 创建设备接口文件 /dev/kvm 暴露给 User Application(e.g. QEMU)。

需要注意的是,KVM 运行在 Kernel space 且本身不具备任何设备模拟的能力。所以,KVM 还必须借助于一个运行在 User space 用户态的 Application(e.g. QEMU)来模拟 “组装“ 出一台完整 VM 所需要的各种虚拟设备(e.g. 网卡、显卡、存储控制器和硬盘)。

QEMU

QEMU(Quick Emulator)最早于 2001 由天才程序员 Fabrice Bellard 发布,是一款开源的、采用了动态二进制翻译技术的 TYPE2 Hypervisor(寄居式类型)VMM 软件。

QEMU-KVM

上述提到,KVM 和 QEMU 各有千秋、互为补充,并且都具有开源底色,所以后来在 KVM 开发者社区对 QEMU 进行稍加改造之后,就推出了 QEMU-KVM 分支发行版(一个特殊的 QEMU 版本)。

KVM 社区提供的软件分发包中包含了以下 4 个文件内容:

  1. KVM 内核模块
  2. QEMU
  3. QEMU-KVM
  4. virtio 驱动程序

其中,QEMU-KVM 就是专门针对 KVM 的 QEMU 分支,现已经被广泛的集成(二次开发)到各种著名的商业产品中,包括:AWS、阿里云等等。

虽然,在后来的 QEMU 1.3 版本中,开发者社区又将 QEMU 和 QEMU-KVM 这两个分支合并了,但为了清晰的区分两者,所以还是习惯性的在 KVM 语境中将其称之为 QEMU-KVM。

集成软件架构

CPU 虚拟化实现

Kernel 在加载 kvm.ko 成为 VMM 之后,就具备了 3 种不同的运行模式,分别对应了 Intel VT-x 的 2 种特权模式:

  1. User Mode(User space):运行 QEMU(User Process)代码。
  2. Kernel Mode(Kernel space,CPU Root Mode):运行 kvm.ko 代码。
  3. Guest Mode(Kernel space,CPU Non-root Mode):运行 GuestOS 代码。

Kernel Mode 作为 User Mode 和 Guest Mode 之间沟通的桥梁。在 User Mode 中,QEMU 通过 ioctl() 来操作 VM。然后 Kernel Mode 收到 ioctl() 请求,首先完成一些准备工作(e.g. 将 vCPU 上下文加载到 VMCS 等),然后 CPU 执行 VM Entry 指令,进入到 Non-Root Mode,CPU 开始执行 GuestOS 的代码。

Memory 虚拟化实现

KVM 同样通过 /dev/kvm 向 QEMU 提供了 Memory 虚拟化的功能。

QEMU 调用了 KVM_CREATE_VM 接口后得到了一个 vmfd,针对这个句柄执行 ioctl 调用就可以为 VM 创建 GPA,并自动维护 GPA 和 HVA、HPA 之间的映射关系,底层依旧是通过 Intel VT-x 提供的 EPT 技术来实现。

在操作系统层面表现为,/dev 目录树下的 Devices 对于所有 User Process 或 Kernel Thread 而言都是一致且通用的,但是对于所有打开了 /dev/kvm 设备的 VM 进程或线程,其所能使用到的都是唯一且各不相同的地址映射(实现了 GuestOS 间的隔离)。

I/O 虚拟化实现

前面提到,I/O 设备的模拟和 I/O 虚拟化功能的实现主要由 QEMU 来完成。

例如:在执行 GuestOS 代码的过程中,GuestOS 发出了一个 I/O 请求,该事件会被 VMM 捕获,然后执行 VM Exit 执行,CPU 会自动将 GuestOS 的上下文加载到 VMCS Guest State Area 中,挂起 GuestOS,并从 VMCS Host State Area 中加载 VMM 的通用事件处理函数的入口地址,开始执行 VMM 的代码。VMM 根据 I/O 事件类型,将 I/O 请求交由 QEMU 最终完成处理。

QEMU-KVM 虚拟机的本质

综合上述内容,再回头看 QEMU-KVM 虚拟机的本质。

QEMU-KVM 虚拟机,简称 VM,由 vCPU、vMemory、虚拟 I/O 设备以及 GuestOS 组成:

  • 一个 VM 就是一个 User Process,包含了下列几种 User Threads:
  1. vCPU 线程:用于运行 GuestOS 的代码。
  2. I/O 线程:用于运行 QEMU 模拟 I/O 设备的代码。
  3. 其它线程:比如处理 event loop,offloaded tasks 等的线程。

  • VM 的一个 vCPU 就是一个 User Process 内的一个 User Thread。
  • VM 的 vMemory 就是分配给 User Process 的虚拟地址空间中的一块内存。
  • VM 可以继承 HostOS 中的 NUMA 和大页内存特性。
  • VM 在 HostOS 的 FIle System 层面体现为一个 XML 文件和一个 QCOW2 文件。前者描述了 GuestOS 的特征信息,后者储存了 GuestOS 的数据。

VM 的 vCPU 两级调度

在操作系统的调度层面,GuestOS 与 VMM(HostOS)共同构成了 vCPU 的两级调度系统。vCPU threads、QEMU Process threads、Linux Scheduler、pCPU 之间的二级调度模型如下图所示:

  • GuestOS 负责第 2 级调度:将运行在 GuestOS 之上的 User Application 调度到 vCPU 上。
  • VMM(HostOS)负责第 1 级调度:将 QEMU Process threads、vCPU threads 调度到 pCPU 上。

两级调度模型之间的调度策略和调度器类型互不相关。当 vCPU 的数量大于 pCPU 时,vCPU 可能会在多个 pCPU 之间 “偏移“(pCPU 分时复用或空间复用)。在 VMM 层面,也可以根据 NUMA 亲和性、CPU 绑定等策略将 vCPU 绑定到指定的 pCPU 上,用空间换时间的方式获得更高的性能。

VM 的 vCPU 多核拓扑

KVM 支持 SMP 和 NUMA 等多核处理器架构,可以自定义 VM 的 vCPU 拓扑。

  • 对 SMP 类型的客户机,使用:
qemu-kvm -smp <n>[,cores=<ncores>][,threads=<nthreads>][,sockets=<nsocks>][,maxcpus=<maxcpus>]
  • 对 NUMA 类型的客户机,使用:
qemu-kvm -numa <nodes>[,mem=<size>][,cpus=<cpu[-cpu>]][,nodeid=<node>] 

VM 的 vCPU 模型

KVM 支持自定义 CPU 模型 (models),CPU 模型定义了哪些宿主机的 CPU 功能(features)会暴露给 Guest OS。为了让 VM 能够在具有不同 CPU 功能的 Hosts 之间做安全迁移,往往不会将所有的 Host CPU 功能都暴露给 VM,而是会取得 Server 集群的交集,以保证 VM 迁移的安全性。

可以通过以下指令获取 Host CPU 模型清单:

$ kvm -cpu ?
x86       Opteron_G5  AMD Opteron 63xx class CPU                      
x86       Opteron_G4  AMD Opteron 62xx class CPU                      
x86       Opteron_G3  AMD Opteron 23xx (Gen 3 Class Opteron)          
x86       Opteron_G2  AMD Opteron 22xx (Gen 2 Class Opteron)          
x86       Opteron_G1  AMD Opteron 240 (Gen 1 Class Opteron)           
x86          Haswell  Intel Core Processor (Haswell)                  
x86      SandyBridge  Intel Xeon E312xx (Sandy Bridge)                
x86         Westmere  Westmere E56xx/L56xx/X56xx (Nehalem-C)          
x86          Nehalem  Intel Core i7 9xx (Nehalem Class Core i7)       
x86           Penryn  Intel Core 2 Duo P9xxx (Penryn Class Core 2)    
x86           Conroe  Intel Celeron_4x0 (Conroe/Merom Class Core 2)   
x86      cpu64-rhel5  QEMU Virtual CPU version (cpu64-rhel5)          
x86      cpu64-rhel6  QEMU Virtual CPU version (cpu64-rhel6)          
x86             n270  Intel(R) Atom(TM) CPU N270   @ 1.60GHz          
x86           athlon  QEMU Virtual CPU version 0.12.1                 
x86         pentium3                                                  
x86         pentium2                                                  
x86          pentium                                                  
x86              486                                                  
x86          coreduo  Genuine Intel(R) CPU           T2600  @ 2.16GHz 
x86           qemu32  QEMU Virtual CPU version 0.12.1                 
x86            kvm64  Common KVM processor                            
x86         core2duo  Intel(R) Core(TM)2 Duo CPU     T7700  @ 2.40GHz 
x86           phenom  AMD Phenom(tm) 9550 Quad-Core Processor         
x86           qemu64  QEMU Virtual CPU version 0.12.1                 

Recognized CPUID flags:
  f_edx: pbe ia64 tm ht ss sse2 sse fxsr mmx acpi ds clflush pn pse36 pat cmov mca pge mtrr sep apic cx8 mce pae msr tsc pse de vme fpu
  f_ecx: hypervisor rdrand f16c avx osxsave xsave aes tsc-deadline popcnt movbe x2apic sse4.2|sse4_2 sse4.1|sse4_1 dca pcid pdcm xtpr cx16 fma cid ssse3 tm2 est smx vmx ds_cpl monitor dtes64 pclmulqdq|pclmuldq pni|sse3
  extf_edx: 3dnow 3dnowext lm|i64 rdtscp pdpe1gb fxsr_opt|ffxsr fxsr mmx mmxext nx|xd pse36 pat cmov mca pge mtrr syscall apic cx8 mce pae msr tsc pse de vme fpu
  extf_ecx: perfctr_nb perfctr_core topoext tbm nodeid_msr tce fma4 lwp wdt skinit xop ibs osvw 3dnowprefetch misalignsse sse4a abm cr8legacy extapic svm cmp_legacy lahf_lm

自定义 VM 的 CPU 模型,-cpu 选项除了可以指定 GuestOS 的 CPU 模型,还可以指定附加的 CPU 特性。并且 -cpu 会将指定的 CPU 模型的所有功能全部暴露给 GuestOS,即使某些特性在实际的 Host 上并不支持,此时 QEMU-KVM 就会通过软件模拟的方式来支持这些特性,因此,也会消耗一定的性能。

qemu-kvm -cpu <models>

# -cpu host 表示 Guest OS 使用和 Host OS 相同的 CPU model。

VM 的磁盘设备

QEMU-KVM 定义一个磁盘设备的可用选项有很多。

qemu-kvm -drive option[,option[,option[,...]]]
 # 硬件映像文件路径;
    file=/path/to/somefile
    # 指定硬盘设备所连接的接口类型,即控制器类型,如:ide、scsi、sd、mtd、floppy、pflash 及 virtio 等;
    if=interface
    # 设定同一种控制器类型中不同设备的索引号,即标识号;
    index=index
    # 定义介质类型为硬盘(disk)还是光盘(cdrom);    
    media=media
    # 指定映像文件的格式,具体格式可参见 qemu-img 命令;
    format=format

定义 VM 的 boot 磁盘设备,默认为 -boot order=dc,once=d。每种设备使用一个字符表示,不同的 CPU 架构所支持的设备及其表示字符也不尽相同,例如:在 x86 架构上,a、b 表示软驱、c 表示第一块硬盘,d 表示第一个光驱设备,n-p 表示网络适配器;

qemu-kvm -boot [order=drives][,once=drives][,menu=on|off]

VM 的网络设备

QEMU 支持模拟多个类型的网卡设备。可以使用 qemu-kvm -net nic,model=? 来获取当前 CPU 架构支持模拟的 NIC 类型,例如:x86 架构上默认的 NIC 为 e1000。

  • 创建一个新的 NIC 设备并连接至 VLAN n 中:
    • macaddr 用于为其指定 MAC 地址;
    • name 用于指定一个在监控时显示的网上设备名称。
qemu-kvm -net nic[,vlan=n][,macaddr=mac][,model=type][,name=name][,addr=addr][,vectors=v]
  • 通过物理机的 Tap 网络接口连接至 VLAN n 中:
    • 使用 script=file 指定的脚本(默认为 /etc/qemu-ifup)来配置当前网络接口;
    • 使用 downscript=file 指定的脚本(默认为 /etc/qemu-ifdown)来撤消接口配置;
    • 使用 script=no 和 downscript=no 可分别用来禁止执行脚本;
qemu-kvm -net tap[,vlan=n][,name=name][,fd=h][,ifname=name][,script=file][,downscript=dfile]
  • 在 user(用户模式)下创建网络栈,不依赖 root 权限;有效选项有:
    • vlan=n:连接至 VLAN n,默认 n=0;
    • name=name:指定接口的显示名称,常用于监控模式中;
    • net=addr[/mask]:设定 GuestOS 可见的 IP 网络,掩码可选,默认为 10.0.2.0/8;
    • host=addr:指定 GuestOS 中看到的物理机的 IP 地址,默认为指定网络中的第二个,即:x.x.x.2;
    • dhcpstart=addr:指定 DHCP 服务地址池中 16 个地址的起始 IP,默认为第 16 个至第 31 个,即:x.x.x.16-x.x.x.31;
    • dns=addr:指定 GuestOS 可见的 DNS 服务器地址;默认为 GuestOS 网络中的第三个地址,即:x.x.x.3;
    • tftp=dir:激活内置的 TFTP 服务器,并使用指定的 dir 作为 TFTP 服务器的默认根目录;
    • bootfile=file:BOOTP 文件名称,用于实现网络引导 GuestOS;如:qemu -hda linux.img -boot n -net user,tftp=/tftpserver/pub,bootfile=/pxelinux.0
qemu-kvm -net user[,option][,option][,...]

 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐