KVM进阶
Linux 既有良好的灵活性,在虚拟化方面同样出色。但是最近,随着内核虚拟机(KVM)的出现,Linux 虚拟化的前景发生了变化。KVM 是构成主流 Linux 内核(V2.6.20)一部分的第一个虚拟化解决方案。KVM 支持 Linux 客户操作系统的虚拟化 —— 甚至支持其硬件对虚拟化敏感的 Windows 系统的虚拟化。了解 Linux KVM 的架构并了解它与内核的紧密集成为何会改变您使用 Linux 的方式。
简介
虚拟化 概念很早就已出现。简单来说,虚拟化就是使用某些程序,并使其看起来类似于其他程序的过程。将这个概念应用到计算机系统中可以让不同用户看到不同的单个系统(例如,一台计算机可以同时运行 Linux 和 MicrosoftWindows)。这通常称为全虚拟化(full virtualization)。
虚拟化也可以使用更加复杂的格式,其中单个计算机看上去具有多个架构(对于一个用户 来说,它是一个标准的 x86 平台;对于另外一个用户来说,它是 IBM Power PC 平台)。这种虚拟化形式通常被称为硬件仿真。
最后,更加简单的一种虚拟化是操作系统虚拟化,其中一台计算机可以运行相同类型的多 个操作系统。这种虚拟化可以将一个操作系统的多个服务器隔离开来(这意味着全都必须使用相同类型和版本的操作系统)。
虚拟化和准虚拟化(para-virtualization)
虚拟化最常使用的两种方法是全虚拟化 和准虚拟化。使用全虚拟化,在虚拟化的操作系统和硬件之间存在一个层,用于决定访问。这个层称为系统管理程序或虚拟机监视器(VMM)。准虚拟化与之类似,但是系统管理程序会以一种更具协作性的方式进行操作。这是因为每个客户操作系统都了解自己正在虚拟化模式中运行,因此每个系统都与系统管理程序协作,来实现底层硬件的虚拟化。
全虚拟化的例子包括商业虚拟化解决方案 VMware,以及商业 IBM zSeries 计算机上使用的 IBM System z9 VirtualMachine(z/VM)操作系统。准虚拟化的例子有 Xen 和 User-Mode-Linux (UML)。 KVM 也被认为是一个全虚拟化解决方案,不过我们稍后再介绍这个问题。
虚拟化的工作原理
我们首先简要介绍一下虚拟化技术及其涉及的元素。虚拟化解决方案的底部是要进行虚拟 化的机器。这台机器可能直接支持虚拟化,也可能不会直接支持虚拟化;那 么就需要系统管理程序 层的支持。系统管理程序,或称为 VMM,可以看作是平台硬件和操作系统的抽象化。在某些情况中,这个系统管理程序就是一个操作系统;此时,它就称为主机操作系统,如 图 1 所示。
图 1. 虚拟化的分层抽象
系统管理程序之上是客户机操作系统,也称为虚拟机(VM)。这些 VM 都是一些相互隔离的操作系统,将底层硬件平台视为自己所有。但是实际上,是系统管理程序为它们制造了这种假象。
目前使用虚拟化解决方案的问题是,并非所有硬件都可以很好地支持虚拟化。较老的 x86 处理器根据执行范围对特定指令会产生不同结果。这就产生了一个问题,因为系统管理程序应该只能在一个最受保护的范围中执行。由于这个原因,诸如 VMWare 之类的虚拟化解决方案会提前扫描要执行的代码,从而将这些指令替换为一些陷阱指令(trap instruction),这样系统管理程序就可以正确地处理它们。Xen 可以支持一种协作的虚拟化方法,它不需要任何修改,因为客户机知道自己正在进行虚拟化,并已经进行了修改。KVM 会简单地忽略这个问题,如果您希望进行虚拟化,就强制必须在更新的硬件上运行。
刚开始会觉得这有些不方便,但是考虑到目前上市的较新机器都可以支持虚拟化(例如 Intel VT 和 AMD SVM),用不了多久,这将成为标准方法而不是少数例外情况。
KVM 系统管理程序
考虑到虚拟化技术的发展时间并不长,KVM 实际上还是一种相对来说比较新的技术。目前存在各具功能的开源技术,例如 Xen、Bochs、UML、Linux-VServer 和 coLinux,但是 KVM 目前正在被大量使用。另外,KVM 不再仅仅是一个全虚拟化解决方案,而将成为更大的解决方案的一部分。
KVM 所使用的方法是通过简单地加载内核模块而将 Linux 内核转换为一个系统管理程序。这个内核模块导出了一个名为 /dev/kvm 的设备,它可以启用内核的客户模式(除了传统的内核模式和用户模式)。有了 /dev/kvm 设备,VM 使自己的地址空间独立于内核或运行着的任何其他 VM 的地址空间。设备树(/dev)中的设备对于所有用户空间进程来说都是通用的。但是每个打开 /dev/kvm 的进程看到的是不同的映射(为了支持 VM 间的隔离)。
KVM 然后会简单地将 Linux 内核转换成一个系统管理程序(在安装 kvm 内核模块时)。由于标准 Linux 内核就是一个系统管理程序,因此它会从对标准内核的修改中获益良多(内存支持、调度程序等)。对这些 Linux 组件进行优化(例如 2.6 版本内核中的新 O(1) 调度程序)都可以让系统管理程序(主机操作系统)和 Linux 客户操作系统同时受益。但是 KVM 并不是第一个这样做的程序。UML 很久以前就将 Linux 内核转换成一个系统管理程序了。使用内核作为一个系统管理程序,您就可以启动其他操作系统,例如另一个 Linux 内核或 Windows 系统。
KVM
安装 KVM 之后,您可以在用户空间启动客户操作系统。每个客户操作系统都是主机操作系统(或系统管理程序)的一个单个进程。图 2 提供了一个使用 KVM 进行虚拟化的视图。底部是能够进行虚拟化的硬件平台(目前指的是 Intel VT 或 AMD-SVM 处理器)。在裸硬件上运行的是系统管理程序(带有 KVM 模块的 Linux 内核)。这个系统管理程序与可以运行其他应用程序的普通 Linux 内核类似。但是这个内核也可以支持通过 kvm 工具加载的客户操作系统。最后,客户操作系统可以支持主机操作系统所支持的相同应用程序。
图 2. 使用 KVM 的虚拟化组件
记住 KVM 只是虚拟化解决方案的一部分。处理器直接提供了虚拟化支持(可以为多个操作系统虚拟化处理器)。内存可以通过 kvm 进行虚拟化(这在下一节中将会讨论)。最后,I/O 通过一个稍加修改的 QEMU 进程(执行每个客户操作系统进程的一个拷贝)进行虚拟化。
KVM 向Linux 中引入了一种除现有的内核和用户模式之外的新进程模式。这种新模式就称为客户 模式,顾名思义,它用来执行客户操作系统代码(至少是一部分代码)。回想一下内核模式表示代码执行的特权模式,而用户模式则表示非特权模式(用于那些运行在内核之外的程序)。根据运行内容和目的,执行模式可以针对不同的目的进行定义。客户模式的存在就是为了执行客户操作系统代码,但是只针对那些非 I/O 的代码。在客户模式中有两种标准模式,因此客户操作系统在客户模式中运行可以支持标准的内核,而在用户模式下运行则支持自己的内核和用户空间应用程序。客户操作系统的用户模式可以用来执行 I/O 操作,这是单独进行管理的。
在客户操作系统上执行 I/O 的功能是由 QEMU 提供的。QEMU 是一个平台虚拟化解决方案,允许对一个完整的 PC 环境进行虚拟化(包括磁盘、图形适配器和网络设备)。客户操作系统所生成的任何I/O 请求都会被中途截获,并重新发送到 QEMU 进程模拟的用户模式中。
KVM 通过/dev/kvm 设备提供了内存虚拟化。每个客户操作系统都有自己的地址空间,并且是在实例化客户操作系统时映射的。映射给客户操作系统的物理内存实际上是映射给这个进程的虚拟内存。为了支持客户物理地址到主机物理地址的转换,系统维护了一组影子页表(shadow page table)。处理器也可以通过在访问未经映射的内存位置时使用系统管理程序(主机内核)来支持内存转换进程。
实例化新客户操作系统
新客户操作系统的实例化是由一个名为 kvm 的工具提供的。这个工具可以与 kvm 模块协同工作,使用 /dev/kvm 来加载客户操作系统,将它与虚拟磁盘(主机操作系统中的一个普通文件)关联起来,然后启动客户操作系统。
通过一组在 /dev/kvm设备上执行的 ioctls 可以提供控制支持。当第一次打开这个特殊文件时,就会创建一个新的 VM 对象,它与一个虚拟 CPU 关联在一起。您然后可以使用几个 ioctls 来创建一个虚拟 CPU,检查 kvm 版本,创建内存区域,然后启动一个虚拟 CPU。您可以使用 kvm 命令实现这种功能。在接下来的几节中,我们将介绍 kvm 命令,并给出几个受支持的 ioctls 的示例。
使用 KVM
如果硬件支持的话,使用 KVM 实际上非常简单。您需要一个具有虚拟化支持的处理器。通过查看 /proc/cpuinfo可以知道系统是否支持虚拟化。这个文件指定了是否支持 vmx(Intel)或 svm(AMD)扩展。
接下来,您需要一个启用了 KVM 支持的 Linux 内核。您可以在 Device Drivers > Virtualization 下的内核配置中完成这种配置。还必须启用处理器对环境的支持。另外,还必须具有 kvm 和 qemu 用户空间应用程序。
有了启用了虚拟化支持的引导内核,接下来的一个步骤是为客户操作系统创建一个磁盘映像。您可以使用 qeumu-img 来完成此操作,如下所示。注意这个映像的大小是 4GB,但是使用 QEMU 的写时复制格式(copy-on-write,qcow)时,整个文件将根据需要增长,而不是完全占据这 4 GB 的空间。 $ qemu-img create -f qcowvm-disk.img 4G
复制代码在创建虚拟磁盘之后,就可以将客户操作系统加载到其上。下面的例子假设客户操作系统是在 CD-ROM 上。除了使用 CD-ROM ISO 映像来填充虚拟磁盘之外,还必须在结束时启动这个映像。 $ kvm -no-acpi -m 384 -cdrom guestos.iso -hda vm-disk.img -boot d
复制代码AriKivity 已经编写了一组测试工具来测试 KVM,而不需要全部的设备模型。下面的代码片断(来自于 kvm-12/user/main.c)从较高的层次上查看了 VM 的启动(请参见清单 1)。控制特性是由内核中的 ioctls 提供的(具体来说,在 ./linux-2.6.20/drivers/kvm/kvm_main.c 文件中)。
对 kvm_init的调用会打开 /dev/kvm 设备,检查版本号(由 KVM 内核模块导出),然后分配一个 KVM 上下文对象并填充一些回调函数。kvm_create 函数会建立并映射两个内存区域,然后使用 ioctl(KVM_CREATE_VCPU)创建一个虚拟 CPU(VCPU)。
load_file 函数然后会将映像加载到给定的 VM 的地址空间中,然后调用 kvm_run 执行该 VM(使用ioctl KVM_RUN)。尽管这个过程非常简单,但是它解释了如何使用 KVM 实例化新客户操作系统。
清单 1. 测试 KVM 系统管理程序的应用程序片断 int main()
{ void *vm_mem; kvm = kvm_init(&test_callbacks, 0); if (!kvm) { fprintf(stderr, "kvm_init failed\n"); return 1; } if (kvm_create(kvm, 128 * 1024 * 1024, &vm_mem) < 0) { kvm_finalize(kvm); fprintf(stderr, "kvm_create failed\n"); return 1; } if (ac > 1) if (strcmp(av[1], "-32") != 0) load_file(vm_mem + 0xf0000, av[1]); else enter_32(kvm); if (ac > 2) load_file(vm_mem + 0x100000, av[2]); kvm_show_regs(kvm, 0); kvm_run(kvm, 0); return 0; }
KVM 是解决虚拟化问题的一个有趣的解决方案,但是由于它是第一个进入内核的虚拟化解决方案,很难想象它会很快用于服务器虚拟化。还有其他一些方法一直在为进入内核而竞争(例如 UML 和 Xen),但是由于 KVM 需要的修改较少,并且可以将标准内核转换成一个系统管理程序,因此它的优势不言而喻。
KVM的另外一个优点是它是内核本身的一部分,因此可以利用内核的优化和改进。与其他独立的系统管理程序解决方案相比,这种方法是一种不会过时的技术。KVM 两个最大的缺点是需要较新的能够支持虚拟化的处理器,以及一个用户空间的 QEMU 进程来提供 I/O 虚拟化。但是不论好坏,KVM 位于内核中,这对于现有解决方案来说是一个巨大的飞跃。