参考:《深入浅出DPDK》及大佬们的各种博客
Virtio简介&运行环境
Virtio 是一种用于虚拟化环境中的半虚拟化 I/O 框架,目的是在虚拟机和主机之间提供一种高效的 I/O 机制。关于什么是半虚拟化和全虚拟化:见SR-IOV学习笔记。
YES,virtio 是作为客户机访问主机上的设备的接口而开发的,因此可以分为两部分:
- virtio Guest OS(虚拟机)中实现的前端程序,定义了如何在客户机和主机之间创建控制平面和数据平面
- vhost 协议,在宿主机(物理机或主机)实现的后端驱动, 允许将 virtio 数据平面实现外置到另一部分(用户进程或内核模块)以提高性能的协议,核心目的是通过将虚拟设备的数据处理任务从虚拟机监视器(如 QEMU)转移到主机上的用户态或内核态进程
ps:大家伙耳熟能详(呃,或许并不太熟悉)的virtio-net是虚拟以太网卡,是一种virtio支持的IO设备。
Virtio 被广泛应用于 KVM(Kernel-based Virtual Machine)和 QEMU 等虚拟化平台,提供了包括网络、块设备等在内的多种虚拟设备支持。其工作环境如下:
图片来源:https://www.redhat.com/en/blog/introduction-virtio-networking-and-vhost-net
下面浅谈下Virtio的同事们:
-
KVM
- 个人简介:基于Linux内核的开源虚拟化解决方案
- 工作团队:内核空间
- 工作内容:提供虚拟化支持,允许在同一台物理机上运行多个操作系统作为虚拟机,实现硬件资源的高效利用和灵活管理,相当于把 Linux 内核转变为虚拟机管理程序,使得虚拟机可以直接利用硬件虚拟化扩展(如 Intel VT-x 和 AMD-V)运行。
-
QEMU
- 个人简介:通用且开源的虚拟机模拟器和虚拟化工具
- 工作团队:用户空间
- 工作内容:1. 模拟整个计算机系统,包括处理器、内存、硬盘、网络接口等, 适用于运行不同架构的完整操作系统(比如在x86系统上运行ARM, 斗宗强者,竟恐怖如斯) 2. 提供虚拟化支持,可以利用硬件虚拟化技术(如 Intel VT-x 和 AMD-V)运行虚拟机,大大的提高性能。
-
Libvirt
- 个人简介:虚拟化技术管理员
- 工作团队:用户空间
- 工作内容:1. 调用qemu创建、启动、恢复、暂停虚拟机,是真正的虚拟机管理者,并提供API供用户调用 2. 为每个虚拟机启动一个 qemu 进程,并将 XML 格式的配置转换为 qemu CLI 调用的接口,供用户使用
半虚拟化Virtio
Virtio使用场景
在现代数据中心中大量采用虚拟化技术,IO全虚拟方案中,虚拟机以寄存器的方式访问外设,比如网卡发送一个报文,需要写很多次寄存器,造成大量的(VM-exit,虚拟机暂停运行),性能很差。IO透传方案性能很好,但不能从硬件上支持虚拟机的动态迁移以及缺乏足够灵活的流分类规则。于是,就有了很多Vritio的使用场景。其实现有的云计算厂商,很多都是用的virtio实现的虚拟机和裸金属场景的热迁移。
以下是virio设备的典型场景
宿主机使用虚拟交换机联通物理网卡和虚拟机。虚拟交换机内部有DPDK vhost,实现了Virtio的后端网络设备驱动程序逻辑,虚拟机里有DPDK的前端网络设备驱动。前端和后端通过virtio的虚拟队列交换数据。
这样虚拟机中的网络数据遍剋发送到虚拟交换机中,通过转发逻辑,经由物理网卡进入外部网络。
Virtio架构&原理
Virtio架构如下图所示,
-
前端驱动程序
- 包含不同前端驱动程序,如块设备(例如磁盘)驱动、网络设备驱动等驱动程序。每个前端驱动程序在虚拟机管理程序中都有相应的后端驱动程序。 驱动程序可以根据需要使用零个或多个队列。例如,virtio网络驱动程序使用两个虚拟队列(一个用于接收,一个用于发送),而virtio块驱动程序仅使用一个。虚拟队列是虚拟的,实际上被实现为环以遍历客户机到虚拟机管理程序的转换。
-
虚拟队列
- 连接客户机操作系统 和宿主机后端驱动的实际数据链路
虚拟队列
虚拟队列主要由描述符列表(descriptor table)、可用换标和已用环表(used ring)组成。描述符列表指向的是实际要传输的数据。两个环表指向的是描述符列表,分别用来标记前端和后端驱动程序对描述符列表中描述符的处理进度。详细来看:
- 描述符列表
/* dpdk/drivers/net/virtio/virtio_ring.h */
struct vring_desc {uint64_t addr; /* 数据缓冲区的客户机物理地址 */uint32_t len; /* 数据缓冲区的长度 */uint16_t flags; /* 标志位,表示当前描述符的书香,如next是否有效等 */uint16_t next; /* 描述符链中下一个描述符的地址 */
};
- 可用环表
/* dpdk/drivers/net/virtio/virtio_ring.h */
struct vring_avail {uint16_t flags; /* 标志位,表示环表的一些属性,比如是否需要设备在使用了环表中的表项后发送中断给驱动 */uint16_t idx; /* 驱动写入下一个可用描述符的位置 */uint16_t ring[0]; /* 存储描述符指针(id)的数组 */
};
- 已用环表
/* dpdk/drivers/net/virtio/virtio_ring.h */
struct vring_used_elem {/* Index of start of used descriptor chain. */uint32_t id;/* Total length of the descriptor chain which was written to. */uint32_t len;
};struct vring_used {uint16_t flags; /* 标志位,包括是否需要驱动在回收了已用环表中的表项后发送提醒给设备 */volatile uint16_t idx;struct vring_used_elem ring[0];
};
Virtio设备的使用
设备的使用主要包括两部分:
- 驱动通过描述符列表和可用环表提供的数据缓冲区给设备用
- 把数据缓冲区地址、长度等信息赋值到空闲的描述符中
- 把改描述符指针添加到该虚拟队列的可用环表的头部
- 更新可用环表中的头部指针
- 写入该虚拟队列编号到Queue Notify寄存器以通知设备
- 设备使用描述符后再通过已用环表还给驱动
- 把使用过的数据缓冲区描述符的头指针添加到该虚拟队列的已用环表的头部
- 更新该已用环表的头部指针
- 根据是否开启MSI-X中断,用不同的中断方式通知驱动。
Virtio网络设备
virtio网络设备Linux驱动主要包括三部分:
- 底层PCI-e设备层
- 负责监测PCI-e设备,并初始化设备对应的驱动程序
- 中间Virtio虚拟队列层
- 实现了Virtiio协议中的虚拟队列
- 上层网络设备层
- 实现了两个抽象类:virtio设备和网络设备,从而Linux系统能够对待普通网卡一样操作这个virtio网络设备
/* dpdk/lib/librte_vhost/rte_virtio_net.h */
/*** Device and vring operations.*/
struct virtio_net_device_ops {int (*new_device)(int vid); /**< Add device. */void (*destroy_device)(int vid); /**< Remove device. */int (*vring_state_changed)(int vid, uint16_t queue_id, int enable); /**< triggered when a vring is enabled or disabled */void *reserved[5]; /**< Reserved for future extension */
};
以上,顺心顺意!
参考:
《深入浅出dpdk》
https://www.redhat.com/en/blog/introduction-virtio-networking-and-vhost-net
https://developer.ibm.com/articles/l-virtio/
https://tinylab.org/virtio-intro/