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


__EOF__

本文作者EwanHai
本文链接https://www.cnblogs.com/haiyonghao/p/14440094.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   EwanHai  阅读(1224)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示