Lwip pbuf分析
2016-12-06 21:53
549 查看
在BSD中用mbuf结构体来管理网络上来的各种数据包,同样lwip中也有一个类似的结构体pbuf用来管理数据包。Pbuf结构体定义如下:
next: 指向下一个pbuf,在数据很大时,需要多个pbuf结构体管理,这里就是通过next指针将这些next链接起来。
payload:指向实际载荷数据的起始地址。
tot_len: 是当前pbuf数据加上next之后所有pbuf数据之和。Pbuf链表的第一个pbuf的tot_len就是所有pbuf数据的长度,最后一个pbuf的tot_len等于len。
len: 是当前pbuf的数据长度。
type: 此字段表示pbuf的类型,有四种类型:PBUF_RAM、PBUF_ROM、PBUF_REF和PBUF_POOL。
ref:此字段初始化为1,当其他pbuf->next指向自己时ref加1,在释放的时候,ref大于1的不能删除。
此种类型的pbuf的分配代码片段如下:
PBUF_RAM类型的pbuf是调用mem_malloc函数从内存堆分配得到的,分配的大小由三部分组成:数据存储空间length、pbuf管理结构体空间SIZEOF_STRUCT_PBUF和存储协议栈头的offset。
分配内存成功之后,就是对pbuf管理结构体的初始化。Pbuf管理结构体位于分配的堆内存的开始,接着的存储协议头的offset空间,最后才是存储数据的空间。
此种类型的pbuf内存布局如下:
代码调用memp_malloc从内存池分配MEMP_PBUF类型的内存池,仅仅分配pbuf结构体大内存,指向存储数据空间的payload指针置位NULL,此值由调用者设置为另外的一片内存空间。
从上面代码片段可以看出PBUF_POOL类型的pbuf和PBUF_RAM类型布局相似,但有一点不同的是:PBUF_POOL类型pbuf是从MEMP_PBUF_POOL内存的内存池中分配内存的,每种类型的内存池大小时固定的,如果存储数据和协议头所需要的空间大于此种类型内存池大小,则需要分配多个此种类型的内存池,并将这些内存池通过pbuf->next指针连接起来。而PBUF_RAM类型的pbuf是从内存堆中分配内存,之用申请的内存空间有剩余的连续空闲空间满足要求,则一次分配成功。
上面代码首先计算需要的内存空间length和第一个pbuf指针p指向的可以空间len之差rem_len。如果rem_len大于0,说明还需要分配此种类型的内存池,于是进入while循环之中。在while循环中会分配更多的PBUF_POOL类型的pbuf直到能够存储下length大小的数据,并将这些pbuf连接起来。
需要注意的一点是除了第一个pbuf在pbuf后面需要留offset大小空间存储协议头之外,其它的pbuf中是不需要的。
PBUF_POOL类型的内存池MEMP_PBUF_POOL定义如下:
LWIP_PBUF_MEMPOOL(PBUF_POOL, PBUF_POOL_SIZE,
PBUF_POOL_BUFSIZE, "PBUF_POOL")
其大小为PBUF_POOL_BUFSIZE宏:
LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_HLEN)
包括TCP报文段最大长度MSS,40字节的tcp+IP头长度和链接层长度PBUF_LINK_HLEN
最终的PBUF_POOL类型的pbuf内存布局如下:
下面分析pbuf_free函数代码片段:
pbuf的ref成员初始化时设置为1,每当有其它pbuf的next指针指向自己时ref值加1。只有当ref值为1时,表示没有其它pbuf指向自己时,才可以释放此pbuf。这里首先将pbuf的ref值减1,并赋值给变量ref。如果ref值为0说明此pbuf没有被其它的pbuf引用,这也是此pbuf能够释放的前提。如果ref值不为0,说明此pbuf不能释放,设置p为NULL,则直接退出此while循环。
对于ref等于0的情况,首先获取p->next指向的pbuf并赋值给指针q,然后根据此pbuf的type分别调用不同的释放函数。
◆PBUF_POOL类型
调用memp_free函数将p的内存空间返回给MEMP_PBUF_POOL类型的内存池。
◆PBUF_ROM和PBUF_REF类型
调用memp_free函数将p的内存空间返回给MEMP_PBUF类型的内存池
◆PBUF_RAM类型
调用mem_free将p的内存空间返回给内存堆。
内存堆的释放和内存池的释放这里不分析。这里看下ref值不同,对于pbuf链表在pbuf_free函数执行后产生的不同结果。在pbuf_free函数的前面有一段注释:
假设有一个链表由a、b和c三个pbuf按顺序链接起来的,对于这三个pbuf的ref值在如下几种情况下调用pbuf_free(a)所产生的结果如下:
struct pbuf { struct pbuf *next; void *payload; u16_t tot_len; u16_t len; u8_t /*pbuf_type*/ type; u8_t flags; u16_t ref; } ; |
payload:指向实际载荷数据的起始地址。
tot_len: 是当前pbuf数据加上next之后所有pbuf数据之和。Pbuf链表的第一个pbuf的tot_len就是所有pbuf数据的长度,最后一个pbuf的tot_len等于len。
len: 是当前pbuf的数据长度。
type: 此字段表示pbuf的类型,有四种类型:PBUF_RAM、PBUF_ROM、PBUF_REF和PBUF_POOL。
ref:此字段初始化为1,当其他pbuf->next指向自己时ref加1,在释放的时候,ref大于1的不能删除。
pbuf类型
PBUF_RAM此种类型的pbuf的分配代码片段如下:
case PBUF_RAM: p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length)); /* Set up internal structure of the 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; |
分配内存成功之后,就是对pbuf管理结构体的初始化。Pbuf管理结构体位于分配的堆内存的开始,接着的存储协议头的offset空间,最后才是存储数据的空间。
此种类型的pbuf内存布局如下:
PBUF_ROM
PBUF_REF
上述两种类型的pbuf相似,在pbuf_alloc函数中申请内存时共用代码:/* pbuf references existing (non-volatile static constant) ROM payload? */ case PBUF_ROM: /* pbuf references existing (externally allocated) RAM payload? */ case PBUF_REF: /* only allocate memory for the pbuf structure */ p = (struct pbuf *)memp_malloc(MEMP_PBUF); /* caller must set this field properly, afterwards */ p->payload = NULL; p->len = p->tot_len = length; p->next = NULL; p->type = type; break; |
PBUF_POOL
PBUF_POOL类型的pbuf是调用memp_malloc函数从内存池中分配内存的。case PBUF_POOL: p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); p->type = type; p->next = NULL; p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset))); p->tot_len = length; p->len=LWIP_MIN(length,PBUF_POOL_BUFSIZE_ALIGNED-LWIP_MEM_ALIGN_SIZE(offset)); |
r = p; rem_len = length - p->len ; while (rem_len > 0) { q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); 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); q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF); q->ref = 1; rem_len -= q->len ; r = q; } |
需要注意的一点是除了第一个pbuf在pbuf后面需要留offset大小空间存储协议头之外,其它的pbuf中是不需要的。
PBUF_POOL类型的内存池MEMP_PBUF_POOL定义如下:
LWIP_PBUF_MEMPOOL(PBUF_POOL, PBUF_POOL_SIZE,
PBUF_POOL_BUFSIZE, "PBUF_POOL")
其大小为PBUF_POOL_BUFSIZE宏:
LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_HLEN)
包括TCP报文段最大长度MSS,40字节的tcp+IP头长度和链接层长度PBUF_LINK_HLEN
最终的PBUF_POOL类型的pbuf内存布局如下:
Pbuf释放
前面分析pbuf类型的时候讲了分配内存函数pbuf_alloc对于四种类型的pbuf各自的代码,对于PBUF_RAM类型的pbuf是从调用mem_malloc内存堆中内存,其它三种是调用函数memp_malloc从内存池中分配内存。相对应的,PBUF_RAM类型pbuf的释放就需要调用mem_free函数将内存释放回内存堆,其它三种就是memp_free函数将内存释放回相应类型的内存池了。下面分析pbuf_free函数代码片段:
while (p != NULL) { ref = --(p->ref ); if (ref == 0) { q = p->next ; 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); } p = q; } else { p = NULL; } } |
对于ref等于0的情况,首先获取p->next指向的pbuf并赋值给指针q,然后根据此pbuf的type分别调用不同的释放函数。
◆PBUF_POOL类型
调用memp_free函数将p的内存空间返回给MEMP_PBUF_POOL类型的内存池。
◆PBUF_ROM和PBUF_REF类型
调用memp_free函数将p的内存空间返回给MEMP_PBUF类型的内存池
◆PBUF_RAM类型
调用mem_free将p的内存空间返回给内存堆。
内存堆的释放和内存池的释放这里不分析。这里看下ref值不同,对于pbuf链表在pbuf_free函数执行后产生的不同结果。在pbuf_free函数的前面有一段注释:
* @note the reference counter of a pbuf equals the number of pointers * that refer to the pbuf (or into the pbuf). * * @internal examples: * * Assuming existing chains a->b->c with the following reference * counts, calling pbuf_free(a) results in: * * 1->2->3 becomes ...1->3 * 3->3->3 becomes 2->3->3 * 1->1->2 becomes ......1 * 2->1->1 becomes 1->1->1 * 1->1->1 becomes ....... |
执行前 | 执行后 | |
1->2->3 | a->ref=1 b->ref=2 c->ref=3 | a的ref减1之后为0,a对应pbuf释放。接着遍历到b,b的ref减1之后为1,不能释放,也停止遍历。 |
3->3->3 | a->ref=3 b->ref=3 c->ref=3 | a的ref减1之后为2,a对应pbuf不能释放,停止遍历。 |
1->1->2 | a->ref=1 b->ref=1 c->ref=2 | a的ref减1之后为0,a对应pbuf释放。接着遍历到b,b的ref减1之后为0,释放b对应pbuf。接着遍历c,c的ref减1之后为1,不能释放。 |
2->1->1 | a->ref=2 b->ref=1 c->ref=1 | a的ref减1之后为1,a对应pbuf不能释放,停止遍历。 |
1->1->1 | a->ref=1 b->ref=1 c->ref=1 | a的ref减1之后为0,a对应pbuf释放。接着遍历到b,b的ref减1之后为0,释放b对应pbuf。接着遍历c,c的ref减1之后为0,释放c对应的pbuf。 |
相关文章推荐
- 基于rt-thread+lwip分析数据是怎么从网卡芯片接收数据到pbuf的(lwip源码解析一)
- [LWIP学习]--pbuf_realloc函数分析
- lwIP协议栈的pbuf结构体
- LWIP_pbuf.c
- lwIP(V1.0.0) RAW API函数源码分析2----tcp_bind()函数
- lwip 分析一
- lwip源码分析2----ARP
- LWIP 分析二之udp
- lwIP(V1.0.0) RAW API函数源码分析3----tcp_listen()函数
- lwIP配置文件opt.h和lwipopts.h初步分析
- lwIP ARP协议分析
- DE2开发板:NiosII+LWIP环境下DM9000A的驱动程序分析(web_server.c)
- lwIP ARP协议分析1
- LWIP源码结构分析
- LwIP(V1.0.0) RAW API函数源码分析1----tcp_new()函数
- lwIP ARP协议分析1
- lwIP(V1.3.0) RAW API函数源码分析1----tcp_new()函数
- lwIP ARP协议分析0
- lwIP(V1.0.0) RAW API函数源码分析4----tcp_accept()函数
- lwip源码分析1------综述及设备驱动层