skb_buff操作

skb_buff的数据空间

空间分配

相关操作函数

对skb_buff结构体的相关操作,并不是直接修改相关数据,内核提供了一系列的函数用来操作此结构体

skb_put

向后扩大数据区空间。headroom空间不变,tailroom空间降低,skb->data指针不变,skb->tail指针下移;

unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
{
     /* 获取当前skb->tail */
     unsigned char *tmp = skb_tail_pointer(skb);
 
     /* 要求skb数据区必须为线性 */
     SKB_LINEAR_ASSERT(skb);
     
     /* skb尾部增加len字节 */
     skb->tail += len;
     /* skb数据总长度增加len字节 */
     skb->len  += len;
 
     /* 如果增加之后的tail > end ,则panic */
     if (unlikely(skb->tail > skb->end))
         skb_over_panic(skb, len, __builtin_return_address(0));
     
     //返回添加数据的第一个字节位置
     return tmp;
 }

skb_push

向前扩大数据区空间。headroom空间降低,tailroom空间不变。skb->tail指针不变,skb->data指针上
移;

unsigned char *skb_push(struct sk_buff *skb, unsigned int len)
{
     /* 数据区data指针前移len字节 */
     skb->data -= len;
     /* 数据总长度增加len字节 */
     skb->len  += len;
 
     /* 添加数据长度溢出过header ,panic*/
     if (unlikely(skb->data<skb->head))
         skb_under_panic(skb, len, __builtin_return_address(0));
 
     /* 返回新的data指针 */
     return skb->data;
 }

skb_pull

缩小数据区空间,headroom空间增大。tailroom空间不变,skb->data指针下移,skb->tail指针不变;

unsigned char *skb_pull(struct sk_buff *skb, unsigned int len)
{
     return skb_pull_inline(skb, len);
}

skb_reserve

数据区不变,headroom空间增大,tailroom空间降低,skb->data和skb->tail同一时候下移;

static inline void skb_reserve(struct sk_buff *skb, int len)
 {
     /* 数据区data指针增加len字节*/
     skb->data += len;
     /* 数据区tail指针增加len字节 */
     skb->tail += len;
 } 

skb_buff结构体相关

kfree_skb

释放结构体空间

void kfree_skb(struct sk_buff *skb)  
{  
    if (unlikely(!skb))  
        return;  
    if (likely(atomic_read(&skb->users) == 1))  
        smp_rmb();  
    else if (likely(!atomic_dec_and_test(&skb->users)))  
        return;  
    trace_kfree_skb(skb, __builtin_return_address(0));  
    __kfree_skb(skb);  
}  

alloc_skb

该函数的作用是在上层协议要发送数据包的时候或网络设备准备接收数据包的时候会调用alloc_skb()函数分配sk_buff结构体

static inline struct sk_buff *alloc_skb(unsigned int size,  
                    gfp_t priority)  
{  
    return __alloc_skb(size, priority, 0, NUMA_NO_NODE);  
} 

该函数调用__alloc_skb函数,原型为:

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 = fclone ? skbuff_fclone_cache : skbuff_head_cache;  
  
    /* Get the HEAD */  
    skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node);//分配存储空间   
    if (!skb)  
        goto out;//分配失败,返回NULL   
    prefetchw(skb);  
  
    /* We do our best to align skb_shared_info on a separate cache 
     * line. It usually works because kmalloc(X > SMP_CACHE_BYTES) gives 
     * aligned memory blocks, unless SLUB/SLAB debug is enabled. 
     * Both skb->head and skb_shared_info are cache line aligned. 
     */  
    size = SKB_DATA_ALIGN(size);//调整skb大小   
    size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));  
    data = kmalloc_node_track_caller(size, gfp_mask, node);//分配数据区   
    if (!data)  
        goto nodata;  
    /* kmalloc(size) might give us more room than requested. 
     * Put skb_shared_info exactly at the end of allocated zone, 
     * to allow max possible filling before reallocation. 
     */  
    size = SKB_WITH_OVERHEAD(ksize(data));  
    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 sk_buff! 
     */  
     //sk_buff结构体中最后6个属性不能改变位置,只能在最后   
    memset(skb, 0, offsetof(struct sk_buff, tail));//将sk_buff结构体中tail属性之前的属性清零   
    /* Account for allocated memory : skb + skb->head */  
    skb->truesize = SKB_TRUESIZE(size);//计算缓冲区的尺寸   
    atomic_set(&skb->users, 1);  
    //初始化数据区的指针   
    skb->head = data;  
    skb->data = data;  
    skb_reset_tail_pointer(skb);  
    skb->end = skb->tail + size;  
#ifdef NET_SKBUFF_DATA_USES_OFFSET   
    skb->mac_header = ~0U;  
#endif   
  
    /* make sure we initialize shinfo sequentially */  
    //初始化skb_shared_info   
    shinfo = skb_shinfo(skb);  
    memset(shinfo, 0, offsetof(struct skb_shared_info, dataref));  
    atomic_set(&shinfo->dataref, 1);  
    kmemcheck_annotate_variable(shinfo->destructor_arg);  
  
    if (fclone) {  
        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;  
}  

LINUX 中 SKB 的分配最终是由函数 :struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, int fclone, int node) 来完成.
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 的指针.

内核中还有一些其他操作skb_buff结构体的函数,这里简单介绍了几个主要的,其他的如有使用到再回来补充

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值