Virtio原理简介
实现IO虚拟化主要有三种方式:全虚拟化、半虚拟化和透传。全虚拟化Guest OS不会感知到自己是虚拟机,也无需修改Guest OS,但是它的效率比较低。半虚拟化Guest OS知道自己是虚拟机,通过Frontend/Backend驱动模拟实现IO虚拟化。透传就是直接分配物理设备给VM用。Virtio是一种半虚拟化的设备抽象接口规范,在Qemu和KVM中得到了广泛使用,本文将简单介绍Virtio的基
目录
实现IO虚拟化主要有三种方式:全虚拟化、半虚拟化和透传。全虚拟化Guest OS不会感知到自己是虚拟机,也无需修改Guest OS,但是它的效率比较低。半虚拟化Guest OS知道自己是虚拟机,通过Frontend/Backend驱动模拟实现IO虚拟化。透传就是直接分配物理设备给VM用。Virtio是一种半虚拟化的设备抽象接口规范,在Qemu和KVM中得到了广泛使用,本文将简单介绍Virtio的基本原理。
代码版本
- Qemu
- qemu-2.10.1
- Kernel
- kernel-3.10.0
Virtio SPEC
- Virtual I/O Device (VIRTIO) Version 1.0
- Virtio PCI Card Specification Version 0.9.5
Virtio在Guest中实现了前端驱动,在Host中实现了后端驱动,前后端之间通过Virtqueue(虚拟队列)交换数据,Host中会使用后端驱动程序模拟一个PCI设备,因此也称前端驱动为Driver,后端驱动为Device。Guest在Host OS上表示为一个Qemu的进程,Guest OS的pa实际上也属于Host OS的地址空间,因此Virtio采用的Virtqueue的方式来避免了Guest和Host间数据的复制。下面先介绍一下Virtio,更为详细的描述请阅读前言中提供的Virtio SPEC文档,那里是最准确而详细的描述,在此仅简单介绍一下后文需要用到的概念。
Virtio规范简介
Basic Facilities of a Virtio Device
每个Virtio设备包括以下部分
- Device status field 设备状态字段
- Feature bits 特征位
- Device Configuration space 设备配置空间
- One or more virtqueues 一个或多个virtqueues
virtqueues
在virtio设备上进行批量数据传输的机制被称为virtqueue,每个设备可以拥有零个或多个virtqueue,每个virtqueue由三部分组成:
- Descriptor Table
- Available Ring
- Used Ring
每部分在客户机内存中是物理连续的,并且有不同的对齐要求,virtqueue的每个部分的内存对齐和大小要求如下表,其中Queue Size对应virtqueue中的最大buffer数,始终为2的n次幂,以特定于总线的方式指定:
| Virtqueue Part | Alignment | Size |
|---|---|---|
| Descriptor Table | 16 | 16*(Queue Size) |
| Available Ring | 2 | 6 + 2*(Queue Size) |
| Used Ring | 4 | 6 + 8*(Queue Size) |
Descriptor Table指的是Driver用于Device的缓冲区,由Queue Size个Descriptor组成。Descriptor中存有GPA的字段addr,长度字段len,可以链接next Descriptor的next指针等(形成描述符链)。如果协商了VIRTIO_F_INDIRECT_DESC feature则可以使用Indirect Descriptors来增加ring的容量。
Available Ring中的每个条目是一个是描述符链的头部,它仅由Driver写入并由Device读取,Device获取Descriptor后,Descriptor对应的缓冲区可能是可读的也可能是可写的,可读的用于Driver发送数据,可写的用于接收数据。
Used Ring的介绍直接贴SPEC文档中的描述上来比翻译过来更容易理解。The used ring is where the device returns buffers once it is done with them: it is only written to by the device,and read by the driver.简单来说Used Ring的作用就是Device使用完Descriptor之后,将Descriptor放入这里,通知Driver回收。
相关数据结构
一些数据结构只列举了部分关键信息
如图所示,virtnet_info作为virtio网络设备的私有数据将virtio_device和net_device链接在一起。对于virtio网络设备来说,它至少有两个virtqueue(如果协商了VIRTIO_NET_F_MQ则可以创建多个队列,详见VirtIO SPEC中5.1.2 ),一个用于TX(send_queue),另一个用于RX(receive_queue),TX和RX队列中都包含了virtqueue和scatterlist[]。virtio_device、virtnet_info、receive_queue、send_queue、virtqueue给定任一结构体均可得到其它结构体信息。

可以把virtqueue理解为一个接口类,而vring_virtqueue作为这个接口的一个实现,vring_virtqueue通过成员vq可以与上述其它struct建立联系。virtio的环形缓冲区机制是由vring来承载的,vring由三部分组成:Descriptor表(vring_desc),Available ring(vring_avail)和Used ring(vring_used)。

Vring机制简介
在virtio设备上进行批量数据传输的机制被称为virtqueue。每个设备可以拥有零个或多个virtqueue,当Driver想要向设备发送数据时,它会填充Descriptor Table中的一项(或将几项链接在一起),并将描述符索引写入Available Ring中,然后它通知Device,当Device完成后,它将描述符索引写入Used Ring中并发送中断。(详见SPEC 2.4)
下图Virtio网络设备发包过程为例讲解上述机制,Driver将sk_buffer填充进scatterlist table中(只是设置地址没有数据搬移),然后通过计算得到GPA并将GPA写入Descriptor Table中,同时将Desc chain的head记录到Available Ring中,然后通过PIO的方式通知Device,Device发包并更新Used Ring。
后续会有一篇文章专门分析发包过程

- 本文作者: Lauren
- 本文链接: http://lihanlu.cn/virtio-introduction/
- 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
《Linux虚拟化KVM-Qemu分析(八)之virtio初探》
《Linux虚拟化KVM-Qemu分析(九)之virtio设备》
《使用DPDK打开Open vSwitch(OvS) *概述》
《《深入浅出DPDK》读书笔记(十五):DPDK应用篇(Open vSwitch(OVS)中的DPDK性能加速)》
《virtio 网络的演化:原始virtio > vhost-net(内核态) > vhost-user(DPDK) > vDPA》
站外参考文章
更多推荐



所有评论(0)