本文轉(zhuǎn)自公眾號歡迎關(guān)注
基于DWC_ether_qos的以太網(wǎng)驅(qū)動開發(fā)-LWIP的堆管理介紹 (qq.com)
https://mp.weixin.qq.com/s/OMnn1WsbdvqeqL6UOGsQVA
一.前言
堆管理是重點(diǎn)的基礎(chǔ)代碼,需要重點(diǎn)關(guān)注,移植時也需要關(guān)注。所以這一篇就來講講LWIP的堆管理。
二.LWIP的堆管理實(shí)現(xiàn)
LWIP實(shí)現(xiàn)了內(nèi)部的堆管理,這樣無OS等環(huán)境也可以直接移植使用,不依賴系統(tǒng)的堆管理。
當(dāng)然也可以配置為使用系統(tǒng)的堆管理。
源碼位于mem.c,mem.h
如果使能MEM_LIBC_MALLOC則使用系統(tǒng)的堆管理接口
需要配置以下宏
mem_clib_free
mem_clib_malloc
mem_clib_calloc
默認(rèn)是
/* in case C library malloc() needs extra protection,
如果使能MEM_USE_POOLS則使用內(nèi)存池實(shí)現(xiàn),這個上一篇已經(jīng)講解了。
否則使用mem.c的實(shí)現(xiàn),我們重點(diǎn)關(guān)注這一部分。
三.堆的存儲分配
如果沒有定義LWIP_RAM_HEAP_POINTER則
mem.c中定義一個大數(shù)組LWIP_DECLARE_MEMORY_ALIGNED
大小是MEM_SIZE_ALIGNED + (2U * SIZEOF_STRUCT_MEM)
用戶可用空間是#define MEM_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEM_SIZE)
即用戶定義的MEM_SIZE,默認(rèn)值是1600
/**
這里多分配了2U * SIZEOF_STRUCT_MEM是一方面多一個ram_end標(biāo)記末尾,作為末尾的邊界節(jié)點(diǎn),一方面預(yù)留對齊的浪費(fèi)空間。
用戶也可以直接定義宏LWIP_RAM_HEAP_POINTER指定存儲的位置。
個人覺得這里L(fēng)WIP_RAM_HEAP_POINTER靜態(tài)指定,改為指針變量,初始化變量值為LWIP_RAM_HEAP_POINTER,然后也可以通過接口設(shè)定會更好。這樣用戶可以根據(jù)實(shí)際情況動態(tài)分配出這部分存儲來,否則靜態(tài)分配,程序不運(yùn)行也要占用空間比較浪費(fèi)。
LWIP_DECLARE_MEMORY_ALIGNED
實(shí)現(xiàn)如下
/** If you want to relocate the heap to external memory, simply define
四.源碼分析
4.1數(shù)據(jù)結(jié)構(gòu)
核心數(shù)據(jù)結(jié)構(gòu)如下struct mem
/**
* The heap is made up as a list of structs of this type.
* This does not have to be aligned since for getting its size,
* we only use the macro SIZEOF_STRUCT_MEM, which automatically aligns.
*/
struct mem {
/** index (-> ram[next]) of the next struct */
mem_size_t next;
/** index (-> ram[prev]) of the previous struct */
mem_size_t prev;
/** 1: this area is used; 0: this area is unused */
u8_t used;
#if MEM_OVERFLOW_CHECK
/** this keeps track of the user allocation size for guard checks */
mem_size_t user_size;
#endif
};
一個雙向鏈表來實(shí)現(xiàn),該結(jié)構(gòu)體描述某一個區(qū)塊,used表示當(dāng)前區(qū)塊是否使用。
Next和prev分別指向前后區(qū)塊,用于入鏈表和出鏈表操作。
這里為什么沒有描述本區(qū)塊大小的字段呢?
因?yàn)榭梢灾苯訌膎ext減去當(dāng)前區(qū)塊的基地址得到,所以不需要額外的大小信息了。
比如當(dāng)前區(qū)塊基地址是ptr則當(dāng)前區(qū)塊可用于分配的有效空間如下計算
(mem->next - (ptr + SIZEOF_STRUCT_MEM))
即后一個區(qū)塊的開始的地址-本區(qū)塊開始地址-信息頭。
這個實(shí)現(xiàn)其實(shí)和uCOS的堆管理實(shí)現(xiàn)差不多。
4.2接口
mem_init
初始化時,初始全局變量ram即對齊后的存儲空間,
lfree指向空閑塊的開頭,初始化時為ram,
lfree始終用于指向未分配的區(qū)塊。
此時只有一個整的未分配的區(qū)塊,next指向MEM_SIZE后,
MEM_SIZE外ram_end用于標(biāo)記結(jié)束,這也是之前存儲多分配的原因。
mem_malloc/mem_calloc
分配算法核心思想如下,
從lfree開始查找空閑空間大于等于需求size的塊。
如果找到了就分配它。
這里有一個處理,如果本塊比較大,則分配了size后還有剩余,所以要拆分,即分配出size后剩余的部分成為空閑塊。
這個到底多大要拆分,標(biāo)準(zhǔn)是大于等于(size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)拆分,即可用空間是MIN_SIZE_ALIGNED才拆分。
分配完后,如果分配出的塊剛好是lfree位置,則要更新lfree指向后續(xù)空閑塊。
因?yàn)閘free始終是指向的空閑塊,即lfree鏈接起來的都是空閑塊。分配出的塊就從lfree中去掉了。
比如分配黃色空間后如下
注意返回的是結(jié)構(gòu)體之后的可用空間部分。
mem_free
將區(qū)塊鏈接到lfree空閑塊中去,如果該塊前后為空閑則和前后拼接成大的空閑塊。
mem = (struct mem *)(void *)((u8_t *)rmem - (SIZEOF_STRUCT_MEM + MEM_SANITY_OFFSET));
先偏移結(jié)構(gòu)到塊頭。
如果mem小于lfree則要更新lfree為mem。
核心處理是plug_holes,判斷mem前后是否空閑,如果是空閑則和前后合并。
mem_trim
縮小
如果本塊縮小后剩余的空間不夠SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED,則沒有必要縮小,不處理。
如果縮小后剩余空間大于上述大小則看next是否空閑如果是則和next合并,否則單獨(dú)獨(dú)立出來一個空閑塊。
五.總結(jié)
LWIP實(shí)現(xiàn)了小型的堆管理,這樣無OS也可以直接移植使用,另外也可以配置為實(shí)用內(nèi)存池和系統(tǒng)實(shí)現(xiàn),比較靈活。
審核編輯 黃宇
-
以太網(wǎng)
+關(guān)注
關(guān)注
40文章
5424瀏覽量
171701
發(fā)布評論請先 登錄
相關(guān)推薦
評論