当前位置:首页>Linux>Linux 内核协议栈 sk_buff 内存布局(线性区 vs 非线性区)

Linux 内核协议栈 sk_buff 内存布局(线性区 vs 非线性区)

  • 2026-08-13 23:58:16
Linux 内核协议栈 sk_buff 内存布局(线性区 vs 非线性区)

在 Linux 内核网络驱动开发中,sk_buff 的内存布局管理直接影响吞吐量与 CPU 负载。理解线性区(Linear Area)与分段区(Frags/Non-linear)的转换时机是设计高性能驱动的基础。以下是对常见内存布局应用场景的技术梳理:

内核的核心原则是:效率优先

  • 小包:拷贝比管理页表快,所以凑在一起(线性区)。
  • 大包:拷贝太慢,所以只拷头,肉不动(非线性区)。
  • 硬件不行:硬着头皮也要凑在一起。

Linux 内核 sk_buff 布局决策全景表

场景分类
数据包大小 / 类型
网卡特性
(NETIF_F_SG)
线性区
(skb->data)
非线性区
(frags)
决策核心依据 (Rationale)
普通小包< MSS
(如 TCP ACK, SSH, DNS)
无关
(支持或不支持)
全部
(协议头 + 负载)
CPU 缓存亲和性 (Cache Locality)
对于几百字节的数据,memcpy 的开销远小于分配物理页 (struct page) 和建立 DMA 映射的开销。
标准大包≈ MTU
(如 1500B 视频流/文件)
支持全部
(协议头 + 负载)
内存分配效率
内核 kmalloc 分配一个 2KB 的 Slab 非常快。为了 1.4KB 的负载去动用 frags 机制属于“杀鸡用牛刀”。
TSO / GSO
(最常见的大包)
> MTU
(如 64KB 大文件传输)
支持仅协议头
(MAC+IP+TCP)
负载 (Payload)
(挂载多个 Page)
避免拷贝 (Avoid Copy)
数据量太大,拷贝到线性区太耗 CPU。内核只构建头部,剩下的数据直接以 Page 形式挂载,让网卡 TSO 引擎去切分。
TSO / GSO
(硬件受限)
> MTU不支持
(无 SG 能力)
全部
(头 + 64KB 肉)
硬件强迫 (Hardware Constraint)
网卡不支持分散读取,内核被迫申请 huge contiguous memory (Order > 0)。风险: 容易因内存碎片导致申请失败(OOM)。
零拷贝
(Zero Copy)
任意大小
(sendfilespliceMSG_ZEROCOPY)
必须支持仅协议头负载 (Payload)
(直接指向 Page Cache)
极致性能
用户显式要求“零拷贝”。内核直接引用文件系统在内存中的 Page Cache 页,绝对不执行 CPU 数据拷贝。
接收方向
(RX Header Split)
高性能接收
(如 Intel ICE 开启 Split)
N/A仅协议头负载
(Page 对齐)
CPU 预取优化
网卡硬件把头剥离出来让 CPU 分析,数据单独放。防止 CPU 在分析协议头时,Cache 被无关的数据塞满。
RAW Socket用户构造包无关全部实现简单
用户态 buffer 通常是通过 copy_from_user 一次性拷入内核申请的线性区。

总结:如何一眼判断 Payload 在哪里?

  1. 看 skb_shinfo(skb)->nr_frags

    • 如果是 0:数据全在 线性区skb->data)。
    • 如果不为 0:数据分布在 线性区(头) + 非线性区(肉)
  2. skb->data_len

    • data_len == 0线性区
    • data_len > 0混合模式(Header 在线性区,Payload 在 Frags)。
  3. NETIF_F_SG

    • 如果网卡没这功能:永远在线性区(除非发不出去了)。

最新文章

随机文章