Skip to the content.

KVM 原理简介

1. 硬件虚拟化技术

1.1. CPU 虚拟化

Intel 在处理器级别提供了对虚拟化技术的支持,被称为 VMX(virtual-machine extensions)。有两种 VMX 操作模式:VMX 根操作(root operation)VMX 非根操作(non-root operation)。作为虚拟机监控器中的 KVM 就是运行在根操作模式下,而虚拟机客户机的整个软件栈(包括操作系统和应用程序)则运行在非根操作模式下。进入 VMX 非根操作模式被称为“VM Entry”;从非根操作模式退出,被称为“VM Exit”。

x86 架构本身是不能进行全虚拟化的,因为其存在虚拟化漏洞,而 VMX 的非根操作模式就是为了适应虚拟化而专门做了一定的修改;在客户机中执行的一些敏感指令或者一些异常会触发“VM Exit”退到虚拟机监控器中,从而运行在 VMX 根模式。正是这样的限制,让虚拟机监控器保持了对处理器资源的控制(这很重要)。

1.2 内存虚拟化

内存虚拟化的目的是给虚拟客户机操作系统提供一个从 0 地址开始的连续物理内存空间,同时在多个客户机之间实现隔离和调度。在虚拟化环境中,内存地址的访问会涉及如下四个概念:

1)客户机虚拟地址,GVA(Guest Virtual Address)

2)客户机物理地址,GPA(Guest Physical Address)

3)宿主机虚拟地址,HVA(Host Virtual Address)

4)宿主机物理地址,HPA(Host Physical Address)

内存虚拟化就是要将 GVA 转化为最终能够访问的 HPA。在没有硬件提供的内存虚拟化之前,系统通过影子页表(Shadow Page Table)完成这一转化。内存的访问和更新通常是非常频繁的,要维护影子页表中对应关系会非常复杂,开销也较大。同时需要为每一个客户机都维护一份影子页表,当客户机数量较多时,其影子页表占用的内存较大也会是一个问题。

Intel CPU 在硬件设计上就引入了EPT(Extended Page Tables,扩展页表),从而将 GVA 到 HPA 的转换通过硬件来实现。这个转换分为两步,如图所示。首先,通过客户机 CR3 寄存器将 GVA 转化为 GPA,然后通过查询 EPT 来实现 GPA 到 HPA 的转化。

EPT 的控制权在虚拟机监控器中,只有当 CPU 工作在非根模式时才参与内存地址的转换。使用 EPT 后,客户机在读写 CR3 和执行 INVLPG 指令时不会导致 VM Exit,而且客户页表结构自身导致的页故障也不会导致 VM Exit。所以通过引入硬件上 EPT 的支持,简化了内存虚拟化的实现复杂度,同时也提高了内存地址转换的效率。

1.3 设备虚拟化

在虚拟化的架构下,虚拟机监控器必须支持来自客户机的 I/O 请求。通常情况下有以下 4 种 I/O 虚拟化方式。

(1)设备模拟:在虚拟机监控器中模拟一个传统的 I/O 设备的特性,比如在 QEMU 中模拟一个 Intel 的千兆网卡或者一个 IDE 硬盘驱动器,在客户机中就暴露为对应的硬件设备。客户机中的 I/O 请求都由虚拟机监控器捕获并模拟执行后返回给客户机。

(2)前后端驱动接口:在虚拟机监控器与客户机之间定义一种全新的适合于虚拟化环境的交互接口,比如常见的 virtio 协议就是在客户机中暴露为 virtio-net、virtio-blk 等网络和磁盘设备,在 QEMU 中实现相应的 virtio 后端驱动。

(3)设备直接分配:将一个物理设备,如一个网卡或硬盘驱动器直接分配给客户机使用,这种情况下 I/O 请求的链路中很少需要或基本不需要虚拟机监控器的参与,所以性能很好。

(4)设备共享分配:其实是设备直接分配方式的一个扩展。在这种模式下,一个(具有特定特性的)物理设备可以支持多个虚拟机功能接口,可以将虚拟功能接口独立地分配给不同的客户机使用。如 SR-IOV 就是这种方式的一个标准协议。

设备直接分配在 Intel 平台上就是 VT-d(Virtualization Technology For Directed I/O)特性,一般在系统 BIOS 中可以看到相关的参数设置。Intel VT-d 为虚拟机监控器提供了几个重要的能力:I/O 设备分配、DMA 重定向、中断重定向、中断投递等。

2. KVM 架构概述

KVM 就是在硬件辅助虚拟化技术之上构建起来的虚拟机监控器。当然,并非要所有这些硬件虚拟化都支持才能运行 KVM 虚拟化,KVM 对硬件最低的依赖是CPU 的硬件虚拟化支持(也就是说,KVM 可以运行在所有支持 CPU 虚拟化的机器上),比如:Intel 的 VT 技术和 AMD 的 AMD-V 技术,而其他的内存和 I/O 的硬件虚拟化支持,会让整个 KVM 虚拟化下的性能得到更多的提升。

KVM 虚拟化的核心主要由以下两个模块组成:

(1)KVM 内核模块,它属于标准 Linux 内核的一部分,是一个专门提供虚拟化功能的模块,主要负责 CPU 和内存的虚拟化,包括:客户机的创建、虚拟内存的分配、CPU 执行模式的切换、vCPU 寄存器的访问、vCPU 的执行。

(2)QEMU 用户态工具,它是一个普通的 Linux 进程,为客户机提供设备模拟的功能,包括模拟 BIOS、PCI/PCIE 总线、磁盘、网卡、显卡、声卡、键盘、鼠标等。同时它通过 ioctl 系统调用与内核态的 KVM 模块进行交互。

KVM 是在硬件虚拟化支持下的完全虚拟化技术,所以它能支持在相应硬件上能运行的几乎所有的操作系统,x86 下如:Linux、Windows、FreeBSD、MacOS 等。KVM 的基础架构如图所示。在 KVM 虚拟化架构下,每个客户机就是一个 QEMU 进程,在一个宿主机上有多少个虚拟机就会有多少 QEMU 进程;客户机中的每一个虚拟 CPU 对应 QEMU 进程中的一个执行线程;一个宿主机中只有一个 KVM 内核模块,所有客户机都与这个内核模块进行交互。

3. KVM 内核模块

KVM 内核模块是标准 Linux 内核的一部分,由于 KVM 的存在让 Linux 本身就变成了一个Hypervisor,可以原生地支持虚拟化功能。目前,KVM 支持多种处理器平台,它支持最常见的以 Intel 和 AMD 为代表的 x86 和 x86_64 平台,也支持 PowerPC、S/390、ARM 等非 x86 架构的平台。

KVM 模块的任务是打开并初始化系统硬件以支持虚拟机的运行。以 KVM 在 Intel 公司的 CPU 上运行为例,在被内核加载的时候,KVM 模块会先初始化内部的数据结构;做好准备之后,KVM 模块检测系统当前的 CPU,然后打开 CPU 控制寄存器 CR4 中的虚拟化模式开关,并通过执行 VMXON 指令将宿主操作系统(包括 KVM 模块本身)置于 CPU 执行模式的虚拟化模式中的根模式;最后,KVM 模块创建特殊设备文件/dev/kvm 并等待来自用户空间的命令。接下来,虚拟机的创建和运行将是一个用户空间的应用程序(QEMU)和 KVM 模块相互配合的过程。

4. QEMU 用户态设备模拟

与 KVM 不同,QEMU 最初实现的虚拟机是一个纯软件的实现,通过二进制翻译来实现虚拟化客户机中的 CPU 指令模拟,所以性能比较低。但是,其优点是跨平台,跨 ISA。

QEMU 的代码中有完整的虚拟机实现,包括处理器虚拟化、内存虚拟化,以及 KVM 也会用到的虚拟设备模拟(比如网卡、显卡、存储控制器和硬盘等)。除了二进制翻译的方式,QEMU 也能与基于硬件虚拟化的 Xen、KVM 结合,为它们提供客户机的设备模拟。通过与 KVM 的密切结合,让虚拟化的性能提升得非常高,在真实的企业级虚拟化场景中发挥重要作用,所以我们通常提及 KVM 虚拟化时就会说“QEMU/KVM”这样的软件栈。

虚拟机运行期间,QEMU 会通过 KVM 模块提供的系统调用进入内核,由 KVM 模块负责将虚拟机置于处理器的特殊模式下运行。遇到虚拟机进行 I/O 操作时,KVM 模块会从上次的系统调用出口处返回 QEMU,由 QEMU 来负责解析和模拟这些设备。

总之,QEMU 既是一个功能完整的虚拟机监控器,也在 QEMU/KVM 的软件栈中承担设备模拟的工作。