概述
virtio是一种IO半虚拟化解决方案, 提供guest OS与hypervisor虚拟化设备之间的通信框架和编程接口. 好处是能提高性能, 且减少跨平台带来的兼容性问题.
一个简单的例子
![](https://i-blog.csdnimg.cn/blog_migrate/90e807235940a094d25070f2a622a303.png)
上图表示VM请求和从host接收网络数据的过程:
- vm: 我想要访问google.com. Hey, virtio-net, 你能告诉host帮我获取下这个页面吗?
- virtio-net: ok. Hey host, 你能帮忙拉取下google的页面吗?
- host: ok, 我正在拉取网页数据.
- host: 拉取完了, 这是你想要的数据.
- virtio-net: 感谢, hey vm, 这是你请求的页面.
整个过程尽量让主机硬件完成更多工作, 让virtio处理发送和接收数据. 这样相比于纯软件模拟设备, 将大量的工作offload到宿主机可以让vm执行的速率更快, 效率更高.
另一个方面, virtio核心框架按照官方的VirtIO规范进行了虚拟化, VirtIO规范定义了VirtIO设备和驱动程序必须满足的标准要求, 比如feature bits, statuses, configuration, general operations 等, 也就是无论运行在什么环境下, 只要使用VirtIO, 其实现的核心框架都必须是一样的.
为什么要使用VirtIO?
上面的例子中, 我们使用宿主机的网络设备来实现虚拟机访问外部网络, 使用一个的模拟设备(比如rtl8139)会怎么样呢? 通过模拟, 我们可以模拟任何设备, 甚至是宿主机上不支持的设备, 所以, 如果我们能让vm模拟任何设备, 为什么要把自己限制在主机的设备和能力上呢? 要回答这个问题, 需要先了解下虚拟化和模拟之前的区别.
虚拟化 vs 模拟
模拟(emulation)时, 软件填补硬件, 让模拟的设备像实际的硬件一样工作.
如果我们想让虚拟机使用主机不支持的网卡, 比如rtl8139这个历史很久远的设备, 已经不再生产了, 我们可以使用模拟来弥补硬件的缺失.
当vm需要使用宿主机上不具备或者不支持的设备时, 首选模拟. 然而, 软件模拟缺失的硬件时, 宿主机cpu需要运行额外的代码, 如果有专有的硬件一定会更快.
虚拟化(virtualization)时, 软件将主机的物理硬件进行拆分, 供虚拟机使用. 这一部分的硬件给虚拟机, 虚拟机认为自己有硬件, 而实际上它只是从宿主机"借用"的硬件.
虚拟化的核心思想是, 每一个虚拟机都可以直接访问主机硬件的那一部分.
当然, 由于虚拟化会分割主机的资源, 所以虚拟机自然会受到宿主机硬件的限制. VirtIO是对IO的虚拟化, 即提供宿主机和虚拟机之间用于IO设备的通信框架.
VirtIO架构
VirtIO框架有三个关键部分:
- 前端驱动
- 后端设备
- VirtQueue & VRing
从上图可以看到, 前端驱动位于guest内核中, 后端设备存在于hypervisor(qemu), 它们之间数据平面的通信是通过VirtQueue和VRing来处理的.
也可以看到从VirtIO驱动和设备来的通知(比如VMExits, vCPU IRQs), 最后被路由到KVM中断.
VirtIO Drivers(前端)
VirtIO drivers一般存在于guest内核, 驱动程序的核心职责是:
- 接收用户进程的IO请求
- 将IO请求传输到相应的后端设备
- 从对应的后端设备获取完成的请求
以一个从virtio-scsi的IO请求为例. 用户想要从存储获取一个文档, virtio-scsi驱动接收了这个请求, 并将这个请求发送给virtio-scsi设备(后端). 当virtio设备完成这个请求后, 这个文档对于virtio驱动来说就是可用的了, virtio驱动获取这个文档并返回给用户.
VirtIO Devices(后端)
一般地, VirtIO Devices存在于hypervisor, 在上图中使用qemu作为hypervisor, 也就是VirtIO Device存在于qemu进程中. VirtIO设备的核心职责是:
- 接收来自相应前端驱动的IO请求
- 处理请求(将IO操作offload到宿主机的物理硬件上)
- 使处理过后的请求数据对VirtIO驱动可用
还是用上面的virtio-scsi的例子, vistio-scsi驱动通知设备, 让后端设备知道它需要从实际物理硬件(存储设备)中获取请求的文档, virtio-scsi接收这个请求并执行必要的调用以从物理硬件获取到数据, 然后将检索到的数据放到共享的VirtQueue中, 使数据对前端驱动可用.
VirtQueues
VirtQueues是一种帮助设备和驱动执行各种VRing操作的数据结构. virtqueue在guest物理内存中共享.
有的地方会将VirtQueues与VRings(virtio-rings)当成同义词, 有的地方会分开描述. 其实VRings是VirtQueues的主要特性, 因为VRings是促进VirtIO设备和驱动程序之间数据传输的实际数据结构. 这里我们分别描述它们, 因为VirtQueue不仅仅是它的VRings.
qumu中的VirtQueue和VRing数据结构
从上图qemu中VirtIO框架, 我们可以看到VirtQueue和VRing数据结构之间的关系.
![](https://i-blog.csdnimg.cn/blog_migrate/fa9443d674d12aa8c8dcc9f58633526d.png)
linux内核中VirtQueue和VRing数据结构
linux版本和qemu版本对比, 它们的组织方式不一样, 但是还好有VirtIO规范, VRing结构是相似的(desc, avail, used).
不需要了解每个字段的含义, 这里的要点是, VirtQueue和VRing是两种不同的数据结构, VirtQueue的组织形式根据操作系统以及我们讨论的是用户态还是内核态VirtIO框架而变化.
VRings
已经提到, VRings是VirtQueue的主要特性, 也是保存正在传输的实际数据的核心数据结构.
它们被称作"ring"的原因是, 构成了一个环状的结构, 本质是一个数组, 一旦最后一个条目被写入, 它就会回到自己的开头位置. 我们也称之为"区域".
每一个VirtQueue最多有三种类型的VRing:
- descriptor ring
- avialable ring
- used ring
Descriptor Ring
也叫descriptor table, descriptor area, 本质上是一个描述符的循环数组, 其中描述符是一个描述数据结构缓冲区的数据结构, 描述符保存了数据缓冲区的以下信息:
- addr:guest物理地址
- len: 数据缓冲区的长度
- flags: 一些标志位, 比如NEXT, WRITE, INDIRECT
- next: 下一个链接的描述符索引
只有驱动程序才可以向desc ring添加(写入)描述符, 后端设备只有在描述符标志表明缓冲区是可写的情况下才能写入设备可写缓冲区. 缓冲区可以是只写, 也可以是只读. 但不能同时是可读可写.
学习地址: Dpdk/网络协议栈/vpp/OvS/DDos/NFV/虚拟化/高性能专家-学习视频教程-腾讯课堂
更多DPDK相关学习资料有需要的可以自行报名学习,免费订阅,久学习,或点击这里加qun免费
领取,关注我持续更新哦! !
![](https://i-blog.csdnimg.cn/blog_migrate/5814384d32b8a6d2fb3676b7895bc2f3.png)
上图中descriptor [0] 表明数据缓冲区在GPA 0x600, 数据长度是0x100, 标志是writable, 没有next标志
Availabile Ring(Driver Area)
也叫avail ring, driver area, 是一个descriptor ring中可用描述符的循环数组, 也就是avail ring中的每一个条目都执行desc ring中的一个描述符(描述符链的头部).
avail ring除了available ring数组外, 还包括:
- flags: 配置flags
- idx: 下一个可用ring条目
- ring[]: 实际的avail ring array
![](https://i-blog.csdnimg.cn/blog_migrate/b0c8ad87a6d9b141ec6beeb4c8f34b6d.png)
只有驱动才可以将描述符条目和available ring条目分别添加到desc ring和avail ring. 在驱动程序将相应的的desc ring索引添加到avail ring之前, 设备无法访问该数据.
Used Ring(Device Area)
一个引用描述符环上已经使用的描述符条目的循环数组. 也就是设备写入或者读取描述符的数据缓冲区.
由以下部分组成:
- flags: 配置flags
- idx:索引指向下一个可用的used ring条目
- ring[]: 实际的used ring array
- id: 索引指向desc ring上的元素
- len: 写入descriptor buffer的数据长度
![](https://i-blog.csdnimg.cn/blog_migrate/11592ef540af4f3d1aab18faaa7b91a9.png)
TODO
参考