目录
- 手写 DPDK 协议栈(七):用 rte_ring 拆出物理层、协议栈层和应用层
- 1. 三层数据流
- 2. ring 为什么适合这里
- 3. 所有权规则比线程数更重要
- 4. 优化不等于盲目加线程
- 小结
手写 DPDK 协议栈(七):用 rte_ring 拆出物理层、协议栈层和应用层
标签:DPDKrte_ring多线程架构设计
随着 ARP、UDP、TCP 处理加入同一轮询循环,网卡收发、协议解析和业务逻辑会相互阻塞。stack_optimize/stack.c采用三层结构,以 DPDK ring 解耦各阶段。
1. 三层数据流
+------------------------+ NIC RX -> [物理层] -> stack.in -> [协议栈层] -> app.in -> [应用层] ^ | | | v v NIC TX <- [物理层] <- stack.out <--- app.out <-----+- 物理层:
rte_eth_rx_burst收包入stack.in;从stack.out取 mbuf 后rte_eth_tx_burst。 - 协议栈层:解析 Ethernet/IP/ARP/UDP/TCP,按类型交给应用 ring 或回写物理层 ring。
- 应用层:只处理 socket 语义,不直接触碰网卡队列。
2. ring 为什么适合这里
rte_ring是无锁/低锁环形队列,支持 SP/SC、MP/MC 等生产消费模式。它把“谁生产、谁消费”从业务逻辑中抽离:
rte_ring_mp_enqueue(g_inout_ring->in,mbuf);/* 协议线程 */if(rte_ring_mc_dequeue(g_inout_ring->in,(void**)&mbuf)==0)process_packet(mbuf,mbuf_pool);选择 API 时应匹配真实并发模型。只有单生产者/单消费者时才使用 SP/SC 变体;否则会出现隐蔽的数据竞争。
3. 所有权规则比线程数更重要
为每个队列明确一条规则:入队成功后由消费者释放,入队失败则由当前线程释放或重试。以 mbuf 为例:
NIC RX 获得 mbuf -> 入 stack.in 成功:协议层拥有 -> 解析为本地包:协议层释放或转应用 -> 需要发送:物理层最终发送或释放没有这条规则,队列满时最容易产生泄漏、重复释放或“发送后又释放”的 use-after-free。
4. 优化不等于盲目加线程
先用 pps、平均/尾延迟、丢包率和队列占用定位瓶颈。低负载下,额外线程和跨核 ring 可能反而增加缓存同步成本;高负载下,RX、协议和应用分核才有收益。NUMA 主机还应让端口队列、lcore 和 mbuf pool 尽量位于同一 socket。
小结
三层架构的收益是职责清晰和可独立扩展:协议层不再被应用阻塞,应用不再直接占用 RX 轮询。下一步用 Hash 将线性 socket 查找替换为常数时间的连接定位。
学习链接: https://github.com/0voice