Virtio 是一套 I/O 半虚拟化的程序,是对半虚拟化 Hypervisor 中的一组通用 I/O 设备(如网络设备、块设备等)的抽象,为各种 I/O 设备 的虚拟化提供了通用的、标准化的前端接口,增加了各个虚拟化平台的代码复用。
驱动抽象
virtio 为半虚拟化提供了一系列通用设备仿真的接口,它可以抽象为两部分:
Front-end drivers:通过 半虚拟化 在客户机操作系统中修改 Guest OS 代码实现driver
Back-end Drivers:Hyptervisor 提供设备仿真的后端驱动,实现前端接口
Virtio 架构
除了 Front-end drivers 和 Back-end Drivers 之外,Virtio 还定义了两层来支持虚拟机与 Hypervisor 进行通讯,Virtio 四层如下:
Front-end层 guest 中各种驱动程序模块
virtio层 是 虚拟队列(virtual queue) 接口,它是 前端驱动程序 和 后端驱动程序 通信的桥梁。驱动程序可以根据需要使用零个或多个队列。例如:
virtio-net 网络驱动程序使用两个虚拟队列(一个用于接收,一个用于发送)
virtio-blk 块驱动程序只使用一个队列
virtio-ring层 是 virtio层 的具体实现,它实现了两个环形缓冲区,分别用于保存前端驱动程序和后端处理程序执行的信息。virtio-ring 实现了 virtio 的具体 通信机制 和 数据流程
虚拟队列(virtual queue)是虚拟的,它实际上被实现为一个环(rings),用于遍历 guest-to-hypervisor 过渡。它也可以以其他方式实现,只要guest和hypervisor以相同的方式实现即可
virtio 的核心机制就是通过共享内存在前端驱动与后端实现间进行数据传输,共享内存区域被称作 vring
Back-end Hypervisor(实现在Qemu上)中的处理程序模块
上图,列出五个前端驱动程序:
virtio-blk (如磁盘)
virtio-net 网络设备
virtio-pci PCI模拟
virtio-balloon (用于动态管理客户内存使用)
virtio-console 控制台
每个前端驱动程序在hypervisor中都有一个对应的后端驱动程序。
层次结构概念
virtio_driver表示客户机中的前端驱动。与驱动相匹配的设备被封装在virtio_device(在客户机中表示设备),其中有成员config指向virtio_config_ops结构(其中定义了配置virtio设备的操作)
virtqueue 中有成员vdev指向virtio_device(也就是指向它所服务的某一设备virtio_device)
- 每个
virtio_queue中有个类型为virtqueue_ops的对象,其中定义了与hypervisor交互的虚拟队列操作
vring 组成
vring 由三部分构成:
Descriptor Table:描述内存 buffer,主要包括 addr/len 等信息
Available Ring:用于驱动通知设备有新的可用的描述符。比如,通知后端设备,有一个待发送的报文描述符
Used Ring:用于通知驱动设备侧已用的描述符。比如,后端设备收到一个报文,需要将报文数据放入可用的描述符,并更新 Used Ring,同时通知前端驱动
Virtio缓冲
客户机驱动(前端)与hypervisor(后端)通过缓冲区进行通信。对于一次I/O,客户机提供一个或多个缓冲区表示请求。
Virtio Drivers 示例
Front-end drivers 在Linux内核源码的 ./drivers 子目录
- 网络驱动
./driver/net/virtio_net.c
- 块驱动
./driver/block/virtio_blk.c
./driver/virtio 子目录下提供了 virtio 接口的实现(virtio设备、驱动、virtqueue和环形缓冲区)
其他
virtio-fs 是红帽在2018年12月10号在 kata社区 提出的一种在 guest 之间共享文件系统的方案。
一般 Linux 系统都内置 virtio 驱动,但 windows系统 需要单独安装 virtio 驱动。检测 Linux 是否包含 virtio 驱动命令:
$ find /lib/modules/3.10.0-1160.*/ -name "virtio*"
将网卡类型设置为 virtio
<interface ...>
<model type='virtio' />
...
</interface>
将磁盘类型设置为 virtio
<disk ...>
<target dev='hda' bus='virtio' />
<address type='pci' />
...
</disk>