01記憶體池
LWIP中的記憶體池(POOL)分配策略簡單,但是記憶體的分配、釋放效率高,可以有效的防止記憶體碎片的產生,在記憶體的策略下用戶只能申請固定大小的空間,記憶體池方法主要用于LWIP內核中固定資料結構的分配,比如UDP控制塊,TCP控制塊等,LWIP內核在初始化的時候已經為每個資料結構型別都初始化了一定數量的POOL,檔案memp.c和memp.h就是記憶體池相關內容,
至于LWIP內核建立多少種POOL依賴于用戶和系統配置,比如如果定義了宏LWIP_UDP為1,那么在編譯時與UDP控制塊資料結構相關的記憶體池POOL就會被建立(MEMP_UDP_PCB),如果定義了宏LWIP_TCP為1,編譯時與TCP資料結構相關的記憶體池就會被建立(MEMP_TCP_PCB、MEMP_TCP_SEG)等等!每種型別的POOL大小都是固定的,用戶可以在lwipopts.h檔案中定義,LWIP在opt.h中已經配置了默認值,
有6個與LWIP記憶體池有關的全域變數和資料結構:memp_t、memp_tab[]、memp_sizes[]、memp_num[]、memp_desc[]和memp_memory[],
1.1、memp_t資料型別
memp_t為一個列舉型別變數,用來給每個POOL取個名字,或者說是編號,memp_t在檔案memp.h檔案中定義,定義如下:

#define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name,
這句代碼意思是:
遇到LWIP_MEMPOOL(name,num,size,desc)換成MEMP_##name
例如:在memp_std.h的34行
LWIP_MEMPOOL(RAW_PCB, MEMP_NUM_RAW_PCB, sizeof(structraw_pcb), "RAW_PCB")
變成了
MEMP_RAW_PCB
所以memp_t編譯之后如下:
typedefenum
{
MEMP_ RAW_PCB,
MEMP_ UDP_PCB,
MEMP_ TCP_PCB,
MEMP_ TCP_PCB_LISTEN,
MEMP_TCP_SEG,
MEMP_ REASSDATA,
…….
MEMP_MAX
} memp_t;
其中MEMP_MAX代表memp_t代表列舉型別中元素總個數(C語言基礎知識),并不代表任何型別的POOL
1.2、memp_tab全域指標陣列
memp_tab為一個全域指標陣列,指向每類POOL的第一個POOL,memp_tab在檔案memp.c檔案中定義,定義如下:

1.3、memp_sizes全域陣列
memp_sizes為一個全域陣列,用來記錄每個POOL的大小,memp_sizes在檔案memp.c檔案中定義,定義如下:

編譯之后
const u16_t memp_sizes[MEMP_MAX] = { LWIP_MEM_ALIGN_SIZE(sizeof(struct raw_pcb)), LWIP_MEM_ALIGN_SIZE(sizeof(struct udp_pcb)), LWIP_MEM_ALIGN_SIZE(sizeof(struct tcp_pcb)), LWIP_MEM_ALIGN_SIZE(sizeof(structtcp_pcb_listen)), LWIP_MEM_ALIGN_SIZE(sizeof(struct tcp_seg)), ……. }
memp_sizes中保存了每種型別POOL的大小,這里的大小都是進行了記憶體對齊的,

這個宏定義,MEM_ALIGNMENT為4,也就是4位元組對齊,
分析這個宏定義,也就說,當MEM_ALIGNMENT為4時,傳入的size為3時,變為4
申請3個位元組,實際申請4個位元組
申請6個位元組,實際申請8個位元組
申請18個位元組,實際申請20個位元組
1.4、 memp_num[]全域陣列
memp_num為一個全域陣列,用來記錄每類POOL中POOL的個數,memp_num在檔案memp.c檔案中定義,定義如下:

編譯之后
constu16_t memp_num[MEMP_MAX] =
{
(MEMP_NUM_RAW_PCB),
(MEMP_NUM_UDP_PCB),
(MEMP_NUM_TCP_PCB),
(MEMP_NUM_TCP_PCB_LISTEN),
(MEMP_NUM_TCP_SEG),
……
};
上面的MEMP_NUM_RAW_PCB、MEMP_NUM_UDP_PCB等等都是由用戶定義的,用來記錄對應的POOL的數量,用戶可以在lwipopts.h檔案中定義,LWIP在opt.h中已經配置了默認值,
1.5、memp_desc[]全域型指標陣列
memp_desc為一個全域型指標陣列,指向每類POOL的描述符,memp_desc在檔案memp.c檔案中定義,定義如下:

編譯之后
staticconst char *memp_desc[MEMP_MAX] = { ("RAW_PCB"), ("UDP_PCB"), ("TCP_PCB"), ("TCP_PCB_LISTEN"), ("TCP_PCB_LISTEN"), ……. };
memp_desc中的每個元素指向了一個字串,這些字串在統計資訊輸出中可能用到,
1.6、memp_memory[]陣列
memp_memory為一個陣列,這個陣列才是真正的記憶體池!!!這個陣列在檔案memp.c檔案中定義,定義如下:

編譯之后
static u8_t memp_memory [ MEM_ALIGNMENT – 1 +((MEMP_NUM_RAW_PCB) * (MEMP_SIZE + MEMP_ALIGN_SIZE(sizeof(struct raw_pcb)) )) +((MEMP_NUM_UDP_PCB) * (MEMP_SIZE + MEMP_ALIGN_SIZE(sizeof(struct udp_pcb)) )) +((MEMP_NUM_TCP_PCB) * (MEMP_SIZE + MEMP_ALIGN_SIZE(sizeof(struct tcp_pcb)) )) …….. ];
其中MEMP_SIZE表示需要在每個POOL頭部預留的空間,LWIP中在某些特殊場合使用該空間中的值來對POOL進行特殊處理,這里不使用該項功能,所以MEMP_SIZE為0,,如果使用到MEMP_SIZE的話也需要對這個大小進行記憶體對齊!
1.7、與記憶體池管理相關的函式:

使用記憶體池分配記憶體的優點在于速度快,效率高,不會產生記憶體碎片,但是缺點在于只能分配各種固定大小的記憶體空間,LWIP必須實作知道用戶要使用哪些型別的POOL,每種型別的POOL數量,然后根據這個需求建立記憶體池,
02記憶體堆
LWIP還提供了另外一種記憶體策略—記憶體堆,使用記憶體堆策略就可以隨便申請任意大小的記憶體了,但是這種方法效率和速度會有所下降,
使用記憶體堆策略的話用戶申請的記憶體大小有最小限制,所申請的記憶體大小不能小于MIN_SIZE,LWIP默認的MIN_SIZE為12個位元組,在mem.c檔案中,該值用戶可以自行定義,

使用記憶體堆策略,其有點事記憶體浪費小,比較簡單,適合于小記憶體的管理,但是缺點就是如果頻繁的進行動態記憶體申請和釋放的話,可能會造成嚴重的記憶體碎片,如果碎片嚴重的話可能會導致記憶體分配失敗!
記憶體堆策略下的記憶體空間是陣列:ram_heap[],在mem.c檔案中,
![]()
與記憶體堆有關的函式有3個:
mem_init()
mem_malloc()
mem_free()
03其他記憶體策略
前面講的記憶體池(POOL)和記憶體堆(HEAP)這兩個記憶體策略都是LWIP默認的記憶體策略,LWIP內核中大量的使用了這兩個策略,不過LWIP也給我們提供了其他可選的記憶體策略,
(1)、當定義宏MEM_LIBC_MALLOC為1,那么與記憶體堆相關的代碼就不會被編譯的,記憶體堆中的mem_malloc()和mem_free()就會被ANSIC編譯器自帶的malloc()和free()替代,
(2)、當定義宏MEMP_MEM_MALLOC為1,那么記憶體池檔案memp.c就不會被編譯,
(3)、當定義宏MEM_USE_POOLS定義為1,那么記憶體堆分配相關的函式及全域變數不會被編譯,這個時候就用記憶體池分配方式來實作記憶體堆的的分配方式,因為記憶體池的分配策略效率很高,
但是(3)中的方法使用起來比較麻煩,需要在lwipopts.h中定義宏MEM_USE_POOLS和MEM_USE_CUSTOM_POOLS都為1,還需要在另外一個頭檔案lwippools.h中開辟一些用于記憶體堆分配函式的記憶體池,

代碼和工程已經開源
代碼和KeilIAR工程開源地址:
https://github.com/strongercjd/STM32F207VCT6
點擊查看本文所在的專輯,STM32F207網路開發
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/280599.html
標籤:嵌入式
