当前位置: 首页 > news >正文

KVM-QEMU 的完整工作流程案例解析

阶段一:虚拟机启动准备

步骤 1:用户发起启动命令

# 用户执行类似命令
qemu-system-x86_64 -enable-kvm -m 2048 -hda vm_disk.img -vnc :0

步骤 2:QEMU 进程初始化

  • QEMU 解析命令行参数
  • 初始化模拟的设备树(虚拟主板、芯片组等)
  • 加载虚拟 BIOS 或 UEFI 固件
  • 准备虚拟设备(磁盘镜像、网络后端等)

步骤 3:KVM 初始化

// QEMU 内部执行类似操作
kvm_fd = open("/dev/kvm", O_RDWR);        // 打开 KVM 设备
vm_fd = ioctl(kvm_fd, KVM_CREATE_VM, 0);  // 创建虚拟机上下文

步骤 4:内存分配与映射

  • QEMU 通过 mmap() 分配一大块内存作为客户机物理内存
  • 通过 ioctl(vm_fd, KVM_SET_USER_MEMORY_REGION, &mem) 告知 KVM 内存布局
  • 建立客户机物理地址(GPA)到主机虚拟地址(HVA)的映射

步骤 5:创建虚拟 CPU

// 对每个 vCPU
vcpu_fd = ioctl(vm_fd, KVM_CREATE_VCPU, vcpu_id);
vcpu_mmap_size = ioctl(kvm_fd, KVM_GET_VCPU_MMAP_SIZE, 0);
vcpu_state = mmap(NULL, vcpu_mmap_size, PROT_READ|PROT_WRITE, MAP_SHARED, vcpu_fd, 0);

阶段二:虚拟机运行循环

步骤 6:启动 vCPU 线程

  • 对每个虚拟 CPU,QEMU 创建一个独立的 POSIX 线程
  • 线程进入主循环,不断执行:
while (1) {// 进入 KVM 运行状态ret = ioctl(vcpu_fd, KVM_RUN, 0);// 处理退出原因switch (vcpu_state->exit_reason) {case KVM_EXIT_IO: handle_io(vcpu_state);break;case KVM_EXIT_MMIO:handle_mmio(vcpu_state);break;case KVM_EXIT_HLT:handle_hlt(vcpu_state);break;// ... 其他退出原因}
}

阶段三:详细退出处理流程

场景 A:端口 I/O 退出(如磁盘写入)

步骤 7a:触发退出

虚拟机内程序 → 执行 OUT 指令(端口 0x1F0) → CPU 触发 VM-Exit

步骤 8a:KVM 捕获并分类

  • KVM 检查退出原因:KVM_EXIT_IO
  • 读取退出信息:
    • 方向:输出(OUT)
    • 端口:0x1F0(IDE 数据端口)
    • 数据大小:4 字节
    • 数据内容:要写入的扇区数据

步骤 9a:QEMU 设备模拟

void handle_io(struct kvm_run *run) {if (run->io.port == 0x1F0 && run->io.direction == KVM_EXIT_IO_OUT) {// 这是 IDE 磁盘数据端口ide_write_data(run->io.data, run->io.size);}
}void ide_write_data(void *data, int size) {// 将数据写入主机上的磁盘镜像文件fwrite(data, size, 1, vm_disk_file);
}

步骤 10a:恢复执行

  • QEMU 完成模拟后,循环回到 KVM_RUN
  • KVM 通过 VM-Entry 重新进入客户机
  • 虚拟机从 OUT 指令的下一条指令继续执行
场景 B:内存映射 I/O 退出(如 Virtio 设备)

步骤 7b:触发 MMIO 退出

虚拟机 → 访问 0xFE000000(GPA) → EPT 违规 → VM-Exit

步骤 8b:KVM 处理 MMIO

  • 退出原因:KVM_EXIT_MMIO
  • 信息包含:
    • 物理地址:0xFE000000
    • 访问类型:读/写
    • 数据长度:8 字节
    • 数据值(如果是写操作)

步骤 9b:QEMU Virtio 模拟

void handle_mmio(struct kvm_run *run) {if (run->mmio.phys_addr >= VIRTIO_MMIO_BASE) {// 这是 Virtio 设备区域virtio_mmio_write(run->mmio.phys_addr, run->mmio.data, run->mmio.len);}
}void virtio_mmio_write(uint64_t addr, void *data, int len) {// 处理 Virtio 队列操作if (addr == VIRTIO_MMIO_QUEUE_NOTIFY) {// 通知 Virtio 设备有新的缓冲区process_virtio_queue();}
}

阶段四:中断注入

步骤 11:设备产生中断

  • QEMU 的虚拟设备完成操作后可能需要中断虚拟机
  • 例如:磁盘读取完成、网络包到达

步骤 12:中断注入流程

// QEMU 请求注入中断
struct kvm_interrupt intr = { .irq = 14 };  // IDE 中断号
ioctl(vcpu_fd, KVM_INTERRUPT, &intr);// 或者设置 LAPIC
ioctl(vcpu_fd, KVM_SET_LAPIC, &lapic_state);

步骤 13:KVM 处理中断

  • 在下次 VM-Entry 时,KVM 将中断注入到客户机
  • 客户机正常处理中断,就像在物理机上一样

阶段五:性能优化路径

Virtio 优化路径

传统模拟 vs Virtio:

传统 IDE 模拟:
虚拟机OUT指令 → VM-Exit → QEMU处理 → 文件写入 → 中断注入Virtio 优化:
虚拟机写入共享环 → 很少VM-Exit → QEMU异步处理 → 事件通知
vhost 进一步优化

vhost-net 工作流程:

  1. QEMU 将 Virtio 后端交给内核的 vhost-net 驱动
  2. 网络数据包直接在 KVM 和 vhost-net 之间传递
  3. 完全绕过 QEMU 进程,减少上下文切换

完整执行流程图

用户空间 (QEMU)        内核空间 (KVM)          硬件│                     │                    ││ 1. qemu-kvm命令      │                    ││ ────────────────────> │                    ││                     │                    ││ 2. open("/dev/kvm") │                    ││ ────────────────────> │                    ││                     │ 3. 初始化VMX/SVM    ││                     │ ◀─────────────────── ││                     │                    ││ 4. 内存分配与映射     │                    ││ ────────────────────> │                    ││                     │                    ││ 5. KVM_RUN循环开始   │                    ││ ────────────────────> │ 6. VM-Entry       ││                     │ ───────────────────> │ 客户机直接执行│                     │                    ││                     │ 7. I/O触发VM-Exit   ││                     │ <─────────────────── ││ 8. 处理退出原因      │                    ││ <──────────────────── │                    ││                     │                    ││ 9. 设备模拟          │                    ││ (文件操作/网络等)    │                    ││                     │                    ││ 10. 可能注入中断     │                    ││ ────────────────────> │                    ││                     │                    ││ 11. 回到步骤5        │                    │└─────────────────────> └────────────────────┘

这个细化的工作流程展示了从虚拟机启动到正常运行的完整路径,包括各种退出场景的处理细节和性能优化机制。

http://www.dtcms.com/a/477457.html

相关文章:

  • 【LeetCode】69. x 的平方根
  • C语言入门教程(第6讲):函数——让程序学会“分工合作”的魔法
  • 福建定制网站开发泰安一级的企业建站公司
  • 公司要建立网站要怎么做太原优化型网站建设
  • 开源 C++ QT QML 开发(十七)进程--LocalSocket
  • 2.CSS3.(3).html
  • 【MQ】RabbitMQ:架构、工作模式、高可用与流程解析
  • 零基础学AI大模型之大模型修复机制:OutputFixingParser解析器
  • 单个服务器部署多个rabbitmq
  • 银行资产管理系统核心业务架构设计
  • 面向快餐店的全程无人化自动化餐厅深度研究方案
  • 开源 C++ QT QML 开发(十八)多媒体--音频播放
  • 【开题答辩全过程】以 宾馆客房管理系统为例,包含答辩的问题和答案
  • 宁波网站建设设计价格我需要做网站
  • 使用 PyTorch 实现 MNIST 手写数字识别
  • ComfyUI安装和启动攻略1
  • h5移动端开发民治网站优化培训
  • uniapp 微信小程序蓝牙接收中文乱码
  • 多制式基站综合测试线的架构与验证实践 (1)
  • Ceph 分布式存储学习笔记(四):文件系统存储管理
  • ceph设置标志位
  • 系统升级丨让VR全景制作更全面、更简单
  • PyTorch 实现 MNIST 手写数字识别全流程
  • PyTorch实现MNIST手写数字识别:从数据到模型全解析
  • PostgreSQL 测试磁盘性能
  • 北京网站开发科技企业网站
  • 干货|腾讯 Linux C/C++ 后端开发岗面试
  • 【深度学习新浪潮】如何入门分布式大模型推理?
  • 基于单片机的螺旋藻生长大棚PH智能控制设计
  • 分布式专题——42 MQ常见问题梳理