在网口调试中比较重要的部分就是对pbuf的操作,本文主要针对pbuf的结构体、pbuf层和pbuf的相关函数的操作指南进行讲解说明。
pbuf结构体
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; } ;
pbuf类型
PBUF_RAM
PBUF_RAM类型的pbuf是调用mem_malloc函数从内存堆分配得到的,分配的大小由三部分组成:数据存储空间length、pbuf管理结构体空间SIZEOF_STRUCT_PBUF和存储协议栈头的offset。分配内存成功之后,就是对pbuf管理结构体的初始化。Pbuf管理结构体位于分配的堆内存的开始,接着的存储协议头的offset空间,最后才是存储数据的空间。
此种类型的pbuf内存布局如下:
PBUF_ROM和PBUF_REF
上述两种类型的pbuf相似。代码调用memp_malloc从内存池分配MEMP_PBUF类型的内存池,仅仅分配pbuf结构体大内存,指向存储数据空间的payload指针置位NULL,此值由调用者设置为另外的一片内存空间。
PBUF_POOL
PBUF_POOL类型的pbuf是调用memp_malloc函数从内存池中分配内存的。PBUF_POOL类型pbuf是从MEMP_PBUF_POOL内存的内存池中分配内存的,每种类型的内存池大小时固定的,如果存储数据和协议头所需要的空间大于此种类型内存池大小,则需要分配多个此种类型的内存池,并将这些内存池通过pbuf->next指针连接起来。而PBUF_RAM类型的pbuf是从内存堆中分配内存,之用申请的内存空间有剩余的连续空闲空间满足要求,则一次分配成功。
PBUF_POOL类型的pbuf内存布局如下:
pbuf 层
pbuf层包括如下:PBUF_TRANSPORT、PBUF_IP、PBUF_LINK、PBUF_RAW_TX、PBUF_RAW。
pbuf相关函数
pbuf_alloc
分配给定类型的 pbuf(可能是 PBUF_POOL 类型的链)。
为 pbuf 分配的实际内存由分配 pbuf 的层和请求的大小决定。(由参数决定)
struct pbuf * pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
pbuf_alloced_custom
该函数用于初始化已分配的pbuf,相当于自定义pbuf。
struct pbuf* pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p, void *payload_mem, u16_t payload_mem_len)
pbuf_realloc
该函数将 pbuf 链收缩到所需的长度。
void pbuf_realloc(struct pbuf *p, u16_t new_len)
根据所需的长度,链中的前几个 pbuf 可能会被跳过并保持不变。新的链中最后一个 pbuf 将被调整大小,并且其他剩余的 pbuf 将被释放。
如果 pbuf 是 ROM/REF,则只调整 ->tot_len 和 ->len 字段 。 不能在数据包队列上调用。 pbuf_realloc 不能增加 pbuf(链)的大小。
pbuf_free
取消引用 pbuf 链或队列,并在此链或队列的头部释放任何不再使用的 pbuf。 减少 pbuf 引用计数。如果它达到零,则 pbuf 被释放。
对于 pbuf 链,这对链中的每个 pbuf 重复,直到第一个 pbuf 在递减后具有非零引用计数。因此,当所有引用计数为 1 时,整个链都被释放了。
u8_t pbuf_free(struct pbuf *p)
假设现有链 a->b->c 具有以下引用计数,调用 pbuf_free(a) 结果:
1->2->3 变成 free...1->3 3->3->3 变成 2->3->3 1->1->2 变成free...free...1 2->1->1 变成 1->1->1 1->1->1 变成free...free...free
pbuf_free对链操作的解释简单来说就是,当第一个被释放后才会对后面的脸引用计数进行释放,否者值对最前面的那个链进行释放操作。
pbuf_clen
计算链中 pbuf 的数量 。
u16_t pbuf_clen(const struct pbuf *p)
pbuf_ref
增加 pbuf 的引用计数 。
void pbuf_ref(struct pbuf *p)
pbuf_cat
该函数用于连接两个pbuf,每个pbuf都可能是一个pbuf链。
void pbuf_cat(struct pbuf *h, struct pbuf *t)
使用此函数后,我们仅需要操作头部的pbuf即可,表示我们后面再也不会引用尾部的pbuf。 pbuf_clen得到的连接后的pbuf链中有2个pbuf。
pbuf_chain
将两个 pbuf(或 pbuf 链)链接在一起。一旦停止使用,调用者必须调用 pbuf_free(t)。如果不再使用 t,请改用 pbuf_cat()。
void pbuf_chain(struct pbuf *h, struct pbuf *t)
使用该函数会调整链中所有pbuf的tot_len字段、头的最后一个pbuf的next字段、尾的第一个pbuf的ref字段。