
简介《深入浅出DPDK》全书读书笔记以PDF整理了DPDK高性能网络I/O框架的知识脉络面向需要理解用户态驱动、多队列流分类、内存管理的开发者与网络工程师。资源包仅1个PDF文档约6.57MB便于系统阅读。已有3849人学习下载。笔记从传统内核中断切入逐步讲解NAPI轮询、Netmap共享数据包池、用户态驱动规避内存拷贝与系统调用的原理随后覆盖核心库大页内存、缓存池、定时器、无锁环、PMD驱动、精确/最长/通配符匹配查表、rte_eal_init初始化及三层转发流程。还结合虚拟化、NFV/SDN讨论了PCIe带宽与降低访存开销的优化思路。这份笔记既可当作《深入浅出DPDK》的浓缩导读也能作为日常开发排错时快速定位知识点的参考。1. 一份《深入浅出DPDK》的读书笔记为什么值得花一个晚上精读软中断打满 100%带宽卡在 3 Gbps 上不去翻出那份攒了很久的《深入浅出DPDK》读书笔记顺着大页配置和收包测试一步步重跑才意识到瓶颈根本不在 CPU 主频而在内核收包路径上。这份读书笔记解决的就是这件事DPDK 是什么、数据面为什么要绕开内核、怎么在一台物理机上把第一包数据收进来。它不像 API 手册那样按函数罗列而是把大页内存、UIO/VFIO、轮询模型这些零散概念串成一条可落地的路径。网上 dpdk 中文资料不算少但大多是单点讲解这份笔记的价值在于按读书顺序把原理和安装串起来读完能直接动手。适合正在做网关、接入层、NFV或者被高并发收包卡住的开发者也适合想系统学 DPDK、但不想一上来就啃源码的人——先把这份笔记读透再决定要不要深挖。2. 先立骨架数据面绕开内核只是表象读书笔记里的三条主线才是重点读这份笔记第一件要做的事不是从头抄到尾。大多数人翻几页就卡在“轮询与中断”的对比上以为 DPDK 的核心就是“不用中断”。实际上这只是表象。笔记真正反复出现的是三条主线收包路径、内存模型、设备隔离。先抓住这三条线后面每一章都能挂上去读起来会顺很多。2.1 轮询代替中断性能来源和它换来的 CPU 代价为什么中断在高速小包下撑不住万兆口满速率每秒约 1488 万个小包每个包一次中断就意味着每秒钟上千万次上下文切换和 cache 污染。CPU 大量时间在“跑去处理中断再跑回来”真正用在收包上的时间很少。中断合并NAPI能缓解一部分但收包延迟会抖动。DPDK 选择轮询一个核固定转圈检查收包队列省掉切换包处理时间更稳定。这是性能的第一来源但不是免费的。轮询的代价是“没有包时也在消费 CPU”所以笔记里把 CPU 隔离放在很靠前的位置。实际操作上要在内核启动参数里加isolcpus2,3让这两个核不被调度器随手分配任务还要关掉irqbalance否则中断随时会把线程赶来赶去。这一条经常被当成“性能玄学”跳过实际影响比 DPDK 的任何编译选项都大。验证方法也简单cat /proc/interrupts看网卡中断是否落在隔离核以外再用top观察 DPDK 进程是不是稳定占满指定核。如果发现进程在一个核上但中断和软中断还在别的核上跳动说明前面几步没做。2.2 收包路径与内存模型读笔记时要抓的两根主线收包路径这条主线从头到尾不经过内核协议栈也不经过 socket。链路是网卡收到包DMA 把数据写到内存驱动程序把这段内存包装成mbuf放入无锁 ring应用在另一个核上从 ring 里取走。笔记里每讲一个组件都是在为这条路径上的某个节点服务。读的时候先在纸上画出这条线再往上面挂细节不然很容易迷失在函数名里。内存模型是第二条主线。DMA 要求物理连续所以大页内存是起点包要被反复分配和释放所以有mempool每个包的数据区要预留头部空间并做对齐所以有mbuf结构。两条主线的合流点就是rte_eth_rx_burst返回一个mbuf数组。这份笔记之所以总在讲驱动之前先讲内存就是因为收包的本质是“把数据放到一段预先规划好的内存里”而不是“收到数据再临时找地方”。读的时候有个技巧把每个章节标题挂到线上。比如大页是“让 DMA 有地方落脚”UIO 是“让用户态能访问设备”ring 是“在核与核之间传递包”。挂完钩再决定要不要深挖源码效率高很多。不建议一开始就从rte_ring的源码读起那会陷入实现细节忘了它到底解决什么问题。2.3 UIO 还是 VFIO先做完三个检查再选型两个驱动框架的作用都是把设备直接交给用户态。UIO 通过 sysfs 把设备内存映射给用户态模块少、实现简单但能力有限没有设备隔离。VFIO 依赖 IOMMU 做 DMA 重映射和隔离安全性好支持中断和直通但要求 CPU 和 BIOS 都开了相关虚拟化特性。怎么选学习阶段用 UIO 更省事生产环境、要做多租户隔离、要配合虚拟机直通网卡时考虑 VFIO。不少人在笔记本上直接绑 VFIO 翻车原因就是没开 VT-d这时退回 UIO 就是后悔药。# 选驱动前先做三个检查 lscpu | grep -E vmx|svm # CPU是否支持虚拟化扩展 cat /proc/cmdline | tr \n | grep -E intel_iommuon|amd_iommuon # IOMMU是否已在内核启动参数里开启 lspci -nn | grep -i ethernet # 确认网卡的PCI地址和设备型号第一条没有vmx或svmVFIO 基本不用考虑第二条没有intel_iommuonBIOS 开了也白搭第三条用来确认你要绑的是哪块网卡避免把管理口绑走。把这三条跑完再选驱动能省掉后面一整个晚上的排查时间。对比项UIO (igb_uio)VFIO (vfio-pci)内核模块igb_uio需单独加载vfio-pci内核自带中断支持轮询为主中断支持弱支持中断配合更好的轮询模型设备隔离无IOMMU 隔离DMA 重映射IOMMU 依赖不依赖强依赖学习成本低中典型场景学习、单机收包生产、虚拟化直通、多租户提示绑定网卡前先在系统里记录这块网卡的 IP 和管理方式。这条后面讨论绑定网卡时还会再踩一次。3. dpdk安装后的最小验证环境从大页配置到 testpmd 收包骨架立住之后下一步是“真的收到一包”。dpdk 安装完成后第一件事不是写业务代码而是先搭一个最小验证环境。这里假设已经用常见方式编译好了 DPDK源码 meson 构建或者发行版打包都可并且有 root 权限。下面三步做完就能看到第一包数据从网卡流进 DPDK。3.1 大页内存配置让 DMA 有连续物理内存可写网卡 DMA 要把数据写进内存需要物理上连续的地址另外 4KB 普通页在大量收包时会产生大量 TLB miss。大页内存同时解决这两个问题默认用 2MB 大页就够了。配置方式不是临时echo一下而是写进 sysctl 配置让系统在开机时预留。# 预留 1024 个 2MB 大页共 2GB写入配置并立即生效 echo vm.nr_hugepages1024 | sudo tee /etc/sysctl.d/99-hugepages.conf sudo sysctl -p /etc/sysctl.d/99-hugepages.conf # 确认是否真的分配成功 grep -E HugePages_Total|HugePages_Free /proc/meminfo1024是个适合学习阶段的值2GB 预留给 DPDK剩余内存还能跑系统。如果机器内存小改成 512 也够跑 testpmd如果后面要上 1GB 大页需要在内核启动参数里加hugepagesz1G然后 sysctl 里写vm.nr_hugepages2。注意大页的内存是直接预留的配置后不重启系统可能因为内存碎片凑不出连续空间所以最稳的做法是写入/etc/sysctl.d/后重启一次让内核在开机阶段预留干净内存。接着要挂载 hugetlbfsDPDK 启动时会从这个文件系统里映射大页# 挂载大页文件系统DPDK 从这里取内存 sudo mkdir -p /mnt/huge sudo mount -t hugetlbfs -o pagesize2M nodev /mnt/huge还有一件事容易被忽略透明大页THP。它是给通用场景用的后台合并机制和 DPDK 自己管理大页的方式冲突运行时会带来不确定的缺页行为。学习阶段建议关掉# 关闭透明大页避免和 DPDK 内存管理抢地盘 echo never | sudo tee /sys/kernel/mm/transparent_hugepage/enabled这一步不做之后的报错会非常诡异/proc/meminfo里明明有大页DPDK 却报无法分配内存。先把大页和 THP 搞定后面很多问题都不会出现。3.2 绑定网卡到用户态驱动一条命令和一个回退方案大页就绪后要把网卡从内核驱动切到用户态驱动。使用 UIO 方式先加载igb_uio模块再用 DPDK 自带的dpdk-devbind.py脚本绑定。这里的顺序很重要先确认网卡没在跑业务再绑定。# 加载 UIO 内核模块 sudo modprobe uio sudo insmod /path/to/dpdk/build/kernel/linux/igb_uio/igb_uio.ko # 查看当前网卡状态确认 PCI 地址 sudo dpdk-devbind.py --status # 绑定把 PCI 地址上的内核驱动换成 igb_uio sudo dpdk-devbind.py --bindigb_uio 0000:02:00.0 # 再查一次状态确认驱动栏变成 igb_uio sudo dpdk-devbind.py --status0000:02:00.0是 PCI 地址从lspci或--status的输出里抄。绑定前务必用ip addr show看下这块网卡是不是管理口--status也会显示当前内核驱动如果是ixgbe、e1000这类驱动说明这块卡还在被内核使用。绑卡后原来的 IP 会消失SSH 会话如果走了这张网卡会当场断连。这是血泪经验。回退方案同样是脚本一行搞定# 回退到内核驱动后续可重新配 IP sudo dpdk-devbind.py --bindixgbe 0000:02:00.0 sudo ip link set up dev eth0ixgbe要换成你这块网卡原本的内核驱动名回退后原 IP 一般需要重新配置。绑定这种事别在远程会话里对着管理网卡做真要做就用本地控制台或者先准备好另外一条管理通路。3.3 testpmd 跑通收包验证环境可用的最小命令绑定成功后用testpmd做冒烟测试。它是 DPDK 自带的收发包测试程序不用写一行 C 代码就能验证大页、UIO、网卡整条链路是否通畅。# 启动 testpmd使用 0、1 两个逻辑核2 个内存通道 sudo ./build/app/dpdk-testpmd -l 0-1 -n 2 -- -i --total-num-mbufs65535-l 0-1指定使用的核心列表-n 2是内存通道数必须和主板实际配置一致一般 2 或 4 是安全的这个值填错会导致启动失败。--total-num-mbufs65535是预分配的 mbuf 数学习阶段用默认或这个值都行。进入交互模式后执行testpmd start testpmd show port stats allstart让网卡开始收包并按默认 io 模式转发show port stats all会打印每个端口统计。期望看到RX-packets持续增长如果对端没发包可以再开一个终端用dpdk-testpmd从同一个网卡的另一端口发流或者用简单工具打流。如果统计里RX-packets一直为 0先看testpmd show port info all确认端口状态是 UP再回去查绑定和大页。退出时执行quit即可。testpmd 参数作用常见误设-l 0-1指定逻辑核核号和 NUMA 不对齐影响吞吐-n 2内存通道数填 1 或 8 启动直接失败-i进入交互模式不加则启动后直接转发不好观察--total-num-mbufs预分配 mbuf 数量太小导致高吞吐下丢包4. 读书笔记里的三个机制mempool、ring、mbuf 怎么在运行中验证骨架和环境都有了接下来要解决的是“为什么这么设计”。笔记后半部分的高频主角是 mempool、ring、mbuf。这三个机制最难的地方不在概念而在“跑起来怎么验证”。只看不跑过了两周就忘。4.1 mempool为什么“分配一次、反复复用”才是性能关键如果每个包都走malloc/free锁竞争和内存碎片会直接杀死收包性能。mempool 的解法是预先分配一大块内存取包和还包都走池子配合每核本地缓存绝大多数场景连锁都不用碰。这也是为什么rte_pktmbuf_pool_create的cache_size参数值得仔细调它决定了每个核本地缓存多少个 mbuf太小会有频繁回池请求太大浪费内存。testpmd 里可以直接观察池子消耗testpmd show mempool mem0这行命令在 testpmd 交互界面里查看名为mem0的 mbuf 池状态输出里有used_count和free_count。开始收包后used_count会上升并稳定在一个水位说明 mbuf 在收发路径上被反复复用。如果free_count降到 0说明池子不够用位置高一点的--total-num-mbufs就是给你的后悔药。这块池子分配在 HugePages 上这也是为什么前面说大页是整个内存模型的起点。4.2 ringSPSC/MPSC 的边界和笔记里常被忽略的细节rte_ring 是无锁环形队列但“无锁”是有条件的。单生产者单消费者模式下无锁且几乎零开销多生产者参与时入队要处理竞争多消费者时出队也要处理竞争。笔记里最常见的误区是把“无锁”理解成“随便并发”。真实情况是能设计成单生产者单消费者就别轻易用多生产者模式成本不在功能上而在性能上。还有一块容易被略过ABA 问题。在 32 位环境或旧版本 DPDK 里无锁队列的头部操作会涉及到指针的重复比较稍有不慎就会取出已释放的节点。新版 DPDK 通过扩展头部字段规避了这个问题笔记未必跟到那么细。真要验证 SPSC 和 MPMC 的性能差异最直接的办法是用rte_ring_create配合rte_ring_enqueue/dequeue写个一百行内的基准测试分别跑单生产和双生产场景对比吞吐和时延。这个实验做完对“无锁”三个字的理解会比读十篇文章都深。4.3 mbuf 对齐与 cachelinefalse sharing 是最隐蔽的掉速点mbuf 不只描述一个包它还承担了对齐和头部预留的职责。结构上要求对齐到 cacheline通常 64 字节头部预留 headroom这样协议栈每加一层头不需要搬移整个数据。笔记里如果只强调“headroom 预留”很容易漏掉另一个更隐蔽的问题false sharing。多核场景下两个核如果各自写同一个结构体的不同字段而这些字段恰好落在同一条 cacheline 上一次写入就会导致整条 cacheline 在核间反复失效。表现很魔幻单核跑性能正常加到多核反而掉速。RTE 提供的__rte_cache_aligned就是干这个用的/* 每个核一个统计变量按 cacheline 对齐避免 false sharing */ struct lcore_stats { uint64_t rx_pkts; uint64_t tx_pkts; } __rte_cache_aligned; /* 在收发路径里stats[lcore].rx_pkts; */不加对齐时两个核的rx_pkts可能落在同一条 cacheline 上互相拖累。实际排查时可以先用perf stat看 cache-misses如果多核吞吐明显低于单核累加优先怀疑 false sharing而不是去调队列长度。这一条在笔记里往往只有一句话但它在真实业务里能让人排查一整天。5. 上手踩坑记录配置看着没问题却收不到包的 5 个排查点环境搭完真正的考验才来。下面这些坑大半不是 DPDK 本身的问题而是环境准备和认知偏差。每一条都按“现象 → 原因 → 解决”写照着排查能省下大量时间。5.1 HugePages 配置了testpmd 却报无法分配内存现象testpmd 一启动就报EAL: Cannot get hugepage或者启动后收包异常卡顿。原因/proc/meminfo里HugePages_Total是 0预留没成功。常见情况是系统运行一段时间后内存碎片化echo大页数量时内核凑不出连续内存或者配置写到了临时参数里重启后失效。解决把vm.nr_hugepages写进/etc/sysctl.d/后重启开机阶段预留最干净。还有另一种情况多 NUMA 节点机器默认从 node 0 分配内存node 0 剩余不足也会报错此时用--socket-mem给每个节点显式指定大页数量。另外容器和 cgroup 的内存限制会挡住大页申请学 DPDK 尽量用物理机或特权容器。5.2 绑定网卡后 SSH 断连现象执行dpdk-devbind.py --bindigb_uio后终端卡住连接断开。原因把正在跑管理业务的网卡绑给了用户态驱动原来的 IP 随内核驱动一起卸载了。解决绑卡前先ip addr show确认这块网卡不是你的 SSH 管理口如果只有一块可用网卡先给另一块网卡配好管理地址再动手。绑卡操作最好在本地控制台或 IPMI 里做。回退命令要记熟dpdk-devbind.py --bind原驱动名加上ip link set up。这条是我踩得最实在的一次当时在机房折腾到半夜只能跑过去插显示器。5.3 VFIO 绑定报权限或设备找不到现象执行--bindvfio-pci时提示Operation not permitted或者No such device。原因IOMMU 没开。要么 BIOS 里 VT-d 是 disabled要么内核启动参数里少了intel_iommuon要么两者都没配。还有一类是权限问题普通用户不在 vfio 允许的用户组里。解决按 2.3 节的三条命令逐项检查BIOS 打开 VT-d内核参数加上intel_iommuon或amd_iommuon权限不够就把用户加进 vfio 组或者临时用 root 验证。对于没有 IOMMU 的机器直接退回igb_uio学习阶段性能差距可以忽略。5.4 单核吞吐上不去CPU 看起来忙但包没少现象top看 DPDK 进程占满一个核但网卡吞吐只有两三百万 pps和网卡标称相差很远。原因CPU 隔离没做。irqbalance还在跑内核调度器把其他任务塞到 DPDK 用的核上或者收包核和网卡不在同一个 NUMA 节点访问内存走远端路径延迟翻倍。解决内核启动参数加isolcpus2,3并关闭irqbalance然后把 DPDK 进程绑定到隔离核用--socket-mem指定与网卡同节点的内存。网卡挂在哪个 NUMA 节点看/sys/bus/pci/devices/0000:02:00.0/numa_node就能确认。这个坑排查顺序错了会很痛苦先查隔离再查 NUMA不要一上来就调 DPDK 编译选项。5.5 按笔记抄的代码段编译不过现象照着读书笔记粘贴rte_eth_dev_start、rte_eth_rx_queue_setup等代码编译时大量报错不是未声明就是函数签名不对。原因DPDK 版本差异太大。笔记可能基于 20.11 或更早而当前环境装的是 23.x很多 API 的返回值、参数顺序、字段名在不同版本里都有调整。解决以当前安装版本的头文件为准用grep在/usr/include或 DPDK 源码里搜函数原型直接看定义再改代码。不要盲目加-m编译选项那不是根本问题。读书笔记的价值在于给思路和框架不是给你一份能直接交差的代码。这个观念摆正了后面学习会顺利很多。6. 把读书笔记变成排查手册一张验证清单和两个习惯读完 PDF 只是开始真正留下的是这套验证动作。我现在每次调 DPDK 环境都走同一张清单十分钟内能定位是环境问题还是代码问题。整份笔记读完后建议把它压缩成下面这张表贴在工位旁边验证项命令期望结果大页已预留grep HugePages_Total /proc/meminfo数值等于配置数如 1024IOMMU 状态选 VFIO 时cat /proc/cmdline含intel_iommuon网卡绑定状态dpdk-devbind.py --status驱动为igb_uio或vfio-pci最小收包链路可用testpmd 中show port stats allRX-packets持续增长NUMA 一致cat /sys/bus/pci/devices/.../numa_node与lscpu中 lcore 所在节点一致两个习惯对你后续看任何底层技术书都有用。第一个改内核参数前先记录 baseline。把/proc/meminfo、--status、testpmd 的 stats 存一行日志避免调完参数后不知道是变好了还是变差了。第二个每读完一节写一个三行验证动作。读完大页章节就写个检查大页的脚本读完 ring 章节就写条 testpmd 收包统计命令。“读过”和“跑通”之间差的往往只是一条命令的执行。前两年我读这类笔记总是“眼睛会了手不会”后来改成“每个机制必须有一个对应的验证动作”效率高了很多。最贵的是时间《深入浅出DPDK》这份笔记能帮你把坑提前标出来剩下的就是自己动手把清单里的每一项真实跑一遍。希望帮到你。本文还有配套的精品资源点击获取