当前位置: 首页 > news >正文

`sk_buff` 结构体详解(包含全生命周期解析)

一、sk_buff 结构体解析

sk_buff(socket buffer)是 Linux 网络协议栈的核心数据结构,用于表示网络数据包的所有元信息和数据内容。

核心成员解析
struct sk_buff {// 数据缓冲区管理unsigned char *head;    // 分配内存的起始地址unsigned char *data;    // 当前协议层数据的起始地址unsigned char *tail;    // 当前协议层数据的结束地址unsigned char *end;     // 分配内存的结束地址// 长度信息unsigned int len;       // 当前数据总长度 (data 到 tail)unsigned int data_len;  // 分片数据长度 (当有分片时)// 协议栈信息__u16 protocol;         // 上层协议 (e.g., ETH_P_IP)__u16 transport_header; // 传输层头偏移__u16 network_header;   // 网络层头偏移__u16 mac_header;       // MAC 层头偏移// 网络设备信息struct net_device *dev; // 接收/发送的设备// 路由和转发struct dst_entry *dst;  // 路由缓存信息// 控制信息char cb[48];            // 控制缓冲区 (各协议私有数据)// 引用计数refcount_t users;       // 引用计数器// 分片管理struct sk_buff *next;   // 下一个分片 (用于GRO/GSO)
};
关键内存区域
      head                              end|                                |▼                                ▼
+-------+----------------+--------------+-------+
| headroom |   packet data   |  tailroom | 
+-------+----------------+--------------+-------+▲                ▲              ▲|                |              |mac_header      network_header   tail|           (e.g., IP头)        |data ─────────────────────────────┘

二、sk_buff 生命周期流程图

1. 接收路径生命周期 (RX)
物理设备/virtio后端网卡驱动协议栈应用程序数据包到达分配sk_buff (或使用预分配)填充数据到sk_buffnetif_receive_skb(skb)协议处理 (MAC→IP→TCP)数据就绪 (sock_queue_rcv_skb)recv() 读取数据释放sk_buff (kfree_skb)物理设备/virtio后端网卡驱动协议栈应用程序
2. 发送路径生命周期 (TX)
应用程序协议栈网卡驱动物理设备/virtio后端send() 发送数据分配sk_buff构建包头 (TCP→IP→MAC)dev_queue_xmit(skb)映射DMA区域添加到发送队列发送数据包发送完成中断释放sk_buff (dev_consume_skb_any)应用程序协议栈网卡驱动物理设备/virtio后端

三、关键操作函数解析

1. 核心创建/销毁函数
函数作用
alloc_skb()分配新的sk_buff(指定headroom大小)
dev_alloc_skb()为驱动优化的分配(额外16字节headroom)
kfree_skb()通用释放函数(处理引用计数)
dev_kfree_skb_any()驱动专用释放(可在中断上下文使用)
2. 数据操作函数
// 添加头部(移动data指针)
unsigned char *skb_push(skb, len);// 移除头部(移动data指针)
unsigned char *skb_pull(skb, len);// 添加尾部数据(移动tail指针)
unsigned char *skb_put(skb, len);// 移除尾部数据(移动tail指针)
void skb_trim(skb, len);
3. 分片管理函数
// 分片skb(用于GSO)
struct sk_buff *skb_segment(skb, features);// 合并skb(用于GRO)
int skb_gro_receive(struct sk_buff *head, struct sk_buff *skb);

四、生命周期关键节点详解

接收路径关键阶段
  1. 驱动层创建

    // 分配sk_buff(预分配或动态分配)
    skb = netdev_alloc_skb(dev, len);// 填充数据(DMA映射)
    skb_put(skb, pkt_len);
    memcpy(skb->data, pkt_data, pkt_len);
    
  2. 协议栈处理

    // 提交给协议栈
    netif_receive_skb(skb);// 协议处理(示例:IP层)
    ip_rcv(skb, dev, pt, orig_dev);
    
  3. 应用层消费

    // Socket层接收
    sock_queue_rcv_skb(sk, skb);// 应用读取后释放
    kfree_skb(skb);
    
发送路径关键阶段
  1. 协议栈创建

    // 分配sk_buff
    skb = alloc_skb(len + headroom, GFP_KERNEL);// 构建协议头
    skb_reserve(skb, headroom);
    skb_put(skb, data_len);
    memcpy(skb->data, user_data, data_len);
    
  2. 驱动层发送

    // 驱动发送入口
    start_xmit(skb, dev) {// 映射DMA区域dma_map_single(dev, skb->data, skb->len);// 添加到发送队列virtqueue_add_outbuf(vq, &sg, 1, skb);
    }
    
  3. 发送完成释放

    // 中断处理中释放
    while ((skb = virtqueue_get_buf(vq, &len))) {dma_unmap_single(dev, skb->data, skb->len);dev_consume_skb_any(skb); // 驱动负责释放!
    }
    

五、特殊场景处理

1. 零拷贝发送 (Zero-Copy TX)
mmap
用户空间
驱动环形缓冲区
直接DMA发送
发送完成后通知
  • 优势:避免用户态到内核态拷贝
  • APIsendfile()splice()
2. 零拷贝接收 (Zero-Copy RX)
// 1. 用户空间预注册内存区域
setsockopt(sock, SOL_PACKET, PACKET_RX_RING, &req);// 2. 驱动直接DMA到用户内存
while ((skb = virtqueue_get_buf(vq))) {// skb->data 指向用户空间内存// 无需拷贝直接提交协议栈
}
3. skb 克隆与拷贝
操作函数内存开销使用场景
完整拷贝skb_copy()需要修改原始数据
浅拷贝skb_clone()多路径转发
头部分离skb_unshare()协议栈修改包头

六、性能优化技巧

内存管理优化
  1. skb 池缓存

    // 初始化每CPU缓存
    netdev_alloc_skb() -> __alloc_skb() -> kmem_cache_alloc()
    
  2. 预分配策略

    // 接收路径预分配(virtio示例)
    while (virtqueue_num_free(vq) > 0) {skb = alloc_skb();virtqueue_add_inbuf(vq, skb);
    }
    
批量处理优化
  1. 发送批处理

    // 一次添加多个skb
    virtqueue_add_outbufs(vq, sg_array, num_skbs);// 延迟通知(积攒多个包后通知)
    if (packet_count > BATCH_SIZE) {virtqueue_kick(vq);
    }
    
  2. 接收软中断聚合

    // NAPI处理循环
    while (processed < budget) {skb = virtqueue_get_buf(vq);napi_gro_receive(napi, skb); // GRO聚合processed++;
    }
    

七、总结:sk_buff 设计哲学

  1. 统一数据包表示

    • 贯穿协议栈各层
    • 支持任意协议(以太网/IPv6/VxLAN等)
  2. 高效内存管理

    • headroom/tailroom 避免频繁重分配
    • 引用计数支持零拷贝转发
  3. 分层协议支持

    • 通过 mac_header/network_header 等实现协议栈分层处理
    • 支持分片(GSO)和重组(GRO)
  4. 生命周期明确

    • 发送路径:由驱动在发送完成后释放
    • 接收路径:由应用消费后释放
    • 转发路径:引用计数控制释放时机

sk_buff 的精心设计使其成为 Linux 网络栈高效处理每秒百万级数据包的核心基石,同时支持从嵌入式设备到数据中心的各种复杂场景。

http://www.lryc.cn/news/615762.html

相关文章:

  • 如何回收内存对象,有哪些回收算法?
  • XML 指南
  • LeetCode_字符串
  • Jenkins | 账号及权限管理
  • Pytorch深度学习框架实战教程-番外篇02-Pytorch池化层概念定义、工作原理和作用
  • 怎么能更好的降低论文AI率呢?
  • 分布微服务电商订单系统Rust编码开发[下]
  • SpringBoot学习日记(三)
  • 【C++/STL】list模拟实现和迭代器失效问题
  • 基于 RabbitMQ 死信队列+TTL 实现延迟消息+延迟插件基本使用
  • 十、Linux Shell脚本:流程控制语句
  • [Julia] LinearAlgebra.jl 自带包
  • LeetCode 刷题【37. 解数独】
  • LabVIEW 机器人避障控制
  • 企业架构之导论(1)
  • C++设计模式单例模式(饿汉、懒汉模式)
  • Linux操作系统从入门到实战(十六)冯诺依曼体系结构,操作系统与系统调用和库函数概念
  • 【软件测试】BUG篇 — 详解
  • AI测试助手如何让Bug无处可藏
  • uni-app 网络请求终极选型:uni.request、axios、uni-network、alova 谁才是你的真命请求库?
  • Eclipse JSP/Servlet:深入解析与最佳实践
  • 繁花深处:花店建设的时代意义与多元应用—仙盟创梦IDE
  • 计算机视觉全景指南:从OpenCV预处理到YOLOv8实战,解锁多模态AI时代(第五章)
  • 【Docker进阶实战】从多容器编排到集群部署
  • [Linux]学习笔记系列 -- [arm][lib]
  • 13. 是否可以在static环境中访问非static变量
  • 如何在 Ubuntu 24.04 LTS Linux 上安装 MySQL 服务器
  • opencv颜色识别项目:识别水果
  • jmeter常规压测【读取csv文件】
  • Ubuntu 22.04 离线环境下完整安装 Anaconda、CUDA 12.1、NVIDIA 驱动及 cuDNN 8.9.3 教程