前两节介绍了CPU虚拟化实现的难点以及Intel VT-x为CPU虚拟化提供的硬件支持。本节以x86架构下的QEMU/KVM实现为例,介绍前述硬件虚拟化技术如何被应用到实践中。
QEMU原本是纯软件实现的一套完整的虚拟化方案,支持CPU虚拟化、内存虚拟化以及设备模拟等,但是性能不太理想。随着硬件辅助虚拟化技术逐渐兴起,Qumranet公司基于新兴的虚拟化硬件实现了KVM。KVM遵循Linux的设计原则,以内核模块的形式动态加载到Linux内核中,利用虚拟化硬件加速CPU虚拟化和内存虚拟化流程,I/O虚拟化则交给用户态的QEMU完成,QEMU/KVM架构如图2-10 所示。CPU虚拟化主要关心图2-10的左侧部分,即vCPU是如何创建并运行的,以及当vCPU执行敏感指令触发VM-Exit时,QEMU/KVM又是如何处理这些VM-Exit的。
图2-10 QEMU/KVM架构
当QEMU启动时,首先会解析用户传入的命令行参数,确定创建的虚拟机类型(通过QEMU-machine参数指定)与CPU类型(通过QEMU-cpu参数指定),并创建相应的机器模型和CPU模型。而后QEMU打开KVM模块设备文件并发起KVM_CREATE_VM ioctl,请求KVM创建一个虚拟机。KVM创建虚拟机相应的结构体并为QEMU返回一个虚拟机文件描述符。QEMU通过虚拟机文件描述符发起KVM_CREATE_VCPU ioctl,请求KVM创建vCPU。与创建虚拟机流程类似,KVM创建vCPU相应的结构体并初始化,返回一个vCPU文件描述符。QEMU通过vCPU文件描述符发起KVM_RUN ioctl,vCPU线程执行VMLAUNCH指令进入非根模式,执行虚拟机代码直至发生VM-Exit。KVM根据VM-Exit的原因进行相应处理,如果与I/O有关,则需要进一步返回到QEMU中进行处理,以上就是QEMU/KVM CPU虚拟化的主要流程。本节将从KVM模块初始化、虚拟机创建、vCPU创建和vCPU运行四个方面进行介绍,最后给出CPU虚拟化实例。后续章节将深入QEMU/KVM源码讲解每部分的具体实现。在没有特殊说明的情况下,本节以及后续章节所列出的示例代码对应的Linux内核版本为 4.19.0 ,QEMU版本为 4.1.1 ,由于篇幅有限,示例代码只摘取了源码中的一部分,感兴趣的读者可以自行下载相应版本的源码查看完整代码。