2. 虚拟化与存储基础

要理解 VirtIO Block 为什么能显著提升虚拟机磁盘性能,必须先回到虚拟化与存储的基础层:CPU 如何被虚拟化、Hypervisor 处在什么位置、Guest 的一次“写磁盘”究竟经过哪些层次。只有把这条路径看清楚,才能明白 VirtIO 到底优化了哪一段。

一、虚拟化原理:把物理资源抽象成可分配的“虚拟资源”

虚拟化的本质是资源抽象与隔离。物理机只有一套 CPU、内存、磁盘和网卡,Hypervisor 通过指令模拟、特权级切换、地址翻译和 I/O 拦截,把这些资源切分成多个互相隔离的虚拟机(VM)。每个 VM 都以为自己独占一套硬件,实际上它的特权指令和敏感操作会被 Hypervisor 截获并代为执行。

以磁盘 I/O 为例,Guest 内核发出一次写请求,通常会经历:Guest 文件系统 → Guest 块层 → 虚拟磁盘驱动 → 触发 VM Exit → Hypervisor 捕获 → 由 Hypervisor 或后端进程访问真实磁盘。这个“陷入—模拟—返回”的过程,就是虚拟化 I/O 开销的主要来源。

虚拟化的三大支柱:CPU 虚拟化(特权指令截获与模拟)、内存虚拟化(影子页表或 EPT/NPT 二级地址翻译)、I/O 虚拟化(设备模拟、直通或半虚拟化)。VirtIO 属于 I/O 虚拟化中的半虚拟化路线。

二、Hypervisor 类型:Type 1 与 Type 2

Hypervisor(又称 VMM,虚拟机监视器)按运行位置分为两类:

  • Type 1(裸金属型):直接运行在物理硬件上,如 KVM(以内核模块形式融入 Linux)、Xen、VMware ESXi。性能好、延迟低,是数据中心主流。
  • Type 2(宿主型):运行在宿主操作系统之上,如 VirtualBox、VMware Workstation。部署方便,但多了一层宿主内核,I/O 路径更长。

KVM 常被归为 Type 1,因为它依托 Linux 内核直接管理硬件;但它的设备模拟部分(QEMU)运行在用户态,因此实际是“内核加速 + 用户态设备模型”的混合结构。理解这一点,对后面分析 VirtIO 前后端分离非常关键。

三、全虚拟化与半虚拟化

全虚拟化(Full Virtualization):Guest 不需要修改,Hypervisor 通过二进制翻译或硬件辅助(Intel VT-x、AMD-V)完整模拟硬件。Guest 里的普通驱动(如 IDE、e1000)可直接使用,但每次 I/O 都要陷入模拟,开销大。

半虚拟化(Paravirtualization):Guest 知道自己运行在虚拟机中,使用专门优化的驱动,通过共享内存和通知机制与 Hypervisor 协作,减少陷入次数。VirtIO 就是半虚拟化 I/O 的标准框架:Guest 端是 virtio-blk 前端驱动,Host 端是 QEMU 中的 virtio-blk 后端设备。

对比维度全虚拟化半虚拟化(VirtIO)
Guest 驱动标准硬件驱动专用 virtio 驱动
是否需改 Guest不需要需要安装 virtio 驱动
I/O 路径每次操作触发 VM Exit批量描述符 + 通知,减少陷入
性能较低接近原生
典型设备IDE、e1000virtio-blk、virtio-net

四、块设备与字符设备

Linux 把设备抽象为两类接口:

  • 块设备:以固定大小的“块”为单位随机访问,支持缓存和寻址,如 /dev/vda、/dev/sda。磁盘、SSD、virtio-blk 都属于块设备。
  • 字符设备:以字节流顺序访问,通常不支持随机寻址,如 /dev/tty、/dev/null。串口、键盘属于字符设备。

这个区分直接决定了 I/O 栈的走向:块设备请求会进入块层,被合并、排序后交给调度器;字符设备则直接走字符驱动。VirtIO Block 作为块设备,其请求同样要经过 Guest 块层的合并与调度,再进入 virtio 队列。

五、I/O 栈分层:一次写请求的完整旅程

下面这张图展示了从 Guest 应用到物理磁盘的分层路径,以及 VirtIO 在其中的位置。可以看到,VirtIO 并没有绕过块层,而是替换了“虚拟磁盘驱动 + 设备模拟”这一段。

Guest 写请求的 I/O 栈分层与 VirtIO 位置 Guest 虚拟机 应用 write() / 文件系统 Guest 块层:合并、排序、调度 virtio-blk 前端驱动 virtqueue:描述符环 + Avail/Used KVM:VM Exit / 通知注入 共享内存(Guest 与 Host 可见) Host / Hypervisor QEMU virtio-blk 后端设备 Host 块层:IO 调度、合并 文件系统 / 裸设备映射 物理块设备驱动(NVMe/SATA) 物理磁盘 / SSD 完成中断 / Used 环更新 共享内存 VirtIO 相关层 共享内存 / 物理设备 通用内核层

从图中可以看出,VirtIO 的优化点集中在“前端驱动 + virtqueue + 后端设备”这一段:Guest 不再模拟真实磁盘寄存器,而是把请求描述符写入共享内存,通过 Avail 环通知 Host;Host 处理完后通过 Used 环回写完成状态。这样把多次 VM Exit 合并为批量通知,显著降低陷入开销。

六、案例与排障:为什么 Guest 里看不到 /dev/vda

一个常见场景是:用 QEMU 启动虚拟机时指定了 -drive if=virtio,但 Guest 启动后只有 /dev/sda,没有 /dev/vda。原因通常是 Guest 内核没有加载 virtio-blk 驱动,或者 initramfs 中缺少该模块。

# 查看 Guest 是否识别到 virtio 设备
lspci | grep -i virtio
lsmod | grep virtio
dmesg | grep -i virtio

# 若缺少驱动,可在 Guest 中加载
modprobe virtio_blk
modprobe virtio_pci

# 检查块设备
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT
排查顺序建议:先确认 QEMU 命令行设备类型,再确认 Guest 内核配置(CONFIG_VIRTIO_BLK),最后检查 initramfs 是否包含 virtio 模块。三者缺一,都会导致块设备不可见。

七、常见误区复盘

  • 误区一:VirtIO 等于直通。 直通(VFIO)是把物理设备直接分配给 Guest,绕过 Hypervisor 模拟;VirtIO 仍是半虚拟化,后端由 QEMU 处理。
  • 误区二:块设备和字符设备只是名字不同。 两者在访问方式、缓存策略、是否可寻址上有本质区别,直接影响 I/O 栈走向。
  • 误区三:Type 1 一定比 Type 2 快。 性能取决于具体 I/O 路径和加速方式,KVM + VirtIO 的 Type 1 混合结构往往优于纯用户态模拟。
  • 误区四:装了 virtio 驱动就万事大吉。 还需关注 virtqueue 大小、多队列、中断亲和性等参数,否则性能仍可能受限。

掌握这些基础后,后续分析 VirtIO Block 的 virtqueue 结构、描述符链和通知机制时,就能清楚每个环节在整条 I/O 路径中的位置与作用。