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;
}