LWIP学习笔记(3)LWIP数据包管理结构pbuf

pbuf:LWIP是TCP/IP协议栈的一种具体体现,本质就是对数据包的处理,在LWIP中使用一个被称为pbuf的结构体管理数据包,LWIP源码中的pbuf.c和pbuf.h这两个文件就是关于pbuf的。

pbuf结构体

在pbuf.h文件中:

struct pbuf {
  /** next pbuf in singly linked pbuf chain */
  //构成链表的时候指向下一个pbuf结构体
  struct pbuf *next;
  /** pointer to the actual data in the buffer */
  //指向数据缓冲区
  void *payload;
  /**
   * total length of this buffer and all next buffers in chain
   * belonging to the same packet.
   *
   * For non-queue packet chains this is the invariant:
   * p->tot_len == p->len + (p->next? p->next->tot_len: 0)
   */
  //当前及之后所有pbuf的有效数据(p->length)字段之和
  u16_t tot_len;
  /** length of this buffer */
  //当前pbuf有效字段长度
  u16_t len;
   //pbuf的类型
  u8_t type_internal;
  /** misc flags */
  //指出pbuf的一些属性
  u8_t flags;
  //表示此pbuf被引用次数,每当有指针指向pbuf时,该字段就要+1
  LWIP_PBUF_REF_T ref;
  /** For incoming packets, this contains the input netif's index */
  u8_t if_idx;
};

pbuf类型

pbuf.h中定义类型如下:

typedef enum {
      //pbuf数据紧跟着pbuf的结构存储,数据存储在ram中,即pyload指向的区域
  PBUF_RAM = (PBUF_ALLOC_FLAG_DATA_CONTIGUOUS | PBUF_TYPE_FLAG_STRUCT_DATA_CONTIGUOUS | PBUF_TYPE_ALLOC_SRC_MASK_STD_HEAP),
      // pbuf数据存储在rom中
  PBUF_ROM = PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF,
      // pbuf数据存储在ram中,,但是与pbuf结构的位置无关
  PBUF_REF = (PBUF_TYPE_FLAG_DATA_VOLATILE | PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF),
      //pbuf结构和其数据存储在一个内存池中
  PBUF_POOL = (PBUF_ALLOC_FLAG_RX | PBUF_TYPE_FLAG_STRUCT_DATA_CONTIGUOUS | PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF_POOL)
} pbuf_type;
  • PBUF_RAM
    pbuf描述的数据在pbuf结构之后的连续内存堆中,就是说他们是在一块连续的内存堆中,申请方式在pbuf.c中如下:
    case PBUF_RAM: {
      // offset:偏移,用来存储一些首部字段,如TCP报文首部,IP首部等
      //SIZEOF_STRUCT_PBUF:pbuf结构体的大小
      u16_t payload_len = (u16_t)(LWIP_MEM_ALIGN_SIZE(offset) + LWIP_MEM_ALIGN_SIZE(length));
      mem_size_t alloc_len = (mem_size_t)(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF) + payload_len);

      /* bug #50040: Check for integer overflow when calculating alloc_len */
      if ((payload_len < LWIP_MEM_ALIGN_SIZE(length)) ||
          (alloc_len < LWIP_MEM_ALIGN_SIZE(length))) {
        return NULL;
      }

      /* If pbuf is to be allocated in RAM, allocate memory for it. */
      //mem_malloc:内存堆申请的内存
      p = (struct pbuf *)mem_malloc(alloc_len);
      if (p == NULL) {
        return NULL;
      }
      pbuf_init_alloced_pbuf(p, LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)),
                             length, length, type, 0);
      LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned",
                  ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0);
      break;
    }
  • PBUF_POOL

PBUF_POOL类型的pbuf空间是从LWIP的内存池中申请得到的,因为是从内存池中申请,所以这种类型的的pbuf分配时间极短,在网卡接收数据包时,我们使用这种方式:

    case PBUF_POOL: {
      struct pbuf *q, *last;
      u16_t rem_len; /* remaining length */
      p = NULL;
      last = NULL;
      rem_len = length;
      do {
        u16_t qlen;
        q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
        if (q == NULL) {
          PBUF_POOL_IS_EMPTY();
          /* free chain so far allocated */
          if (p) {
            pbuf_free(p);
          }
          /* bail out unsuccessfully */
          return NULL;
        }
        qlen = LWIP_MIN(rem_len, (u16_t)(PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)));
        pbuf_init_alloced_pbuf(q, LWIP_MEM_ALIGN((void *)((u8_t *)q + SIZEOF_STRUCT_PBUF + offset)),
                               rem_len, qlen, type, 0);
        LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned",
                    ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0);
        LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT",
                    (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 );
        if (p == NULL) {
          /* allocated head of pbuf chain (into p) */
          p = q;
        } else {
          /* make previous pbuf point to this pbuf */
          last->next = q;
        }
        last = q;
        rem_len = (u16_t)(rem_len - qlen);
        offset = 0;
      } while (rem_len > 0);
      break;
    }

在这里插入图片描述
在PBUF_POOL类型中,只有第一个pbuf有offset,这是因为这都是一个数据包的,因此只需要一个offset来存储有关数据包的信息,其他的pbuf就不需要了。

PBUF_ROM和PBUF_REF类型的pbuf空间也是从lwip内存池中申请得到的,他们使用的内存池是MEMP_PBUF,这两种类型申请的是指pbuf结构体的内存空间,并不包含数据空间。

pbuf的申请和释放通过函数pbuf_alloc()和pbuf_free()来完成。pbuf函数有两个重要的参数:layer和type,layer决定是协议栈的哪一层申请的,type决定申请的pbuf类型,layer决定了pbuf中的offset,也就是pbuf数据区中为协议预留的首部空间。

pbuf层次
由于lwip各层禁止数据拷贝,所以存在不同层次对数据包pbuf的alloc,前面的offest就是为不同层预留的头部字段,下面枚举了4种层次,分配时除了要知道大小、类型还要传入这个层次,在pbuf.h中。

typedef enum {
 //传输层:预留以太首部+ip首部+tcp首部
  PBUF_TRANSPORT = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN,
 //网络层:预留以太首部+ip
  PBUF_IP = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN,
//链路层:预留以太首部
  PBUF_LINK = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN,
//原始层,不预留
  PBUF_RAW_TX = PBUF_LINK_ENCAPSULATION_HLEN,
  PBUF_RAW = 0
} pbuf_layer;

数据包释放函数pbuf_free

注意只有当pbuf的引用次数为0,即ref字段为0时才能释放pbuf,因为此时没有指针指向这个数据包了,代表他被处理完了
当pbuf链表的首节点, 被释放后还有检查他后面的节点是否能被释放,因为首节点释放会影响后续节点的ref字段
还有只能释放pbuf链表的首节点,不能释放中间节点,会导致非法指针。

u8_t
pbuf_free(struct pbuf *p)
{
  u8_t alloc_src;
  struct pbuf *q;
  u8_t count;

  if (p == NULL) {
    LWIP_ASSERT("p != NULL", p != NULL);
    /* if assertions are disabled, proceed with debug output */
    LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
                ("pbuf_free(p == NULL) was called.\n"));
    return 0;
  }
  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free(%p)\n", (void *)p));

  PERF_START;

  count = 0;
  /* de-allocate all consecutive pbufs from the head of the chain that
   * obtain a zero reference count after decrementing*/
  while (p != NULL) {
    LWIP_PBUF_REF_T ref;
    SYS_ARCH_DECL_PROTECT(old_level);
    /* Since decrementing ref cannot be guaranteed to be a single machine operation
     * we must protect it. We put the new ref into a local variable to prevent
     * further protection. */
    SYS_ARCH_PROTECT(old_level);
    /* all pbufs in a chain are referenced at least once */
    LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0);
    /* decrease reference count (number of pointers to pbuf) */
    ref = --(p->ref);
    SYS_ARCH_UNPROTECT(old_level);
    /* this pbuf is no longer referenced to? */
    if (ref == 0) {
      /* remember next pbuf in chain for next iteration */
      q = p->next;
      LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: deallocating %p\n", (void *)p));
      alloc_src = pbuf_get_allocsrc(p);
#if LWIP_SUPPORT_CUSTOM_PBUF
      /* is this a custom pbuf? */
      if ((p->flags & PBUF_FLAG_IS_CUSTOM) != 0) {
        struct pbuf_custom *pc = (struct pbuf_custom *)p;
        LWIP_ASSERT("pc->custom_free_function != NULL", pc->custom_free_function != NULL);
        pc->custom_free_function(p);
      } else
#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
      {
        /* is this a pbuf from the pool? */
        if (alloc_src == PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF_POOL) {
          memp_free(MEMP_PBUF_POOL, p);
          /* is this a ROM or RAM referencing pbuf? */
        } else if (alloc_src == PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF) {
          memp_free(MEMP_PBUF, p);
          /* type == PBUF_RAM */
        } else if (alloc_src == PBUF_TYPE_ALLOC_SRC_MASK_STD_HEAP) {
          mem_free(p);
        } else {
          /* @todo: support freeing other types */
          LWIP_ASSERT("invalid pbuf type", 0);
        }
      }
      count++;
      /* proceed to next pbuf */
      p = q;
      /* p->ref > 0, this pbuf is still referenced to */
      /* (and so the remaining pbufs in chain as well) */
    } else {
      LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, (u16_t)ref));
      /* stop walking through the chain */
      p = NULL;
    }
  }
  PERF_STOP("pbuf_free");
  /* return number of de-allocated pbufs */
  return count;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值