Chinaunix首页 | 论坛 | 博客
  • 博客访问: 8016123
  • 博文数量: 159
  • 博客积分: 10424
  • 博客等级: 少将
  • 技术积分: 14615
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-14 12:45
个人简介

啦啦啦~~~

文章分类
文章存档

2015年(5)

2014年(1)

2013年(5)

2012年(10)

2011年(116)

2010年(22)

分类: LINUX

2011-04-23 21:33:19

本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
    

今天继续skb相关的函数,skb_copy和
  1. struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask)
  2. {
  3.     /*
  4.     用skb的data减去head就是这层协议头部的大小,也许是L2,L3,和L4。 
  5.     */
  6.     int headerlen = skb->data - skb->head;

  7.     /*
  8.      *    Allocate the copy buffer
  9.      */
  10.     struct sk_buff *n;
     /*
     申请新的skb buffer。
     当定义了NET_SKBUFF_DATA_USES_OFFSET时,skb->end实际上为偏移值。
     而没有定义的时候,skb->end为指针。
     这里我有一个疑惑,为什么还要加上skb->data_len,这样的话,申请的内存比原有的skb的size要大了啊。
     因为不加data_len,就已经等于原skb的大小了。
     */
  1. #ifdef NET_SKBUFF_DATA_USES_OFFSET
  2.     n = alloc_skb(skb->end + skb->data_len, gfp_mask);
  3. #else
  4.     n = alloc_skb(skb->end - skb->head + skb->data_len, gfp_mask);
  5. #endif
  6.     if (!n)
  7.         return NULL;
     /*
     为header保留空间 
     */
  1.     /* Set the data pointer */
  2.     skb_reserve(n, headerlen);
  3.     /*
  4.     保留原skb->len的data段大小
  5.     */
  6.     /* Set the tail pointer and length */
  7.     skb_put(n, skb->len);
     
     /*
     完整的复制skb的数据,包括分片
     */
  1.     if (skb_copy_bits(skb, -headerlen, n->head, headerlen + skb->len))
  2.         BUG();
     /*
     复制skb的报文头部信息以及gso信息
     */
  1.     copy_skb_header(n, skb);
  2.     return n;
  3. }
从这个函数可以看出,skb_copy是对skb的数据完整复制,也可以说是深拷贝。

下面看skb_clone
  1. struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
  2. {
  3.     struct sk_buff *n;
     /*
     这里直接让n指向skb的下一个。为啥啊?
     还记得我前面的博文《TCP/IP源码学习(25)——skb申请函数alloc_skb》中的__alloc_skb吗?
   当它的参数fclone为1时,实际上是申请了一组(2个)skb buffer。
   就是为了当申请skb时,马上就要调用这个skb_clone使用的。
     */
  1.     n = skb + 1;
  2.     if (skb->fclone == SKB_FCLONE_ORIG &&
  3.      n->fclone == SKB_FCLONE_UNAVAILABLE) {
  4.         /*
  5.         现在的skb实际上是一组skb,即fclone标志为1。
  6.        那么n是可用的,不需要再次申请了,只需要增加引用计数。
  7.          */
  8.         atomic_t *fclone_ref = (atomic_t *) (n + 1);
  9.         n->fclone = SKB_FCLONE_CLONE;
  10.         atomic_inc(fclone_ref);
  11.     } else {
  12.        /*
  13.        唉~,n是不可用的。只好重新申请skb 
  14.        */
  15.         n = kmem_cache_alloc(skbuff_head_cache, gfp_mask);
  16.         if (!n)
  17.             return NULL;

  18.         kmemcheck_annotate_bitfield(n, flags1);
  19.         kmemcheck_annotate_bitfield(n, flags2);
  20.         n->fclone = SKB_FCLONE_UNAVAILABLE;
  21.     }
     
      /* 拷贝skb中的信息,各种指针等等,并增加数据段的引用计数 */
  1.     return __skb_clone(n, skb);
  2. }
这里skb_clone并没有真正申请数据段,只是申请了一个skb_struct,并让其指针指向原skb的数据段。

对比skb_copy和skb_clone,前者是一个深拷贝,而后者只是一个浅拷贝。



阅读(12749) | 评论(1) | 转发(11) |
给主人留下些什么吧!~~

wdmjj2014-03-13 17:13:32

感谢楼主的辛勤工作:)
关于你提到的,“这里我有一个疑惑,为什么还要加上skb->data_len,这样的话,申请的内存比原有的skb的size要大了啊。”
我想是不是可以这样解释,在定义NET_SKBUFF_DATA_USES_OFFSET时,skb->head和skb->end都只表示各自域的大小(至于end里的内容我不太确定),再加上data的长度才是整个skb的size.