1.lwIP网络数据包简介
TCP/IP是一种数据通信机制,因此,协议栈的实现本质上就是对数据包进行处理,为了实现高效的效率,lwIP数据包管理要提供一种高效处理的机制。协议栈各层能对数据包进行灵活的处理,同时减少数据在各层间传递时的时间与空间开销,这是提高协议栈工作效率的关键点。在lwIP中它称之为pbuf。
用户的数据,经过申请pbuf,拷贝到pbuf结构的内存堆中,在应用层,数据的前面加上应用层首部,在传输层加上传输层首部,最后在网络层加上网络层首部。
pbuf用于lwIP各层间数据传递,避免各层拷贝数据!
lwIP与标准TCP/IP协议栈区别
lwIP是一种模糊分层的TCP/IP协议,大大提高数据传输效率!
2.lwIP网络数据包结构
这是定义在pbuf.h中的关键结构体pbuf。
通过指针next,构建出了一个数据包的单向链表;payload指向的是现在这个结构体所存储的数据区域;tot_len是所有的数据长度,包括当前pbuf和后续所有pbuf;而len就是指当前pbuf的长度;type_internal有四种类型;ref代表当前pbuf被引用的次数。
右边展示的pbuf_layer就是用来首部地址偏移,用来对应相应的结构体。
3.lwIP网络数据包类型
PBUF_RAM采用内存堆,长度不定,一般都是用在传输数据;PBUF_POOL采用内存池,固定大小的内存块,所以分配速度快(一般1500字节,就是分配3个PBUF_POOL的内存池),一般用在中断服务中;PBUF_ROM和PBUF_REF都是内存池形式,而且只有pbuf没有数据区域,数据都是直接指向了内存区(PBUF_ROM指向ROM中,PBUF_REF指向RAM中)。
左边第一幅对应PBUF_RAM;中间两幅对应PBUF_POOL;最后一幅对应PBUF_ROM和PBUF_REF。
其中PBUF_RAM和PBUF_POOL相对更为常用。
4.lwIP网络数据包函数
更多的函数,都可以在pbuf.c和.h中找到。
pbuf_alloc()
如果是PBUF_REF或者是PBUF_ROM,就会如上图所示,创建一个结构体指针p,然后会进入pbuf_alloc_reference;该函数中,会申请一个pbuf结构体大小的内存;然后调用pbuf_init_alloced_pbuf进行初始化,初始化可以如上图所示。
如果是PBUF_POOL,会定义q和last两个pbuf结构体指针,q和last都初始化为NULL,rem_len(剩余长度)初始化为1500(用户指定需要构建的长度);然后q会经过内存申请,qlen则是去rem_len和当前可申请的数据大小(PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset))取小值,然后同样经过pbuf_init_alloced_pbuf初始化q中的pbuf结构体;然后会把offset清零,就是说之后的pbuf都没有offset了,只有第一个链表的元素有offset;经过if判断并判断rem_len的大小,只要还有剩余就会回去循环继续执行上述操作,直到完成3个内存块的初始化。
首先会计算payload_len和alloc_len,如果是传输数据,那么LWIP_MEM_ALIGN_SIZE(offset)就是54,计算得到payload_len=1554,alloc_len=1570;然后进入判断payload和alloc的长度是否<length(54),如果其中一个满足(或语句)就return NULL;pbuf的结构体p进入内存申请内存堆,然后调用pbuf_init_alloced_pbuf;该函数完成p的初始化。
该函数源码如下:
static void
pbuf_init_alloced_pbuf(struct pbuf *p, void *payload, u16_t tot_len, u16_t len, pbuf_type type, u8_t flags)
{
p->next = NULL;
p->payload = payload;
p->tot_len = tot_len;
p->len = len;
p->type_internal = (u8_t)type;
p->flags = flags;
p->ref = 1;
p->if_idx = NETIF_NO_INDEX;
}
struct pbuf *
pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
{
struct pbuf *p;
u16_t offset = (u16_t)layer;
LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length));
switch (type) {
case PBUF_REF: /* fall through */
case PBUF_ROM:
p = pbuf_alloc_reference(NULL, length, type);
break;
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;
}
case PBUF_RAM: {
mem_size_t payload_len = (mem_size_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. */
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;
}
default:
LWIP_ASSERT("pbuf_alloc: erroneous type", 0);
return NULL;
}
LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p));
return p;
}
pbuf_free()
进入判断p是否为空,不为空证明还没有释放;进入while语句,每一次都–ref(引用次数);然后类似链表删除,调用相应的pbuf类型的内存释放(内存堆或者内存池),直到p全部被释放。源码如下:
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;
}
pbuf_header()
这个就要看你使用的是什么类型,然后会根据类型来决定payload_len的大小,进行相应的payload指针指向数据区前的首部字段。
总结
这一章主要讲述了lwIP中重要的pbuf缓冲,具体有哪些数据构成,为之后的学习奠定基础,确定了pbuf除了所需传输的数据,还有哪些变量需要添加,如何申请对应的pbuf内存大小,以及对应的内存堆和内存池。