虚拟化是一种资源管理技术,它将计算机的各种实体资源(CPU、内存、存储、网络等)予以抽象和转化出来,并提供分割、重新组合,以达到最大化利用物理资源的目的。
VMM(Virtual Machine Monitor),虚拟机监控器,也称为Hypervisor,VMM软件层实现了虚拟化功能。
虚拟化分类
软件虚拟化和硬件虚拟化
软件虚拟化是指通过VMM层纯软件的环境来模拟执行客户机里的指令。其中QEMU可以将使用客户机指令集的二进制代码转换为宿主机指令集的二进制代码,然后交给实际的物理平台执行。
硬件虚拟化技术就是指计算机硬件本身提供能力让客户机指令独立执行,而不完全需要VMM截获并重定向指令。
x86架构为为客户机提供了受限的运行环境(non-root mode), VMM运行在root mode,拥有完整的硬件访问控制权限。Intel在其x86 CPU中加入硬件虚拟化的支持——Intel Virtualization Technology,简称Intel VT。
半虚拟化和全虚拟化
半虚拟化是在软件虚拟化的基础上,修改客户机操作系统,让其配合VMM,可以提升性能,简化VMM的复杂度,同时不依赖硬件虚拟化,跨平台支持较好。典型的半虚拟化技术就是virtio。
全虚拟化与半虚拟化不同的是,全虚拟化不需要修改客户机操作系统,客户机不知道自身运行在虚拟环境中。VMM软件捕获处理客户机操作系统指令,发往硬件,相对于半虚拟化,极大增加了VMM复杂性。
在硬件虚拟化技术出现之前,软件实现的全虚拟化不如VMM和客户机操作系统协同运作的半虚拟化,硬件虚拟化技术的兴起,让由硬件虚拟化辅助的全虚拟化全面超过了半虚拟化。
Type1和Type2虚拟化
从软件框架的角度上,根据虚拟化层是直接位于硬件之上还是在一个宿主操作系统之上,将虚拟化划分为Type1和Type2,
Type1类的Hypervisor直接运行在硬件之上,没有宿主机操作系统,Hypervisor直接控制硬件资源和客户机。典型框架为Xen、Vmware ESX。
Type2类的Hypervisor运行在一个宿主机操作系统之上(Vmware Workstation)或者系统里面(KVM),Hypervisor作为宿主机操作系统中的一个应用程序,客户机就是在宿主机操作系统上的一个进程。
KVM与Xen相比较,KVM利用了现有的Linux内核代码构建了Hypervisor,可以复用进程调度、内存管理等代码。
kvm原理如下图所示。Guest作为客户机运行应用程序,KVM则作为宿主机Host的内核模块,KVM用户态为QEMU。KVM 负责模拟虚拟机的CPU运行,内存管理,设备管理等;QEMU则模拟虚拟机的IO设备接口以及用户态控制接口。QEMU通过设备文件/dev/kvm进行IOCTL控制KVM模块的运行过程。
KVM设备管理
KVM是必须使用硬件虚拟化辅助技术(如Intel VT-x、AMD-V)的Hypervisor,在CPU运行效率方面有硬件支持,效率是比较高的;在有Intel EPT特性支持的平台上,内存虚拟化的效率也较高。IO方面有纯软件模拟IO设备、半虚拟化驱动(virtio)、设备直接分配(Intel VT-d)、单根I/O虚拟化(SR-IOV)。
QEMU/KVM提供了全虚拟化环境,可以让客户机不经过任何修改就能运行在KVM环境中。KVM在I/O虚拟化方面,传统的方式是使用QEMU纯软件的方式来模拟I/O设备,效率较低。可以在客户机中使用半虚拟化驱动(Paravirtualized Drivers,PV Drivers)来提高客户机的性能。
KVM虚拟机在配置磁盘的时候,可以指定IDE、SATA、Virtio、Virtio-SCSI几种磁盘,从虚拟的方式来看IDE、SATA是纯软件模拟的磁盘,Virtio、Virtio-SCSI是半虚拟化的磁盘,通过改造虚拟机系统的驱动来达到提升性能的目标。
Virtio驱动主要是绕过QEMU软件模拟这一层,让虚拟机的操作系统可以和内核的虚拟化层直接通信,提高通信效率。
磁盘IO从客户机到宿主机存储流程如下图所示。
KVM模块初始化
kvm模块在内核源码中的路径为“\linux-4.14.191\arch\x86\kvm”,该内核模块的入口函数根据平台的不同,分别位于svm.c与vmx.c。SVM是AMD,VMX是intel。
1 | ./arch/x86/kvm/svm.c:module_init(svm_init) |
目录“linux-4.14.191\virt\kvm”下,存放了架构性质的文件,独立于处理器架构,主要提供了公用的方法和数据结构。kvm_main.c中的kvm_init函数是kvm的入口函数。
1 | linux-4.14.191/virt/kvm/kvm_main.c(kvm_init) |
KVM的初始化可以分为两步:
- 在平台相关的KVM模块中通过module_init宏进入KVM的初始化,执行硬件初始化相关操作。
- kvm_init函数进行正式的初始化工作,期间进行一系列子操作。包含了体系结构相关的初始化设置,也包含了各类回调函数的设置,资源分配,以及设备注册等
kvm_arch_init进行体系结构相关的初始化
cpuhp_setup_state_nocalls设置CPU热插拔时的回调函数
register_reboot_notifier 注册重启时回调函数
kmem_cache_create 创建用于分配kvm_vcpu结构体的slab缓存
注册设备
注册vfio操作集
代码如下:
1 | int kvm_init(void *opaque, unsigned vcpu_size, unsigned vcpu_align, |
设备注册
用户态程序打开字符设备文件/dev/kvm获得文件描述符fd,通过ioctl系统调用操作kvm内核模块,例如传入指令KVM_CREATE_KVM,即可创建一个VM虚拟机。
misc_register用于注册该字符设备驱动,该字符设备文件用于操作kvm内核模块。
虚拟机创建完成后,会注册第二个字符设备文件/dev/kvm-vm,该字符设备文件用于创建vcpu,设置内存区间,分配中断等。
创建vcpu后,会注册第三个字符设备文件/dev/kvm-vcpu,该字符设备文件用于创建vcpu,设置内存区间,分配中断等。
每个字符设备文件都定义了函数操作集,其关系如下图所示。
相关结构体
KVM结构体在KVM的系统架构中代表一个具体的虚拟机。当通过VM_CREATE_KVM指令字创建一个新的KVM虚拟机之后,就会创建一个新的KVM结构体对象。
1 | //代表一个具体的虚拟机 |
硬件虚拟化使用vCPU ( Virtual CPU)描述符来描述虚拟CPU。vCPU描述符类似于操作系统中的进程描述符(或进程控制块),其本质是一个结构体,
1 |
|
本次有关虚拟化知识就介绍到这里了。