lwip (二)資料報pbuf

2021-10-14 06:46:29 字數 4559 閱讀 3840

總結一下,lwip中常用到的記憶體分配策略有兩種,一種是記憶體堆分配,一種是記憶體池分配前者可以說能隨心所欲的分配我們需要的合理大小的記憶體塊,缺點是當經過多次的分配釋放後,記憶體堆中間會出現很多碎片,使得需要分配較大記憶體塊時分配失敗後者分配速度快,就是簡單的鍊錶操作,因為各種型別的pool是我們事先建立好的,但是採用pool會有些情況下會浪費掉一定的記憶體空間。在lwip中,將這兩種分配策略混合使用,達到了很好的記憶體使用效率。

下面我們將來看看lwip中是怎樣合理利用這兩種分配策略的。這就順利的過渡到了這節要討論的話題:lwip的資料報緩衝的實現。 在協議棧中移動的資料報,最無疑的是整個記憶體管理中最重要的部分了。

資料報的種類和大小也可以說是五花八門,數數,首先從網絡卡上來的原始資料報,它可以是長達上千個位元組的tcp資料報,也可以是僅有幾個位元組的icmp資料報;再從要傳送的資料報看,上層應用可能將自己要傳送的千奇百怪形態各異的資料報遞交給lwip協議棧傳送,這些資料可能存在於應用程式管理的記憶體空間內,也可能存在於某個rom上。注意,這裡有個核心的東西是當資料在各層之間傳遞時,lwip極力禁止資料的拷貝工作,因為這樣會耗費大量的時間和記憶體

綜上,lwip必須有個高效的資料報管理核心,它即能海納百川似的相容各種型別的資料,又能避免在各層之間的複製資料的巨大開銷。

資料報管理機構採用資料結構pbuf來描述資料報,其原始碼如下:

struct pbuf 

;

這個看似簡單的資料結構,卻內容豐富!

next字段指標指向下乙個pbuf結構,因為實際傳送或接收的資料報可能很大,而每個pbuf能夠管理的資料可能很少,所以,往往需要多個pbuf結構才能完全描述乙個資料報。所以,所有的描述同乙個資料報的pbuf結構需要鏈在乙個鍊錶上,這一點用next實現。

payload是資料指標,指向該pbuf管理的資料的起始位址,這裡,資料的起始位址可以是緊跟在pbuf結構之後的ram,也可能是在rom上的某個位址,而決定這點的是當前pbuf是什麼型別的,即type欄位的值,這在下面將繼續討論。

len字段表示當前pbuf中的有效資料長度。

tot_len表示當前pbuf和其後所有pbuf的有效資料的長度。顯然,tot_len欄位是len欄位與pbuf鏈中隨後乙個pbuftot_len欄位的和;pbuf鏈中第乙個pbuftot_len字段表示整個資料報的長度,而最後乙個pbuftot_len欄位必和len欄位相等。

type字段表示pbuf的型別,主要有四種型別,這點基本上涉及到pbuf管理中最難的部分,將在下節仔細討論。

文件上說flags欄位也表示pbuf的型別,不懂,type字段不是說明了pbuf的型別嗎?不過在源**裡,初始化乙個pbuf的時候,是將該字段的值設為0,而在其他地方也沒有用到該欄位,所以,這裡直接忽略掉。

最後ref字段表示該pbuf被引用的次數。這裡又是乙個糾結的地方啊。初始化乙個pbuf的時候,ref字段值被設定為1,當有其他pbufnext指標指向該pbuf時,該pbufref欄位值加一。所以,要刪除乙個pbuf時,ref的值必須為1才能刪除成功,否則刪除失敗。

pbuf的型別很多。pbuf有四類:pbuf_ram、pbuf_rom、pbuf_ref和pbuf_pool。下面,乙個乙個的來看看各種型別的特點。

pbuf_ram型別的pbuf主要通過記憶體堆分配得到的。這種型別的pbuf在協議棧中是用得最多的。協議棧要傳送的資料和應用程式要傳遞的資料一般都採用這個形式。申請pbuf_ram型別時,協議棧會在記憶體堆中分配相應的大小,注意,這裡的大小包括如前所述的pbuf結構頭大小和相應資料緩衝區,他們是在一片連續的記憶體區的。下面來看看源**是怎樣申請pbuf_ram型的。

其中ppbuf型指標。

p =

(struct pbuf*

)mem_malloc

(lwip_mem_align_size

(sizeof_struct_pbuf + offset)

+lwip_mem_align_size

(length)

);

可以看出,系統是呼叫記憶體堆分配函式mem_malloc進行記憶體分配的。分配空間的大小包括:pbuf結構頭大小sizeof_struct_pbuf,需要的資料儲存空間大小length,還有乙個offset。關於這個offset,也有一大堆可以討論的東西,不過先到此打住。總之,分配成功的pbuf_ram型別的pbuf如下圖:

圖中是分配指定大小的資料緩衝的結果,系統呼叫會分配多個固定大小的pbuf_pool型別pbuf,並把這些pbufs鏈成乙個鍊錶,以滿足使用者的分配空間請求。

pbuf_rompbuf_ref型別的pbuf基本相同,它們的申請都是在記憶體堆中分配乙個相應的pbuf結構頭,而不申請資料區的空間。這就是它們與pbuf_rampbuf_pool的最大區別。pbuf_rompbuf_ref型別的區別在於前者指向rom空間內的某段資料,而後者指向ram空間內的某段資料。下面來看看源**是怎樣申請pbuf_rompbuf_ref型別的。其中ppbuf型指標。

p =

memp_malloc

(memp_pbuf)

;

可以看出,系統是呼叫記憶體池分配函式memp_malloc進行記憶體分配的。而此刻請求的記憶體池型別為memp_pbuf,而不是memp_pbuf_poolmemp_pbuf型別的記憶體池大小恰好為乙個pbuf頭的大小,因為這種池是lwip專為pbuf_rompbuf_ref型別的pbuf量身製作的。lwip還是真的很周到啊,它會為不同的資料結構量身定做不同型別的池。正確分配的pbuf_rompbuf_ref型別的pbuf,其結構如下圖:

注:以上所有都來自文件《design and implementation of the lwip:tcp/ip stack》,這些圖都有個共同的錯誤,即lentot_len字段位置搞反了,竊喜。

最後說明,對於乙個資料報,它可能使用上述的任意的pbuf型別,很可能的情況是,一大串不同型別的pbufs連在一起,用以儲存乙個資料報的資料。

下節看點,關於pbuf的記憶體釋放問題。

一LWIP學習筆記之資料報管理

一 資料報管理 tcp ip 是一種資料通訊機制,因此,協議棧的實現本質上就是對資料報進行處理。資料報管理應該能提供一種高效的機制,使協議棧各層能對資料報進行靈活的處理,同時減少資料在各層間傳遞時的時間與空間開銷,這是提高協議棧工作效率的關鍵點。在 lwip 中,也有個類似的結構,稱之為 pbuf,...

解讀TCP UDP資料報(二) TCP資料報結構

tcp資料報由首部和資料組成,每行4個位元組 32位 其中首部最少20個位元組 5行 最多60個位元組 15行 選項部分是可選的 tcp首部並沒有字段表明整個資料報的長度,是因為tcp資料報是包含在ip資料報中的,而ip資料報已有長度字段,除去ip首部和tcp首部,剩餘部分就是tcp包的淨荷資料。1...

資料報格式 USB資料報解析

由域構成的包有四種型別,分別是令牌包 資料報 握手包和特殊包,前面三種是重要的包,不同包的域結構不同,介紹如下 1 令牌包 分為輸入包 輸出包 設定包和幀起始包 注意這裡的輸入包是用於設定輸入命令的,輸出包是用來設定輸出命令的,而不是放資料的 其中輸入包 輸出包和設定包的格式都是一樣的 sync p...