skb操作

套接字缓存之skb_put、skb_push、skb_pull、skb_reserve

skb操作中的预留和对齐操作主要由skb_put、skb_push、skb_pull、skb_reserve完成;这几个函数的区别通过下面图(图片来自:深入理解linux网络技术内幕)可以清晰的区分;另外,需要注意的是skb_reserve只能操作空skb,即在分配了空间,尚未填充数据时调用;

 

以下为四个函数的源码分析;

 1 /**
 2  *    skb_put - add data to a buffer
 3  *    @skb: buffer to use
 4  *    @len: amount of data to add
 5  *
 6  *    This function extends the used data area of the buffer. If this would
 7  *    exceed the total buffer size the kernel will panic. A pointer to the
 8  *    first byte of the extra data is returned.
 9  */
10 /*
11     向skb尾部添加数据
12 */
13 unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
14 {
15     /* 获取当前skb->tail */
16     unsigned char *tmp = skb_tail_pointer(skb);
17 
18     /* 要求skb数据区必须为线性 */
19     SKB_LINEAR_ASSERT(skb);
20     
21     /* skb尾部增加len字节 */
22     skb->tail += len;
23     /* skb数据总长度增加len字节 */
24     skb->len  += len;
25 
26     /* 如果增加之后的tail > end ,则panic */
27     if (unlikely(skb->tail > skb->end))
28         skb_over_panic(skb, len, __builtin_return_address(0));
29     
30     //返回添加数据的第一个字节位置
31     return tmp;
32 }

 

 1 /**
 2  *    skb_push - add data to the start of a buffer
 3  *    @skb: buffer to use
 4  *    @len: amount of data to add
 5  *
 6  *    This function extends the used data area of the buffer at the buffer
 7  *    start. If this would exceed the total buffer headroom the kernel will
 8  *    panic. A pointer to the first byte of the extra data is returned.
 9  */
10 /*
11     向skb数据区头部添加数据
12 */
13 unsigned char *skb_push(struct sk_buff *skb, unsigned int len)
14 {
15     /* 数据区data指针前移len字节 */
16     skb->data -= len;
17     /* 数据总长度增加len字节 */
18     skb->len  += len;
19 
20     /* 添加数据长度溢出过header ,panic*/
21     if (unlikely(skb->data<skb->head))
22         skb_under_panic(skb, len, __builtin_return_address(0));
23 
24     /* 返回新的data指针 */
25     return skb->data;
26 }

 

 1 /**
 2  *    skb_pull - remove data from the start of a buffer
 3  *    @skb: buffer to use
 4  *    @len: amount of data to remove
 5  *
 6  *    This function removes data from the start of a buffer, returning
 7  *    the memory to the headroom. A pointer to the next data in the buffer
 8  *    is returned. Once the data has been pulled future pushes will overwrite
 9  *    the old data.
10  */
11 /*
12     从数据区头部移除数据
13 */
14 unsigned char *skb_pull(struct sk_buff *skb, unsigned int len)
15 {
16     return skb_pull_inline(skb, len);
17 }

 

1 /* 根据移除数据长度判断函数调用 */
2 static inline unsigned char *skb_pull_inline(struct sk_buff *skb, unsigned int len)
3 {
4     /* 
5         移除长度> skb数据总长度,返回NULL
6         否则,继续调用_skb_pull函数
7     */
8     return unlikely(len > skb->len) ? NULL : __skb_pull(skb, len);
9 }

 

 1 /* 从skb数据区头部移除数据 */
 2 static inline unsigned char *__skb_pull(struct sk_buff *skb, unsigned int len)
 3 {
 4     /* 数据总长度减去len字节 */
 5     skb->len -= len;
 6     /* 数据总长度是否有异常 */
 7     BUG_ON(skb->len < skb->data_len);
 8 
 9     /* 
10         data指针移动len字节
11         返回移除之后新的data指针
12     */
13     return skb->data += len;
14 }

 

 1 /*
 2     保留头部空间,只能对空的skb使用
 3 */
 4 static inline void skb_reserve(struct sk_buff *skb, int len)
 5 {
 6     /* 数据区data指针增加len字节*/
 7     skb->data += len;
 8     /* 数据区tail指针增加len字节 */
 9     skb->tail += len;
10 }

https://www.cnblogs.com/wanpengcoder/p/7529512.html

 

kfree_skb()只在skb->users为1的情况下才释放内存,否则只简单地递减skb->users,因此假设SKB有三个引用者,那么只有第三次调用dev_kfree_skb()或kfree_skb()时才释放内存。

 

dev_kfree_skb()只是一个简单调用kfree_skb()的宏。


__kfree_skb绕过了对skb引用计数的判断,一般来说,在内核中函数名前面加“__”的都提示要小心使用,也就是它略去了一些检查,于是在调用这类函数之前要做检查。


kfree()调用了slab_free()并不会释放skb的线性区及分片等。


kfree_skb()释放一个SKB的步骤:

1)kfree_skb()检测sk_buff结构的引用计数users,如果不为1,则说明此次释放后该SKB还将被用户占用,
因此递减引用计数users后即返回;否则说明不再有其他用户占用该sk_buff结构,调用__kfree_skb()释放之。

2)SKB描述符中包含一个dst_entry结构的引用,在释放SKB后,会调用dst_release()来递减dst_entry结构的引用计数。

3)如果初始化了SKB的析构函数,则调用相应的函数。

4)一个SKB描述符是与一个存有真正数据的内存块,即数据区相关的。如果存在聚合分散I/O数据,
该数据区底部的skb_shared_info结构还会包含指向聚合分散I/O数据的指针,同样需要释放这些分片所占用的内存。

https://blog.csdn.net/qq_33336155/article/details/53640324?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160791768119195265161980%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=160791768119195265161980&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v29-1-53640324.pc_search_result_cache&utm_term=dev_kfree_skb&spm=1018.2118.3001.4449

alloc_skb--分配skb

dev_alloc_skb--分配skb,通常被设备驱动用在中断上下文中,它是alloc_skb的封装函数,因为在中断处理函数中被调用,因此要求原子操作(GFP_ATOMIC)

kfree_skb--减少skb引用,为0则释放,用于出错丢包时释放skb使用;

dev_kfree_skb==consume_skb--减少skb引用,为0则释放,成功状态下释放skb使用;

---free系列---

 1 /**
 2  *    kfree_skb - free an sk_buff
 3  *    @skb: buffer to free
 4  *
 5  *    Drop a reference to the buffer and free it if the usage count has
 6  *    hit zero.
 7  */
 8 /*
 9     释放skb
10 */
11 void kfree_skb(struct sk_buff *skb)
12 {
13     if (unlikely(!skb))
14         return;
15     /* 引用为1,可直接释放 */
16     if (likely(atomic_read(&skb->users) == 1))
17         smp_rmb();
18     /*
19         对引用减1,并且判断,如果结果不为0
20         说明还有引用,返回
21     */
22     else if (likely(!atomic_dec_and_test(&skb->users)))
23         return;
24     trace_kfree_skb(skb, __builtin_return_address(0));
25 
26     //真正的skb释放
27     __kfree_skb(skb);
28 }

 

 1 /**
 2  *    __kfree_skb - private function
 3  *    @skb: buffer
 4  *
 5  *    Free an sk_buff. Release anything attached to the buffer.
 6  *    Clean the state. This is an internal helper function. Users should
 7  *    always call kfree_skb
 8  */
 9 /* 释放skb */
10 void __kfree_skb(struct sk_buff *skb)
11 {
12     /* 释放skb附带的所有数据 */
13     skb_release_all(skb);
14     /* 释放skb */
15     kfree_skbmem(skb);
16 }

 

1 #define dev_kfree_skb(a)    consume_skb(a)

 

 1 /**
 2  *    consume_skb - free an skbuff
 3  *    @skb: buffer to free
 4  *
 5  *    Drop a ref to the buffer and free it if the usage count has hit zero
 6  *    Functions identically to kfree_skb, but kfree_skb assumes that the frame
 7  *    is being dropped after a failure and notes that
 8  */
 9 /* 释放skb,与kfree_skb区别是,kfree_skb用于失败时丢包释放 */
10 void consume_skb(struct sk_buff *skb)
11 {
12     if (unlikely(!skb))
13         return;
14     if (likely(atomic_read(&skb->users) == 1))
15         smp_rmb();
16     else if (likely(!atomic_dec_and_test(&skb->users)))
17         return;
18     trace_consume_skb(skb);
19     __kfree_skb(skb);
20 }

 

转载于:https://www.cnblogs.com/wanpengcoder/p/7529576.html

中断处理的有趣部分处理"发送结束"的情况. 在这个情况下, 统计量被更新, 调用 dev_kfree_skb 来返回 socket 缓存给系统. 实际上, 有这个函数的 3 个变体可以调用:

dev_kfree_skb(struct sk_buff *skb);

这个版本应当在你知道你的代码不会在中断上下文中运行时调用. 因为 snull 没有实际的硬件中断, 我们使用这个版本.

dev_kfree_skb_irq(struct sk_buff *skb);

如果你知道会在中断处理中释放缓存, 使用这个版本, 它对这个情况做了优化.

dev_kfree_skb_any(struct sk_buff *skb);

如果相关代码可能在中断或非中断上下文运行时, 使用这个版本.

https://blog.csdn.net/mounter625/article/details/55253454

dev_kfree_skb_any是一个释放skb的封装函数,它根据是否在irq环境下,决定直接释放skb还是等softirq去释放skb

void dev_kfree_skb_any(struct sk_buff *skb)
{
 if (in_irq() || irqs_disabled())
  dev_kfree_skb_irq(skb); //将skb放在softnet_data->completion_queue的首部,等待softirq释放。
 else
  dev_kfree_skb(skb); // 直接释放
}
EXPORT_SYMBOL(dev_kfree_skb_any);

来看一下dev_kfree_skb_irq,它的功能其实就是将skb放入softnet_data的completion_queue队列头部

void dev_kfree_skb_irq(struct sk_buff *skb)
{
  if (atomic_dec_and_test(&skb->users)) {
   struct softnet_data *sd;
   unsigned long flags;

  local_irq_save(flags);
   sd = &__get_cpu_var(softnet_data);
   skb->next = sd->completion_queue; // skb放在softnet_data->completion_queue的首部
   sd->completion_queue = skb;
// 触发softirq, NET_TX_SOFTIRQ 软中断处理函数net_tx_action处理的第一件事情就是遍历completion_queue
// 然后释放skb
   raise_softirq_irqoff(NET_TX_SOFTIRQ);
   local_irq_restore(flags);
  }
}
EXPORT_SYMBOL(dev_kfree_skb_irq);

http://blog.chinaunix.net/uid-26902809-id-4106241.html?utm_source=jiancool

复制代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值