首页
关于
Search
1
PacketForge 博客部署文档
16 阅读
2
深入 SONiC 与 SAI:从命令行到芯片的完整数据之旅
14 阅读
3
DPU 与 CPO:AI 时代的网络基础设施革命
11 阅读
4
Broadcom芯片
10 阅读
5
DPDK高性能网络
10 阅读
默认分类
交换芯片
高性能网络
AI算力网络
WebSocket高性能项目
DPDK 成长路线
①性能工程基础
②DPDK 源码深读
③用户态协议栈
④软件交换与 SONiC
⑤PCIe、虚拟化与 DPU
⑥面试与复盘
登录
Search
标签搜索
DPDK→DPU 学习路线
P0
P1
P2
P3
P5
PCIe
P4
P6
libmtcp
NUMA
mTCP
OVS
SONiC
SAI
DPU
perf
gdb
EAL
mempool
PacketForge
累计撰写
53
篇文章
累计收到
3
条评论
首页
栏目
页面
关于
最新文章
热门文章
评论最多
点赞最多
2026-08-18
P1-04 · mbuf:包的一生都住在里面
对应路线图:Phase 1 (Day 22-49) | DPDK 24.11.6 LTS | 内核对照 v6.6代码出处:source/dpdk-stable-24.11.6/lib/mbuf/,引用均标 文件:行号上一篇:P1-03 ring | 下一篇:P1-05 PMD一句话定位mbuf 之于 DPDK,等于 sk_buff 之于内核:数据面所有结构都围绕它转。mbuf 的设计处处为 cache line 优化:一个包的处理全程只碰 1-2 个 cache line。真实布局(lib/mbuf/rte_mbuf_core.h:467-674)第一缓存行的真实定义(节选,rte_mbuf_core.h:468-518):struct __rte_cache_aligned rte_mbuf { void *buf_addr; /**< Virtual address of segment buffer. */ #if RTE_IOVA_IN_MBUF alignas(sizeof(rte_iova_t)) rte_iova_t buf_iova; // ... #endif /* next 8 bytes are initialised on RX descriptor rearm */ union { uint64_t rearm_data[1]; __extension__ struct { uint16_t data_off; RTE_ATOMIC(uint16_t) refcnt; // 引用计数(原子性可配) uint16_t nb_segs; // 仅首段有效 uint16_t port; // 入端口号 }; }; uint64_t ol_flags; /**< Offload features. */ /* remaining 24 bytes are set on RX when pulling packet from descriptor */ union { void *rx_descriptor_fields1[24 / sizeof(void *)]; __extension__ struct { union { uint32_t packet_type; ... }; uint32_t pkt_len; /**< Total pkt len: sum of all segments. */ uint16_t data_len; /**< Amount of data in segment buffer. */ uint16_t vlan_tci; union { uint32_t rss; ... } hash; /**< RSS hash result */ // ... }; }; struct rte_mempool *pool; /**< Pool from which mbuf was allocated. */第二缓存行(节选,rte_mbuf_core.h:606-673): /* second cache line - fields only used in slow path or on TX */ #if RTE_IOVA_IN_MBUF alignas(RTE_CACHE_LINE_MIN_SIZE) struct rte_mbuf *next; // 分段链(IOVA_IN_MBUF=1 时在第二行) #endif /* fields to support TX offloads */ union { uint64_t tx_offload; /**< combined for easy fetch */ __extension__ struct { uint64_t l2_len:RTE_MBUF_L2_LEN_BITS; uint64_t l3_len:RTE_MBUF_L3_LEN_BITS; uint64_t l4_len:RTE_MBUF_L4_LEN_BITS; uint64_t tso_segsz:RTE_MBUF_TSO_SEGSZ_BITS; uint64_t outer_l3_len:RTE_MBUF_OUTL3_LEN_BITS; uint64_t outer_l2_len:RTE_MBUF_OUTL2_LEN_BITS; }; }; struct rte_mbuf_ext_shared_info *shinfo; // 外部缓冲共享信息 uint16_t priv_size; uint16_t timesync; uint32_t dynfield1[9]; /**< Reserved for dynamic fields. */ };三个设计点:rearm_data 打包:收包时要复位的 data_off/refcnt/nb_segs/port 打包成 64 位,一条 8B store 完成 mbuf 重装。实测证据在 ixgbe_rx_alloc_bufs(ixgbe_rxtx.c:1654-1655):rte_mbuf_refcnt_set(mb, 1); mb->data_off = RTE_PKTMBUF_HEADROOM;。headroom = 128B(build/rte_build_config.h:454):留给封装。VXLAN 外层头 = ETH14 + IP20 + UDP8 + VXLAN8 = 50B,128 绰绰有余。rte_pktmbuf_prepend() 向前扩 data_off,零拷贝加隧道头。默认缓冲 RTE_MBUF_DEFAULT_BUF_SIZE = 2048 + 128(rte_mbuf_core.h:405-407)。pool 指针:mbuf 记得自己的 mempool(rte_mbuf_core.h:604),rte_pktmbuf_free 才能按池归还。外部缓冲:rte_mbuf_ext_shared_info(rte_mbuf_core.h:684-688)struct rte_mbuf_ext_shared_info { rte_mbuf_extbuf_free_callback_t free_cb; /**< Free callback function */ void *fcb_opaque; /**< Free callback argument */ RTE_ATOMIC(uint16_t) refcnt; };rte_pktmbuf_attach_extbuf() 把用户自己的内存挂进 mbuf:refcnt 原子计数管共享,归零时调用户的 free_cb——生命周期完全由用户控制。典型场景:TLS 记录、文件缓存直接交给网卡 DMA 收发,全程不拷贝。这是 P1 零拷贝 echo 的技术基础。🐧 内核对照:sk_buff 的真实结构head 区模型(skbuff.h:3122-3124):#ifndef NET_SKB_PAD #define NET_SKB_PAD max(32, L1_CACHE_BYTES) #endif(x86 上 L1_CACHE_BYTES=64,即 64B headroom——和 mbuf 的 128B 是同一思想,留的余量不同。)非线性数据(include/linux/skbuff.h:572-597,全文):struct skb_shared_info { __u8 flags; __u8 meta_len; __u8 nr_frags; __u8 tx_flags; unsigned short gso_size; unsigned short gso_segs; struct sk_buff *frag_list; struct skb_shared_hwtstamps hwtstamps; unsigned int gso_type; u32 tskey; /* * Warning : all fields before dataref are cleared in __alloc_skb() */ atomic_t dataref; unsigned int xdp_frags_size; void * destructor_arg; /* must be last field, see pskb_expand_head() */ skb_frag_t frags[MAX_SKB_FRAGS]; };struct sk_buff 开头(skbuff.h:843-876 节选):struct sk_buff { union { struct { /* These two members must be first to match sk_buff_head. */ struct sk_buff *next; struct sk_buff *prev; // ... }; struct rb_node rbnode; /* used in netem, ip4 defrag, and tcp stack */ struct list_head list; struct llist_node ll_node; }; struct sock *sk; // ... /* * This is the control buffer. It is free to use for every * layer. ... This is owned by whoever has the skb queued ATM. */ char cb[48] __aligned(8); // ... unsigned int len, data_len;对照图:DPDK mbuf内核 sk_buff差异本质数据区独立,buf_addr 指针引用head 区 kmalloc 一段(末端挂 shared_info)都要 headroom(128 vs 64)多段 = mbuf 链(next 指针)非线性数据 = frags[] 页数组段链表 vs 页片段数组refcnt(rearm_data 内)skb_shared_info.dataref(原子计数)同为引用计数,skb 放在数据区末端共享结构mbuf 记得 pool,free 即归还每包进 slab/page_pool 分配池化预分配 vs 按需分配+池化回收面试金句:mbuf 把"段"建模为元数据链表,适配用户态预分配池 + 批量轮询;skb 把"非线性数据"建模为页数组,适配内核页缓存零拷贝生态——不是谁好谁坏,是谁服务谁的场景。面试 3 问rearm_data 打包 4 字段的意义? → 收包热路径一次 8B store 重置 4 个字段;对应 rte_mbuf_core.h:488-516。headroom 为什么 128B? → 隧道封装零拷贝 prepend;VXLAN 外层 50B 加得下。extbuf 与 clone 的计数分别在哪? → extbuf 在 rte_mbuf_ext_shared_info.refcnt(用户内存共享);clone 在 mbuf 自身 refcnt。🔧 动手实验写零拷贝 echo(code/P1/zerocopy-echo/):不分配新 mbuf,in-place 交换 src/dst MAC+IP 原路发回。dperf 打满对比"重新分配 mbuf"版本,量化省掉的 mempool get/put 值多少 Mpps。数据落点 perf-data/P1/。下一篇:P1-05 · PMD:把中断换成轮询
2026年08月18日
0 阅读
0 评论
0 点赞
第 1/1 页 · 本页 1 条 · 共 1 条