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中,因此通过初始化对象就可以直接调用对象所属类的方法。

AccelClassinit_machine()方法可以获得vm_fd.

QEMU设置vCPU

与加速器类似,vCPU也被设计为一种CPU类型(x86_vcpu_type), 将qemu的main函数中的current_machine->type指向x86_vcpu_type,即可调用该类的方法使用vCPU.

x86_vcpu_typex86_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框架整理后,画了以下结构图.

posted @ 2021-02-24 10:57  EwanHai  阅读(1067)  评论(0编辑  收藏  举报