KVM初始化过程
本文展示的代码中,qemu版本为1.6.0, Linux内核版本为3.12.73
为了介绍qemu和kvm的交互过程,我首先介绍一下kvm给用户提供的接口。kvm是一个内核模块,它实现了一个/dev/kvm的字符设备来与用户进行交互,通过调用一系列ioctl函数可以实现qemu和kvm之间的切换。当要创建一个新的虚拟机时,首先打开/dev/kvm设备,在其上调用ioctl函数:
1 | system_fd = open("/dev/kvm", ORDWR); |
ioctl函数在kvm中的实现为virt/kvm/kvm_main.c中kvm_dev_ioctl函数,当传入的参数为KVM_CREATE_VM时,该函数会创建一个VM,并且返回一个fd,通过该fd可以操作虚拟机。
创建完虚拟机之后,需要在该虚拟机上面创建vcpu,调用的接口也是ioctl,只是此时对应的fd为创建虚拟机时返回的fd。
1 | vcpu_fd = ioctl(vm_fd, VM_CREATE_VCPU, 0); |
此时ioctl函数对应的实现为virt/kvm/kvm_main.c中kvm_vm_ioctl函数,当传入的参数为VM_CREATE_VCPU时,与KVM_CREATE_VM过程类似,它创建一个vcpu并且返回可以操作该vcpu的fd。
创建完vcpu后,可以在该vcpu上面调用ioctl函数进入guest vm。
1 | ret = ioctl(vcpu_fd, KVM_RUN, 0); |
此时ioctl函数对应的实现为virt/kvm/kvm_main.c中kvm_vcpu_ioctl函数,若传入的参数为KVM_RUN,它最终会调用vcpu_enter_guest函数进入guest vm。
qemu作为一个user mode的程序,其入口为main函数,该main函数定义在vl.c文件中。main函数比较长,其中跟KVM初始化相关的主要有两个函数:configure_accelerator()和machine->init(&args)。cofigure_accelerator()函数选择运用哪一种虚拟化方案,其应用到的数据结构为accel_list,会调用accel_list[i].init函数。accel_list的初始化如下所示,当使用kvm虚拟化解决方案时,accel_list[i].init对应的函数即为kvm_init。
1 | static struct { |
kvm_init函数定义在kvm-all.c文件中,其主要功能是打开/dev/kvm设备,创建一个虚拟机。
machine->init(&arg)函数主要初始化硬件设备,并且调用qemu_init_vcpu为每一个vcpu创建一个线程,线程执行的函数为qemu_kvm_cpu_thread_fn。
qemu_kvm_cpu_thread_fn函数创建vcpu,然后调用kvm_cpu_exec函数。kvm_cpu_exec函数调用ioctl进入kvm并最终进入guest vm。
参考资料: