早年间做DPDK开发时,我经常性的第一次部署程序时都很顺利:
- 编译通过
- 网卡绑定成功
- 程序正常启动
- 收发包也没问题
但奇怪的是,运行一段时间后,或者连续重启几次程序,就会突然启动失败。
最典型的报错是:
EAL: Cannot reserve memory EAL: Cannot init memory EAL: FATAL: Cannot init EAL更诡异的是:重启机器后又恢复正常。
第一次遇到这个问题时,很多人会怀疑:
- 内存不够?
- 程序泄漏?
- DPDK bug?
- hugepage 配置有问题?
实际上,这个问题背后,恰好藏着 DPDK 最核心的基础设施之一:hugepage 内存管理机制。
一、问题现场
某次开发一个简单转发程序:
启动命令:
./app -l 2-5 -n 4第一次启动:正常。
第二次启动:正常。
第三次:突然报错:
Cannot reserve memory程序退出。
但系统内存明明还很多:
free -h显示:
6 GB free看起来完全不合理。
二、为什么普通内存很多,DPDK 还是启动失败?
因为 DPDK 用的并不是普通 malloc 内存。
它依赖:hugepage
即:大页内存。
三、什么是 hugepage
Linux 默认内存页大小是:
4 KB而 hugepage 常见是:
- 2 MB
- 1 GB
即:更大的物理页。
为什么需要大页
因为高性能网络程序访问内存非常频繁。
如果仍使用 4 KB 页面:
会导致:
- TLB miss 增加
- page table 开销大
- DMA 映射复杂
四、DPDK 为什么必须依赖 hugepage
在 DPDK 中:以下对象都依赖 hugepage:
- mbuf pool
- ring
- memzone
- hash table
- flow table
也就是说:几乎所有核心数据结构都在 hugepage 中。
五、第一次误判:是不是内存泄漏
我最开始怀疑:程序退出时某些:rte_mbuf 没释放。导致下次启动失败。
于是排查:
- 所有 mbuf free
- 所有 mempool release
- 所有 ring destroy
结果:没问题。
六、真正原因:hugepage 并不是自动立即回收
这才是关键。
很多人误解:程序退出 → hugepage 自动完全释放。
其实不一定。
在某些情况下:DPDK 保留 hugepage 映射文件。
例如:
/dev/hugepages中的残留文件。
七、查看现场
执行:
ls /dev/hugepages发现:很多残留:
rtemap_0 rtemap_1 rtemap_2这些就是历史映射。
如果异常退出:可能没清理。
八、这就引出 EAL
DPDK 启动第一步:
初始化:EAL(Environment Abstraction Layer)
即:环境抽象层。
它负责:
- hugepage 映射
- lcore 初始化
- PCI 枚举
- memzone 管理
如果 hugepage 被占用:EAL 初始化失败。
九、为什么重启机器又好了
因为系统重启时:Linux 会重新初始化 hugepage 文件系统。残留映射被清除。
所以看起来:“重启治百病”。
其实只是:资源重置。
十、更深层原因:主进程锁文件
另一个容易忽略的点:
DPDK 会创建:
/var/run/dpdk/下的锁文件。
例如:
config mp_socket如果异常退出:可能残留。导致新实例启动失败。
十一、如何彻底排查
建议检查三处。
1. hugepage 文件
ls /dev/hugepages2. dpdk runtime
ls /var/run/dpdk3. hugepage 数量
cat /proc/meminfo | grep Huge重点:
HugePages_Free HugePages_Rsvd十二、为什么连续重启更容易触发
因为:hugepage 分配是预留模式。
程序启动时:一次申请大量。
例如:
1024 pages退出时如果未彻底清理:下一次可用页减少。
连续几次后:耗尽。
十三、一个关键知识:DPDK 不是按需 malloc
很多人以为:需要多少申请多少。
其实 DPDK 通常是:启动时预分配。一次性申请大量 hugepage。
例如:
- mempool
- ring
- tx buffer
所以启动阶段失败最常见。
十四、实际修复方法
最终采用:
方法一:手工清理
rm -rf /dev/hugepages/*方法二:清理 runtime
rm -rf /var/run/dpdk/*方法三:优雅退出
在程序中处理:
SIGINT SIGTERM保证:
rte_eal_cleanup();执行。
十五、为什么 rte_eal_cleanup 很重要
很多 sample 程序甚至没写。
直接:
return 0;看似没问题。
但生产环境中:建议明确调用:
rte_eal_cleanup();可以清理:
- hugepage mapping
- shared config
- runtime file
十六、一个经验教训
这次排查让我意识到:DPDK 不只是网络库。
它更像:用户态网络操作系统
因为它自己管理:
- 网卡
- CPU
- 内存
- DMA
- hugepage
所以很多问题不是代码 bug。而是资源管理问题。
十七、总结
为什么程序重启几次后启动失败?
根因通常不是:
- 内存不足
- 程序逻辑错误
- 网卡问题
而是:hugepage 资源未正确释放。
通过这个问题,可以深入理解 DPDK 的几个基础概念:
包括
- hugepage
- EAL
- memzone
- shared memory
- runtime file
这也是学习 DPDK 的关键:不仅要会写收发包代码。
更要理解:程序启动前到底发生了什么。