二、KVM模块

KVM模块是KVM虚拟化的核心组件,主要功能是初始化CPU 硬件,打开虚拟化模式,然后将虚拟客户机运行在虚拟机模式下,并对虚拟客户机的运行提供一定的支持KVM 仅支持硬件虚拟化,打开并初始化系统硬件以支持虚拟机的运行,是KVM 模块的职责所在。

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

KVM 模块与用户空间QEMU 的通信接口主要是一系列针对特殊设备文件的IOCTL 调用。KVM 模块加载之初, 只存在/dev/kvm 文件, 而针对该文件的最重要的IOCTL 调用就是“创建虚拟机”。“创建虚拟机”可以理解成KVM 为了某个特定的虚拟客户机(用户空间程序创建并初始化)创建对应的内核数据结构。同时,KVM 还会返回一个文件句柄来代表所创建的虚拟机。针对该文件句柄的IOCTL 调用可以对虚拟机做相应的管理,比如创建用户空间虚拟地址和客户机物理地址及真实内存物理地址的映射关系等。

CPU虚拟化是由KVM负责的,对于创建的vcpu,KVM 模块会为每一个创建出来的虚拟处理器生成对应的文件句柄,对虚拟处理器相应的文件句柄进行相应的IOCTL 调用,就可以对虚拟处理器进行管理。针对虚拟处理器的最重要的IOCTL 调用就是“执行虚拟处理器”。通过它,用户空间准备好的虚拟机在KVM 模块的支持下,被置于虚拟化模式中的非根模式下,开始执行二进制指令。在非根模式下,所有敏感的二进制指令都会被处理器捕捉到,处理器在保存现场之后自动切换到根模式,由KVM 决定如何进一步处理(要么由KVM 模块直接处理,要么返回用户空间交由用户空间程序处理)。

内存虚拟化也是由KVM 模块实现的,内存虚拟化往往是一个虚拟机实现中代码量最大、实现最复杂的部分。
处理器中的内存管理单元(MMU)通过页表的形式将程序运行的虚拟地址转换成为物理内存地址,但是在虚拟机模式下,内存管理
单元的页表则必须在一次查询的时候完成两次地址转换,将客户机程序的虚拟地址转换成为客户机物理地址以外,还必须将客户机物理地址转换成为真实物理地址。KVM 模块开始使用了影子页表的技术来解决这个问题:在客户机运行时候,处理器真正使用的页表并不是客户机操作系统维护的页表,而是KVM 模块根据这个页表维护的另外一套影子页表。影子页表技术非常复杂,消耗的资源开药也比较大,所以新的处理器在硬件上支持了内存虚拟化(比如Intel 的EPT 技术)。通过引入第二级页表来描述客户机虚拟地址和真实物理地址的转换,硬件可以自动进行两级转换生成正确的内存访问地址。KVM 模块将其称为二维分页机制。

处理器对设备的访问主要是通过IO 指令和MMIO,其中IO 指令会被处理器直接截获,MMIO 会通过配置内存虚拟化来捕捉。但是,外设的模拟一般并不由KVM 模块负责。一般来说,只有对性能要求比较高的虚拟设备才会由KVM 内核模块来直接负责,比如虚拟中断控制器和虚拟时钟,这样可以大量减少处理器的模式切换的开销。大部分的输入输出设备还是会交给QEMN负责。

results matching ""

    No results matching ""