深入理解virtio-net和vhost-net

深入理解virtio-net和vhost-net这篇文章中 我们将详细解析 virtio networking 和 vhost net 介绍中描述的 vhost net 架构 以从技术角度说明一切如何协同工作

大家好,欢迎来到IT知识分享网。

这篇文章中,我们将详细解析virtio-networking和vhost-net介绍中描述的vhost-net架构,以从技术角度说明一切如何协同工作。

我们将从描述虚拟机管理程序中如何排列不同的virtio规范中标准组件和共享内存开始,qemu如何模拟virtio网络设备,以及guest如何使用virtio规范来实现虚拟化驱动程序来管理设备和与其通信。

先简要地解释一些概念。

Networking

从基础开始,一个物理NIC(网络接口卡)是真实的硬件,允许host连接到外部世界。它可以执行一些卸载,例如,在NIC中而不是在CPU中执行检验和计算,分段卸载(将较大的数据碎片化为小块,如以太网MTU大小)或大接收卸载(将许多接收到的数据包的数据合并到一起)。

另一方面,我们有tun/tap设备,即用户空间的应用程序可以用它来交换数据包的虚拟点对点网络设备。当交换的数据是第2层(以太网帧)时,该设备称为tap设备;如果交换的数据是第3层(IP数据包)时,则该设备称为tun设备。

加载tun内核模块时,它将创建一个特殊的设备/dev/net/tun。进程可以创建一个tap设备,将其打开并向其发送特殊的ioctl命令。新的tap设备在/dev文件系统中有一个名称,另一个进程可以打开它,发送和接收以太网帧。

IPC,系统编程

  • Unix套接字是一种高效地在同一台计算机上进行进程间通信(IPC)的方法。在本帖子中,通信服务器将Unix套接字绑定到文件系统中的路径,因此客户端可以使用该路径连接到该套接字。从而,进程可以交换消息。注意,unix套接字也可以用于在进程之间交换文件描述符。
  • eventfd是执行IPC的一种简便方法。尽管Unix套接字允许发送和接收任何类型的数据,但eventfd只是生产者可以更改且消费者可以轮询和读取的整数。这使它们更适合用作wait/notify机制,而不是信息传递。

这两个IPC系统都为通信中的每个进程公开了一个文件描述符。fcntl调用执行对文件描述符不同的操作,它们无阻塞(如果没有什么可读,读操作就会立马返回)。ioctl调用遵循相同的模式,但是实现特定于设备的操作,例如发送命令。

  • 共享内存是我们将在这里介绍的IPC的最后一种方法。它没有提供两个进程通信的通道,而是使某些进程的内存区域指向同一内存页面,因此,一个进程在其上进行的写入更改将影响后续另一个进程的读取。

QEMU和设备仿真

QEMU是一个托管虚拟机仿真器,为guest提供了一组不同的硬件和设备模型。对于host,qemu看起来是由标准Linux调度程序调度的常规进程,具有自己的进程内存。在此过程中,QEMU分配guest看到的物理内存区域,并执行虚拟机的CPU指令。

为了在诸如存储或网络之类的裸机硬件上执行I/O,CPU必须与执行特殊指令并访问特定内存区域(例如设备映射到的内存区域)的物理设备进行交互。

当guest访问这些内存区域时,控制权将返回给QEMU,QEMU对guest以透明的方式执行设备的仿真。

KVM

基于内核的虚拟机(KVM)是内置在Linux中的一种开源虚拟化技术。它使用内置的CPU虚拟化技术为虚拟化软件提供硬件辅助,以减少虚拟化开销(缓存,I/O,内存)并提高安全性。

借助KVM,QEMU可以创建一个虚拟机,该虚拟机具有处理器知道的运行本机速度指令的虚拟CPU(vCPU)。当KVM收到一条特殊指令(例如与设备或特殊内存区域交互的指令)时,vCPU会暂停并通知QEMU暂停原因,从而使虚拟机监控程序可以对该事件做出反应。

在常规KVM操作中,管理程序打开设备/dev/kvm,并使用ioctl调用与之通信以创建VM,添加CPU,添加内存(由qemu分配,但从虚拟机的角度来看是物理的),发送CPU中断(作为外部设备发送的中断)等。例如,这些ioctl之一运行实际的KVM vCPU,阻止QEMU并使vCPU运行,直到找到需要硬件帮助的指令。在那一刻,ioctl返回(这称为vmexit),并且QEMU知道该退出的原因。

对于特殊的内存区域,KVM采用类似的方法,将内存区域标记为只读或根本不映射它们,从而导致具有KVM_EXIT_MMIO原因的vmexit。

virtio规范

virtio规范:设备和驱动程序

Virtio是虚拟机数据I/O通信的规范,它为虚拟设备提供了一种直接,高效,标准和可扩展的机制,而不是针对每个环境或每个OS的机制。它利用guest可以与host共享内存,来实现I/O。

virtio规范基于两个元素:设备和驱动程序。在典型的实现中,管理程序通过多种传输方法将virtio设备暴露给guest。对于虚拟机中的guest来说,它们看起来像物理设备。

最常见的传输方法是PCI或PCIe总线。但是,可以在某些预定义的guest的内存地址(MMIO传输)处使用该设备。这些设备可以是完全虚拟的,没有物理配对物或物理设备公开兼容的接口。

virtio设备的典型(最简单)方法是通过PCI端口,因为我们可以利用以下事实:PCI是QEMU和Linux驱动程序中成熟且受到良好支持的协议。真正的PCI硬件使用特定的物理内存地址范围(即驱动程序可以通过访问该内存范围来读取或写入设备的寄存器)和/或特殊的处理器指令来公开其配置空间。在VM世界中,虚拟机监控程序捕获对该内存范围的访问并执行设备仿真,公开与真实计算机相同的内存布局,并提供相同的响应。virtio规范还定义了其PCI配置空间的布局,因此实现起来很简单。

当guest启动并使用PCI/PCIe自动发现机制时,virtio设备将使用PCI供应商ID和其PCI设备ID进行标识。guest的内核使用这些标识符来知道哪个驱动程序必须处理该设备。特别是,Linux内核已经包含了virtio驱动程序。

virtio驱动程序必须能够分配管理程序和设备都可以访问以进行读取和写入(即通过内存共享)的内存区域。我们将数据层称为使用这些内存区域的数据通信部分,控制层为设置它们的过程。我们将在以后的文章中提供有关virtio协议实现和内存布局的更多详细信息。

virtio内核驱动程序共享通用的特定传输的接口(例如:virtio-pci),由实际的传输和设备实现(例如virtio-net或virtio-scsi)使用。

virtio规范:virtqueues

Virtqueues是在virtio设备上进行批量数据传输的机制。每个设备可以具有零个或多个虚拟队列。它由客户机分配的缓冲区队列组成,主机可以通过读取或写入来与之交互。此外,virtio规范还定义了双向通知:

  • 可用缓冲区通知:驱动程序使用该信号来通知设备已准备好处理缓冲区
  • 已用缓冲区通知:由设备用于通知其已完成对某些缓冲区的处理。

在PCI情况下,guest虚拟机通过写入特定的内存地址来发送可用的缓冲区通知,并且设备(在这种情况下为QEMU)使用vCPU中断来发送已使用的缓冲区通知。

virtio规范还允许动态地启用或禁用通知。这样,设备和驱动程序可以批处理缓冲区通知,甚至可以主动轮询虚拟队列中的新缓冲区(繁忙轮询)。这种方法更适合于高流量。

总而言之,virtio驱动程序接口公开:

  • 设备的功能位(设备和来宾必须协商)
  • 状态位
  • 配置空间(包含设备特定信息,例如MAC地址)
  • 通知系统(配置已更改,可用缓冲区,已使用缓冲区)
  • 零个或多个virtqueues
  • 传输特定的接口到设备

使用Virtio联网:QEMU实现

深入理解virtio-net和vhost-net

图1 qemu上的virtio-net

virtio网络设备是虚拟以太网卡,它支持多队列的TX/RX。空缓冲区被放置在N个virtqueues中以接收数据包,而发送数据包则被排入另外N个virtqueues中以进行传输。另一个virtqueue用于数据面外部的驱动程序和设备通信,如控制高级过滤功能,诸如mac地址之类的设置或活动队列数。作为物理NIC,virtio设备支持多卸载等特性,并且可以让真实主机的设备来完成这些特性。

为了发送数据包,驱动程序向设备发送一个缓冲区,该缓冲区包含元数据信息(例如,卸载数据包的所需信息),然后是要传输的数据包帧。驱动程序还可以将缓冲区拆分为多个收集项,例如,可以从数据包帧拆分元数据头。

这些缓冲区由驱动程序管理,并由设备映射。在这种情况下,设备位于管理程序“内部”。由于管理程序(qemu)可以访问所有guest的内存,因此它能够找到缓冲区并对其进行读写。

以下流程图显示了virtio-net设备配置以及使用virtio-net驱动程序发送数据包的过程,该驱动程序通过PCI与virtio-net设备进行通信。填充要发送的数据包后,它将触发“可用缓冲区通知”,将控制权返回给QEMU,以便它可以通过TAP设备发送数据包。

然后,Qemu通知guest该缓冲区操作(读取或写入)已完成,并通过将数据放入虚拟队列中并发送已使用的通知事件来执行此操作,从而触发guest vCPU中断。

接收数据包的过程与发送数据包的过程相似。唯一的区别是,在这种情况下,guest会预先分配空缓冲区并将其提供给设备,以便设备可以将传入的数据写入其中。

深入理解virtio-net和vhost-net

图2:Qemu virtio发送缓冲区流程图

vhost协议

介绍

先前的方法存在一些效率低下的问题:

  • virtio驱动程序发送“可用缓冲区通知”后,vCPU停止运行,控制权返回给虚拟机监控程序,从而导致昂贵的上下文切换。
  • QEMU的其他任务/线程同步机制。
  • 系统调用和每个数据包的拷贝在通过tap发送或接收的时候(无批处理)。
  • ioctl发送可用的缓冲区通知(vCPU中断)。
  • 我们还需要添加另一个syscall来恢复vCPU执行,以及所有相关的映射切换等。

为了解决这些限制,设计了vhost协议。vhost API是基于消息的协议,允许管理程序将数据层卸载到另一个组件(处理程序),该组件可以更有效地执行数据转发。使用此协议,主服务器将以下配置信息发送到处理程序:

  • 系统管理程序的内存布局。这样,处理程序可以在管理程序的内存空间中定位virtqueue和缓冲区。
  • 一对文件描述符,用于处理程序发送和接收virtio规范中定义的通知。这些文件描述符在处理程序和KVM之间共享,因此它们可以直接进行通信,而无需管理程序的干预。注意,仍然可以根据每个virtqueue动态禁用此通知。

在此过程之后,系统管理程序将不再处理数据包(对virtqueue的读/写操作)。取而代之的是,数据层将完全卸载到处理程序,该处理程序现在可以直接访问virtqueues的内存区域,以及直接与guest之间发送和接收通知。

可以在任何主机的本地传输协议(例如Unix套接字或字符设备)中交换vhost消息,并且虚拟机管理程序可以充当服务端或客户端(在通信通道的上下文中)。系统管理程序是协议的领导者,卸载设备是处理程序,它们中的任何一个都可以发送消息。

为了进一步了解该协议的好处,我们将分析基于vhost协议的内核实现的详细信息: vhost-net内核驱动程序。

vhost-net

vhost-net是内核驱动程序,实现vhost协议的处理程序端以实现有效的数据层,即数据包转发。在此实现中,qemu和vhost-net内核驱动程序(处理程序)使用ioctl交换vhost消息,并使用几个类似于eventfd的文件描述符称为irffd和ioeventfdt用于与guest交换通知。

加载vhost-net内核驱动程序后,它将在/dev/vhost-net上公开一个字符设备。当qemu在vhost-net支持下启动时,它将打开它并使用几个ioctl调用初始化vhost-net实例。这些是将虚拟机监控程序进程与vhost-net实例相关联,为virtio功能协商做准备并将guest物理内存映射传递给vhost-net驱动程序所必需的。

在初始化期间,vhost-net内核驱动程序会创建一个名为vhost-$pid的内核线程,其中$pid是系统管理程序进程pid。该线程称为“vhost worker线程”。

Tap设备仍用于host与VM通信,但是现在工作线程处理I/O事件,即它轮询驱动程序通知或tap事件,并转发数据。

Qemu分配一个eventfd并将其注册到vhost和KVM中,以实现通知旁路。vhost-$pid内核线程轮询它,并且当guest写入特定地址时,KVM会对其进行写入。该机制称为ioeventfd。这样,对特定guest内存地址的简单读/写操作就不需要经过昂贵的QEMU进程唤醒,并且可以直接路由到vhost worker线程。这也具有异步的优势,无需停止vCPU(因此无需立即进行上下文切换)。

另一方面,qemu分配另一个eventfd并将其再次注册到KVM和vhost,以直接进行vCPU中断注入。这种机制称为irqfd,它允许主机中的任何进程通过对其进行写入来将vCPU中断注入guest,同时具有相同的优势(异步,无需立即进行上下文切换等)。

注意,virtio数据包处理后端中的此类更改对于仍使用标准virtio接口的guest是完全透明的。

以下方框图和流程图显示了从qemu卸载到vhost-net内核驱动程序的数据路径:

深入理解virtio-net和vhost-net

图3:vhost-net框图

深入理解virtio-net和vhost-net

图4:vhost-net发送缓冲区的流程图

与外界沟通

guest可以使用tap设备与主机进行通信,但是问题仍然在于,它如何与同一主机上的其他VM或与主机外部的计算机进行通信(例如:与Internet进行通信)

我们可以通过使用内核网络堆栈提供的任何转发或路由机制(例如标准Linux桥接器)来实现此目的。但是,更高级的解决方案是使用完全虚拟化的分布式受管交换机,例如开放式虚拟交换机(OVS)。

如概述文章中所述,在这种情况下,OVS数据路径作为内核模块运行,ovs-vswitchd作为用户区控制并管理守护程序,ovsdb-server作为转发数据库。

如下图所示,OVS数据路径在内核中运行,并且正在物理NIC和虚拟tap设备之间转发数据包:

深入理解virtio-net和vhost-net

图5:介绍OVS

我们可以将这种情况扩展到在同一主机环境上运行的多个VM,每个VM都具有qemu进程,TAP端口和vhost-net驱动程序,这有助于避免qemu上下文切换。

欢迎关注公众号:小树学习驿站

参考文献:

https://www.redhat.com/en/blog/deep-dive-virtio-networking-and-vhost-net

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/175599.html

(0)
上一篇 2025-04-07 12:10
下一篇 2025-04-07 12:20

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信