1 skb克隆
在什么时候需要对Socket Buffer进行克隆,某些时候,同一个Socket Buffer会由不同的进程进行独立处理,但这些进程所需要操作的只是sk_buff数据结构描述符,不需要对数据包本身做改动。这是为了提高处理性能,内核不需要对整个Socket Buffer(sk_buff数据结构和数据包缓冲区)做完全的复制,只对sk_buff数据结构做完全的复制,并对数据包的引用计数(dataref)加1,以此防止在还有进程使用该Socket Buffer的数据包情况下,缓冲区被释放。这就是sk_buff克隆函数的功能。这时原sk_buff和克隆的ks_buff共享同一个数据包。实现克隆操作的函数为:struct sk_buff *sk_clone(struct sk_buff *skb, gfp_t gtp_mask)。skb_clone产生一个队skb的克隆数据结构,返回指向克隆出来的sk_buff数据结构的指针。
克隆的sk_buff都具有以下特点:
- 不放任何sk_buff的管理队列
- 不属于任何套接字,即没有任何套接字拥有克隆的sk_buff.
- 两个sk_buff的结构的skb->cloned域都需要设置为1,克隆出来的sk_buff的sk->users域应设置为1,这样当要释放克隆出来的sk_buff数据结构时,第一次对他的释放就能成功了。
- 当一个sk_buff被克隆后,他的数据包中的值就不能再修改,这时我们访问数据包是可以不加锁,因为只能对数据包作读操作,也就不存在并发访问的问题
2 skb_clone函数
struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
{
struct sk_buff_fclones *fclones = container_of(skb,
struct sk_buff_fclones,
skb1);
struct sk_buff *n = &fclones->skb2;
if (skb_orphan_frags(skb, gfp_mask))
return NULL;
if (skb->fclone == SKB_FCLONE_ORIG &&
n->fclone == SKB_FCLONE_FREE) {
n->fclone = SKB_FCLONE_CLONE;
atomic_inc(&fclones->fclone_ref);
} else {
if (skb_pfmemalloc(skb))
gfp_mask |= __GFP_MEMALLOC;
n = kmem_cache_alloc(skbuff_head_cache, gfp_mask);
if (!n)
return NULL;
kmemcheck_annotate_bitfield(n, flags1);
n->fclone = SKB_FCLONE_UNAVAILABLE;
}
return __skb_clone(n, skb);
}
实际调用的时__skb_clone
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_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(head_frag);
C(data);
C(truesize);
atomic_set(&n->users, 1); //设置标识位
atomic_inc(&(skb_shinfo(skb)->dataref)); //引用计数加1
skb->cloned = 1; //克隆标识
return n;
#undef C
}
__skb_clone主要做了4件事
- 对Socket Buffer数据结构各元素做复制
- skb->cloned设置为1
- 克隆的数据包skb->users设置为1
- skb->dataref引用计数加1