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结构的数据缓冲区是不能被修改的。
2、pskb_copy()
与skb_copy()不同,当一个函数不仅需要修改skb描述符,而且需要修改其缓冲区中的数据时,就需要复制缓冲区的数据。如果需要修改的数据在skb->head到skb->end之间,即数据是一个线性空间的数据时,便可调用pskb_copy()函数来完成此操作。
- /**
* pskb_copy - create copy of an sk_buff with private head.
- * 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)
- {
- 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[i] = skb_shinfo(skb)->frags[i];
- get_page(skb_shinfo(n)->frags[i].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;
- }
- 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相同的数据。
- /**