☰
CentOS 7 上编译部署 DPDK 19.11 完整指南:从环境搭建到性能调优
2026/10/17 12:47:46 网站建设 项目流程

1. 为什么还要在 CentOS 上折腾 DPDK 19.11

先把话说在前头:DPDK 的版本迭代很快,现在新版本已经出到 20 以后很多了,但 19.11 这个版本在不少存量项目里依然是主力。原因很实在——它是 LTS 版本,很多网卡厂商的驱动适配、不少自研数据面程序的接口都是照着这个版本写的。你如果接手一个跑了几年的转发程序,贸然升级到新版本,API 变动、依赖库版本冲突能让你调上一整天。所以在 CentOS 上把 19.11 编译跑通,是一项很实用的基本功。

这篇内容面向的是谁?一类是刚接触 DPDK、需要在服务器上把环境搭起来跑通第一个 helloworld 或者 l2fwd 的同学;另一类是老手,手头有台 CentOS 机器,想快速把 19.11 的编译链路重新捋一遍,省得每次翻旧笔记。我会把整个流程拆成“环境准备、依赖安装、编译配置、大页内存、绑定网卡、跑测试程序”这几块,每一块都讲清楚为什么这么做,以及我踩过的坑。

需要提前说明的是,DPDK 是数据平面开发套件,它绕过内核协议栈直接操作网卡,所以对硬件、内核版本、内存配置都有要求。你在虚拟机里跑和物理机上跑,体验完全不一样。我下面会以物理机 CentOS 7 为主,虚拟机的情况单独提。

2. 环境准备与依赖梳理

2.1 系统版本与内核的匹配关系

CentOS 7 自带的内核是 3.10 系列,这个内核跑 DPDK 19.11 是没问题的,官方文档里明确支持。但要注意一点:如果你用的是 CentOS 8 或者 Stream,内核是 4.18 起步,编译本身没问题,但有些老网卡的 igb_uio 驱动在 4.18 上编译会报错,需要打补丁或者换用 vfio-pci。我个人的建议是,如果只是学习验证,CentOS 7.6 到 7.9 是最省心的选择,内核不用动,直接编译就能过。

先确认一下系统信息:

cat /etc/redhat-release uname -r

输出应该是类似CentOS Linux release 7.9.2009 (Core)和3.10.0-1160.el7.x86_64。如果你的内核是 3.10.0-1127 之前的版本,建议先yum update一下,因为早期 3.10 内核对大页内存和 vfio 的支持有些小问题。

提示:不要用 CentOS 6,glibc 版本太低,DPDK 19.11 的 meson 构建系统跑不起来。CentOS 6 的 gcc 是 4.4,连 C11 标准都不完整支持。

2.2 依赖包一次性装齐

DPDK 19.11 同时支持 make 和 meson 两套构建系统。make 是老路子,meson 是官方推荐的新方式。我建议两套都装上依赖,因为有些老项目还在用 make,你保不齐哪天就要切回去。

用 yum 装依赖,一条命令搞定:

yum install -y gcc gcc-c++ make meson ninja-build numactl-devel \ kernel-devel-$(uname -r) kernel-headers-$(uname -r) \ libpcap-devel openssl-devel zlib-devel \ python3 python3-pip git wget

这里解释几个关键依赖的作用。numactl-devel是给 NUMA 架构用的,多路服务器上 DPDK 要绑定内存到特定 NUMA 节点,没这个库编译会报错。kernel-devel和kernel-headers必须和当前运行的内核版本完全一致,否则编译 igb_uio 内核模块时会提示找不到头文件。libpcap-devel是给 pcap 驱动用的,如果你要用 DPDK 抓包对比,这个必须有。

装完之后验证一下 meson 和 ninja 的版本:

meson --version ninja --version

CentOS 7 自带的 meson 版本可能偏低,如果低于 0.47,用 pip3 升级:

pip3 install --upgrade meson ninja

注意:pip 升级后的 meson 可能装在/usr/local/bin下,而系统里/usr/bin/meson还是老版本。用which meson确认一下路径,必要时把/usr/local/bin放到 PATH 前面。

2.3 关闭 SELinux 和防火墙的干扰

这一步不是必须的,但能省掉很多莫名其妙的权限问题。SELinux 会拦截 DPDK 对大页内存和网卡设备的访问,表现就是程序跑起来报Permission denied,但你用 root 又看不出来哪里不对。

临时关闭:

setenforce 0

永久关闭就改/etc/selinux/config,把SELINUX=enforcing改成SELINUX=disabled,然后重启。防火墙对 DPDK 本身没影响,因为 DPDK 不走内核网络栈,但如果你要跑 testpmd 做回环测试,防火墙规则可能会干扰你判断结果,建议也一并关掉。

3. 获取源码与编译配置

3.1 下载 DPDK 19.11 源码

官方源码可以从 dpdk.org 的下载页拿,但国内访问有时候慢。我一般直接用 git 克隆指定分支:

git clone -b v19.11 https://github.com/DPDK/dpdk.git dpdk-19.11 cd dpdk-19.11

如果你网络环境不方便用 git,也可以下载 tar.xz 包解压。注意 19.11 后面还有 19.11.1 到 19.11.14 这些稳定版更新,建议直接用最新的 19.11.14,bug 修复更全。分支名就是v19.11.14。

下载完之后看一眼目录结构,关键目录有这几个:lib是核心库,drivers是网卡驱动,app是示例程序,config是编译配置文件,usertools是辅助脚本。后面编译配置主要跟config目录打交道。

3.2 用 meson 构建还是用 make 构建

这是很多人纠结的第一个点。我直接给结论:新项目用 meson,老项目维护用 make。meson 构建速度快,依赖管理清晰,而且 19.11 之后官方主推 meson。make 的好处是配置直观,改config/defconfig_x86_64-native-linuxapp-gcc就行,很多老教程都是这么写的。

先讲 meson 的路子。在源码根目录建一个构建目录:

mkdir build && cd build meson .. -Dexamples=l2fwd,l3fwd -Dplatform=native

-Dexamples指定要编译哪些示例程序,不指定的话默认不编译示例。-Dplatform=native让编译器针对当前 CPU 做优化,生成的二进制性能更好,但换到别的 CPU 上可能跑不了。如果你要跨机器部署,改成-Dplatform=generic。

配置完成后编译:

ninja

编译过程大概五到十分钟,取决于机器性能。编译完成后,库文件在build/lib下,示例程序在build/examples下。

再讲 make 的路子,作为对照。先设置环境变量:

export RTE_SDK=$(pwd) export RTE_TARGET=x86_64-native-linuxapp-gcc make config T=$RTE_TARGET make -j$(nproc)

make config会在build目录下生成配置文件。-j$(nproc)用满所有 CPU 核心并行编译,能快不少。make 构建的产物在$RTE_SDK/$RTE_TARGET目录下。

实操心得:如果你编译时报numa.h: No such file or directory,说明numactl-devel没装或者装错了架构。用rpm -qa | grep numactl确认一下。另一个常见错误是cannot find -lrte_eal,这通常是 RTE_TARGET 设错了,检查一下是不是写成了x86_64-native-linux-gcc少了app。

3.3 编译选项的取舍

有几个编译选项值得单独说。第一个是CONFIG_RTE_BUILD_SHARED_LIB,默认是静态库。如果你要自己写程序链接 DPDK,静态库省事,不用配LD_LIBRARY_PATH。但如果你要多个程序共享 DPDK 库,或者做插件式加载,就开共享库。

第二个是CONFIG_RTE_LIBRTE_PMD_PCAP,这个默认关闭。开了之后可以用 pcap 驱动,把 DPDK 程序接到普通网卡甚至抓包文件上,调试特别方便。我强烈建议打开,编译时加-Dc_args=-DRTE_LIBRTE_PMD_PCAP或者在 meson 配置里加-Denable_drivers=net/pcap。

第三个是CONFIG_RTE_MAX_MEMSEG,默认值 256。如果你要处理大量小内存段,或者跑很多个 DPDK 进程,这个值可能不够,会报Failed to allocate memory segment。改大到 1024 或者 2048 试试。

4. 大页内存配置与网卡绑定

4.1 大页内存的原理和配置

DPDK 性能高的核心原因之一就是用了大页内存。普通内存页是 4KB,大页是 2MB 或者 1GB。页越大,TLB 命中率越高,地址转换开销越小。你可以把 TLB 想象成一个地址簿,4KB 页需要记很多条,2MB 页条目少得多,查起来快。

配置 2MB 大页,先看当前状态:

cat /proc/meminfo | grep Huge

然后分配大页。假设你要给 DPDK 留 4GB 内存,2MB 一页就是 2048 页:

echo 2048 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

永久生效要改/etc/sysctl.conf,加一行vm.nr_hugepages = 2048,然后sysctl -p。但更推荐用内核启动参数,在/etc/default/grub的GRUB_CMDLINE_LINUX里加default_hugepagesz=1G hugepagesz=1G hugepages=4,这样开机就分配 1GB 大页。1GB 大页性能比 2MB 更好,但需要 CPU 支持,而且分配后不能动态调整。

注意:大页内存是预留的,普通程序用不了。你分配了 4GB 大页,free命令看到的内存会少 4GB,这是正常的。如果分配失败,检查一下是不是内存不够,或者 NUMA 节点上内存分布不均。多路服务器上,每个 NUMA 节点都要单独分配。

挂载大页文件系统:

mkdir -p /mnt/huge mount -t hugetlbfs nodev /mnt/huge

永久挂载就写进/etc/fstab:nodev /mnt/huge hugetlbfs defaults 0 0。

4.2 网卡绑定到 DPDK 驱动

DPDK 要接管网卡,必须把网卡从内核驱动解绑,绑定到igb_uio或者vfio-pci。先看网卡列表:

cd dpdk-19.11/usertools ./dpdk-devbind.py --status

输出会列出所有网络设备,包括内核驱动和 DPDK 驱动的绑定情况。找到你要用的网卡,记下 PCI 地址,比如0000:03:00.0。

加载 igb_uio 模块:

modprobe uio insmod /path/to/dpdk-19.11/build/kmod/igb_uio.ko

如果你用 meson 构建,igb_uio.ko 在build/kmod下;用 make 构建就在$RTE_TARGET/kmod下。加载后绑定网卡:

./dpdk-devbind.py --bind=igb_uio 0000:03:00.0

再跑一次--status确认绑定成功。这时候内核就看不到这张网卡了,ifconfig里也不会显示。

实操心得:绑定前一定要确认这张网卡没在用,比如没配 IP、没跑业务。绑定后如果发现绑错了,用--bind=ixgbe或者对应的内核驱动名绑回去。另外,虚拟机里的网卡通常是 virtio 或者 e1000,virtio 需要 vfio-pci 而不是 igb_uio,e1000 可以用 igb_uio。如果你在 VMware 里跑,网卡是 e1000e,绑定 igb_uio 后可能收不到包,这是驱动兼容性问题,换 vfio-pci 试试。

4.3 vfio-pci 的配置方法

vfio-pci 比 igb_uio 更现代,支持 IOMMU,安全性更好。但配置稍微麻烦一点。先开 IOMMU,在 grub 启动参数里加intel_iommu=on iommu=pt,重启。然后加载 vfio 模块:

modprobe vfio-pci

绑定网卡:

./dpdk-devbind.py --bind=vfio-pci 0000:03:00.0

vfio-pci 需要网卡所在的 IOMMU 组里所有设备都绑定到 vfio,否则会报错。用ls /sys/bus/pci/devices/0000:03:00.0/iommu_group/devices/看组里有哪些设备,全部绑上。

5. 跑通第一个 DPDK 程序

5.1 用 helloworld 验证环境

helloworld 是最简单的 DPDK 程序,只初始化 EAL 然后打印每个核心的 hello。跑之前设置环境变量:

export RTE_SDK=/path/to/dpdk-19.11 export RTE_TARGET=x86_64-native-linuxapp-gcc

然后运行:

./build/examples/helloworld -l 0-3 -n 4 --socket-mem 1024,0

参数解释:-l 0-3指定用 0 到 3 号逻辑核,-n 4指定内存通道数,--socket-mem 1024,0指定每个 NUMA 节点分配的内存,第一个节点 1024MB,第二个 0。如果你机器是单路,写--socket-mem 1024就行。

看到每个核心打印hello from core X就说明环境通了。如果报EAL: No free hugepages reported,回去检查大页配置。如果报EAL: Cannot init memory,可能是大页内存不够或者 NUMA 配置不对。

5.2 用 l2fwd 做二层转发测试

l2fwd 比 helloworld 复杂一点,它从一块网卡收包,从另一块网卡发出去,做二层转发。跑之前需要至少两块绑定了 DPDK 驱动的网卡。

./build/examples/l2fwd -l 0-1 -n 4 --socket-mem 1024 -- -p 0x3

-p 0x3是端口掩码,0x3 表示用端口 0 和端口 1。程序跑起来后会周期性打印统计信息,包括收发包数量、丢包数量。你可以用另一台机器接上网线打流,或者用 pcap 驱动从文件读包。

提示:l2fwd 默认不做 MAC 地址学习,它只是简单地把从端口 0 收到的包从端口 1 发出去,反之亦然。如果你发现包发出去了但对面收不到,检查一下网线是不是交叉线,或者交换机端口是不是配了隔离。

5.3 testpmd 的交互式调试

testpmd 是 DPDK 自带的瑞士军刀,功能比 l2fwd 丰富得多。启动:

./build/app/testpmd -l 0-3 -n 4 --socket-mem 1024 -- -i --portmask=0x3

-i是交互模式,进去之后可以敲命令。常用的有start开始转发,stop停止,show port stats all看统计,set fwd mac切换转发模式。testpmd 支持 mac、io、rxonly、txonly 等多种转发模式,调试网卡收发包问题特别有用。

我一般用 testpmd 做两件事:一是验证网卡绑定是否正确,show port info 0能看到网卡的 MAC、速率、队列数;二是做性能摸底,start tx_first然后看show port stats all的 pps 和带宽。

6. 常见问题与排查技巧实录

6.1 编译阶段常见报错

编译阶段最容易卡在依赖和内核头文件上。我整理了一个速查表:

报错信息原因解决方法
numa.h: No such file缺 numactl-develyum install numactl-devel
cannot find -lrte_ealRTE_TARGET 设错检查是否写成x86_64-native-linuxapp-gcc
kernel headers not foundkernel-devel 版本不匹配装与uname -r一致的 kernel-devel
meson version too oldmeson 版本低于 0.47pip3 install --upgrade meson
igb_uio.ko compile error内核 4.18+ 不兼容换 vfio-pci 或打补丁

还有一个隐蔽的坑:如果你之前编译过其他版本的 DPDK,环境变量RTE_SDK和RTE_TARGET可能还指向旧路径。编译前用echo $RTE_SDK确认一下,不对就重新 export。

6.2 运行阶段常见报错

运行阶段的报错更让人头疼,因为往往没有明确提示。常见的有这几类:

第一类是内存相关。EAL: No free hugepages reported in hugepages-2048kB说明大页没分配或者分配的数量不够。EAL: Cannot get hugepage information通常是 hugetlbfs 没挂载。EAL: memzone reserve failed可能是内存段碎片太多,试试加大CONFIG_RTE_MAX_MEMSEG。

第二类是网卡相关。EAL: Error - exiting with code: 1后面跟着Cause: No Ethernet devices found,说明网卡没绑定或者绑定失败。EAL: Driver cannot attach the device可能是驱动不匹配,比如用 igb_uio 绑了 virtio 网卡。

第三类是权限相关。EAL: Permission denied检查是不是没用 root 跑,或者 SELinux 没关。EAL: Cannot open /dev/hugepages检查 hugetlbfs 挂载点的权限。

实操心得:遇到报错先看 EAL 的日志,它会把初始化过程每一步都打出来。日志级别用--log-level=debug调到 debug,能看到更详细的信息。另外,--syslog=local0可以把日志写到 syslog,方便集中排查。

6.3 性能不达预期的排查思路

程序跑通了但性能上不去,这是另一个层面的问题。先确认几个基础点:CPU 是不是绑定了正确的核心,有没有开超线程,NUMA 节点是不是和网卡在同一个 socket。用lscpu看 CPU 拓扑,用cat /sys/bus/pci/devices/0000:03:00.0/numa_node看网卡在哪个 NUMA 节点。

如果这些都没问题,再看是不是内存带宽瓶颈。用dmidecode -t memory看内存频率和通道数,DPDK 的-n参数要和实际内存通道数一致,否则性能会打折。比如双通道内存你写-n 4,反而可能因为交错访问变慢。

还有一个容易被忽略的点:BIOS 里的电源管理。如果 CPU 开了节能模式,频率会动态调整,DPDK 的轮询模式会让 CPU 一直跑在高频,但 BIOS 可能限制功耗。进 BIOS 把电源模式改成 performance,关掉 C-state 和 P-state。

7. 从编译到部署的完整链路梳理

把上面这些串起来,一个完整的流程是这样的:装依赖、下源码、meson 配置、ninja 编译、配大页、挂 hugetlbfs、加载 igb_uio、绑定网卡、跑 helloworld 验证、跑 l2fwd 或 testpmd 做功能测试、最后部署自己的程序。每一步都有坑,但踩过一遍之后就有肌肉记忆了。

我自己的习惯是写一个 setup 脚本,把环境变量、大页配置、模块加载、网卡绑定都放进去,换机器的时候直接跑脚本,省得一条条敲。脚本里加一些检查,比如大页数量对不对、网卡绑定成没成功,失败就报错退出。这样能避免“以为配好了其实没配好”的情况。

另外,如果你要在容器里跑 DPDK,情况会更复杂。容器需要 privileged 权限,大页和网卡设备要挂载进去,/dev/hugepages和/dev/uioX都要映射。我试过在 Docker 里跑 testpmd,能跑通但性能有损耗,生产环境还是建议直接跑在宿主机上。

最后分享一个小技巧:如果你只是想验证 DPDK 程序逻辑,不想折腾网卡绑定,可以用 pcap 驱动。编译时打开 pcap 支持,运行时用--vdev=net_pcap0,rx_pcap=input.pcap,tx_pcap=output.pcap,这样 DPDK 程序就从 pcap 文件读包、往 pcap 文件写包,完全不碰真实网卡。调试协议解析、转发逻辑的时候特别方便,我在开发阶段基本都用这个方式。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询