QEMU-KVM框架总结
kvm模块的加载
arch/x86/kvm/vmx/vmx.c
载入kvm模块时,调用顺序为module_init()->vmx_init()->kvm_init(),所以kvm_init()为核心函数
int kvm_init()
{
...
/*
/* 1. 将vmx_x86_ops重命名为kvm_x86_ops
/* 2. 检查cpu是否支持kvm
/* 3. 检查是否包含FPU Feature,并为FPU分配存储空间
/* 4. 为所有CPU分配共享MSR
/* 5. 初始化及设置MMU
/* 6. 设置一个回调函数,用于获取guest信息
/* 7. 初始化LAPIC
*/
r = kvm_arch_init(opaque); // 这里的opaque为一个结构体(vmx_x86_ops),包含一系列kvm主要操作
...
/*
/* 1. 对kvm_x86_ops结构中与硬件相关的内容进行初始化赋值
/* 2. 为每个cpu申请vmcs空间
/* 3. 设置TSC
*/
r = kvm_arch_hardware_setup();
}
kvm模块加载完成后,在系统的/dev目录下出现kvm设备,用于外界与kvm交互
kvm模块向外界提供的ioctl分类
virt/kvm/kvm_main.c
分为3大类,大多都在kvm_main.c中实现,剩余一小部分在arch/x86/kvm/vmx/vmx.c中.3类ioctl分别为设备、虚拟机、虚拟cpu级别.3个层次逐级概念变小,虚拟机由设备层面的ioctl创建,而虚拟cpu由虚拟机级别的ioctl创建.
设备级别ioctl
以kvm_dev_ioctl为前缀,负责以下通用设备级别的ioctl功能的处理
其余设备级别的ioctl功能与架构有关,因此在kvm_arch_dev_ioctl()中实现.
虚拟机级别ioctl
以kvm_vm_ioctl为前缀,负责以下通用虚拟机级别的ioctl功能的处理
其余虚拟机级别的ioctl功能与架构有关,因此在kvm_arch_vm_ioctl()中实现.
虚拟CPU级别ioctl
以kvm_vcpu_ioctl为前缀,负责以下通用虚拟CPU级别的ioctl功能的处理
其余虚拟CPU级别的ioctl功能与架构有关,因此在kvm_arch_vcpu_ioctl()中实现.
kvm中虚拟机的创建流程
主要创建流程就是dev->vm->vcpu,这期间最重要的数据结构为kvm_x86_ops(在vmx.c中定义),但是在ioctl函数中展现的形式与实际调用的函数形式有些许不同. 如在创建vm时,函数调用流程为:
kvm_dev_ioctl_create_vm() -> kvm_create_vm() -> kvm_arch_alloc_vm()
在kvm_arch_alloc_vm()中,具体实现只有一句return kvm_x86_ops->vm_alloc(),此时使用vim插件无法找到vm_alloc()的定义,但在vmx.c中可以看到类似.vm_alloc = vmx_vm_alloc, 因此其实vm_alloc()调用的是vmx_vm_alloc,其它kvm_x86_ops的子方法也可以使用类似的方法找到.
创建vm
kvm_dev_ioctl_create_vm() -> kvm_create_vm() -> kvm_arch_alloc_vm() -> vmx_vm_alloc()
| -> kvm_arch_init_vm() | -> vmx_vm_init()
因此可以通过调用kvm_dev_ioctl_create_vm()完成对一个vm的内存分配和初始化,最终返回的是一个kvm_vmx结构的结构.
创建vCPU
kvm_vm_ioctl_create_vcpu() -> kvm_arch_vcpu_create() -> vmx_create_vcpu()
| -> kvm_arch_vcpu_setup() -> vmx_vcpu_setup()
| -> create_vcpu_fd
kvm_arch_vcpu_create()为vcpu_vmx结构申请空间,初始化vcpu,为guest_msr,vmcs结构申请空间,并利用vmx_vcpu_setup()将vmcs设置为实模式状态,利用vmx_vcpu_put()准备将vcpu切换至host状态.
运行vCPU
kvm_vcpu_ioctl(...,KVM_RUN,...) -> kvm_arch_vcpu_ioctl_run() -> vcpu_run() -> vcpu_enter_guest()|
-> need_resched() <-|
vcpu_enter_guest()使虚拟vcpu进入non-root操作,退出该函数等效于执行了VM exit,如果退出该函数时返回1,即代表guest无法处理本次exit reasion,需要进入到用户空间(qemu),使用qemu对本次exit reason进行处理,处理完成之后再进入vcpu_enter_guest().
QEMU与KVM的交互
QEMU的ioctl
由于这部分与qemu有交互的部分,因此我提前对qemu进行了学习.
qemu与kvm对应有4大类的ioctl,用于对kvm提供的3个级别的fd进行控制.
- kvm_device_ioctl()
用于对device_fd(/dev/kvm)进行操作 ---> hypervisor
- kvm_ioctl()
用于对KVMState结构体中的fd进行操作 ---> 对应kvm的device级别
- kvm_vm_ioctl()
用于对KVMState结构体中vmfd进行操作 ---> 对应kvm的vm级别
- kvm_vcpu_ioctl()
用于对CPUState结构体中的kvm_fd进行操作 ---> 对应kvm的vcpu级别
kvm的kvm_vcpu结构体中含有kvm_run结构,用于QEMU的用户空间和kvm模块的交互. 例如在VM exit时,为了对虚拟硬件的访问进行模拟,kvm必须返回到QEMU用户空间中,因此kvm将相关信息存储到kvm_run结构中,留给QEMU获取信息.
【update】 2019.11.22
QEMU配置加速器(kvm)
qemu使用了大量面向对象的编程方式,并用c语言实现了类和类的构建、析构函数等。
qemu将kvm定义为一种加速类型(AccelClass),注册到了type_table中,因此通过初始化对象就可以直接调用对象所属类的方法。
AccelClass的init_machine()方法可以获得vm_fd.
QEMU设置vCPU
与加速器类似,vCPU也被设计为一种CPU类型(x86_vcpu_type), 将qemu的main函数中的current_machine->type指向x86_vcpu_type,即可调用该类的方法使用vCPU.
x86_vcpu_type的x86_vcpu_common_class_init()方法可以获得vCPU的inode(qemu中称为kvm_fd).
值得注意的是qemu为每个vCPU申请了一个线程(thread),所占空间在qemu本身在host上所占的空间中.
QEMU的main函数中与kvm有关的部分
QEMU的main函数中与kvm有关的部分对应的为配置加速器和设置vCPU两部分,以及最后的循环运行vCPU,主要函数为:
configure_accelerator();
current_machine->cpu_type的赋值;
main_loop();
我对qemu-kvm框架整理后,画了以下结构图.


浙公网安备 33010602011771号