参考:http://www.cnblogs.com/sammyliu/
一、KVM介绍
1、虚拟化简史
其中,KVM 全称是 基于内核的虚拟机(Kernel-based Virtual Machine),它是一个 Linux 的一个内核模块,该内核模块使得 Linux 变成了一个 Hypervisor:
- 它由 Quramnet 开发,该公司于 2008年被 Red Hat 收购。
- 它支持 x86 (32 and 64 位), s390, Powerpc 等 CPU。
- 它从 Linux 2.6.20 起就作为一模块被包含在 Linux 内核中。
- 它需要支持虚拟化扩展的 CPU。
- 它是完全开源的。
本文介绍的是基于 X86 CPU 的 KVM。
2、KVM架构
KVM 是基于虚拟化扩展(Intel VT 或者 AMD-V)的 X86 硬件的开源的 Linux 原生的全虚拟化解决方案。KVM 中,虚拟机被实现为常规的 Linux 进程,由标准 Linux 调度程序进行调度;虚机的每个虚拟 CPU 被实现为一个常规的 Linux 进程。这使得 KMV 能够使用 Linux 内核的已有功能。
但是,KVM 本身不执行任何硬件模拟,需要客户空间程序通过 /dev/kvm 接口设置一个客户机虚拟服务器的地址空间,向它提供模拟的 I/O,并将它的视频显示映射回宿主的显示屏。目前这个应用程序是 QEMU。
Linux 上的用户空间、内核空间和虚机:
- Guest:客户机系统,包括CPU(vCPU)、内存、驱动(Console、网卡、I/O 设备驱动等),被 KVM 置于一种受限制的 CPU 模式下运行。
- KVM:运行在内核空间,提供CPU 和内存的虚级化,以及客户机的 I/O 拦截。Guest 的 I/O 被 KVM 拦截后,交给 QEMU 处理。
- QEMU:修改过的为 KVM 虚机使用的 QEMU 代码,运行在用户空间,提供硬件 I/O 虚拟化,通过 IOCTL /dev/kvm 设备和 KVM 交互。
KVM 是实现拦截虚机的 I/O 请求的原理:
现代 CPU 本身了对特殊指令的截获和重定向的硬件支持,甚至新的硬件会提供额外的资源来帮助软件实现对关键硬件资源的虚拟化从而提高性能。以 X86 平台为例,支持虚拟化技术的 CPU 带有特别优化过的指令集来控制虚拟化过程。通过这些指令集,VMM 很容易将客户机置于一种受限制的模式下运行,一旦客户机视图访问物理资源,硬件会暂停客户机的运行,将控制权交回给 VMM 处理。VMM 还可以利用硬件的虚级化增强机制,将客户机在受限模式下对一些特定资源的访问,完全由硬件重定向到 VMM 指定的虚拟资源,整个过程不需要暂停客户机的运行和 VMM 的参与。由于虚拟化硬件提供全新的架构,支持操作系统直接在上面运行,无需进行二进制转换,减少了相关的性能开销,极大简化了VMM的设计,使得VMM性能更加强大。从 2005 年开始,Intel 在其处理器产品线中推广 Intel Virtualization Technology 即 IntelVT 技术。
QEMU-KVM:
其实 QEMU 原本不是 KVM 的一部分,它自己就是一个纯软件实现的虚拟化系统,所以其性能低下。但是,QEMU 代码中包含整套的虚拟机实现,包括处理器虚拟化,内存虚拟化,以及 KVM需要使用到的虚拟设备模拟(网卡、显卡、存储控制器和硬盘等)。 为了简化代码,KVM 在 QEMU 的基础上做了修改。VM 运行期间,QEMU 会通过 KVM 模块提供的系统调用进入内核,由 KVM 负责将虚拟机置于处理的特殊模式运行。遇到虚机进行 I/O 操作,KVM 会从上次的系统调用出口处返回 QEMU,由 QEMU 来负责解析和模拟这些设备。 从 QEMU 的角度看,也可以说是 QEMU 使用了 KVM 模块的虚拟化功能,为自己的虚机提供了硬件虚拟化加速。除此以外,虚机的配置和创建、虚机运行说依赖的虚拟设备、虚机运行时的用户环境和交互,以及一些虚机的特定技术比如动态迁移,都是 QEMU 自己实现的。
KVM:
KVM 内核模块在运行时按需加载进入内核空间运行。KVM 本身不执行任何设备模拟,需要 QEMU 通过 /dev/kvm 接口设置一个 GUEST OS 的地址空间,向它提供模拟的 I/O 设备,并将它的视频显示映射回宿主机的显示屏。它是KVM 虚机的核心部分,其主要功能是初始化 CPU 硬件,打开虚拟化模式,然后将虚拟客户机运行在虚拟机模式下,并对虚机的运行提供一定的支持。以在 Intel 上运行为例,KVM 模块被加载的时候,它:
- 首先初始化内部的数据结构;
- 做好准备后,KVM 模块检测当前的 CPU,然后打开 CPU 控制及存取 CR4 的虚拟化模式开关,并通过执行 VMXON 指令将宿主操作系统置于虚拟化模式的根模式;
- 最后,KVM 模块创建特殊设备文件 /dev/kvm 并等待来自用户空间的指令。
接下来的虚机的创建和运行将是 QEMU 和 KVM 相互配合的过程。两者的通信接口主要是一系列针对特殊设备文件 dev/kvm 的 IOCTL 调用。其中最重要的是创建虚机。它可以理解成KVM 为了某个特定的虚机创建对应的内核数据结构,同时,KVM 返回一个文件句柄来代表所创建的虚机。
针对该句柄的调用可以对虚机做相应地管理,比如创建用户空间虚拟地址和客户机物理地址、真实物理地址之间的映射关系,再比如创建多个 vCPU。KVM 为每一个 vCPU 生成对应的文件句柄,对其相应地 IOCTL 调用,就可以对vCPU进行管理。其中最重要的就是“执行虚拟处理器”。通过它,虚机在 KVM 的支持下,被置于虚拟化模式的非根模式下,开始执行二进制指令。在非根模式下,所有敏感的二进制指令都被CPU捕捉到,CPU 在保存现场之后自动切换到根模式,由 KVM 决定如何处理。
除了 CPU 的虚拟化,内存虚拟化也由 KVM 实现。实际上,内存虚拟化往往是一个虚机实现中最复杂的部分。CPU 中的内存管理单元 MMU 是通过页表的形式将程序运行的虚拟地址转换成实际物理地址。在虚拟机模式下,MMU 的页表则必须在一次查询的时候完成两次地址转换。因为除了将客户机程序的虚拟地址转换了客户机的物理地址外,还要将客户机物理地址转化成真实物理地址。
3、KVM 的功能列表
KVM 所支持的功能包括:
- 支持CPU 和 memory 超分(Overcommit)
- 支持半虚拟化I/O (virtio)
- 支持热插拔 (cpu,块设备、网络设备等)
- 支持对称多处理(Symmetric Multi-Processing,缩写为 SMP )
- 支持实时迁移(Live Migration)
- 支持 PCI 设备直接分配和 单根I/O 虚拟化 (SR-IOV)
- 支持 内核同页合并 (KSM )
- 支持 NUMA (Non-Uniform Memory Access,非一致存储访问结构 )
4、KVM 工具集合
- libvirt:操作和管理KVM虚机的虚拟化 API,使用 C 语言编写,可以由 Python,Ruby, Perl, PHP, Java 等语言调用。可以操作包括 KVM,vmware,XEN,Hyper-v, LXC 等 Hypervisor。
- Virsh:基于 libvirt 的 命令行工具 (CLI)
- Virt-Manager:基于 libvirt 的 GUI 工具
- virt-v2v:虚机格式迁移工具
- virt-* 工具:包括 Virt-install (创建KVM虚机的命令行工具), Virt-viewer (连接到虚机屏幕的工具),Virt-clone(虚机克隆工具),virt-top 等
- sVirt:安全工具
二、CPU虚拟化
1、为什么需要 CPU 虚拟化
X86 操作系统是设计在直接运行在裸硬件设备上的,因此它们自动认为它们完全占有计算机硬件。x86 架构提供四个特权级别给操作系统和应用程序来访问硬件。 Ring 是指 CPU 的运行级别,Ring 0是最高级别,Ring1次之,Ring2更次之…… 就 Linux+x86 来说,
- 操作系统(内核)需要直接访问硬件和内存,因此它的代码需要运行在最高运行级别 Ring0上,这样它可以使用特权指令,控制中断、修改页表、访问设备等等。
- 应用程序的代码运行在最低运行级别上ring3上,不能做受控操作。如果要做,比如要访问磁盘,写文件,那就要通过执行系统调用(函数),执行系统调用的时候,CPU的运行级别会发生从ring3到ring0的切换,并跳转到系统调用对应的内核代码位置执行,这样内核就为你完成了设备访问,完成之后再从ring0返回ring3。这个过程也称作用户态和内核态的切换。
那么,虚拟化在这里就遇到了一个难题,因为宿主操作系统是工作在 ring0 的,客户操作系统就不能也在 ring0 了,但是它不知道这一点,以前执行什么指令,现在还是执行什么指令,但是没有执行权限是会出错的。所以这时候虚拟机管理程序(VMM)需要避免这件事情发生。 虚机怎么通过 VMM 实现 Guest CPU 对硬件的访问,根据其原理不同有三种实现技术:
- 全虚拟化
- 半虚拟化
- 硬件辅助的虚拟化
2、基于二进制翻译的全虚拟化(Full Virtualization with Binary Translation)
客户操作系统运行在 Ring 1,它在执行特权指令时,会触发异常(CPU的机制,没权限的指令会触发异常),然后 VMM 捕获这个异常,在异常里面做翻译,模拟,最后返回到客户操作系统内,客户操作系统认为自己的特权指令工作正常,继续运行。但是这个性能损耗,就非常的大,简单的一条指令,执行完,了事,现在却要通过复杂的异常处理过程。
异常 “捕获(trap)-翻译(handle)-模拟(emulate)” 过程:
3、超虚拟化(或者半虚拟化/操作系统辅助虚拟化 Paravirtualization)
半虚拟化的思想就是,修改操作系统内核,替换掉不能虚拟化的指令,通过超级调用(hypercall)直接和底层的虚拟化层hypervisor来通讯,hypervisor 同时也提供了超级调用接口来满足其他关键内核操作,比如内存管理、中断和时间保持。
这种做法省去了全虚拟化中的捕获和模拟,大大提高了效率。所以像XEN这种半虚拟化技术,客户机操作系统都是有一个专门的定制内核版本,和x86、mips、arm这些内核版本等价。这样以来,就不会有捕获异常、翻译、模拟的过程了,性能损耗非常低。这就是XEN这种半虚拟化架构的优势。这也是为什么XEN只支持虚拟化Linux,无法虚拟化windows原因,微软不改代码啊。
4、硬件辅助的全虚拟化
2005年后,CPU厂商Intel 和 AMD 开始支持虚拟化了。 Intel 引入了 Intel-VT (Virtualization Technology)技术。 这种 CPU,有 VMX root operation 和 VMX non-root operation两种模式,两种模式都支持Ring 0 ~ Ring 3 共 4 个运行级别。这样,VMM 可以运行在 VMX root operation模式下,客户 OS 运行在VMX non-root operation模式下。
而且两种操作模式可以互相转换。运行在 VMX root operation 模式下的 VMM 通过显式调用 VMLAUNCH 或 VMRESUME 指令切换到 VMX non-root operation 模式,硬件自动加载 Guest OS 的上下文,于是 Guest OS 获得运行,这种转换称为 VM entry。Guest OS 运行过程中遇到需要 VMM 处理的事件,例如外部中断或缺页异常,或者主动调用 VMCALL 指令调用 VMM 的服务的时候(与系统调用类似),硬件自动挂起 Guest OS,切换到 VMX root operation 模式,恢复 VMM 的运行,这种转换称为 VM exit。VMX root operation 模式下软件的行为与在没有 VT-x 技术的处理器上的行为基本一致;而VMX non-root operation 模式则有很大不同,最主要的区别是此时运行某些指令或遇到某些事件时,发生 VM exit。
也就说,硬件这层就做了些区分,这样全虚拟化下,那些靠“捕获异常-翻译-模拟”的实现就不需要了。而且CPU厂商,支持虚拟化的力度越来越大,靠硬件辅助的全虚拟化技术的性能逐渐逼近半虚拟化,再加上全虚拟化不需要修改客户操作系统这一优势,全虚拟化技术应该是未来的发展趋势。
三、KVM 内存虚拟化
1、内存虚拟化的概念
除了 CPU 虚拟化,另一个关键是内存虚拟化,通过内存虚拟化共享物理系统内存,动态分配给虚拟机。虚拟机的内存虚拟化很象现在的操作系统支持的虚拟内存方式,应用程序看到邻近的内存地址空间,这个地址空间无需和下面的物理机器内存直接对应,操作系统保持着虚拟页到物理页的映射。现在所有的 x86 CPU 都包括了一个称为内存管理的模块MMU(Memory Management Unit)和 TLB(Translation Lookaside Buffer),通过MMU和TLB来优化虚拟内存的性能。
KVM 实现客户机内存的方式是,利用mmap系统调用,在QEMU主线程的虚拟地址空间中申明一段连续的大小的空间用于客户机物理内存映射。
(HVA 同下面的 MA,GPA 同下面的 PA,GVA 同下面的 VA)
在有两个虚机的情况下,情形是这样的:
可见,KVM 为了在一台机器上运行多个虚拟机,需要增加一个新的内存虚拟化层,也就是说,必须虚拟 MMU 来支持客户操作系统,来实现 VA -> PA -> MA 的翻译。客户操作系统继续控制虚拟地址到客户内存物理地址的映射 (VA -> PA),但是客户操作系统不能直接访问实际机器内存,因此VMM 需要负责映射客户物理内存到实际机器内存 (PA -> MA)。
VMM 内存虚拟化的实现方式:
- 软件方式:通过软件实现内存地址的翻译,比如 Shadow page table (影子页表)技术
- 硬件实现:基于 CPU 的辅助虚拟化功能,比如 AMD 的 NPT 和 Intel 的 EPT 技术
影子页表技术:
2、KVM 内存虚拟化
KVM 中,虚机的物理内存即为 qemu-kvm 进程所占用的内存空间。KVM 使用 CPU 辅助的内存虚拟化方式。在 Intel 和 AMD 平台,其内存虚拟化的实现方式分别为:
- AMD 平台上的 NPT (Nested Page Tables) 技术
- Intel 平台上的 EPT (Extended Page Tables)技术
EPT 和 NPT采用类似的原理,都是作为 CPU 中新的一层,用来将客户机的物理地址翻译为主机的物理地址。关于 EPT, Intel 官方文档中的技术如下(实在看不懂...)
EPT的好处是,它的两阶段记忆体转换,特点就是将 Guest Physical Address → System Physical Address,VMM不用再保留一份 SPT (Shadow Page Table),以及以往还得经过 SPT 这个转换过程。除了降低各部虚拟机器在切换时所造成的效能损耗外,硬体指令集也比虚拟化软体处理来得可靠与稳定。
3、KVM内存管理
KVM继承了Linux系统管理内存的诸多特性,比如,分配给虚拟使用的内存可以被交换至交换空间、能够使用大内存页以实现更好的性能,以及对NUMA的支持能够让虚拟机高效访问更大的内存空间等。
KVM基于Intel的EPT(Extended Page Table)或AMD的RVI(Rapid Virtualization Indexing)技术可以支持更新的内存虚拟功能,这可以降低CPU的占用率,并提供较好的吞吐量。
此外,KVM还借助于KSM(Kernel Same-page Merging)这个内核特性实现了内存页面共享。KSM通过扫描每个虚拟机的内存查找各虚拟机间相同的内存页,并将这些内存页合并为一个被各相关虚拟机共享的单独页面。在某虚拟机试图修改此页面中的数据时,KSM会重新为其提供一个新的页面副本。实践中,运行于同一台物理主机上的具有相同GuestOS的虚拟机之间出现相同内存页面的概率是很的,比如共享库、内核或其它内存对象等都有可能表现为相同的内存页,因此,KSM技术可以降低内存占用进而提高整体性能。
四、I/O 设备虚拟化
1、全虚拟化 I/O 设备
KVM 在 IO 虚拟化方面,传统或者默认的方式是使用 QEMU 纯软件的方式来模拟 I/O 设备,包括键盘、鼠标、显示器,硬盘 和 网卡 等。模拟设备可能会使用物理的设备,或者使用纯软件来模拟。模拟设备只存在于软件中。
过程:
- 客户机的设备驱动程序发起 I/O 请求操作请求
- KVM 模块中的 I/O 操作捕获代码拦截这次 I/O 请求
- 经过处理后将本次 I/O 请求的信息放到 I/O 共享页 (sharing page),并通知用户空间的 QEMU 程序。
- QEMU 程序获得 I/O 操作的具体信息之后,交由硬件模拟代码来模拟出本次 I/O 操作。
- 完成之后,QEMU 将结果放回 I/O 共享页,并通知 KMV 模块中的 I/O 操作捕获代码。
- KVM 模块的捕获代码读取 I/O 共享页中的操作结果,并把结果放回客户机。
注意:当客户机通过DMA (Direct Memory Access)访问大块I/O时,QEMU 模拟程序将不会把结果放进共享页中,而是通过内存映射的方式将结果直接写到客户机的内存中共,然后通知KVM模块告诉客户机DMA操作已经完成。
这种方式的优点是可以模拟出各种各样的硬件设备;其缺点是每次 I/O 操作的路径比较长,需要多次上下文切换,也需要多次数据复制,所以性能较差。
2、准虚拟化 (Para-virtualizaiton) I/O 驱动 virtio
在 KVM 中可以使用准虚拟化驱动来提供客户机的I/O 性能。目前 KVM 采用的的是 virtio 这个 Linux 上的设备驱动标准框架,它提供了一种 Host 与 Guest 交互的 IO 框架。
KVM/QEMU 的 vitio 实现采用在 Guest OS 内核中安装前端驱动 (Front-end driver)和在 QEMU 中实现后端驱动(Back-end)的方式。前后端驱动通过 vring 直接通信,这就绕过了经过 KVM 内核模块的过程,达到提高 I/O 性能的目的。
virtio 的架构:
纯软件模拟的设备和 Virtio 设备的区别:virtio 省去了纯模拟模式下的异常捕获环节,Guest OS 可以和 QEMU 的 I/O 模块直接通信。
使用 Virtio 的完整虚机 I/O流程:
Host 数据发到 Guest:
- KVM 通过中断的方式通知 QEMU 去获取数据,放到 virtio queue 中
- KVM 再通知 Guest 去 virtio queue 中取数据。
3、PCI/PCI-E 设备直接分配给虚机 (PCI Pass-through)
设备直接分配 (Device assignment)也称为 Device Pass-Through。 先简单看看PCI 和 PCI-E 的区别(AMD CPU):
(简单点看,PCI 卡的性能没有 PCI-E 高,因为 PCI-E 是直接连在 IOMMU 上,而 PCI 卡是连在一个 IO Hub 上。)
主要的 PCI 设备类型:
- Network cards (wired or wireless)
- SCSI adapters
- Bus controllers: USB, PCMCIA, I2C, FireWire, IDE
- Graphics and video cards
- Sound cards
PCI/PCIe Pass-through 原理:
这种方式,允许将宿主机中的物理 PCI 设备直接分配给客户机使用。较新的x86平台已经支持这种类型,Intel 定义的 I/O 虚拟化技术成为 VT-d,AMD 的称为 AMD-V。KVM 支持客户机以独占方式访问这个宿主机的 PCI/PCI-E 设备。通过硬件支持的 VT-d 技术将设备分给客户机后,在客户机看来,设备是物理上连接在PCI或者PCI-E总线上的,客户机对该设备的I/O交互操作和实际的物理设备操作完全一样,不需要或者很少需要 KVM 的参与。运行在 VT-d 平台上的 QEMU/KVM,可以分配网卡、磁盘控制器、USB控制器、VGA 显卡等设备供客户机直接使用。
几乎所有的 PCI 和 PCI-E 设备都支持直接分配,除了显卡以外(显卡的特殊性在这里)。PCI Pass-through 需要硬件平台 Intel VT-d 或者 AMD IOMMU 的支持。这些特性必须在 BIOS 中被启用。Red Hat Enterprise Linux 6.0 及以上版本支持热插拔的 PCI 设备直接分配到虚拟机。
网卡直接分配:
硬盘直接分配:
- 一般 SATA 或者 SAS 等类型的硬盘的控制器都是直接接入到 PCI 或者 PCI-E 总线的,所以也可以将硬盘作为普通的PCI设备直接分配个客户机。需要注意的是,当分配硬盘时,实际上将其控制器作为一个整体分配到客户机中,因此需要在硬件平台上至少有另两个或者多个SATA或者 SAS控制器。
设备直接分配让客户机的优势和不足:
- 好处:在执行 I/O 操作时大量减少甚至避免 VM-Exit 陷入到 Hypervisor 中,极大地提高了性能,可以达到几乎和原生系统一样的性能。VT-d 克服了 virtio 兼容性不好和 CPU 使用频率较高的问题。
- 不足:(1)一台服务器主板上的空间比较有限,因此允许添加的 PCI 和 PCI-E 设备是有限的。大量使用 VT-d 独立分配设备给客户机,让硬件设备数量增加,这会增加硬件投资成本。(2)对于使用 VT-d 直接分配了设备的客户机,其动态迁移功能将受限,不过也可以使用热插拔或者libvirt 工具等方式来缓解这个问题。
- 不足的解决方案:(1)在一台物理宿主机上,仅少数 I/O 如网络性能要求较高的客户机使用 VT-d直接分配设备,其他的使用纯模拟或者 virtio 已达到多个客户机共享同一个设备的目的 (2)对于网络I/O的解决办法,可以选择 SR-IOV 是一个网卡产生多个独立的虚拟网卡,将每个虚拟网卡分配个一个客户机使用。
4、SR-IOV 设备分配
VT-d 的性能非常好,但是它的物理设备只能分配给一个客户机使用。为了实现多个虚机共享一个物理设备,并且达到直接分配的目的,PCI-SIG 组织发布了 SR-IOV (Single Root I/O Virtualization and sharing) 规范,它定义了一个标准化的机制用以原生地支持实现多个客户机共享一个设备。不过,目前 SR-IOV (单根 I/O 虚拟化)最广泛地应用还是网卡上。
SR-IOV 使得一个单一的功能单元(比如,一个以太网端口)能看起来像多个独立的物理设备。一个带有 SR-IOV 功能的物理设备能被配置为多个功能单元。SR-IOV 使用两种功能(function):
- 物理功能(Physical Functions,PF):这是完整的带有 SR-IOV 能力的PCIe 设备。PF 能像普通 PCI 设备那样被发现、管理和配置。
- 虚拟功能(Virtual Functions,VF):简单的 PCIe 功能,它只能处理I/O。每个 VF 都是从 PF 中分离出来的。每个物理硬件都有一个 VF 数目的限制。一个 PF,能被虚拟成多个 VF 用于分配给多个虚拟机。
Hypervisor 能将一个或者多个 VF 分配给一个虚机。在某一时刻,一个 VF 只能被分配给一个虚机。一个虚机可以拥有多个 VF。在虚机的操作系统看来,一个 VF 网卡看起来和一个普通网卡没有区别。SR-IOV 驱动是在内核中实现的。
网卡 SR-IOV 的例子:
SR-IOV 的条件
- 需要 CPU 支持 Intel VT-x 和 VT-D (或者 AMD 的 SVM 和 IOMMU)
- 需要有支持 SR-IOV 规范的设备:目前这种设备较多,比如Intel的很多中高端网卡等。
- 需要 QEMU/KAM 的支持。
RedHat Linux 6.0 官方只完整测试了下面的几款 SR-IOV 网卡:
- Intel 82576NS Gigabit Ethernet Controller ( igb 驱动)
- Intel 82576EB Gigabit Ethernet Controller ( igb 驱动)
- Intel 82599ES 10 Gigabit Ethernet Controller ( ixgbe 驱动)
- Intel 82599EB 10 Gigabit Ethernet Controller ( ixgbe 驱动)
优势和不足:
5、VMM对IO的驱动有三种模式
- 自主的VMM:vmm自行提供驱动和控制台
- 混合的VMM:借助于OS提供驱动
- 依赖于外部OS实现特权域
- 自我提供特权域
- 寄宿式VMM
6、IO虚拟化模型
- 模拟
- 半虚拟化
- 透传
- SR-IOV:主要用于网卡