SKB几个复制函数的区别

 

1、skb_clone()

Skb_clone()函数只是复制sk_buff结构,并不复制skb的数据缓冲区。Clone后的sk_buff结构与原始的sk_buff指向同一数据缓冲区。原始的和clone后的skb描述符的cloned值都会被置1,clone的skb描述符的users值置1,同时数据缓冲区的引用计数dataref增加1

  1. /**
  2.  *    skb_clone    -    duplicate an sk_buff
  3.  *    @skb: buffer to clone
  4.  *    @gfp_mask: allocation priority
  5.  *
  6.  *    Duplicate an &sk_buff. The new one is not owned by a socket. Both
  7.  *    copies share the same packet data but not structure. The new
  8.  *    buffer has a reference count of 1. If the allocation fails the
  9.  *    function returns %NULL otherwise the new buffer is returned.
  10.  *
  11.  *    If this function is called from an interrupt gfp_mask() must be
  12.  *    %GFP_ATOMIC.
  13.  */
  1. struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
  2. {
  3.     struct sk_buff *n;

  4.     /* n指向被clone的skb */
  5.     n = skb + 1;
  6.     /* 判断原始skb是否是从skbuff_fclone_cache 缓冲区中分配的,从skbuff_fclone_cache 分配将预先为clone的skb分配好内存,同时判定该预先分配的clone skb是否被使用 */
  7.     if (skb->fclone == SKB_FCLONE_ORIG &&
  8.      n->fclone == SKB_FCLONE_UNAVAILABLE) {
  9.         /* 预先从skbuff_fclone_cache 中分配的skb结构,且未使用,则增加dataref计数*/
  10.         atomic_t *fclone_ref = (atomic_t *) (n + 1);
  11.         n->fclone = SKB_FCLONE_CLONE; /* 置clone的skb中fclone值为SKB_FCLONE_CLONE ,标明其数据区指向原始skb同一数据区 */
  12.         atomic_inc(fclone_ref);
  13.     } else {
  14.         /* 主skb并未同时分配clone skb的情况,将重新独立分配skb结构作为clone的skb */
  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.         /* 指明该clone的skb并未分配独立的数据缓冲区 */
  21.         n->fclone = SKB_FCLONE_UNAVAILABLE;
  22.     }
  23.     /* 完成后续的skb结构体的复制工作 */
  24.     return __skb_clone(n, skb);
  25. }


  26. /*
  27.  * You should not add any new code to this function. Add it to
  28.  * __copy_skb_header above instead.
  29.  */
  30. static struct sk_buff *__skb_clone(struct sk_buff *n, struct sk_buff *skb)
  31. {
  32. #define C(x) n->x = skb->x

  33.     n->next = n->prev = NULL;
  34.     n->sk = NULL;
  35.     /* copy 头部字段,详细请参考源代码,很简单 */
  36.     __copy_skb_header(n, skb);

  37.     C(len);
  38.     C(data_len);
  39.     C(mac_len);
  40.     n->hdr_len = skb->nohdr ? skb_headroom(skb) : skb->hdr_len;
  41.     n->cloned = 1;
  42.     n->nohdr = 0;
  43.     n->destructor = NULL;
  44.     C(tail);
  45.     C(end);
  46.     C(head);
  47.     C(data);
  48.     C(truesize);
  49.     /* 设置skb描述符的users为1 */
  50.     atomic_set(&n->users, 1);

  51.     /* 增加shinfo中dataref的引用计数,因为clone的skb与原始skb指向同一数据缓冲区*/
  52.     atomic_inc(&(skb_shinfo(skb)->dataref));
  53.     skb->cloned = 1; /* 指明原始skb是被clone过的 */

  54.     return n;
  55. #undef C
  56. }
  57. 特别说明,skb_clone()函数复制的只是skb描述符,而复制后的skb与原始skb指向的是同一数据缓冲区,由于数据缓冲区并未加什么同步锁机制,因此skb_clone()操作的skb结构的数据缓冲区是不能被修改的

    2、pskb_copy()

    与skb_copy()不同,当一个函数不仅需要修改skb描述符,而且需要修改其缓冲区中的数据时,就需要复制缓冲区的数据。如果需要修改的数据在skb->head到skb->end之间,即数据是一个线性空间的数据时,便可调用pskb_copy()函数来完成此操作。

    1. /**
       * pskb_copy - create copy of an sk_buff with private head.

    2.  * Make a copy of both an &sk_buff and part of its data, located

    3.  * in header. Fragmented data remain shared. This is used when

    4.  * the caller wishes to modify only header of &sk_buff and needs

    5.  * private copy of the header to alter. Returns %NULL on failure

    6.  * or the pointer to the buffer on success.

    7.  * The returned buffer has a reference count of 1.

    8.  */

    9. struct sk_buff *pskb_copy(struct sk_buff *skb, gfp_t gfp_mask)
    10. {

    11.        struct sk_buff *n;
    12. /* 先申请skb描述符字段的内存空间,在这种情况下,skb描述符是不能继用预先分配的skb描述符的 */
    13. #ifdef NET_SKBUFF_DATA_USES_OFFSET
    14.        n = alloc_skb(skb->end, gfp_mask);
    15. #else
    16.        n = alloc_skb(skb->end - skb->head, gfp_mask);
    17. #endif
             if (!n)
                    goto out;
    18.        /* Set the data pointer */
          /* 设置数据指针 */
             skb_reserve(n, skb->data - skb->head);
             /* Set the tail pointer and length */
          /* 设置skb->tail指针和skb->len 长度 */
    19.        skb_put(n, skb_headlen(skb));
             /* Copy the bytes */
          /* 拷贝线性空间的数据 */
             skb_copy_from_linear_data(skb, n->data, n->len);
    20.     /* 对share info结构进行拷贝,并设置相关字段的值 */
    21.        n->truesize += skb->data_len;

    22.        n->data_len = skb->data_len;
    23.        n->len = skb->len;
    24.        if (skb_shinfo(skb)->nr_frags) {
                    int i;
    25.         /*在share info中有数据的情况下,拷贝share字段,特别注意:这里并没有拷贝share info中的数据 */
    26.               for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
    27.                      skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i];
    28.                      get_page(skb_shinfo(n)->frags[i].page);

    29.               }
    30.               skb_shinfo(n)->nr_frags = i;

    31.        }

    32.        if (skb_has_frags(skb)) {
                    skb_shinfo(n)->frag_list = skb_shinfo(skb)->frag_list;
    33.               skb_clone_fraglist(n);
    34.        }
    35.     /* 拷贝skb头部的相关字段 */

    36.        copy_skb_header(n, skb);

    37. out:
    38.        return n;

    39. }

    40. static inline void skb_reserve(struct sk_buff *skb, int len
    41. {
    42.        skb->data += len;
    43.        skb->tail += len;
    44. }

    特别说明:pskb_copy()与skb_copy()更重量级一些,他不仅仅拷贝skb描述符,还需要拷贝skb->data指向的数据,但他并不拷贝share info指向的非线性数据,新skb的share info指向与原始skb的share info相同的数据。

`skb_release` 函数是 Linux 内核中用来释放 `skb` 数据结构(网络数据包缓冲区)的函数。它的定义在文件 `net/core/skbuff.c` 中。 下面是 `skb_release` 函数的代码及注释: ```c void skb_release(struct sk_buff *skb) { struct nf_hook_state state; unsigned int i; /* Call the packet destroy function for nf_hooks */ if (unlikely(nf_hooks_needed(skb))) nf_hook_state_init(&state, NFPROTO_UNSPEC, NF_HOOK_INVALID, NULL, skb, NULL, dev_net(skb->dev), 0); for (i = 0; i < NFPROTO_NUMPROTO; i++) nf_hook(NFPROTO(i), NF_INET_PRE_ROUTING, &state, skb, skb->dev, NULL, skb_dst(skb)->dev, NULL, skb->protocol); /* Call the destructor for each extension header. */ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { if (skb_shinfo(skb)->frags[i].page) kmap_atomic(skb_shinfo(skb)->frags[i].page); if (skb_shinfo(skb)->destructor[i]) skb_shinfo(skb)->destructor[i](skb); if (skb_shinfo(skb)->frags[i].page) kunmap_atomic(skb_shinfo(skb)->frags[i].page); } if (skb_shinfo(skb)->frag_list) kfree_skb_list(skb_shinfo(skb)->frag_list); /* Release the skb itself */ kmem_cache_free(skbuff_head_cache, skb); } ``` 该函数的作用是释放 `skb` 数据结构,它的主要步骤包括: 1. 如果 `skb` 上注册了网络钩子(`nf_hook`),则依次调用每个网络钩子的销毁函数(`nf_hook` 函数的第 4 个参数)。 2. 对于 `skb` 中的每个扩展头,调用其析构函数(`destructor`)进行清理,以释放扩展头占用的内存。 3. 如果 `skb` 中存在分段数据(`frag_list`),则释放分段数据占用的内存。 4. 最后,释放 `skb` 本身占用的内存。 需要注意的是,该函数只是释放 `skb` 数据结构本身占用的内存,但并不会释放 `skb` 引用的其他内存(例如数据包的内容、关联的网络设备等)。这些内存的释放由其他函数负责。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值