OVS 与 OVS-DPDK 对比分析 (opus 4.6)
架构差异总览
【OVS 内核态】
应用 → 内核协议栈 → OVS内核模块(openvswitch.ko) → 网卡驱动 → 网卡
↕ (upcall/首包)
ovs-vswitchd (用户空间)
【OVS-DPDK】
应用 → 内核协议栈 → ovs-vswitchd + DPDK PMD (用户空间轮询) → 网卡(UIO/VFIO)
(完全绕过内核数据面)
核心区别
1. 数据面位置
| 维度 | OVS(内核态) | OVS-DPDK |
|---|
| 数据转发位置 | 内核空间(openvswitch.ko) | 用户空间(DPDK PMD 轮询) |
| 网卡驱动 | 传统内核驱动 | DPDK PMD(UIO/VFIO 接管网卡) |
| 包处理方式 | 中断驱动 | 轮询驱动(Polling) |
| 控制面 | ovs-vswitchd(用户空间) | ovs-vswitchd(用户空间,同时承载数据面) |
2. 数据包处理流程对比
OVS 内核态
网卡收包
→ 硬件中断 → 软中断(NET_RX)
→ 内核网卡驱动收包
→ OVS内核模块流表匹配
→ 命中:内核直接转发 ✅(快速路径)
→ 未命中:Netlink upcall → ovs-vswitchd(慢速路径)
→ 上下文切换 × 2
→ 用户空间计算决策,下发流表
OVS-DPDK
DPDK PMD 线程持续轮询网卡(忙等待)
→ 直接从网卡队列取包(无中断、无系统调用)
→ 用户空间流表匹配(EMC → SMC → dpcls → upcall)
→ 命中:用户空间直接转发 ✅
→ 未命中:同进程内 upcall(无内核态切换)
→ 直接写入目标网卡/vhost-user 队列
3. 关键性能差异
| 指标 | OVS 内核态 | OVS-DPDK |
|---|
| 延迟 | ~50-100 μs | ~10-20 μs |
| 吞吐量(小包64B) | ~1-3 Mpps | ~10-20+ Mpps |
| 上下文切换 | 频繁(中断 + upcall) | 几乎为零 |
| 数据复制 | 内核态 sk_buff 操作 | 用户空间 mbuf,零拷贝 |
| CPU 使用 | 按需使用 | 持续 100% 占用(轮询线程) |
| 首包延迟 | 高(Netlink upcall 跨内核/用户空间) | 低(进程内 upcall) |
详细差异分析
中断 vs 轮询
OVS 内核态(中断驱动)
网卡收到包
→ 触发硬中断(IRQ)
→ CPU 保存上下文,跳转到中断处理程序
→ 调度软中断 NAPI 轮询
→ 处理一批数据包
→ 软中断退出
- 每次中断有 数μs 的开销
- 高流量下中断风暴导致 CPU 被中断处理占满
- NAPI 做了优化(中断+轮询混合),但仍有开销
OVS-DPDK(PMD 轮询)
PMD 线程(绑定到专用 CPU 核)
while(true) {
nb_rx = rte_eth_rx_burst(port, queue, mbufs, batch_size);
if (nb_rx > 0) {
process_packets(mbufs, nb_rx);
rte_eth_tx_burst(port, queue, mbufs, nb_tx);
}
}
- 零中断:完全消除中断开销
- 零系统调用:网卡通过 UIO/VFIO 映射到用户空间
- 代价:PMD 线程独占 CPU 核,即使没有流量也 100% 占用
内存管理差异
| 维度 | OVS 内核态 | OVS-DPDK |
|---|
| 包缓冲区 | sk_buff(内核动态分配) | mbuf + mempool(预分配大页内存) |
| 内存分配 | 每包可能触发 kmalloc | 预分配池,零动态分配 |
| 大页内存 | 不需要 | 必须配置 HugePages |
| NUMA 感知 | 有限 | 完整 NUMA 亲和性 |
与 KubeVirt VM 的连接方式
OVS 内核态 + vhost-net
VM(virtio-net) → vring → vhost-net(内核) → tap设备 → OVS内核模块
- vhost-net 在内核态处理 vring
- 数据从 vring → tap → OVS 内核模块,路径较短
OVS-DPDK + vhost-user
VM(virtio-net) → vring(共享内存) ← OVS-DPDK PMD 直接轮询
- 使用 vhost-user 协议(替代 tap + vhost-net)
- OVS-DPDK 的 PMD 线程直接读取 VM 的 vring 共享内存
- 省去了 tap 设备和 vhost-net 内核模块
- 数据路径:
Guest 应用 → Guest 内核 → virtio vring → (共享内存) → OVS-DPDK PMD → 网卡
↑
无需经过Host内核
数据复制次数对比(KubeVirt 场景)
OVS 内核态路径
| 序号 | 复制 | 说明 |
|---|
| 1 | 应用 → Guest 内核 | send() 系统调用 |
| 2 | Guest 内核 → vring | virtio 描述符 |
| 3 | vring → tap sk_buff | vhost-net 内核处理 |
| 4 | OVS 内核转发 | sk_buff 指针操作(可能零拷贝) |
| 5 | 内核 → 网卡 | DMA |
| 总计 | 4-5 次 | |
OVS-DPDK 路径
| 序号 | 复制 | 说明 |
|---|
| 1 | 应用 → Guest 内核 | send() 系统调用 |
| 2 | Guest 内核 → vring | virtio 描述符 |
| 3 | vring → DPDK mbuf | PMD 直接从共享内存读取 |
| 4 | DPDK → 网卡 | DMA(用户空间发起) |
| 总计 | 3-4 次 | |
上下文切换对比
| 切换类型 | OVS 内核态 | OVS-DPDK |
|---|
| Guest send() | 1次 | 1次 |
| VM Exit | 1次 | 1次 |
| vhost-net / PMD | 内核态处理 | 无切换(用户空间轮询) |
| 中断处理 | 多次 | 0次 |
| OVS upcall(首包) | 2次(内核↔用户空间) | 0次(进程内) |
| 总计 | 5-7 次 | 2-3 次 |
部署复杂度对比
| 维度 | OVS 内核态 | OVS-DPDK |
|---|
| 配置复杂度 | 低 | 高 |
| HugePages | 不需要 | 必须配置(通常 1G 页) |
| CPU 绑定 | 不需要 | 需要隔离 CPU 核给 PMD |
| 内存预留 | 不需要 | 需要预留大量大页内存 |
| NUMA 规划 | 不敏感 | 必须规划 NUMA 拓扑 |
| K8s 集成 | 成熟 | 需要额外配置(CPU Manager 等) |
| 调试难度 | 低(标准内核工具) | 高(专用工具) |
适用场景
选 OVS 内核态
- 通用工作负载,对延迟不敏感
- 资源有限,不能独占 CPU 核
- 运维团队对 DPDK 不熟悉
- VM 密度优先
选 OVS-DPDK
- 电信/NFV 场景(高吞吐、低延迟)
- 需要线速转发小包
- 有充足的 CPU 和内存资源可以专用
- 对网络性能有严格 SLA 要求
一句话总结
OVS 内核态用中断 + 内核模块处理数据包,通用但开销较多;OVS-DPDK 将整个数据面搬到用户空间轮询,以独占 CPU 核为代价,换取更少的数据复制、几乎零上下文切换和数倍的吞吐性能。