2. 虚拟化基础

要理解 VirtIO 虚拟设备系统,必须先回到虚拟化的地基:谁在管理硬件、谁在运行客户机、CPU 在什么特权级执行、敏感指令如何被拦截、设备又是如何被“假装”出来的。本节把这些概念串成一条完整链路,为后续 VirtIO 队列、通知与驱动开发打基础。

一、Hypervisor:硬件与虚拟机之间的管理层

Hypervisor(虚拟机监控器,VMM)是运行在物理硬件之上、负责创建和调度虚拟机的软件层。它的核心职责包括:CPU 虚拟化、内存虚拟化、中断虚拟化、设备虚拟化以及虚拟机生命周期管理。按部署形态可分为两类:

  • Type 1(裸机型):直接运行在硬件上,如 KVM(以内核模块形式)、Xen、VMware ESXi。性能好,常见于服务器与云平台。
  • Type 2(宿主型):运行在宿主操作系统之上,如 QEMU 配合 KVM、VirtualBox。部署方便,常用于开发调试。

在 Linux 生态中,KVM 提供 CPU 与内存虚拟化能力,QEMU 提供设备模型与虚拟机管理,二者组合是最常见的开发环境。理解这一点很关键:VirtIO 设备的前端在客户机内核,后端通常就在 QEMU 进程里。

二、特权级与陷入模拟:虚拟化的核心机制

CPU 通过特权级(x86 的 Ring 0~3,ARM 的 EL0~EL3)隔离权限。客户机操作系统原本以为自己运行在最高特权级,实际上被降级运行在非特权模式,而 Hypervisor 占据真正的最高特权级。当客户机执行敏感指令(如修改页表基址、访问 I/O 端口、执行 HLT)时,CPU 触发异常,控制权被交给 Hypervisor,这个过程称为陷入(trap);Hypervisor 检查并模拟该操作后,再通过 VM-Exit / VM-Entry 返回客户机,称为模拟(emulate)。

这种“陷入—模拟—返回”的路径开销很大,因此现代 CPU 引入硬件辅助虚拟化(Intel VT-x、AMD-V、ARM VHE),把部分拦截与上下文切换交给硬件完成。但设备访问若每次都陷入,性能依然不可接受,这正是 VirtIO 要解决的问题。

特权级与陷入模拟:从客户机到 Hypervisor 的控制流 客户机 VM(非特权级运行) 用户应用(Ring 3 / EL0) 普通计算、系统调用 客户机内核(Ring 0 / EL1) 驱动、页表、I/O 端口访问 敏感指令触发异常 out/in、HLT、改 CR3、MMIO → 陷入 Hypervisor trap / VM-Exit Hypervisor / VMM(最高特权级) 指令解码与模拟 判断敏感操作是否合法 设备模型 / 后端处理 QEMU 设备、VirtIO 后端 调度与资源管理 vCPU 调度、内存映射 物理硬件(CPU / 内存 / 设备) 真实 I/O、中断控制器 VM-Entry 返回

三、设备虚拟化的三种基本方式

设备虚拟化要回答一个问题:客户机看到的“网卡”“磁盘”到底是什么?常见实现有三类:

方式 原理 性能 典型场景
全虚拟化(模拟) Hypervisor 完全用软件模拟真实设备寄存器与行为 低,每次访问都陷入 兼容旧系统、无驱动改造
半虚拟化(VirtIO) 客户机安装专用前端驱动,与后端通过共享内存队列协作 高,减少陷入次数 Linux 云主机、KVM/QEMU
设备直通(Passthrough) 借助 IOMMU 把物理设备直接分配给虚拟机 接近原生 高性能网卡、GPU 直通

VirtIO 属于半虚拟化路线:它不模拟真实硬件寄存器,而是定义一套标准化、可发现、可协商的虚拟设备接口。客户机驱动知道自己在跟虚拟设备打交道,因此可以主动使用共享内存、批量提交、延迟通知等优化手段。

四、案例:一次磁盘写请求的完整路径

假设客户机应用调用 write() 写文件,最终落到 VirtIO 块设备上,路径大致如下:

应用 write()
  → 客户机文件系统 → 块层 → VirtIO-blk 前端驱动
  → 把请求描述符放入 virtqueue(共享内存)
  → 写 MMIO 寄存器通知后端(一次 trap / VM-Exit)
  → QEMU 中 VirtIO 后端取出描述符
  → 后端调用宿主 OS 的 aio/pwrite 写真实磁盘
  → 完成后写中断寄存器,注入虚拟中断给客户机
  → 客户机中断处理,回收描述符,唤醒应用

可以看到,真正昂贵的“陷入”只发生在通知环节,数据本身通过共享内存传递,不需要逐字节模拟。这就是 VirtIO 高性能的根本原因。

关键理解:VirtIO 不是“模拟一块真实网卡”,而是“定义一套前后端都认可的协作协议”。前端驱动与后端设备共享同一份 virtqueue 规范,因此双方可以高效配合。

五、常见误区与复盘

  • 误区一:客户机内核运行在 Ring 0,所以能直接操作硬件。实际上客户机内核运行在受限特权级,物理地址、I/O 端口、中断都受 Hypervisor 管控。
  • 误区二:VirtIO 设备就是 QEMU 模拟出来的普通设备。VirtIO 设备有明确的规范(OASIS VirtIO 标准),前端驱动与后端实现必须遵循同一套队列、特性位与通知机制。
  • 误区三:陷入越少越好,所以全部用直通。直通依赖 IOMMU、牺牲可迁移性与资源超分能力,VirtIO 在性能与灵活性之间取得了更好的平衡。
  • 复盘要点:学习 VirtIO 开发时,要同时关注三条线——客户机前端驱动、virtqueue 共享内存布局、后端设备实现。任何一条线理解偏差,都会导致“设备识别不到”“队列卡死”“中断丢失”等典型问题。