在BSD中用mbuf結構體來管理網絡上來的各種數據包,同樣lwip中也有一個類似的結構體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;
} ;
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申請時候不就是一個鏈式結構麼,next->next...難道第一個是1,後面的
就是2,3,4等等累加?當然不是了,如果這樣的話,你想想怎麼去free,所以一次申請的一個pbuf鏈,裏面
的ref字段當然都是1了。當我們想把兩個pbuf拼接的時候,看你想怎麼操作,可以直接拼接,也可以先用
pbuf_ref把後面的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_RAM類型的pbuf是調用mem_malloc函數從內存堆分配得到的,分配的大小由三部分組成:數據存儲空間length、pbuf管理結構體空間SIZEOF_STRUCT_PBUF和存儲協議棧頭的offset。
分配內存成功之後,就是對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;
代碼調用memp_malloc從內存池分配MEMP_PBUF類型的內存池,僅僅分配pbuf結構體大內存,指向存儲數據空間的payload指針置位NULL,此值由調用者設置爲另外的一片內存空間。
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));
從上面代碼片段可以看出PBUF_POOL類型的pbuf和PBUF_RAM類型佈局相似,但有一點不同的是:PBUF_POOL類型pbuf是從MEMP_PBUF_POOL內存的內存池中分配內存的,每種類型的內存池大小時固定的,如果存儲數據和協議頭所需要的空間大於此種類型內存池大小,則需要分配多個此種類型的內存池,並將這些內存池通過pbuf->next指針連接起來。而PBUF_RAM類型的pbuf是從內存堆中分配內存,之用申請的內存空間有剩餘的連續空閒空間滿足要求,則一次分配成功。
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;
}
上面代碼首先計算需要的內存空間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釋放
前面分析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;
}
}
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的內存空間返回給內存堆。