alloc_skb()用于分配缓冲区的函数。由于"数据缓冲区"和"缓冲区的描述结构"(sk_buff结构)是两种不同的实体,这就意味着,在分配一个缓冲区时,需要分配两块内存(一个是缓冲区,一个是缓冲区的描述结构sk_buff)。
首先看alloc_skb
static inline struct sk_buff *alloc_skb(unsigned int size, gfp_t priority){ return __alloc_skb(size, priority, 0, -1);}这个函数比较简单,参数中的size不用解释,为skb数据段的大小,但是第二个参数priority名字比较奇怪。叫优先级,实际上则是GFP MASK宏,如GFP_KERNEL,GFP_ATOMIC等。 __alloc_skb()调用kmem_cache_alloc()从缓存中获取一个sk_buff结构,并调用kmalloc_track_caller分配缓冲区接下来看__alloc_skb
/*参数:size:skb的数据大小gfp_mask:不用解释fclone:表示从哪个cache中分配 当fclone为1时,从skbuff_fclone_cache上分配 当fclone为0时,从skbuff_head_cache上分配node: NUMA节点*/struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, int fclone, int node){ struct kmem_cache *cache; struct skb_shared_info *shinfo; struct sk_buff *skb; u8 *data; /* 获得指定的cache */ cache = fclone ? skbuff_fclone_cache : skbuff_head_cache; /* 从cache上分配, 如果cache上无法分配,则从内存中申请 */ /* Get the HEAD */ skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node); if (!skb) goto out; prefetchw(skb);size = SKB_DATA_ALIGN(size);
data = kmalloc_node_track_caller(size + sizeof(struct skb_shared_info), gfp_mask, node); if (!data) goto nodata; prefetchw(data + size);/*
* Only clear those fields we need to clear, not those that we will * actually initialise below. Hence, don't put any more fields after * the tail pointer in struct */ memset(skb, 0, offsetof(struct sk_buff, tail)); skb->truesize = size + sizeof(struct sk_buff); atomic_set(&skb->users, 1); skb->head = data; skb->data = data; skb_reset_tail_pointer(skb); skb->end = skb->tail + size; kmemcheck_annotate_bitfield(skb, flags1); kmemcheck_annotate_bitfield(skb, flags2);#ifdef NET_SKBUFF_DATA_USES_OFFSET skb->mac_header = ~0U;#endif/* make sure we initialize shinfo sequentially */
shinfo = skb_shinfo(skb); memset(shinfo, 0, offsetof(struct skb_shared_info, dataref)); atomic_set(&shinfo->dataref, 1);if (fclone) {
/* 如果是fclone cache的话,那么skb的下一个buf,也将被分配*/ struct sk_buff *child = skb + 1; atomic_t *fclone_ref = (atomic_t *) (child + 1);kmemcheck_annotate_bitfield(child, flags1);
kmemcheck_annotate_bitfield(child, flags2); skb->fclone = SKB_FCLONE_ORIG; atomic_set(fclone_ref, 1);child->fclone = SKB_FCLONE_UNAVAILABLE;
}out: return skb;nodata: kmem_cache_free(cache, skb); skb = NULL; goto out;}这里有两个cache,skbuff_fclone_cache和skbuff_head_cache。它们两个的区别是前者是每两个skb为一组。当从skbuff_fclone_cache分配skb时,会两个连续的skb一起分配,但是释放的时候可以分别释放。也就是说当调用者知道需要两个skb时,如后面的操作很可能使用skb_clone时,那么从skbuff_fclone_cache上分配skb会更高效一些。skb的分配细节
1. 关于 SKB 的分配细节.LINUX 中 SKB 的分配最终是由函数 : struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,int fclone) 来完成.
SKB 可以分为 SKB 描述符与 SKB 数据区两个部分,其中描述符必须从 CACHE 中来分配 : 或者从skbuff_fclone_cache 中分配,或者从 skbuff_head_cache 中来分配.如果从分配描述符失败,则直接反回 NULL,表示 SKB 分配失败.SKB 描述符分配成功后,即可分配数据区.
在具体分配数据区之前首先要对数据区的长度进行 ALIGN 操作, 通过宏 SKB_DATA_ALIGN 来重新确定 size 大小. 然后调用 kmalloc 函数分配数据区 :data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);需要注意的是数据区的大小是 SIZE 的大小加上 skb_shared_info 结构的大小.数据区分配成功后,便对 SKB 描述符进行与此数据区相关的赋值操作 :
memset(skb, 0, offsetof(struct sk_buff, truesize));skb->truesize = size + sizeof(struct sk_buff);atomic_set(&skb->users, 1);skb->head = data;skb->data = data;skb->tail = data;skb->end = data + size;需要主意的是, SKB 的 truesize 的大小并不包含 skb_shared_info 结构的大小. 另外,skb 的 end 成员指针也就事 skb_shared_info 结构的起始指针,系统用一个宏 : skb_shinfo 来完成寻找 skb_shared_info 结构指针的操作.最后,系统初始化 skb_shared_info 结构的成员变量 :
atomic_set(&(skb_shinfo(skb)->dataref), 1);skb_shinfo(skb)->nr_frags = 0;skb_shinfo(skb)->tso_size = 0;skb_shinfo(skb)->tso_segs = 0;skb_shinfo(skb)->frag_list = NULL;skb_shinfo(skb)->ufo_size = 0;skb_shinfo(skb)->ip6_frag_id = 0;最后,返回 SKB 的指针.
2. SKB 的分配时机
SKB 的分配时机主要有两种,最常见的一种是在网卡的中断中,有数据包到达的时,系统分配 SKB 包进行包处理; 第二种情况是主动分配 SKB 包用于各种调试或者其他处理环境.3. SKB 的 reserve 操作
SKB 在分配的过程中使用了一个小技巧 : 即在数据区中预留了 128 个字节大小的空间作为协议头使用, 通过移动 SKB 的 data 与 tail 指针的位置来实现这个功能.4. SKB 的 put 操作
put 操作是 SKB 中一个非常频繁也是非常重要的操作, 胆识, skb_put()函数其实什么也没做!它只是根据数据的长度移动了 tail 指针并改写了 skb->len 的值,其他的什么都没做,然后就返回了 skb->data 指针(就是 tail 指针在移动之前的位置). 看上去此函数仿佛要拷贝数据到 skb 的数据区中,其实这事儿是 insl 这个函数干的,跟 skb_put() 函数毫不相关,不过它仍然很重要.5. 中断环境下 SKB 的分配流程
当数据到达网卡后,会触发网卡的中断,从而进入 ISR 中,系统会在 ISR 中计算出此次接收到的数据的字节数 : pkt_len, 然后调用 SKB 分配函数来分配 SKB : skb = dev_alloc_skb(pkt_len+5);我们可以看到, 实际上传入的数据区的长度还要比实际接收到的字节数多,这实际上是一种保护机制. 实际上,在 dev_alloc_skb 函数调用 __dev_alloc_skb 函数,而 __dev_alloc_skb 函数又调用 alloc_skb 函数 时,其数据区的大小又增加了 128 字节, 这 128 字节就事前面我们所说的 reserve 机制预留的 header 空间. 本文参考资料:感谢作者的分享http://blog.chinaunix.net/uid-23629988-id-257902.htmlhttp://blog.chinaunix.net/uid-1728743-id-24312.html--------------------- 作者:不会游泳的鱼star 来源:CSDN 原文:https://blog.csdn.net/star_xiong/article/details/17588629 版权声明:本文为博主原创文章,转载请附上博文链接!