dreamice 发表于 2011-03-01 21:58

【班门弄斧一下】skb_clone分析

1、skb_clone()
Skb_clone()函数只是复制sk_buff结构,并不复制skb的数据缓冲区。Clone后的sk_buff结构与原始的sk_buff指向同一数据缓冲区。原始的和clone后的skb描述符的cloned值都会被置1,clone的skb描述符的users值置1,同时数据缓冲区的引用计数dataref增加1。/**
*    skb_clone    -    duplicate an sk_buff
*    @skb: buffer to clone
*    @gfp_mask: allocation priority
*
*    Duplicate an &sk_buff. The new one is not owned by a socket. Both
*    copies share the same packet data but not structure. The new
*    buffer has a reference count of 1. If the allocation fails the
*    function returns %NULL otherwise the new buffer is returned.
*
*    If this function is called from an interrupt gfp_mask() must be
*    %GFP_ATOMIC.
*/

struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
{
    struct sk_buff *n;

    /* n指向被clone的skb */
    n = skb + 1;
    /* 判断原始skb是否是从skbuff_fclone_cache 缓冲区中分配的,从skbuff_fclone_cache 分配将预先为clone的skb分配好内存,同时判定该预先分配的clone skb是否被使用 */
    if (skb->fclone == SKB_FCLONE_ORIG &&
   n->fclone == SKB_FCLONE_UNAVAILABLE) {
      /* 预先从skbuff_fclone_cache 中分配的skb结构,且未使用,则增加dataref计数*/
      atomic_t *fclone_ref = (atomic_t *) (n + 1);
      n->fclone = SKB_FCLONE_CLONE; /* 置clone的skb中fclone值为SKB_FCLONE_CLONE ,标明其数据区指向原始skb同一数据区 */
      atomic_inc(fclone_ref);
    } else {
      /* 主skb并未同时分配clone skb的情况,将重新独立分配skb结构作为clone的skb */
      n = kmem_cache_alloc(skbuff_head_cache, gfp_mask);
      if (!n)
            return NULL;

      kmemcheck_annotate_bitfield(n, flags1);
      kmemcheck_annotate_bitfield(n, flags2);
      /* 指明该clone的skb并未分配独立的数据缓冲区 */
      n->fclone = SKB_FCLONE_UNAVAILABLE;
    }
    /* 完成后续的skb结构体的复制工作 */
    return __skb_clone(n, skb);
}


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

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

    C(len);
    C(data_len);
    C(mac_len);
    n->hdr_len = skb->nohdr ? skb_headroom(skb) : skb->hdr_len;
    n->cloned = 1;
    n->nohdr = 0;
    n->destructor = NULL;
    C(tail);
    C(end);
    C(head);
    C(data);
    C(truesize);
    /* 设置skb描述符的users为1 */
    atomic_set(&n->users, 1);

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

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

Godbach 发表于 2011-03-01 22:28

dreamice 兄的好文。
等了半天没敢回复,怕是后面还有呢。

dreamice 发表于 2011-03-01 23:34

dreamice 兄的好文。
等了半天没敢回复,怕是后面还有呢。
Godbach 发表于 2011-03-01 22:28 http://linux.chinaunix.net/bbs/images/common/back.gif


    一天写点呢,呵呵

accessory 发表于 2011-03-02 00:29

支持下 :)

amarant 发表于 2011-03-02 14:13

学习

dreamice 发表于 2011-03-02 16:23

晚上继续,不过楼层已经被占了:em17:

Godbach 发表于 2011-03-02 16:34

晚上继续,不过楼层已经被占了
dreamice 发表于 2011-03-02 16:23 http://linux.chinaunix.net/bbs/images/common/back.gif
你当时应该预留几个嘛 :mrgreen:

dreamice 发表于 2011-03-02 21:03

本帖最后由 dreamice 于 2011-03-02 21:04 编辑

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

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

* @skb: buffer to copy

* @gfp_mask: allocation priority

*

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

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

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

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

* or the pointer to the buffer on success.

* The returned buffer has a reference count of 1.

*/



struct sk_buff *pskb_copy(struct sk_buff *skb, gfp_t gfp_mask)

{

       /*

      * Allocate the copy buffer

      */

       struct sk_buff *n;

/* 先申请skb描述符字段的内存空间,在这种情况下,skb描述符是不能继用预先分配的skb描述符的 */

#ifdef NET_SKBUFF_DATA_USES_OFFSET

       n = alloc_skb(skb->end, gfp_mask);

#else

       n = alloc_skb(skb->end - skb->head, gfp_mask);

#endif

       if (!n)

            goto out;



       /* Set the data pointer */

    /* 设置数据指针 */

       skb_reserve(n, skb->data - skb->head);

       /* Set the tail pointer and length */

    /* 设置skb->tail指针和skb->len 长度 */

       skb_put(n, skb_headlen(skb));

       /* Copy the bytes */

    /* 拷贝线性空间的数据 */

       skb_copy_from_linear_data(skb, n->data, n->len);

   

    /* 对share info结构进行拷贝,并设置相关字段的值 */

       n->truesize += skb->data_len;

       n->data_len = skb->data_len;

       n->len = skb->len;



       if (skb_shinfo(skb)->nr_frags) {

            int i;



      /*在share info中有数据的情况下,拷贝share字段,特别注意:这里并没有拷贝share info中的数据 */

            for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {

                     skb_shinfo(n)->frags = skb_shinfo(skb)->frags;

                     get_page(skb_shinfo(n)->frags.page);

            }

            skb_shinfo(n)->nr_frags = i;

       }



       if (skb_has_frags(skb)) {

            skb_shinfo(n)->frag_list = skb_shinfo(skb)->frag_list;

            skb_clone_fraglist(n);

       }



    /* 拷贝skb头部的相关字段 */

       copy_skb_header(n, skb);

out:

       return n;

}



/**

* skb_reserve - adjust headroom

* @skb: buffer to alter

* @len: bytes to move

*

* Increase the headroom of an empty &sk_buff by reducing the tail

* room. This is only allowed for an empty buffer.

*/

static inline void skb_reserve(struct sk_buff *skb, int len)

{

       skb->data += len;

       skb->tail += len;

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

dreamice 发表于 2011-03-02 21:05

你当时应该预留几个嘛
Godbach 发表于 2011-03-02 16:34 http://linux.chinaunix.net/bbs/images/common/back.gif


    你能提升楼层不?不行等我写完了合并到第一楼去,呵呵

Godbach 发表于 2011-03-03 10:46

这个估计得问一下管理员了。
页: [1] 2
查看完整版本: 【班门弄斧一下】skb_clone分析