lwip之数据包管理

2017-01-05 11:30:25来源:CSDN作者:banruoju人点击

                                            数据包结构
/*****************************************************************************************************************
struct pbuf
{
    struct pbuf *next;    // 指向下一个pbuf结构
    void *payload        // 数据指针,指向该pbuf所记录的数据区域
    u16_t tot_len;        // 当前pbuf和后续所有pbuf中包含的数据总长
    u16_t len;            // 当前pbuf单元记录的数据长度
    u8_t type;            // 当前pbuf单元的类型
    u8_t flags;            // 状态位,未用到
    u16_t ref;            // 指向该pbuf的指针数,即该pbuf被引用的次数
}
next:一个数据包需要多个pbuf结构才能完全描述    ,这些pbuf结构就组成了一张链表,通过next实现
payload:数据区域可以是紧跟在pbuf结构后的RAM空间,也可以是ROM中的空间,由type字段决定
type:表示pbuf的类型,定义如下
        typedef enum{
            PBUF_RAM,    // pbuf与其描述的数据处于同一连续内存堆中
            PBUF_ROM,    // pbuf描述的数据在ROM中
            PBUF_REF,    // pbuf描述的数据在RAM中,但位置与pbuf结构所处位置无关
            PBUF_POOL    // pbuf与其描述的数据处于同一内存池中
        }pbuf_type;
ref:新分配pbuf时就会置1,数据包释放时,通过该值判断当前pbuf节点是否可以被释放
*****************************************************************************************************************/

                                            数据包申请、释放
/*****************************************************************************************************************
数据包申请函数有二个重要参数,一个是想申请的数据包pbuf类型,另一个是该数据包是在协议栈哪一层被申请的。
申请函数根据层次的不同,会在pbuf数据区前预留出相应的offset值,层次定义如下
                                                                        typedef enum{
                                                                            PBUF_TRANSTPORT,    // 传输层
                                                                            PBUF_IP,            // 网络层
                                                                            PBUF_LINK,            // 链路层
                                                                            PBUF_RAW            // 原始层,不预留任何空间
                                                                        }pbuf_layer
struct pbuf *pbuf_alloc(pbuf_layer layer,u16_t length,pbuf_type type)
{
    struct pbuf *p,*q,*r;
    u16_t offset;        // 预留首部空间的长度
    s32_t rem_len;        // 还要申请的数据长度
    
    // 根据层次的不同,计算预留长度
    offset = 0;        
    switch (layer)
    {
        case PBUF_TRANSPORT:
        // 传输层,预留出TCP首部长度
        offset += PBUF_TRANSPORT_HLEN;
        case PBUF_IP:
        // 网络层或以上各层,预留出IP首部长度
        offset += PBUF_IP_HLEN;
        case PBUF_LINK:
        // 链路层或以上各层,预留出链路层首部长度
        offset += PBUF_LINK_HLEN;
        break;
        case PBUF_RAW:
        // 如果是原始层,则不预留任何空间(常用于数据包接收)
        break;
        default:
        return NULL;
    }
    
    // 根据pbuf类型,来分配对应的内存空间
    switch (type)
    {
        case PBUF_POOL:        // 这种类型的pbuf,意味着可能需要分配几个POOL
            // 分配一个POOL作为pbuf链表头
            p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
            if (p == NULL)
               {
                  PBUF_POOL_IS_EMPTY();
                  return NULL;
            }
    
            // 分配成功之后,初始化pbuf各字段
            p->type = type;
            p->next = NULL;
            p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset)));    //作为链表头的第一个pbuf,payload指向的数据起始区域,需要预留出首部空间
            p->tot_len = length;
            p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset));    //作为链表头的第一个pbuf,len的取值也需要考虑到首部空间
            
            // 检查已分配的POOL是否满足长度要求,不满足则继续分配
            rem_len = length - p->len;
            r = p;
            while (rem_len > 0)
            {
                // 分配一个新的POOL
                q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
                if (q == NULL)
                {
                    PBUF_POOL_IS_EMPTY();
                    pbuf_free(p);
                    return NULL;
                }
                
                // 分配成功之后,初始化pbuf各字段,并将新的pbuf链接进链表
                q->type = type;
                q->flags = 0;
                q->next = NULL;
                r->next = q;
                q->tot_len = (u16_t)rem_len;
                q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED);    // 除了作为链表头的pbuf,其余pbuf的数据区域都不需要预留首部,所以payload直接指向pbuf结构之后,len最大可取POOL尺寸
                q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF);
                q->ref = 1;

                // 更新还要申请的长度
                rem_len -= q->len;
                
                r = q;
            }
            break;
        case PBUF_RAM:      // 这种类型的pbuf直接在内存堆中申请
            p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length));
            if (p == NULL)
            {
              return NULL;
            }
    
            // 分配成功之后,初始化pbuf各字段
            p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset));
            p->len = p->tot_len = length;
            p->next = NULL;
            p->type = type;
            break;
        case PBUF_ROM:      // 对于PBUF_ROM和PBUF_REF类型的pbuf,只分配pbuf结构
        case PBUF_REF:
            p = (struct pbuf *)memp_malloc(MEMP_PBUF);
            if (p == NULL)
                return NULL;
                
            // 分配成功之后,初始化pbuf各字段,注意这两种类型的pbuf在初始化时payload字段首先置空
            p->payload = NULL;
            p->len = p->tot_len = length;
            p->next = NULL;
            p->type = type;
            break;
        default:
            return NULL;
    }
    
    // 到这里,pbuf申请成功,设置剩余字段,返回pbuf指针
      p->ref = 1;
      p->flags = 0;
      
      return p;
}    

数据包的释放前提是pbuf的ref字段为0,并且能被删除的pbuf必然是某个pbuf链表的首节点,一旦跳过首节点,直接释放某个中间节点,会导致严重错误,调用数据包释放函数时必须格外注意这点    
u8_t pbuf_free(struct pbuf *p)
{
    u16_t type;
    struct pbuf *q;
      u8_t count;

      if (p == NULL)
          return 0;
          
      count = 0;
      while (p != NULL)
      {
          u16_t ref;
          
          ref = --(p->ref);   // 该pbuf引用次数减1
          // 判断该pbuf的引用次数是否为0,为0则删除该pbuf,不为0则不删除
        if (ref == 0)
        {
            q = p->next;
            
            // 判断要删除的pbuf类型,调用相应的内存管理函数删除
            type = p->type;
            if (type == PBUF_POOL)
            {
                memp_free(MEMP_PBUF_POOL, p);
            }
            else if (type == PBUF_ROM || type == PBUF_REF)
            {
                  memp_free(MEMP_PBUF, p);
            }
            else
            {
                  mem_free(p);
            }
            
            count++;    // 更新删除的pbuf数量
            
            p = q;
        }
        else
            p = NULL;
      }
      
      // 返回成功删除的pbuf个数
      return count;
}
*****************************************************************************************************************/

                                    其他数据包操作函数
/*****************************************************************************************************************        
1. 在pbuf链表尾部释放一定空间,从而将数据包截断为某个长度值
void pbuf_realloc(struct pbuf *p, u16_t new_len)
{
    struct pbuf *q;
    u16_t rem_len;
    s32_t grow;
    
    // 确保截断后的数据总长小于原数据总长
    if (new_len >= p->tot_len)
        return;
    
    grow = new_len - p->tot_len;    // 需要截掉的长度,必然是个负值
    
    // 遍历pbuf链表,找到最后一个有效的pbuf节点
    rem_len = new_len;
    q = p;
    while (rem_len > q->len)
    {
        rem_len -= q->len;
        q->tot_len += (u16_t)grow;
        q = q->next;
    }
    
    // 截断最后一个有效pbuf节点中的无效数据,注意!最后一个有效pbuf节点,只有是PBUF_RAM类型时,才需要释放无效数据的内存空间
    if ((q->type == PBUF_RAM) && (rem_len != q->len))
    {
        q = (struct pbuf *)mem_trim(q, (u16_t)((u8_t *)q->payload - (u8_t *)q) + rem_len);
    }
    
    // 更新最后一个有效pbuf节点的字段
    q->len = rem_len;
    q->tot_len = q->len;
    
    // 释放pbuf链表上多余的作废pbuf节点,其实就是针对PBUF_POOL类型
    if (q->next != NULL)
    {
        pbuf_free(q->next);
    }
    q->next = NULL;
}

2. 调整pbuf中的payload指针,通常用于操作协议首部空间
u8_t pbuf_header(struct pbuf *p, s16_t header_size_increment)
{
    u16_t type;
      void *payload;
      u16_t increment_magnitude;
      
      // 入参合法性检测
      if ((header_size_increment == 0) || (p == NULL))
        return 0;
    
    // 获取指针偏移值,非负的,注意入参header_size_increment为正表示指针前移,为负表示指针后移
    if (header_size_increment < 0)
        increment_magnitude = -header_size_increment;
    else
        increment_magnitude = header_size_increment;
    
    // 根据pbuf类型不同,采用相应的偏移策略    
    type = p->type;
    payload = p->payload;
    if (type == PBUF_RAM || type == PBUF_POOL)         // 跟数据区域连续的pbuf类型,允许前后偏移
    {
        // 设置新的payload指针
        p->payload = (u8_t *)p->payload - header_size_increment;
        
        // 检测新的payload指针是否越界
        if ((u8_t *)p->payload < (u8_t *)p + SIZEOF_STRUCT_PBUF)
        {
            p->payload = payload;
            return 1;
        }
    }
    else if (type == PBUF_REF || type == PBUF_ROM)     // 跟数据区域分离的pbuf类型,只允许往后偏移
    {
        if ((header_size_increment < 0) && (increment_magnitude <= p->len))
              p->payload = (u8_t *)p->payload - header_size_increment;
        else
            return 1;
    }
    else
        return 1;
    
    // 更新pbuf字段    
    p->len += header_size_increment;
    p->tot_len += header_size_increment;
    
    return 0;
}
                            
*****************************************************************************************************************/

                                   

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台