qemu-kvm中vcpu虚拟化到底是咋整的?
一句话总结
实例化一个vcpu就是在hostOS中创建了一个线程,线程里有个while循环,循环里不停的调用kvm_cpu_exec方法,kvm_cpu_exec方法调用通过kvm_vcpu_ioctl(cpu, KVM_RUN, 0)使得kvm切换为no-root模式。在no-root模式下处理特权指令的时候,会退回root模式,然后一步步返回到kvm_cpu_exec中根据不同原因,处理返回异常。
如此一个轮回结束,周而复始,vcpu。
再补充说一点,内存中申请一块内存,根模式和非根模式切换的时候,先把当前寄存器值放到这块内存中,然后设置物理cpu使得进入对应模式,这块内存叫vmcs。
背景
vcpu初始化的时候(qemu_init_vcpu)是启动了一个线程,也就是说vcpu其实就是一个线程.线程运行方法是qemu_kvm_cpu_thread_fn
kvm_init_vcpu调用KVM_CREATE_VCPU创建了vcpu返回vm_fdvcpu的运行是在kvm_cpu_exec里面的,这里调用如下命令进入kvm
run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0);
进入KVM后,KVM会切入Guest OS,假如Guest OS运行运行,需要访问IO等也就是说要访问physical device,那么Qemu与KVM就要进行emulate。如果是KVM emulate的则由KVM emulate,然后切回Guest OS。如果是Qemu emulate的,则从KVM中进入Qemu,等Qemu中的device model执行完emulate之后,再次在Qemu中调用kvm_vcpu_ioctl(vcpu_fd, KVM_RUN, xxx)进入KVM运行,然后再切回Guest OS
vm-entrykvm_vcpu_ioctl(kvm_main.c)-->kvm_arch_vcpu_ioctl_run(kvm/x86.c)-->vcpu_run(kvm/x86.c)-->vcpu_enter_guest(kvm/x86.c)在qemu中kvm_vcpu_ioctl(cpu, KVM_RUN, 0)调用kvm后代码层层调用最终核心实现的方法是vcpu_enter_guest
vcpu->requests 处理
上次VM-Exit时可能调用kvm_make_request设置不同的request下次准备VM-Entry时需要处理这些request.
prepare_guest_switch
(vmx.c)
.prepare_guest_switch = vmx_save_host_state,
- 保存host的fs和gs的断选择子(segment selector)到vmcs
- kvm_set_shared_msr设置host对应的msr寄存器 MSR 总体来是为了设置CPU 的工作环境和标示CPU 的工作状态,包括温度控制,性能监控等guest的msr在handle_wrmsr 最终是在vmx_set_msr中更新
- 判断当前是否满足vm-entry vcpu的mode不对,有requests请求,需要重新调度,有pending的信号有异常任何情况,不进入vm_entry开中断,开抢占(在此之前已经关抢占,关中断)
4.kvm_x86_ops->run(vcpu)-->vmx_vcpu_run(vmx.c)更新vmcs中的GUEST_RSP和 GUEST_RIP刷新vmcs中的HOST_CR4字段(其他寄存器在 kvm_arch_vcpu_ioctl_set_regs时设置)(段寄存器在vmx_vcpu_reset时设置)下面调用汇编代码,保存host相关内容,然后加载vmcs中的guest的寄存器值,跳转至guest中代码
vm-exit
前半部分我们知道了如何vm-entry此时进入no-root非根模式执行guest的指令当指令访问特权指令如访问io访问设备的时候会vm-exit
1.vmx_vcpu_run后半段
vmx->idt_vectoring_info = vmcs_read32(IDT_VECTORING_INFO_FIELD);
vmx->loaded_vmcs->launched = 1;
vmx->exit_reason = vmcs_read32(VM_EXIT_REASON);
2.加入vm-exit是由于EXIT_REASON_MCE_DURING_VMENTRY或者EXIT_REASON_EXCEPTION_NMI导致的在vmx_complete_atomic_exit方法中需要进行特殊处理(kvm_machine_check)(kvm_before_handle_nmi和kvm_after_handle_nmi)
- 如果有事件模拟的virtual nmi中断,则用vmx_recover_nmi_blocking处理
- 获取与预处理导致的中断由vmx_complete_interrupts-->__vmx_complete_interrupts处理
(至此退出vmx_vcpu_run重返vmx_vcpu_run)
kvm_x86_ops->handle_exit-->vmx_handle_exit根据不同情况处理异常
至此从kvm中返回到用户态qemu中kvm_cpu_exec方法
根据不同退出原因,处理异常然后退出到线程方法qemu_kvm_cpu_thread_fn继续执行下一次循环
- 数据结构C#版笔记--堆栈(Stack)
- MySQL基础入门-第一课 新建数据库(linux版本)
- 2017年度最不安全密码报告,看看你的密码安全吗?
- 数据结构C#版笔记--顺序表(SeqList)
- 数据结构C#版笔记--单链表(LinkList)
- 操作系统 页式存储 页与块之间的关系详解
- 数据结构C#版笔记--双向链表(DbLinkList)
- 斐波那契数列与IE9
- DateTime.ToString()输出"年/月/日 时:分:秒"的格式
- Flash在线拍摄用户头象
- win7 64位下如何折腾Tubro C 3.0
- TweenLite的又一应用:图片的拼图加载效果
- mysql创建数据表时如何判断是否已经存在?
- 温故知新:接口的隐式实现与显式实现
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- mysqlbinlog命令详解 Part 6 读取远程MySQL服务器日志
- mysqlbinlog命令详解 Part 7 备份二进制日志文件
- mysqlbinlog命令详解 Part 8 指定 Server ID
- mysqlbinlog命令详解 Part 9 MySQL备份策略
- mysqlbinlog命令详解 Part 10 恢复MySQL
- mysqldump命令详解 Part 2- 建立触发器 事件
- mysqlbinlog命令详解 Part 11 其他的一些参数
- mysqldump命令详解 Part 1 -MySQL测试数据的构造
- mysqldump命令详解 Part 3- 备份全库
- mysqldump命令详解 Part 4-备份单表
- mysqldump命令详解 Part 5-按条件备份表数据
- mysqldump命令详解 Part 6-导出事件,函数和存储过程
- StampedLock知识点梳理
- ReentrantReadWriteLock知识点梳理
- 每天都在用String,你真的了解吗?