Virtio
Virtio 是一套实现虚拟化设备的通用协议,它是 hypervisor 和操作系统驱动之间沟通的桥梁。只要遵循 virtio 协议进行设备模拟,就能坐享海量生态资源,实现虚拟设备自由。
架构概述
Virtio 采用前后端分离的架构设计:
- 前端(Frontend):运行在 Guest OS 中的驱动程序
- 后端(Backend):运行在 Hypervisor 中的设备模拟实现
- 通信机制:通过 virtqueue 实现前后端的数据交换
前后端分离
Virtio 设备的通信是侵入式的,它要求 Guest OS 支持和安装 virtio 设备的驱动。
Linux 默认自带了 virtio 协议族的驱动,使用 virtio 模型可以复用大量的驱动代码;QEMU 支持了大量的 virtio 设备,使用 virtio 模型可以复用大量的设备虚拟化代码。
Virtqueue 和 Vring
Virtio 通过 virtqueue 数据结构进行前后端的交互,其核心机制是内部的 vring 数据结构。一个 virtqueue 对应一个 vring,一个 vring 是一个数组,被分成三个独立部分:
Descriptor Table(描述符表)
- 数据准备区
- 只能由前端写入交互信息
- 后端只读
- 包含数据缓冲区的地址和长度信息
Available Ring(可用环)
- 前端通知区
- 前端可写,后端只读
- 用于前端通知后端有新的请求待处理
Used Ring(已用环)
- 后端通知区
- 后端可写,前端只读
- 用于后端通知前端请求处理完成
环形缓冲区机制
索引管理
- Available Ring 和 Used Ring 各自有一个 idx(索引)
- 分别由前端和后端维护
- 这些索引像"指针",标记环形缓冲区的写入位置
- 确保前后端不会覆盖彼此的数据
环形设计
- Available Ring 和 Used Ring 采用环形设计
- 前后端可以在各自的环中独立推进
- 前端可以在 Available Ring 中填入新请求
- 后端在 Used Ring 中标记已完成的任务
通知机制
- 前端通过"kick"(通知后端)触发处理
- 后端通过中断(通知前端)反馈结果
- 这种异步通知避免了忙等待
- 提升并发效率
多队列支持
一个设备可以创建多个队列,每个队列彼此独立,由前后端在通信时并行使用,从而提高设备的吞吐效率。
队列类型
单队列
- 简单的设备采用单队列即可完成基本需求
- 一个 virtqueue 是半双工的
双队列
- 为了提高数据传输效率
- 一个作为 tx_queue(发送队列)
- 一个作为 rx_queue(接收队列)
- 实现双向的全双工数据传输
多队列
- 采用负载均衡的分发策略
- 提高设备和驱动之间的吞吐能力
- 特别适合高性能网络设备
注意:对于网络设备,传输速度取决于整条链路上的瓶颈点,单一一处的吞吐量大不能保证网速的提升。
设备初始化流程
设备发现
- Guest OS 通过 PCI 或 MMIO 发现 virtio 设备
- 读取设备配置空间获取设备信息
设备配置
- 协商特性(Feature bits)
- 设置设备参数
- 分配 virtqueue
驱动加载
- 加载对应的 virtio 驱动
- 初始化驱动数据结构
- 建立与设备的通信通道
设备就绪
- 完成所有初始化步骤
- 设备进入工作状态
- 可以开始处理 I/O 请求
前后端通信流程
数据发送流程
- 前端准备数据缓冲区
- 将缓冲区信息写入 Descriptor Table
- 更新 Available Ring 的索引
- 发送 kick 通知后端
数据接收流程
- 后端处理请求
- 将结果写入 Used Ring
- 发送中断通知前端
- 前端处理完成通知
数据面和控制面
数据面
数据传输
- 通过 virtqueue 进行批量数据传输
- 支持零拷贝技术
- 高效的内存映射机制
性能优化
- 批量处理请求
- 异步通知机制
- 多队列并行处理
控制面
设备管理
- 设备状态监控
- 配置更新
- 错误处理
特性协商
- 前后端特性协商
- 协议版本管理
- 扩展功能支持
Virtio 设备类型
网络设备(virtio-net)
- 虚拟网卡
- 支持多队列
- 支持 TSO/GSO
块设备(virtio-blk)
- 虚拟磁盘
- 支持多队列
- 支持 DISCARD/WRITE_ZEROES
控制台设备(virtio-console)
- 虚拟串口
- 支持多端口
- 支持流控制
输入设备(virtio-input)
- 虚拟键盘/鼠标
- 支持事件上报
- 支持多点触控
GPU 设备(virtio-gpu)
- 虚拟显卡
- 支持 2D/3D 加速
- 支持显示输出
性能优化
批处理
- 合并多个 I/O 请求
- 减少前后端交互次数
- 提高吞吐量
零拷贝
- 避免数据在内存中的复制
- 直接使用共享内存
- 降低 CPU 开销
中断合并
- 合并多个中断为一个
- 减少中断处理开销
- 提高系统响应性
轮询模式
- 在特定场景下使用轮询
- 减少中断开销
- 提高低延迟场景性能
安全考虑
内存隔离
- 前后端内存空间隔离
- 防止越界访问
- 保护敏感数据
权限控制
- 设备访问权限管理
- 资源配额限制
- 防止资源耗尽
数据加密
- 敏感数据传输加密
- 密钥管理
- 安全协议支持