虚拟机的创建始于kvm_init函数,该函数由QEMU调用TYPE_KVM_ACCEL类型QOM(QEMU Object Model,QEMU对象模型)对象的init_machine成员来调用。由于篇幅有限,这里不再详述,仅画出相关函数调用流程,如图2-11所示。QEMU对象模型是QEMU基于C语言实现的一套面向对象机制,将在4.3.1节详细介绍。
图2-11 QEMU/KVM虚拟机创建的函数调用流程
注:①QEMU进行ioctl系统调用。
KVM模块初始化后,kvm_init函数便可以通过前述/dev/kvm设备文件发起KVM_CREATE_VM ioctl,请求创建虚拟机,相关代码如下。
qemu-4.1.1 /accel/kvm/kvm-all.c
kvm_init函数首先打开/dev/kvm设备文件,然后通过相应的文件描述符发起KVM_GET_API_VERSION ioctl获取KVM模块的接口版本,检验与QEMU支持的KVM版本是否相等。随后kvm_init函数发起KVM_CRETAE_VM ioctl便会陷入前述KVM模块中的kvm_dev_ioctl函数进行处理。该函数根据ioctl类型调用kvm_dev_ioctl_create_vm函数创建虚拟机,相关代码如下。
linux-4.19.0/virt/kvm/kvm-main.c
kvm_dev_ioctl_create_vm首先调用kvm_create_vm函数创建虚拟机对应的结构体,然后调用anon_inode_getfd函数为虚拟机创建一个匿名文件,对应的file_operations为kvm_vm_fops,其定义如下。
linux-4.19.0/virt/kvm/kvm_main.c
这个文件为QEMU提供了虚拟机层级的API,如创建vCPU、创建设备等。KVM最终会将该文件的文件句柄作为返回值返回给QEMU供其使用。kvm_create_vm函数除了创建虚拟机对应的结构体以外,还会调用hardware_enable_all函数,该函数最终会在所有的物理CPU上调用hardware_enable_nolock函数,该函数最终会通过前述kvm_x86_ops的hardware_enable成员调用vmx.c中的hardware_enable函数使能VMX操作模式。hardware_enable函数将获取为每个CPU分配的VMXON区域的物理地址作为参数传入kvm_vcpu_vmxon函数。kvm_vcpu_vmxon函数会设置CR4寄存器中的VMXE(VMX Enabled)位使能VMX操作模式,并执行VMXON指令进入VMX操作模式。
linux-4.19.0/virt/kvm/kvm_main.c
linux-4.19.0/arch/x86/kvm/vmx.c