Lwip pbuff分析

在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的內存空間返回給內存堆。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章