skb 添加删除尾部数据

skb_add_data

  skb_add_data()将指定用户空间的数据添加到SKB的数据缓存区的尾部,操作过程如图3-22所示。如果成功则返回0,否则返回相应的错误码。参数skb为待添加数据的SKB;from为待添加的数据源,指向在用户空间的存储缓存区;copy为待添加数据的长度。

  

 

 

 

skb_trim

  skb_trim()根据指定长度删除SKB的数据缓存区尾部的数据,如果新长度大于当前长度,则不作处理,操作过程如图3-23所示。调用该函数的前提条件是,待操作的SKB的数据必须是线性存储的。参数skb为待操作的SKB;len为删除尾部数据后剩余的长度。

 

 

 

 

pskb_strim

  pskb_trim()与skb_trim()功能类似,也是根据指定长度删除SKB尾部的数据。不同的是,pskb_trim()是skb_trim()的功能超集,不仅可以处理线性数据的SKB,还可以处理非线性的SKB。线性数据的处理过程与skb_trim()相同,非线性数据操作过程如图3-24 和图3-25所示。

 

 

 

skb_split

  拆分数据函数是把数据区数据拆分成两个存放到另外一个 skb 中,其实拆分数据函数并不复杂,只是一些指针的赋值,和控制。下面看函数实现:

// skb为原来的skb结构体(将要被拆分的),skb1为拆分后得到的子skb,len为拆分后的skb的新长度
void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len)
{
    int pos = skb_headlen(skb);// pos = skb->len - skb->data_len,pos是skb结构中数据区的有效数据长度
 
    if (len < pos)    // 如果拆分长度小于skb数据区中的有效长度,则调用下面函数
        skb_split_inside_header(skb, skb1, len, pos);// 该函数只拆分skb数据区中的数据
    else  // 反之,如果拆分长度不小于skb数据区中的有效长度,则调用下面函数
        skb_split_no_header(skb, skb1, len, pos);// 拆分skb结构中的分片结构中数据区数据
}
 
// 这是只拆分sk_buff结构数据区的数据,其他参数不变,参数:pos则是sk_buff结构数据区中有效数据长度
static inline void skb_split_inside_header(struct sk_buff *skb,
                       struct sk_buff* skb1,
                       const u32 len, const int pos)
{
    int i;
    // 这是个把sk_buff结构中有效数据拷贝到新的skb1中,pos为有效数据长度,len为剩下数据长度,得:pos-len为要拷贝的数据长度
        // skb_put(skb1,pos-len)是移动tail指针让skb1结构数据区空出空间来存放将要拷贝的数据,该函数返回tail指针
    skb_copy_from_linear_data_offset(skb, len, skb_put(skb1, pos - len),
                     pos - len);
            // 为了方便理解,把该函数实现代码注释进来
            // skb为要被拆分的sk_buff结构,offset为剩下新的skb数据长度,to为skb1结构中tail指针,len为要拷贝的数据长度
            // static inline void skb_copy_from_linear_data_offset(const struct sk_buff *skb,
        //                    const int offset, void *to,
        //                    const unsigned int len)
        // {
        // 从skb要剩下的数据位置开始(即是skb->data+offset,skb->data和skb->data+offset之间的数据是要保留的)
            // to则是tail指针移动前返回的一个位置指针(详细请看skb_put()函数实现),拷贝len长度内容
        //    <span style="white-space:pre">    </span>memcpy(to, skb->data + offset, len);
        //<span style="white-space:pre">    </span>}
        // 如果对sk_buff结构及相关结构体中成员变量了解,则这些代码就非常好理解了。
        // nr_frags为多少个分片数据区,循环把所有分片数据拷贝到skb1中
    for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
        skb_shinfo(skb1)->frags[i] = skb_shinfo(skb)->frags[i];
    
    //下面做的都是些成员字段拷贝赋值操作,并且设置skb的字段
    skb_shinfo(skb1)->nr_frags = skb_shinfo(skb)->nr_frags;
    skb_shinfo(skb)->nr_frags  = 0;
    skb1->data_len           = skb->data_len;
    skb1->len           += skb1->data_len;
    skb->data_len           = 0;
    skb->len           = len;
    skb_set_tail_pointer(skb, len);// 下面把实现函数代码注释进来,方便理解    
         //    static inline void skb_set_tail_pointer(struct sk_buff *skb, const int offset)
        //    {
        //        // 这是把tail指针移到数据区的最后面
        //        skb->tail = skb->data + offset;    
        //    }
}
 
 
// 这是拆分分片结构数据区数据,同理,其他参数不变,参数:pos则是sk_buff结构数据区中有效数据长度
static inline void skb_split_no_header(struct sk_buff *skb,
                       struct sk_buff* skb1,
                       const u32 len, int pos)
{
    int i, k = 0;
    // 开始设置sk_buff结构数据区内容
    const int nfrags = skb_shinfo(skb)->nr_frags;
    skb_shinfo(skb)->nr_frags = 0;
    skb1->len          = skb1->data_len = skb->len - len;
    skb->len          = len;
    skb->data_len          = len - pos;
    
    // 这是循环拆分分片结构数据区数据
    for (i = 0; i < nfrags; i++) {
        int size = skb_shinfo(skb)->frags[i].size;
    // 其实拆分,数据区存储不会动,动的只是指向这些数据存储的位置指针
       //  下面都是把skb的一些指向分片结构数据区的指针赋值给skb1中的数据区相关变量
        if (pos + size > len) {
            skb_shinfo(skb1)->frags[k] = skb_shinfo(skb)->frags[i];
            if (pos < len) {
                get_page(skb_shinfo(skb)->frags[i].page);
                skb_shinfo(skb1)->frags[0].page_offset += len - pos;
                skb_shinfo(skb1)->frags[0].size -= len - pos;
                skb_shinfo(skb)->frags[i].size    = len - pos;
                skb_shinfo(skb)->nr_frags++;
            }
            k++;
        } else
            skb_shinfo(skb)->nr_frags++;
        pos += size;
    }
    skb_shinfo(skb1)->nr_frags = k;
}

 

pskb_expand_head

 pskb_pull():

  对于带有frag page的分片skb来说,data指针往下移动,可能会导致线性区越界,因此需要判断是否线性区有足够的空间用来pull操作,如果空间不够,那么需要执行linearize,重构线性区,把一部分frags中的数据移动到线性区中来操作。

pskb_may_pull():

主要在使用skb_pull之前来检查线性区buffer有没有足够的数据用于 pull。

 

 

 

posted @ 2022-04-23 17:44  codestacklinuxer  阅读(80)  评论(0编辑  收藏  举报