Linux sk_buff 深度解析(八):Socket 内存、零拷贝、丢包与版本演进
写在前面
sk_buff 是 Linux 网络栈最核心、也最容易被误解的数据结构之一。它并不是“装着一个完整网络包的结构体”,而是贯穿驱动、协议栈、队列、Socket、Netfilter、tc、BPF 与硬件 offload 的控制对象和所有权载体。
从 socket truesize/owner、MSG_ZEROCOPY、error queue、drop reason 讲到 Linux 5.10 与 6.1 的 skb 演进,并总结 RK3588 BSP 的验证边界。
本文以本地 Linux 5.10.209 源码为主线;涉及演进时对照 Linux 6.1.99,涉及网卡驱动时结合 Firefly RK3588 SDK Linux 5.10.198。本文是源码分析,不把尚未执行的 QEMU、tracepoint 或开发板实验写成已验证结论。
阅读前先建立四个问题
阅读任何 skb 代码路径,都建议反复追问:
- 当前
skb->data指向哪一层协议头,linear 与 non-linear 数据分别在哪里? - 哪些字段刚被修改,下一层为什么依赖这些字段?
- 当前谁持有 skb shell、head、frag page 以及 socket/dst/conntrack 等外部引用?
- 成功、失败、重试、redirect 和丢弃分支分别由谁消费或释放 skb?
本篇核心结论
- socket 用
truesize而不是 wire length 计量 skb 占用,并通过 destructor 归还记账。 - MSG_ZEROCOPY 主要避免 payload copy;user page、ubuf_info、segment 和 error queue 形成额外生命周期。
- 6.1 的重点是 hot-path cache、page_pool recycle、multi-buffer XDP 和结构化丢包原因。
- RK3588 BSP 必须区分通用 skb 语义、stmmac 实现和真实硬件能力,静态分析不能替代实机验证。
1. Socket 内存计量与 MSG_ZEROCOPY
结论摘要
socket 使用 skb truesize 进行收发内存记账,owner helper 把 sk 和 destructor 绑定到 skb。skb_orphan() 解除 socket owner 并执行 destructor。MSG_ZEROCOPY 不等于完全不复制所有 header,而是让 TX payload 引用用户页,通过 ubuf_info 跟踪 pinned pages;skb clone/segment/completion 共同持有该引用,最终通过 error queue 通知用户可重用内存。
一、接收内存
skb_set_owner_r(skb, sk) 定义于 kernel-5.10/include/net/sock.h:2281。典型动作:
- 设置
skb->sk; - 设置 destructor=
sock_rfree; - 按
skb->truesize增加sk_rmem_alloc。
最终释放调用 sock_rfree() 归还计量并唤醒/更新 socket 状态。
为什么不用 len:一个 64 字节包可能占用 skb slab、head allocation、page、frag 和对齐开销,真实内存远大于 wire length。
二、发送内存
skb_set_owner_w() 位于 kernel-5.10/net/core/sock.c:2091,通常:
- 绑定 sk;
- 设置 destructor=
sock_wfree; - 增加
sk_wmem_alloc; - 关联 socket ref/lifetime。
TCP write/retransmit queue 长期持有 skb,发送内存只有在 ACK/释放相应 skb 引用后才真正归还;网卡 completion 与 TCP ACK 的生命周期层次不同。
三、skb_orphan
skb_orphan() 位于 kernel-5.10/include/linux/skbuff.h:2811,用于清除 socket owner:
- 若有 destructor,调用它归还 rmem/wmem 或其它 owner 状态;
skb->sk=NULL;destructor=NULL。
常见于转发、进入设备、某些 clone/重传或需要解除 socket lifetime 的路径。orphan 不是释放 packet data,skb 仍然存在。
错误地 orphan 太早会让 socket accounting 过早下降;太晚会让 skb 不必要地持有 socket 引用。
四、MSG_ZEROCOPY 目标
用户发送大 payload 时,普通路径从用户空间复制到 kernel skb/page。MSG_ZEROCOPY 尝试:
- pin 用户页;
- 把 page fragment 引用挂入 skb;
- header 仍可在 kernel linear head 中构造;
- DMA 直接读取用户页;
- 完成后异步通知用户。
因此是 payload zerocopy,不保证系统调用、协议头、分段和所有元数据都零成本。
五、ubuf_info
struct ubuf_info 记录:
- completion callback;
- refcount;
- zerocopy success 状态;
- 用户页 accounting;
- ID/range 等通知信息。
skb shared info 的 destructor_arg 指向 ubuf_info,tx_flags 标记 SKBTX_DEV_ZEROCOPY/相关状态。定义见 kernel-5.10/include/linux/skbuff.h:466,相关 helper 见 kernel-5.10/include/linux/skbuff.h:1471-1519。
六、clone、GSO 与 zerocopy ref
一个用户 buffer 可能经过:
- 大 skb;
- GSO segment 多个 skb;
- retransmit clone;
- 设备发送副本。
每个仍可能访问用户页的对象必须持有 ubuf/page 引用。最后一个完成前不能通知用户复用内存。
skb_zcopy_set/clear、clone/copy/segment 路径维护 ubuf_info。某个 segment fallback copy 后,可更新 overall zerocopy success,但仍需等待其它引用。
七、completion 与 error queue
TX completion 或协议确认最终调用 zerocopy callback;通知排队入口见 kernel-5.10/net/core/skbuff.c:4602。通知通常作为 extended error skb 放入 socket error queue:
1 | completion |
error queue 中“error”不一定表示发送失败,也用于 timestamp、zerocopy completion、Wi-Fi ACK 等异步状态。
八、失败与 fallback
- pin 超过 memlock/accounting:返回错误或 fallback copy;
- 设备不支持 SG:软件复制/linearize;
- 加密/协议修改要求可写:部分 copy;
- DMA map 失败:释放当前 refs,通知失败;
- clone/segment 中途失败:已创建对象和 ubuf refs 必须回滚;
- socket close:仍需等待/清理 outstanding completion。
用户必须在收到 completion 前保持 buffer 不修改/不复用。
九、truesize 与 zerocopy
zerocopy payload page 仍占系统资源:
- pinned user pages;
- skb shell/head;
- page refs;
- DMA mappings;
- retransmit/queue 生命周期。
内存 accounting 不能因为“没有 memcpy”就忽略。不同路径可能同时使用 socket wmem、locked_vm、ubuf refs 和 device queue accounting。
十、所有权表
| 对象 | owner/ref | 释放/完成 |
|---|---|---|
| skb shell | users | consume/kfree |
| kernel head | dataref | skb release data |
| user page | pin/page ref | ubuf final completion |
| ubuf_info | callback refcount | last skb/segment complete |
| socket wmem | destructor/truesize | sock_wfree/orphan |
| notification skb | error queue | user recvmsg/free |
十一、常见误区
- MSG_ZEROCOPY 不代表完全没有 CPU copy 或协议头分配。
- 网卡 completion 后用户页不一定立即可复用,可能还有 clone/segment/retransmit 引用。
- error queue 不只传错误。
- orphan 不释放 skb 数据,只解除 owner/destructor。
- truesize 仍然重要,zerocopy 不是免费内存。
- users、dataref、page pin、ubuf ref 和 socket wmem 是不同计数。
字段与所有权统一核对表
| 对象或字段 | 主要修改者/使用者 | 所有权与释放要点 |
|---|---|---|
sk/destructor/truesize |
owner helper | destructor 归还 rmem/wmem 和记账引用 |
destructor_arg/tx_flags |
zerocopy helper | skb/shared info 持有 ubuf_info |
| user page pin/ref | MSG_ZEROCOPY | 最后一个 completion 前用户不得复用 |
最小调用链
1 | sendmsg(MSG_ZEROCOPY) |
2. 从 kfree_skb 到结构化 drop reason
结论摘要
释放 skb 不等于发生丢包:正常 TX completion、用户读取和协议消费也会释放。Linux 5.10 主要通过 kfree_skb tracepoint 记录释放位置,难以表达语义原因;Linux 6.1 的 kfree_skb_reason() 和 enum skb_drop_reason 让调用者携带结构化原因,tracepoint 同时记录 location、protocol 和 reason,从而区分 backlog 满、checksum、header、socket filter、route 等类别。
一、正常消费与丢弃
consume_skb():正常路径消费完成;napi_consume_skb():NAPI/TX completion 正常消费并优化回收;kfree_skb():传统上常用于 drop/error,但也存在历史调用不够严格的情况。
内存结果可能相同,观测语义不同。分析 drop 不能只统计所有 skb free。
二、Linux 5.10 trace_kfree_skb
kfree_skb() 位于 kernel-5.10/net/core/skbuff.c:705,在真正释放前调用:
1 | trace_kfree_skb(skb, __builtin_return_address(0)); |
tracepoint 定义于 kernel-5.10/include/trace/events/skb.h。可看到:
- skb 地址;
- location/调用地址;
- protocol 等有限字段。
主要局限:
- location 需要符号化;
- 同一 helper 可能因多个条件 drop;
- wrapper 后 location 不够接近根因;
- 没有统一 reason taxonomy;
- 正常/异常历史调用混杂。
三、Linux 6.1 drop reason
enum skb_drop_reason 定义于 kernel-6.1/include/net/dropreason.h:78。kfree_skb_reason() 位于 kernel-6.1/net/core/skbuff.c:885。
调用者:
1 | kfree_skb_reason(skb, SKB_DROP_REASON_...); |
tracepoint 记录 reason。普通 kfree_skb() 可映射为 NOT_SPECIFIED,便于逐步迁移旧调用点。
四、reason 分类
枚举覆盖的类型随版本演进,典型类别:
- NOT_SPECIFIED;
- NO_SOCKET / SOCKET_FILTER;
- PKT_TOO_SMALL / TCP_CSUM / UDP_CSUM;
- NETFILTER_DROP;
- IP_INHDR / IPV6_BAD_EXTHDR;
- NO_ROUTE;
- CPU_BACKLOG;
- FULL_RING / QDISC_DROP;
- XFRM_POLICY;
- PROTO_MEM;
- TCP_* 状态原因。
reason 应描述最接近根因的逻辑,而不是简单写“调用了 kfree”。
五、reason 传递
深层 helper 发现错误时可能:
- 返回 enum reason 给上层;
- 通过输出参数设置 reason;
- 上层统一释放并 trace;
- skb list/segment 释放对每个成员传播同一 reason。
好处是把“判断原因”和“真正释放地点”分开后仍保留根因。
六、CPU backlog 示例
RPS enqueue 时目标 CPU 队列达到上限,5.10 通常只能从 enqueue_to_backlog() 的 kfree location 推断。6.1 可标记 SKB_DROP_REASON_CPU_BACKLOG,更直接区分:
- 驱动 ring drop;
- qdisc drop;
- socket rcvbuf drop;
- softnet backlog drop。
七、协议与 reason
tracepoint 还可记录 skb->protocol,但 protocol 字段在不同层可能:
- 尚未设置;
- 为 outer protocol;
- VLAN/隧道后变化;
- 非 Ethernet skb。
reason 与 location 仍需结合调用链、设备和协议字段解释,不能单独作为完整诊断。
八、释放链与 trace 时机
reason trace 在 users 归零并即将释放时触发。若 skb 仍有 users,当前调用只减引用,不一定产生最终 free trace。clone 共享 data 的最后一个 shell/dataref 释放时机也不同。
因此 trace 数量不等于收到包数或 drop packet 数,需要理解 clone/list/segment。
九、设计价值
- 统一 drop taxonomy;
- eBPF/perf/drop monitor 可按 reason 聚合;
- 减少依赖调用地址和版本行号;
- 支持协议逐步细化;
- 让生产环境统计更可读;
- 区分资源压力、策略、安全校验和协议错误。
十、常见误区
- 所有
kfree_skb都算丢包是错误的。 - reason 不是 errno,二者服务不同层次。
NOT_SPECIFIED不代表正常,只表示调用点尚未细化。- clone 的 free event 不能简单按一个 event=一个 wire packet。
- reason 仍需结合 location/dev/protocol/调用链。
- 5.10 没有 reason 枚举时仍可用 location 和协议统计,但准确性较弱。
字段与所有权统一核对表
| 对象或字段 | 主要修改者/使用者 | 所有权与释放要点 |
|---|---|---|
| skb pointer | free/consume/drop helper | 调用后原持有者不得再访问 |
| drop reason | kfree_skb_reason() |
结构化原因随 tracepoint 暴露 |
| location/protocol | 旧式 tracepoint | 只能辅助定位,不能替代原因语义 |
3. Linux 5.10 到 6.1 的关键演进
结论摘要
5.10 到 6.1 的 skb 演进重点不是四指针模型改变,而是高性能内存回收、多 buffer XDP、GRO 分桶和丢包可观测性加强。6.1 用 NAPI skb cache bulk 操作降低 shell slab 开销,用 pp_recycle 把 page_pool 回收信息带入 skb free,用 multi-buffer XDP 对接 shared info,用结构化 drop reason 替代仅靠 location 的诊断。
一、基础模型保持稳定
两版仍保持:
- skb shell 与 head 分离;
head/data/tail/end;- shared info 位于 end;
len/data_len/truesize;- frags/frag_list;
- users/dataref/page ref;
- clone/copy/COW/free 分层。
因此 5.10 是理解核心机制的可靠基线。
二、NAPI skb shell cache
6.1:
napi_skb_cache_get():kernel-6.1/net/core/skbuff.c:252;napi_skb_cache_put():kernel-6.1/net/core/skbuff.c:1054。
per-CPU cache 配合 kmem_cache_alloc_bulk/free_bulk:
- 一次从 slab 获取/归还多个 skb shell;
- NAPI hot path 从本地数组 pop/push;
- 降低 allocator lock、freelist 和 cacheline 成本;
- 适合高 PPS 小包。
5.10 已有 NAPI 分配/延迟回收思想,但 6.1 的 bulk cache 路径更系统。
三、pp_recycle 与 page_pool
6.1 struct sk_buff 增加/使用:
1 | pp_recycle:1 |
位置:kernel-6.1/include/linux/skbuff.h:926。
释放 head/frag 时:
1 | pp_recycle set |
关键位置:
- head recycle:
kernel-6.1/net/core/skbuff.c:758; - frag unref:
kernel-6.1/net/core/skbuff.c:785; - helper:
kernel-6.1/include/linux/skbuff.h:5104; - page_pool API:
kernel-6.1/include/net/page_pool.h:242。
5.10 驱动更常显式区分 copy page 直接 recycle 与 frag page 通用释放,跨 clone/协议栈自动回池能力较弱、驱动耦合更高。
四、clone 与 recycle 风险
clone 会复制 pp_recycle,但共享 page/dataref 带来竞态:不能让某个 clone 过早把 page 回池。6.1 在 clone/copy/expand 路径检查 recycle 状态与共享条件,例如 kernel-6.1/net/core/skbuff.c:793-802、kernel-6.1/net/core/skbuff.c:5459-5463。
这说明 recycle flag 不是“free 时无条件放回 pool”,必须与 page ownership/refcount 协同。
五、XDP multi-buffer
6.1:
xdp_buff_has_frags():include/net/xdp.h:88;xdp_update_skb_shared_info():include/net/xdp.h:223。
XDP packet 可由主 buffer + frags 组成。PASS 转 skb 时把 fragment descriptors 写入 shared info,更新 len/data_len/truesize。5.10 native XDP 设计主要围绕单 buffer,jumbo/multi-descriptor 支持受限。
六、GRO 重构与 hash bucket
5.10 GRO 主要在 net/core/dev.c,一个 NAPI 上的候选 list 比较成本随活跃 flow 增长。
6.1:
- 实现拆到
net/core/gro.c; GRO_HASH_BUCKETS=8:include/linux/netdevice.h:339;gro_hash[]:include/linux/netdevice.h:362;dev_gro_receive():net/core/gro.c:483。
先按 skb hash 分桶,再进行协议级 same-flow 比较,减少多 flow 场景线性扫描。
七、drop reason
5.10 trace_kfree_skb 主要记录 location。6.1:
enum skb_drop_reason;kfree_skb_reason();- tracepoint reason;
- 协议函数逐步返回具体 reason。
对 backlog、checksum、header、socket、policy 等根因可结构化统计。
八、skb extensions
6.1 extension ID/类型继续增加,例如 SKB_EXT_MCTP:
- enum:
kernel-6.1/include/linux/skbuff.h:4626; - size table:
kernel-6.1/net/core/skbuff.c:4512。
extension 让低频/可选 metadata 不必永久膨胀核心 struct sk_buff,代价是独立分配/引用和 clone/free 管理。
九、API/代码位置变化
阅读跨版本代码时需要注意:
- GRO 函数从 dev.c 移到 gro.c;
- API 可能增加 reason/recycle 参数;
- inline helper 行号和字段布局变化;
- 驱动 backport 可能只有部分新机制;
- 同名函数实现细节不一定相同。
不能只用文件名 diff 判断功能有无,应追踪 symbol、config 和调用点。
十、对驱动开发的影响
| 主题 | 5.10 常见责任 | 6.1 改进 |
|---|---|---|
| skb shell | 普通/NAPI alloc/free | bulk per-CPU cache |
| RX page | 驱动显式 recycle 分支 | skb free 可 pp_recycle |
| jumbo XDP | 单 buffer 限制明显 | multi-buffer metadata |
| GRO flow list | 线性候选较多 | hash buckets |
| drop | location 推断 | structured reason |
| optional metadata | extensions 较少 | extension 类型扩展 |
十一、常见误区
- 6.1 并没有推翻 skb 基础模型。
pp_recycle不等于跳过 page ref 安全。- multi-buffer XDP 需要驱动和 BPF/redirect 全链支持,不只是 helper 存在。
- GRO 移文件不只是代码整理,还伴随数据结构/分桶演进。
- drop reason 的覆盖是渐进式,仍会有 NOT_SPECIFIED。
- BSP 5.10 可能 backport 部分 6.x 特性,必须检查本地源码而非仅看版本号。
字段与所有权统一核对表
| 对象或字段 | 主要修改者/使用者 | 所有权与释放要点 |
|---|---|---|
pp_recycle |
page_pool skb 路径 | free 时可归还 page_pool |
| NAPI skb cache | alloc/free hot path | per-CPU cache 批量管理 skb shell |
| drop reason/XDP frags | 6.1 新路径 | 增强可观测性和 multi-buffer 表达 |
最小调用链
1 | 5.10 基线 skb alloc/build/free |
4. RK3588 BSP 中的 skb 生命周期
结论摘要
RK3588 BSP 保持 Linux 5.10 skb 核心模型,平台特性主要体现在 stmmac RX/TX、page_pool、DMA descriptor、checksum/hash/offload 和 vendor patch。源码结论必须分为三层:通用 skb 语义、stmmac 驱动实现、RK3588 平台 glue/硬件能力。QEMU virt 不能验证真实 GMAC DMA/page_pool/offload,最终硬件结论需开发板串口、ethtool、抓包和性能实验。
一、版本与源码边界
- 通用主线学习树:Linux 5.10.209,commit
6c60e1d; - 演进树:Linux 6.1.99,commit
5ab5f07; - Firefly BSP:Linux 5.10.198,commit
11a3d9327b6f。
BSP 版本更早,且可能包含 Android KABI、Rockchip/Firefly 和 stmmac backport。判断差异需对具体文件/symbol diff,不能按 5.10.198 数字推断全部能力。
二、通用 skb 机制
BSP 仍使用:
struct sk_buffshell;- 独立 head + tail shared info;
- linear/frags/frag_list;
- users/dataref/page ref;
- alloc/build/clone/copy/free;
- NAPI/GRO/RPS/qdisc/socket ownership。
因此阶段 0~4、6~21 的通用概念仍适用,行号和 vendor 字段需以 BSP 树重新检索。
三、RK3588 GMAC 软件层次
1 | Rockchip platform glue (dwmac-rk.c) |
dwmac-rk.c 不直接定义 skb 核心语义,它配置平台硬件;真正构造/消费 skb 的主路径在 stmmac core。
四、RX 生命周期
1 | page_pool/RX buffer |
关键入口:firefly_rk3588_SDK/kernel/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c:3862。
BSP 实现的核心特点:
- 首段 copy 优化 header locality;
- 后续 page frag 降低 payload copy;
- copy page 可直接 recycle;
- frag page 生命周期交给 skb;
- refill 在 descriptor 重新 OWN 前准备新 page。
五、TX 生命周期
1 | socket/IP/qdisc |
一个 skb 占多个 descriptor 时,软件通常只在最后 descriptor 关联 skb,确保所有 mapping 完成后再 free。
六、page_pool 适用性
page_pool 为 RX 提供:
- page cache/recycle;
- DMA mapping 生命周期优化;
- NAPI-local fast path;
- 与 XDP/frag 的 memory model。
BSP 5.10 的回收更多由驱动显式管理。Linux 6.1 pp_recycle 可把回收意图带入通用 skb free。若向 BSP backport,不能只加一个 flag,还需:
- page 标识/pool ownership;
- clone/copy/expand 安全;
- frag unref helper 参数;
- head recycle;
- 驱动 build_skb/page_pool API 对齐。
七、checksum/hash/offload
硬件能力通过 netdev features 和 descriptor status 进入 skb:
- RX checksum →
ip_summed; - RX hash →
hash/l4_hash; - RX queue →
queue_mapping; - TX checksum → PARTIAL descriptor flags;
- TSO/GSO → MSS/header/descriptor;
- VLAN → tag insertion/stripping metadata。
必须同时检查:设备树/平台配置、stmmac capability、DMA feature register、netdev feature negotiation 和实际 descriptor path。
八、RPS/RFS/XPS 与单/多队列
stmmac 驱动可为软件流调度提供 hash 和 RX queue 信息。实际收益取决于:
- GMAC queue 数;
- RSS 硬件能力和配置;
- IRQ affinity;
- RPS/XPS maps;
- CPU/cluster/NUMA(RK3588 big.LITTLE);
- 应用绑核;
- GRO 和包长。
源码能确定机制位置,不能仅凭源码宣称某个 mask 在实机性能更好。
九、XDP 能力边界
判断 native XDP 必须在 BSP stmmac 中找到完整闭环:
- XDP program attach;
- RXQ info/memory model;
- RX poll 在 skb 前执行 XDP;
- PASS 转 skb;
- TX ring;
- redirect/flush;
- page_pool return。
若缺失,只能依赖 generic XDP。Linux 6.1 multi-buffer helper 不会自动出现在 BSP 5.10,也不能只复制 helper 而忽略驱动/API 链。
十、BSP 差异分析方法
对每个主题执行:
1 | 1. rg 找 BSP symbol 和调用点 |
不要只比较 include/linux/skbuff.h 就判断完整网络能力;驱动、page_pool、net/core/dev、offload 和平台配置必须一起看。
十一、QEMU 与实机边界
QEMU virt 可以验证 arm64 Linux 协议栈、socket、qdisc、Netfilter 等通用机制,但不能真实模拟:
- RK3588 GMAC/MTL/DMA descriptor;
- Rockchip GRF/clock/delay;
- 真实 PHY;
- stmmac hardware checksum/RSS/TSO;
- page_pool 与真实 DMA 性能。
硬件验证最终需要:串口、dmesg、ethtool -k/-S、debugfs、trace、perf、抓包、iperf 和必要的 IRQ/RPS/XPS 配置实验。本轮笔记仅完成源码分析。
十二、通用结论与 BSP 结论
| 结论 | 类型 |
|---|---|
| len/data_len、clone/free 引用规则 | 通用 skb 语义 |
| stmmac 首段 copy + 后续 frag | 当前驱动实现 |
| RK3588 时钟/GRF/PHY delay | 平台 glue |
| 某 offload 在板上实际有效 | 需实机验证 |
| 6.1 pp_recycle/drop reason | 上游版本演进 |
| BSP 是否 backport 某能力 | 本地源码事实 |
十三、常见误区
- RK3588 BSP 顶层不是单一 Git 仓库,kernel 子仓提交需单独记录。
- stmmac 通用代码存在不等于板级能力已启用。
- QEMU virt 启动 BSP kernel 不等于模拟真实 GMAC。
- 5.10 BSP 可能 backport 新机制,也可能缺少上游 5.10.209 修复。
- page_pool 存在不等于所有 frag 都能自动 recycle。
- 源码支持 XDP 配置不等于 native RX path 完整。
字段与所有权统一核对表
| 对象或字段 | 主要修改者/使用者 | 所有权与释放要点 |
|---|---|---|
| DMA descriptor/page | RK stmmac RX/TX | 驱动、page_pool 与 DMA completion 转移所有权 |
| skb offload metadata | stmmac/BSP | 硬件能力决定 checksum/hash/GSO 处理 |
| 通用 skb refs | 内核 core | vendor glue 不改变 users/dataref/page ref 基本规则 |
最小调用链
1 | RK3588 GMAC descriptor/page_pool |
源码阅读路线
建议不要从 struct sk_buff 声明开始逐字段背诵,而应使用下面的阅读顺序:
- 先在入口函数确认 skb 是新分配、clone、队列取出还是从驱动 buffer 构造;
- 沿成功主线记录
data/len/data_len、header offset、checksum、hash、queue mapping 等字段变化; - 再回到每个失败分支,确认 skb、page、DMA、socket 和记账引用是否回滚;
- 最后对照 5.10、6.1 或 BSP 的同名 symbol,区分语义变化、性能优化和 vendor backport。
本篇总结
- socket 用
truesize而不是 wire length 计量 skb 占用,并通过 destructor 归还记账。 - MSG_ZEROCOPY 主要避免 payload copy;user page、ubuf_info、segment 和 error queue 形成额外生命周期。
- 6.1 的重点是 hot-path cache、page_pool recycle、multi-buffer XDP 和结构化丢包原因。
- RK3588 BSP 必须区分通用 skb 语义、stmmac 实现和真实硬件能力,静态分析不能替代实机验证。
sk_buff 的难点从来不只是字段多,而是同一个对象不断改变数据视图、元数据状态和持有者。只要把“布局、字段、所有权、释放”四条线同时画出来,绝大多数网络栈源码都会从零散函数变成可推导的生命周期。
系列目录
- Linux sk_buff 深度解析(一):数据结构、四指针与核心字段
- Linux sk_buff 深度解析(二):非线性数据、引用计数与生命周期
- Linux sk_buff 深度解析(三):从网卡 DMA 到 Socket 的 RX 路径
- Linux sk_buff 深度解析(四):从 sendmsg 到 TX completion
- Linux sk_buff 深度解析(五):GRO、GSO、Checksum 与多核流调度
- Linux sk_buff 深度解析(六):Netfilter、tc、eBPF 与 XDP
- Linux sk_buff 深度解析(七):VLAN、隧道、分片与重组
- Linux sk_buff 深度解析(八):Socket 内存、零拷贝、丢包与版本演进(本文)
