本文轉自公眾號,歡迎關注
https://mp.weixin.qq.com/s/mBoGSf_u9edFF01U_OZT9g
一.前言
lwIP為基礎結構提供了專用的內存池管理,比如netconn,protocol控制塊,包緩存等。在memp.c下實現。
LWIP的內存池有兩種方式實現,通過宏MEMP_MEM_MALLOC配置,默認opt.h中配置為0.
配置為1使用mem_malloc/mem_free mem.c
配置為0使用單獨實現memp.c。
我們這里重點講后者。
二.相關源碼
src/core/memp.c
src/include/lwip/memp.h
src/include/lwip/priv/memp_std.h
src/include/lwip/priv/memp_priv.h
三.源碼分析
3.1數據結構
內存池的關鍵數據結構是struct memp_desc對應內存池節點,一個類型的內存池是一個節點,
多個類型的內存池可以作為鏈表一起管理。
內存池最基本的數據結構是由宏LWIP_MEMPOOL_DECLARE定義的,
該宏在memp.h根據MEMP_MEM_MALLOC的配置實現為
#define LWIP_MEMPOOL_DECLARE(name,num,size,desc)
和
#define LWIP_MEMPOOL_DECLARE(name,num,size,desc)
前者是使用mem_malloc/mem_free實現內存池時使用,后者是單獨實現時使用,我們重點關注后者。
其中LWIP_DECLARE_MEMORY_ALIGNED可以用戶實現,如果用戶未定義,arch.h默認是
#ifndef LWIP_DECLARE_MEMORY_ALIGNED
即定義了一個數組variable_name,其大小是LWIP_MEM_ALIGN_BUFFER(size)
LWIP_MEM_ALIGN_BUFFER可以用戶實現,如果用戶未定義,arch.h中默認為
#ifndef LWIP_MEM_ALIGN_BUFFER
即人為的放大了,保證對齊之后始終能保證有足夠size的空間。
比如size是8,但是數組的基地址則可能是任意地址,比如是0x0001,要保證地址4字節對齊,
那么只能往后移動實際用的地址是0x0004,那么前面就浪費了3字節,此時8+(4-1)多分配3字節則浪費了這3字節也能保證剩余8字節可用8+(4-1)-3=8。
如果地址是0x0002則浪費2字節,可用8+(4-1)-2>8大于2字節,其他情況類似。
其中MEM_ALIGNMENT在opt中默認為1,用戶在lwipopts.h中可以配置。
繼續來看宏LWIP_MEMPOOL_DECLARE_STATS_INSTANCE和LWIP_MEMPOOL_DECLARE_STATS_REFERENCE,memp_priv.h中
如果定義了宏MEMP_STATS即使能統計信息,則LWIP_MEMPOOL_DECLARE_STATS_INSTANCE定義了結構體變量static struct stats_mem name;
LWIP_MEMPOOL_DECLARE_STATS_REFERENCE即&name,該變量地址
否則都是空
#if MEMP_STATS
其中stats.h中truct stats_mem如下
/** Memory stats */
繼續看DECLARE_LWIP_MEMPOOL_DESC,memp_priv.h中
如果定義了宏LWIP_DEBUG,MEMP_OVERFLOW_CHECK,LWIP_STATS_DISPLAY三者之一,實際就是成員賦值為desc,否則為空。
#if defined(LWIP_DEBUG) || MEMP_OVERFLOW_CHECK || LWIP_STATS_DISPLAY
繼續看LWIP_MEM_ALIGN_SIZE,在arch.h中默認實現,即向上對齊,MEM_ALIGNMENT對齊必須是2的指數。
#ifndef LWIP_MEM_ALIGN_SIZE
最終LWIP_MEMPOOL_DECLARE(name,num,size,desc)展開后為
u8_t memp_memory_name_base[LWIP_MEM_ALIGN_BUFFER(((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size))))]
static struct stats_mem memp_stats_name;
static struct memp*memp_tab_name;
const struct memp_desc memp_name= {
desc,
&memp_stats_name,
LWIP_MEM_ALIGN_SIZE(size),
num,
memp_memory_ name_base,
&memp_tab_name
};
即定義一個數組作為存儲,一個統計變量,一個static struct memp指針變量,一個結構變量static struct memp。
其中struct memp
#if !MEMP_MEM_MALLOC || MEMP_OVERFLOW_CHECK
其中struct memp_desc
/** Memory pool descriptor */
3.2內存池接口
LWIP_MEMPOOL_DECLARE(my_private_pool, 10, sizeof(foo), "Some description")
即上面數據結構說明的,使用該宏定義節點相關存儲數組,變量等。
LWIP_MEMPOOL_INIT(my_private_pool) ->memp_init_pool
將節點的存儲鏈表形式串起來。
*desc->tab指向最后一個item。
void* my_new_mem = LWIP_MEMPOOL_ALLOC(my_private_pool);->memp_malloc_pool
從鏈表中取出一個item。
LWIP_MEMPOOL_FREE(my_private_pool, my_new_mem);->memp_free_pool
Item插入到鏈表中
這里管理內存池實際可以用上述的鏈表形式,也可以用bitmap形式。
上述用鏈表形式每次malloc(出鏈表),free(入鏈表)都是堆鏈表尾,*desc->tab操作,執行時間固定。
而bitmap需要使用for循環去查詢空閑bit執行時間不固定,當然也可以使用類似ucOS優先級調度的查表法來優化。
四.內部使用的內存池
數據結構中分析了LWIP_MEMPOOL_DECLARE宏,實際就是定義內存節點相關的變量(存儲數組,統計變量,描述結構等)
用戶可以直接使用內存池接口。
而lwip內部也使用了內存池進行管理,見memp.c/memp.h
4.1內部使用內存池
memp.h中
/* run once with empty definition to handle all custom includes in lwippools.h */
第一個#include "lwip/priv/memp_std.h"前因為LWIP_MEMPOOL為空,所以include memp_std.h進來所有的LWIP_MEMPOOL為空,此時相當于只是include進來memp_std.h中包含的頭文件。
后一個#include "lwip/priv/memp_std.h"前LWIP_MEMPOOL(name,num,size,desc) MEMP_##name,
所以include memp_std.h后相當于,定義了MEMP_xxx的枚舉
展開就是(當然要對應的宏使能才會有)
typedef enum {
RAW_PCB_RAW_PCB,
UDP_PCB_UDP_PCB,
......
MEMP_MAX
} memp_t;
注意每次memp_std.h中都undef了相關宏,所以不影響后續使用。
這里是一個小的編程技巧,一個頭文件不同的宏配置下展開為不同的內容。
而memp.c中
#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc)
第一個包含memp_std.h 根據LWIP_MEMPOOL_DECLARE,定義了各個內存池節點
后一個memp_std.h則根據LWIP_MEMPOOL(name,num,size,desc) & name展開
即定義了一個結構體數組,數組的成員即上面定義的內存池節點。
const struct memp_desc *const memp_pools[MEMP_MAX] = {
&RAW_PCB,
......
};
所有使用到內建內存池總結如下
4.2內部使用內存池接口
以下都是調用內存池接口,不再贅述。
memp_free
memp_free_pool
memp_malloc
memp_malloc_pool
memp_init
五.堆使用內存池實現
前面我們看到定義宏MEMP_MEM_MALLOC,memp.c內存池可以使用mem.c堆實現.
反過來定義宏MEM_USE_POOLS,mem.c也可以用memp.c內存池實現.所以可以看出LWIP的堆管理實現方式比較靈活的,可以根據實際應用配置。
在opt.h中MEM_USE_POOLS是默認配置為0的,可以在lwipopts.h中修改配置。
#if !defined MEM_USE_POOLS || defined __DOXYGEN__
如果MEMP_USE_CUSTOM_POOLS配置為1,則MEMP_USE_CUSTOM_POOLS也要配置為1
此時
memp_std.h中
/*
可以看出,用戶必須提供lwippools.h文件,申明對應的內存池節點。
即在原來memp_t和memp_pools的基礎上后面繼續添加節點。
內容如下
LWIP_MALLOC_MEMPOOL_START
此時mem_malloc則從
memp_pools中
MEMP_POOL_FIRST~MEMP_POOL_LAST處
搜搜,找到有節點有空閑空間,大小滿足所需大小的即止。
即如下獲取MEMP_POOL_HELPER_FIRST和MEMP_POOL_HELPER_LAST
#if MEM_USE_POOLS && MEMP_USE_CUSTOM_POOLS
六.總結
內存池適合分配大小固定的動態內存分配,比如用于協議包等的緩存等;
其算法簡單,執行時間固定,比較可靠。但是其存儲是單獨分配的靜態數組,相當于需要固定分配一部分空間,不管對應的程序運行還是不運行,比較浪費空間。
當然也可以定義宏MEMP_MEM_MALLOC使用mem_malloc堆的方式實現,而mem_malloc進一步可以配置使用LWIP的實現還是使用系統的malloc(配置宏MEM_LIBC_MALLOC)。
此時和系統堆共用,這樣存儲利用率更高。
LWIP內部實現了堆和內存池管理可以直接使用,也可以配置使用系統的堆管理,非常靈活,移植性也非常好。
審核編輯 黃宇
-
以太網
+關注
關注
40文章
5385瀏覽量
171161 -
內存
+關注
關注
8文章
3004瀏覽量
73900 -
驅動開發
+關注
關注
0文章
130瀏覽量
12065
發布評論請先 登錄
相關推薦
評論