sk_buff 介绍

sk_buff是Linux网络协议栈最重要的数据结构之一,该数据结构贯穿于整个数据包处理的流程。由于协议采用分层结构,上层向下层传递数据时需要增加包头,下层向上层数据时又需要去掉包头。sk_buff中保存了L2,L3,L4层的头指针,这样在层传递时只需要对数据缓冲区改变头部信息,并调整sk_buff中的指针,而不需要拷贝数据,这样大大减少了内存拷贝的需要。
struct sk_buff {//介绍
    struct sk_buff *next *prev;//双向链表指针
    ktime_t tstamp ;//时间撮
    struct sock *sk;   //对应于传输层,标示属于哪个socket ?
    struct net_device *dev;    //数据来自或者发送自哪个设备
    char cb[48];//控制信息buffer,在每个层都可以用,并且目前为止足够大
    int len;      实际总长度
    int data_len; 数据的长度 //也许是paged的data
    __u16 mac_len; 数据链路层头的长度
    __u16 hdr_len; writable header length of cloned skb
    
     sk_buff_data_t   transport_header;   传输层头指针
    sk_buff_data_t   network_header;    网络层头指针
    sk_buff_data_t   mac_header;        数据链路层头

    unsigned char *head; //buffer 头
    unsigned char *data; 数据头
    sk_buff_data_t tail; 数据结尾
    sk_buff_data_t end;  buffer 结尾
    unsigned int truesize; //bufffer 大小

    cloned 是不是cloned
    mark 数据包mark
    destructor 销毁函数指针
    pkt_type : 根据二层头确定的包信息
    __be16 protocol : 三层协议 IP ARP 等,用于和全局数组qtype_base中的数据对比,该数组可以通过dev_add_pack()注册.
}

由于该结构将用于各个层,内核提供了一系列的sk_buff的操作函数
skb_put()  减小tailroom,buffer下后扩展
skb_push() 减小headroom,buffer向上扩张
skb_trim() cut buffer到一个长度
skb_pull   从数据头cut一定长度的数据
skb_reserve 增大headroom,减少tailroom,只能用于buffer为空时
skb_headroom headroom的大小
skb_tailroom tailroom的太小

alloc_skb() 分配一个sk_buff结构及buffer区域
kfree_slb() reference 减一,并且free skb和buffer如果不再有引用

dev_alloc_skb() 方便接收数据的sk_buff的分配函数
dev_free_skb()  

skb_shinfo() 获得和sk_buff 一块分配的struct skb_shared_info

skb_clone() //复制sk_buff ,但是buffer不变
pskb_copy()  //拷贝sk_buff和私有的头部,常用于需要修改sk_buff的头部时
skb_copy() //完全拷贝

skb_queue_head_init()
skb_queue_head()
skb_queue_tail()
skb_dequeue_head()
skb_dequeue_tail()
skb_queue_purge() //list 清空
skb_queue_walk() //遍历list用

`sk_buff_head` 是 Linux 内核网络子系统中用于管理 `sk_buff`(socket buffer)链表的一个结构体,主要用于表示一个双向循环链表的头节点,用来组织多个 `sk_buff` 数据包。它是 Linux 网络栈中实现数据包排队、缓冲和传输的重要基础设施。 ### 1. `sk_buff_head` 的定义 在 Linux 内核源码中(如 `include/linux/skbuff.h`),`sk_buff_head` 的定义如下: ```c struct sk_buff_head { /* These two members must be first. */ struct sk_buff *next; struct sk_buff *prev; __u32 qlen; // 队列中 sk_buff 的数量 spinlock_t lock; // 保护该队列的自旋锁 }; ``` 它本质上是一个**带头节点的双向循环链表**,专门用来链接 `sk_buff` 结构体。 --- ### 2. 关键字段解释 - `next`, `prev`:指向链表中的下一个和上一个 `sk_buff` 节点。注意:虽然这个结构体本身不包含 `sk_buff` 数据,但它通过指针连接一系列 `sk_buff` 实例。 - `qlen`:当前链表中 `sk_buff` 的数量,避免遍历计数,提高性能。 - `lock`:用于多处理器环境下的并发访问保护(自旋锁),确保对链表的操作是线程安全的。 --- ### 3. 初始化函数 内核提供了宏和函数来初始化 `sk_buff_head`: ```c struct sk_buff_head my_list; __skb_queue_head_init(&my_list); ``` 其中 `__skb_queue_head_init()` 定义为: ```c static inline void __skb_queue_head_init(struct sk_buff_head *list) { list->prev = list->next = (struct sk_buff *)list; list->qlen = 0; spin_lock_init(&list->lock); } ``` 这将头节点初始化为指向自己,形成一个空的循环双链表。 --- ### 4. 常用操作函数 #### 添加 skb 到队列头部 ```c struct sk_buff *skb = ...; // 已分配的 sk_buff __skb_queue_head(&my_list, skb); ``` #### 添加到队列尾部 ```c __skb_queue_tail(&my_list, skb); ``` #### 删除并获取第一个 skb ```c struct sk_buff *head_skb = __skb_dequeue(&my_list); if (head_skb) { // 处理数据包 } ``` #### 遍历队列 ```c struct sk_buff *skb; skb_queue_walk(&my_list, skb) { // 对每个 skb 进行处理 printk("Processing skb: %p\n", skb); } ``` --- ### 5. 示例代码:使用 `sk_buff_head` 管理数据包队列 以下是一个简化的内核模块示例(仅供理解用途,不能直接运行): ```c #include <linux/module.h> #include <linux/kernel.h> #include <linux/skbuff.h> #include <linux/netdevice.h> static struct sk_buff_head packet_queue; static int __init queue_init(void) { __skb_queue_head_init(&packet_queue); // 模拟分配几个 sk_buff(实际中需调用 alloc_skb) struct sk_buff *skb1 = alloc_skb(100, GFP_KERNEL); struct sk_buff *skb2 = alloc_skb(100, GFP_KERNEL); if (!skb1 || !skb2) return -ENOMEM; // 将 skb 加入队列尾部 __skb_queue_tail(&packet_queue, skb1); __skb_queue_tail(&packet_queue, skb2); printk(KERN_INFO "Queue length: %u\n", packet_queue.qlen); return 0; } static void __exit queue_exit(void) { struct sk_buff *skb; while ((skb = __skb_dequeue(&packet_queue)) != NULL) { kfree_skb(skb); // 释放 sk_buff } printk(KERN_INFO "Queue cleaned up.\n"); } module_init(queue_init); module_exit(queue_exit); MODULE_LICENSE("GPL"); ``` --- ### 6. 注意事项 - 所有操作通常需要持有 `spinlock`,但内核提供的 `__skb_queue_*` 函数已经内部加锁。 - 若使用 `_irqsave` 版本(如 `skb_queue_head`),会自动保存中断状态,适用于中断上下文。 - `sk_buff_head` 常用于: - 网络设备的发送/接收队列 - 协议层之间的数据包缓存(如 TCP retransmit queue) - 流控机制中的排队规则(qdisc) --- ### 总结 `sk_buff_head` 是 Linux 内核中用于高效管理 `sk_buff` 链表的核心结构,提供线程安全的插入、删除、遍历等操作。它是构建高性能网络协议栈的基础组件之一。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值