Vhost架构解析:vhost-net与vhost-user详解
核心思想:为什么需要 Vhost?
在传统的虚拟化 I/O 架构(如 Virtio)中,数据通路是这样的:
- Guest(虚拟机)将 I/O 请求放入virtio 环。
- Guest 通知 QEMU(运行在用户空间的宿主机进程)。
- QEMU 从内核态切换到用户态,从 virtio 环中取出请求。
- QEMU 执行实际的 I/O 操作(例如,通过内核的 TUN/TAP 设备发送网络包)。
- 数据包进入宿主机内核网络协议栈,最终从物理网卡发出。
这个过程存在一个显著的性能瓶颈:每一次 I/O 操作都需要 QEMU 的参与。QEMU 作为用户空间进程,与 Guest 和内核的交互涉及到大量的上下文切换(用户态/内核态切换)和内存拷贝,这在高吞吐量、低延迟的网络场景下成本非常高。
Vhost 架构的诞生就是为了解决这个问题。 它的核心思想是:“让专业的人做专业的事”,将数据面的处理从通用的 QEMU 进程中卸载到更高效、更专用的组件中,从而绕过 QEMU。
Vhost 架构的通用原理
Vhost 通过以下机制实现性能提升:
- 内存映射:宿主机能够直接访问 Guest 的内存,特别是 virtio 环 和 数据缓冲区。这是实现零拷贝或最少拷贝的基础。
- 事件机制:使用
eventfd
和irqfd
在 Guest、宿主机内核(或后端进程)之间进行直接通知,无需 QEMU 中转。 - 协议协商:QEMU 负责设备的初始化和特性协商,一旦建立连接,就将数据平面的控制权交给 vhost 后端。
1. vhost-net:将网络数据面移到内核空间
目标:将网络数据面的处理从 QEMU 用户空间卸载到宿主机内核,以利用内核成熟且高效的网络栈。
架构与数据流:
下图清晰地展示了 vhost-net
如何通过内核模块优化数据路径:
- 初始化:QEMU 启动时,会创建一个 TUN/TAP 设备(例如
tap0
),并初始化vhost-net
内核模块。QEMU 将virtio
环的信息、Guest 的内存布局以及用于通知的eventfd
传递给vhost-net
驱动。 - 数据发送 (Guest -> Host):
- Guest 将数据包放入
virtio
环。 - Guest 通过写入一个特殊的 I/O 端口(或内存映射IO)来通知后端。这个通知通过
eventfd
直接发送给vhost-net
内核线程,完全绕过 QEMU。 vhost-net
内核线程被唤醒,它直接访问 Guest 内存,从virtio
环中读取数据包。vhost-net
将数据包通过内核的 TUN/TAP 设备注入宿主机内核网络协议栈,最终由物理网卡发出。
- Guest 将数据包放入
- 数据接收 (Host -> Guest):
- 物理网卡收到数据包,送至内核协议栈。
- 内核协议栈将包交给 TUN/TAP 设备。
- TUN/TAP 设备将包传递给
vhost-net
。 vhost-net
直接写入 Guest 内存,将数据包放入virtio
环。vhost-net
通过irqfd
直接向 Guest 注入一个中断,通知它有新数据到达,再次绕过 QEMU。
优点:
- 性能显著提升:消除了 QEMU 进程的上下文切换和内存拷贝。
- 对 Guest 透明:Guest 仍然使用标准的
virtio-net
驱动,无需任何修改。 - 兼容性好:复用宿主机成熟的内核网络栈(TCP/IP, 防火墙等)。
缺点:
- 仍然需要经过宿主机内核协议栈,对于极致性能的场景(如NFV),协议栈本身可能成为瓶颈。
2. vhost-user:将设备处理移到独立进程
目标:将数据面的处理完全卸载到用户空间的独立进程中,这个进程可以是任何专门为高性能 I/O 设计的应用(如使用 DPDK、Snort 等)。
架构与数据流:
下图对比了 vhost-user
与 vhost-net
在架构上的核心区别:
- 通信信道:vhost-user 的后端进程与 QEMU 之间通过一个 Unix Domain Socket 进行通信。这个 Socket 用于传输控制信息,例如建立连接、协商特性、传递内存映射表等。
- 共享内存:QEMU 通过 Socket 将 Guest 的内存映射信息传递给 vhost-user 后端进程。此后,后端进程就可以直接读写 Guest 内存中的 virtio 环和数据缓冲区。
- 事件机制:同样使用
eventfd
和irqfd
进行直接通知。这些文件描述符也通过 Socket 传递。 - 角色分离:
- QEMU:只负责设备模拟的控制平面(设备创建、迁移、销毁等)。
- vhost-user 后端进程:负责全部的数据平面处理。例如,一个基于 DPDK 的 vhost-user 后端可以直接从 virtio 环读取数据,然后通过 DPDK 的轮询模式驱动将数据从物理网卡发送出去,完全绕过内核网络协议栈。
优点:
- 极致性能:允许使用 DPDK 等用户态 I/O 框架,绕过内核协议栈,实现零拷贝和轮询,达到接近硬件的性能。
- 灵活性与可扩展性:后端进程可以是任何应用,实现了网络功能(如交换机、防火墙)、存储目标(如 SPDK)等的定制化和高性能化。这是实现用户态虚拟交换机(如 OVS-DPDK) 的基石。
- 隔离性:设备处理在独立进程中,崩溃不会影响 QEMU 主进程。
缺点:
- 复杂度高:需要管理和维护独立的用户空间后端进程。
- 迁移支持:实时迁移比 vhost-net 更复杂,因为需要同步后端进程的状态。
总结对比
特性 | vhost-net | vhost-user |
---|---|---|
后端位置 | 宿主机内核 | 用户空间独立进程 |
性能 | 高,优于传统 virtio | 极高,可达线速 |
灵活性 | 低,依赖内核网络栈 | 高,后端可任意实现 |
使用场景 | 通用虚拟化,需要良好网络性能 | NFV, SDN, 云原生,极致性能需求 |
关键技术 | 内核模块,eventfd/irqfd | Unix Socket, 共享内存,DPDK/SPDK |
控制平面 | QEMU | QEMU |
数据平面 | vhost-net 内核线程 | 用户空间后端进程 |
演进:后来还出现了 vhost-vDPA,它结合了两者的优点,通过一个标准化的 VDMA (Virtual DMA) 抽象层,让数据平面可以直接分配给硬件或用户空间驱动,进一步提升了性能和硬件兼容性。
总而言之,Vhost 架构是虚拟化 I/O 性能优化的一个里程碑。vhost-net
提供了内核级的高效解决方案,而 vhost-user
则打开了用户态高性能网络的大门,为现代云和数据中心的网络功能虚拟化提供了核心支撑。