DPDK:用户态轮询与大页内存
把网卡从内核手里抢过来自己轮询,换来极低延迟和整核的 CPU 占用。
学完这节你能做到
- 解释轮询模式驱动为什么比中断快
- 列出 DPDK 的部署前提与资源代价
- 判断该选 DPDK 还是 XDP 还是 RDMA
建议先学
跳过这几节会看不懂本节的部分推导
把网卡从内核手里抢过来
L0 讲过收包路径:DMA → 硬中断 → 软中断 NAPI → 协议栈 → socket。 每一步都有开销,而且中断本身在高 PPS 下就是负担。
DPDK 的做法很极端:让内核完全放弃这张网卡,用户态程序自己轮询它。
内核路径: 网卡 → 中断 → 软中断 → 协议栈 → 拷贝到用户态
DPDK: 网卡 → 用户态程序直接轮询读取 ← 没有中断、没有协议栈、没有拷贝
PMD:为什么轮询比中断快
反直觉的一点:忙等(轮询)居然比事件通知(中断)更高效。
原因是中断在高频场景下代价太大:
| 中断的开销 | 说明 |
|---|---|
| 上下文切换 | 保存/恢复现场 |
| 缓存污染 | 打断正在跑的代码,CPU 缓存失效 |
| 调度延迟 | 软中断要等 CPU 调度 |
包速率低时中断是划算的(空闲时不占 CPU)。但在每秒千万包的量级, 中断的固定开销乘以包数就变成了主要成本。
PMD(Poll Mode Driver)干脆不用中断:一个 CPU 核死循环读网卡队列。
while (1) {
nb_rx = rte_eth_rx_burst(port, queue, bufs, BURST_SIZE);
/* 处理这一批包 */
}
PMD 核在 top 里永远是 100%,无论有没有流量。这不是 bug,是设计。
后果要提前想清楚:
- CPU 账要重算:跑 4 个 PMD 核就是 4 个核永久不可用于业务
- 监控告警要排除这些核,否则天天报 CPU 高
- 云上按核计费时,这笔成本很显性
三个必须满足的前提
① 大页内存
DPDK 用大页(HugePage)而不是常规 4KB 页,为的是减少 TLB miss:
4KB 页: 1 GB 内存 = 262144 个页表项 → TLB 频繁失效
2MB 大页:1 GB 内存 = 512 个页表项 → TLB 命中率大幅提升
# 分配大页
echo 2048 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
# 或者启动参数(推荐,避免内存碎片后分配失败)
# default_hugepagesz=1G hugepagesz=1G hugepages=8
# 确认
grep -i huge /proc/meminfo
mount | grep hugetlbfs
大页要在系统启动早期分配——运行久了内存碎片化,可能分不出连续的大页。
② 设备绑定到 VFIO/UIO
要让用户态直接访问网卡,得先把它从内核驱动上解绑:
# 看当前绑定状态
dpdk-devbind.py --status
# 绑到 vfio-pci(推荐,有 IOMMU 保护)
modprobe vfio-pci
dpdk-devbind.py --bind=vfio-pci 0000:31:00.0
# 解绑回内核驱动
dpdk-devbind.py --bind=mlx5_core 0000:31:00.0
Network devices using DPDK-compatible driver
============================================
0000:31:00.0 'MT2892 Family' drv=vfio-pci unused=mlx5_core
Network devices using kernel driver
===================================
0000:31:00.1 'MT2892 Family' if=ens1f1 drv=mlx5_core unused=vfio-pci
绑定之后这张卡在 ip link 里就消失了——它已经不属于内核。
| 驱动 | 说明 |
|---|---|
| vfio-pci | 推荐,用 IOMMU 做内存隔离,更安全 |
| uio_pci_generic | 老方案,无 IOMMU 保护 |
| igb_uio | DPDK 自带,同样无 IOMMU |
③ CPU 独占与 NUMA 亲和
PMD 核要独占,不能被内核调度器打扰:
# 内核启动参数:把这些核从调度器手里隔离出来
# isolcpus=2-9 nohz_full=2-9 rcu_nocbs=2-9
# 确认网卡属于哪个 NUMA 节点
cat /sys/bus/pci/devices/0000:31:00.0/numa_node
lscpu | grep NUMA
PMD 核、大页内存、网卡必须在同一个 NUMA 节点上—— 跨 NUMA 访问的代价(PCIe 那节 讲过)会吃掉大部分收益。
最大的代价:常规工具全部失效
这一条要单独强调,因为它直接决定了运维方式:
ip link show # 看不到这张卡
ethtool -S ens1f0 # 设备不存在
tcpdump -i ens1f0 # 抓不到任何东西
ss -s # 与这张卡无关
iptables # 完全不经过
L0 教的那整套排障方法,在 DPDK 网卡上一条都用不了。
替代手段全部来自应用自己:
| 需求 | DPDK 侧的做法 |
|---|---|
| 看统计 | 应用暴露的计数器(rte_eth_stats_get) |
| 抓包 | dpdk-pdump,且应用要编译时支持 |
| 看队列状态 | 应用自己实现的管理接口 |
| 监控 | 应用主动上报,没有通用 exporter |
这是选型时最容易低估的一点。之前"网络出问题看 ethtool -S"是通用能力,
现在能不能排障完全取决于这个应用实现了多少可观测接口。
如果是自研应用,这部分工作量要算进去;如果是第三方产品, 上线前一定要验证它的可观测能力——否则出问题时你只能重启。
什么场景真的需要它
DPDK 的定位很清晰:每个包都要做复杂的用户态处理,且要跑到极致 PPS。
| 场景 | 适合吗 |
|---|---|
| 网关 / NFV(虚拟路由器、防火墙、CGNAT) | ✅ 天生适合 |
| 专用负载均衡器 | ✅ |
| 高频交易的极低延迟 | ✅ |
| 通用服务器要提升吞吐 | ❌ 用 RSS / 多队列 / 巨帧就够 |
| K8s 节点想加速 | ❌ 用 eBPF/XDP,别夺走网卡 |
| 存储或 AI 集群 | ❌ 用 RDMA,它同时解决延迟与 CPU |
和 XDP、RDMA 的关系
三条"绕开内核"的路线,各自解决不同问题:
| XDP | DPDK | RDMA | |
|---|---|---|---|
| 位置 | 内核驱动层 | 用户态 | 硬件 |
| 网卡独占 | 否 | 是 | 否 |
| CPU 参与搬运 | 是 | 是(还要轮询) | 否 |
| 常规工具 | 可用 | 失效 | 部分失效 |
| 适合 | 高速丢弃/转发 | 复杂包处理 | 大块传输、低延迟 |
关键区别:DPDK 仍然要 CPU 搬每一个包(只是搬得更高效), 而 RDMA 让网卡直接读写内存,CPU 完全不参与 (为什么需要 RDMA 那节讲过)。
所以数据密集型场景(存储、AI)选 RDMA,包处理密集型场景(网关)选 DPDK。
性能不够
├─ 先做常规优化:多队列 RSS、中断亲和、巨帧、offload ← L0 那一节
├─ 还不够 → eBPF/XDP(不夺网卡、工具还能用)
├─ 是大块传输或延迟敏感 → RDMA
└─ 是每包复杂处理且要极致 PPS → DPDK
大多数"性能不够"在第一步就解决了。 L0 闯关那台机器的问题 (多队列只开了 1 个)就是典型——上 DPDK 也救不了配置错误。
检查点
DPDK 的 PMD 核在 top 里永远显示 100% CPU。这说明什么?
网卡绑定到 vfio-pci 之后,ethtool -S 和 tcpdump 都用不了。为什么?
AI 训练集群想降低通信延迟、减少 CPU 开销。该选哪条路线?
这节课的落点
- DPDK = 内核放弃网卡,用户态轮询:没有中断、没有协议栈、没有拷贝
- 轮询在高 PPS 下胜过中断,代价是 PMD 核永久 100% 占用
- 三个前提:大页内存(启动早期分配)、VFIO/UIO 绑定、CPU 独占 + NUMA 亲和
- 绑定后网卡从
ip link消失,ethtool/tcpdump/iptables全部失效 - 可观测性完全转移给应用——第三方产品上线前必须验证这一点
- 适合网关、NFV、专用 LB、高频交易;不适合通用服务器、K8s 节点、存储与 AI 集群
- 与另两条路线的分工:XDP 丢弃转发、DPDK 每包复杂处理、RDMA 大块低延迟
- 决策顺序:常规优化 → eBPF/XDP → RDMA → DPDK,多数问题第一步就解决了
延伸资料
- Systems Performance, 2nd Edition — Brendan Gregg