【班门弄斧一下】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结构的数据缓冲区是不能被修改的。 dreamice 兄的好文。
等了半天没敢回复,怕是后面还有呢。 dreamice 兄的好文。
等了半天没敢回复,怕是后面还有呢。
Godbach 发表于 2011-03-01 22:28 http://linux.chinaunix.net/bbs/images/common/back.gif
一天写点呢,呵呵 支持下 :) 学习 晚上继续,不过楼层已经被占了:em17: 晚上继续,不过楼层已经被占了
dreamice 发表于 2011-03-02 16:23 http://linux.chinaunix.net/bbs/images/common/back.gif
你当时应该预留几个嘛 :mrgreen: 本帖最后由 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相同的数据。 你当时应该预留几个嘛
Godbach 发表于 2011-03-02 16:34 http://linux.chinaunix.net/bbs/images/common/back.gif
你能提升楼层不?不行等我写完了合并到第一楼去,呵呵 这个估计得问一下管理员了。
页:
[1]
2