1. 课程导入
在云计算与虚拟化技术体系中,存储始终是最复杂、最容易成为性能瓶颈的一环。CPU 和内存的虚拟化早已成熟,网络虚拟化也借助 VirtIO、vhost、DPDK 等技术走向高性能,而存储虚拟化则因为涉及块设备语义、I/O 栈层次、缓存一致性与持久化保证,长期处于“能跑但难调优”的状态。本课程围绕 VirtIO Block 这一关键组件,系统讲解从虚拟机前端到宿主机后端的完整存储 I/O 路径,帮助你把“虚拟机里的一块盘”拆解成可观测、可分析、可优化的工程对象。
一、存储虚拟化的发展脉络
存储虚拟化并不是一个新概念,它经历了从“模拟”到“半虚拟化”再到“硬件加速”的演进过程。理解这条脉络,有助于我们看清 VirtIO Block 在其中的位置。
- 全模拟阶段(Full Emulation):早期 QEMU 通过模拟 IDE、SCSI 控制器来为虚拟机提供磁盘。Guest 完全不知道自己在虚拟化环境中,每次 I/O 都要触发 VM-Exit,由 QEMU 逐寄存器模拟,性能开销极大。
- 半虚拟化阶段(Para-virtualization):VirtIO 规范提出“前端驱动 + 后端设备”的共享内存模型,Guest 使用专用驱动,通过 virtqueue 批量提交 I/O 请求,大幅减少陷入次数。VirtIO Block 正是这一阶段的代表性块设备实现。
- 硬件加速阶段(vhost / vhost-user / vDPA):把后端处理从 QEMU 用户态下沉到内核态或独立进程,甚至卸载到智能网卡与 DPU,进一步降低延迟、提升吞吐。
- 云原生存储阶段:在 Kubernetes 场景下,VirtIO Block 后端可能对接本地 NVMe、Ceph RBD、分布式块存储,形成“容器 + 虚拟机 + 分布式存储”的复合栈。
可以看到,VirtIO Block 处于承上启下的核心位置:向上承接 Guest 的块设备语义,向下对接宿主机多种存储后端。
二、VirtIO Block 的定位
VirtIO Block 是 VirtIO 规范中定义的块设备类型,它在 Guest 中表现为一个标准的 /dev/vda 或 /dev/vdb 设备,但底层并不存在真实硬件控制器。其核心机制是:Guest 前端驱动把读写请求放入 virtqueue,后端(QEMU、vhost、vhost-user)取出请求并转发给真实存储,完成后通过中断或事件通知前端。
它的关键定位可以概括为三点:
- 协议层:定义请求头、数据段、状态字节的格式,是前后端通信的契约。
- 驱动层:Guest 内核中的 virtio_blk 驱动负责队列管理、请求封装与完成处理。
- 设备层:QEMU 的 virtio-blk 设备或 vhost-blk 后端负责请求解析与真实 I/O 下发。
三、整体知识框架与信号链
下图展示了本课程的核心知识框架:从 Guest 应用发起 I/O,到 VirtIO Block 前后端交互,再到宿主机存储后端的完整信号链。后续章节将围绕这条链路逐层展开。
四、学习目标与典型场景
本课程的学习目标不是让你背诵 VirtIO 规范,而是建立“从请求到落盘”的全链路认知。具体包括:
- 能说清 VirtIO Block 的请求格式、virtqueue 结构与前后端交互流程;
- 能读懂
/sys/block/vda/queue/下的队列参数并解释其含义; - 能使用
fio、blktrace、bpftrace定位存储 I/O 瓶颈; - 能对比 virtio-blk、virtio-scsi、vhost-user-blk 的适用场景;
- 能识别缓存模式、I/O 调度、队列深度等常见误区。
一个典型场景是:某云主机数据库写入延迟抖动,Guest 内 iostat 显示 %util 接近 100%,但宿主机 NVMe 利用率并不高。问题往往出在 VirtIO Block 的队列深度或后端线程模型上。这类问题只有理解信号链才能快速定位。
五、核心组件对比清单
| 组件 | 所在位置 | 核心职责 | 常见问题 |
|---|---|---|---|
| virtio_blk 驱动 | Guest 内核 | 封装请求、管理 virtqueue | 队列深度不足、中断合并 |
| virtqueue | 共享内存 | 前后端请求传递 | 通知频繁、缓存伪共享 |
| QEMU virtio-blk | 用户态 | 解析请求、调用后端 | 线程竞争、锁开销 |
| vhost-blk | 内核态 | 绕过 QEMU 处理 I/O | 配置复杂、调试困难 |
| 存储后端 | 宿主机/远端 | 真实数据读写 | 网络抖动、缓存不一致 |