這這里是接上一篇內存池管理部分的,這里如果讀者一打開memp.c的話會感覺特別那一理解原作者在干嘛,但是看懂了就明白原作者是怎么巧妙的使用了宏。廢話不多說先說了下我分析是一下宏的條件是
前提條件
MEMP_STATS = 0
MEMP_OVERFLOW_CHECK = 0
首先要去簡單的看下#include "lwip/priv/memp_std.h"文件的格式,只需要明白這個文件依賴LWIP_MEMPOOL(name,num,size,desc)這個宏,並且在文件結尾將宏清除。
因此出現底下的最難的兩塊
#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc) #include "lwip/priv/memp_std.h" const struct memp_desc *const memp_pools[MEMP_MAX] = { #define LWIP_MEMPOOL(name,num,size,desc) &memp_ ## name, #include "lwip/priv/memp_std.h" };
先說第一個,繼續追LWIP_MEMPOOL_DECLARE的定義如下,看完繼續懵逼中。。。,但是不能慌一個個宏替換出來
#define LWIP_MEMPOOL_DECLARE(name,num,size,desc) \ LWIP_DECLARE_MEMORY_ALIGNED(memp_memory_ ## name ## _base, ((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))); \ \ LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(memp_stats_ ## name) \ \ static struct memp *memp_tab_ ## name; \ \ const struct memp_desc memp_ ## name = { \ DECLARE_LWIP_MEMPOOL_DESC(desc) \ LWIP_MEMPOOL_DECLARE_STATS_REFERENCE(memp_stats_ ## name) \ LWIP_MEM_ALIGN_SIZE(size), \ (num), \ memp_memory_ ## name ## _base, \ &memp_tab_ ## name \ };
里面相關宏的實現匯總如下
#ifndef LWIP_DECLARE_MEMORY_ALIGNED #define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u8_t variable_name[LWIP_MEM_ALIGN_BUFFER(size)] #endif #define LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(name) #define DECLARE_LWIP_MEMPOOL_DESC(desc) #define LWIP_MEMPOOL_DECLARE_STATS_REFERENCE(name) #define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1U) & ~(MEM_ALIGNMENT-1U))
最后就有這樣一個過程
#define LWIP_MEMPOOL_DECLARE(name,num,size,desc) \ LWIP_DECLARE_MEMORY_ALIGNED(memp_memory_ ## name ## _base, ((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))); \ \ LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(memp_stats_ ## name) \ \ static struct memp *memp_tab_ ## name; \ \ const struct memp_desc memp_ ## name = { \ DECLARE_LWIP_MEMPOOL_DESC(desc) \ LWIP_MEMPOOL_DECLARE_STATS_REFERENCE(memp_stats_ ## name) \ LWIP_MEM_ALIGN_SIZE(size), \ (num), \ memp_memory_ ## name ## _base, \ &memp_tab_ ## name \ }; | | \|/ #define LWIP_MEMPOOL_DECLARE(name,num,size,desc) \ memp_memory_RAW_PCB_base[(num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))]; \ static struct memp *memp_tab_RAW_PCB; \ const struct memp_desc memp_RAW_PCB = {\ LWIP_MEM_ALIGN_SIZE(size), \ (num), \ memp_memory_RAW_PCB _base,\ &memp_tab_ RAW_PCB\ };
然后就是這樣子的宏替換,此處未全部列舉
#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc) #include "lwip/priv/memp_std.h" | | \|/ memp_memory_RAW_PCB_base[(num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))]; \ static struct memp *memp_tab_RAW_PCB; const struct memp_desc memp_RAW_PCB = { “RAW_PCB” LWIP_MEM_ALIGN_SIZE(size), (num), memp_memory_RAW_PCB _base, &memp_tab_ RAW_PCB }; memp_memory_UDP_PCB_base[(num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))]; \ static struct memp *memp_tab_UDP_PCB; const struct memp_desc memp_UDP_PCB = { “UDP_PCB” LWIP_MEM_ALIGN_SIZE(size), (num), memp_memory_UDP_PCB _base, &memp_tab_UDP_PCB }; . . .
,同理理解到這里下面繼續第二個宏就是同理結果如下
const struct memp_desc *const memp_pools[MEMP_MAX] = { #define LWIP_MEMPOOL(name,num,size,desc) &memp_ ## name, #include "lwip/priv/memp_std.h" }; | | \|/ const struct memp_desc *const memp_pools[MEMP_MAX] = { &memp_RAW_PCB, &memp_UDP_PCB, . . . }
注意這里的MEMP_MAX是這樣來的
typedef enum { #define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name, #include "lwip/priv/memp_std.h" MEMP_MAX } memp_t; | | \|/ typedef enum { MEMP_RAW_PCB, MEMP_UDP_PCB, . . . MEMP_MAX } memp_t;
然后這里還還需要了解一個結構體的定義如下,
struct memp_desc { #if defined(LWIP_DEBUG) || MEMP_OVERFLOW_CHECK || LWIP_STATS_DISPLAY /** Textual description */ const char *desc; #endif /* LWIP_DEBUG || MEMP_OVERFLOW_CHECK || LWIP_STATS_DISPLAY */ /** Element size */ u16_t size; #if !MEMP_MEM_MALLOC /** Number of elements */ u16_t num; /** Base address */ u8_t *base; /** First free element of each pool. Elements form a linked list. */ struct memp **tab; #endif /* MEMP_MEM_MALLOC */ };
這樣memp_pools就將整個mempool的內存串到了一個結構體數組中。要注意此時每個memp_pools中的memp_desc結構體中的memp_tab_UDP_PCB還只是一個指針的指針,並未有具體的實際意義。然后memp_init會進行這一工作,去掉宏不編譯的部分
memp_init如下
void memp_init(void) { u16_t i; /* for every pool: */ for (i = 0; i < LWIP_ARRAYSIZE(memp_pools); i++) { memp_init_pool(memp_pools[i]); } }
就是循環調用memp_init_pool,接着看去掉宏簡化后的memp_init_pool
void memp_init_pool(const struct memp_desc *desc) { int i; struct memp *memp; *desc->tab = NULL; memp = (struct memp *)LWIP_MEM_ALIGN(desc->base); /* create a linked list of memp elements */ for (i = 0; i < desc->num; ++i) { memp->next = *desc->tab; *desc->tab = memp; memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + desc->size } }
到這里所有內存池的定義和初始化已經完成了借用野火的一張圖,初始化后的pool結構如下
每一個類型的池最后由,tab將所有的空閑池串起來,組成一個內存池單向鏈表。到此最難理解的部分已經完了,接下來內存池的內存分配和釋放就是很簡單的內容了。
內存申請
void * memp_malloc(memp_t type){ void *memp; // 取對應內存池的控制塊 memp = do_memp_malloc_pool(memp_pools[type]); return memp; } //這個函數內部實際上調用了 do_memp_malloc_pool簡化后如下, static void * do_memp_malloc_pool(const struct memp_desc *desc) { struct memp *memp; SYS_ARCH_DECL_PROTECT(old_level); SYS_ARCH_PROTECT(old_level); memp = *desc->tab; if (memp != NULL) { *desc->tab = memp->next; SYS_ARCH_UNPROTECT(old_level); /* cast through u8_t* to get rid of alignment warnings */ return ((u8_t *)memp + MEMP_SIZE); } else { SYS_ARCH_UNPROTECT(old_level); } return NULL; }
因為tab是空閑pool的頭,所以內存申請直接就是返回tab指向pool就可以了。同時內存釋放就是將pool從新插入單向鏈表的操作了。具體簡化的代碼如下
內存釋放
void memp_free(memp_t type, void *mem) { if (mem == NULL) { return; } do_memp_free_pool(memp_pools[type], mem); } //調用do_memp_free_pool static void do_memp_free_pool(const struct memp_desc *desc, void *mem) { struct memp *memp; SYS_ARCH_DECL_PROTECT(old_level); /* cast through void* to get rid of alignment warnings */ memp = (struct memp *)(void *)((u8_t *)mem - MEMP_SIZE); SYS_ARCH_PROTECT(old_level); memp->next = *desc->tab; *desc->tab = memp; SYS_ARCH_UNPROTECT(old_level); }
現在LWIP的兩種內存策略的實現方式,都已經理解過了,其中內存池的溢出檢測部分沒有說,但是已經可以幫助我們使用LWIP了,作者設計兩種內存策略是有他的設計初衷的,看了#include "lwip/priv/memp_std.h"文件就知道,內存池的出現就是為一些特殊的長度固定的數據結構設計的,他分配快速,釋放亦是,並且很定不會有內存碎片,但是這還是一種空間換時間的做法,因為內存池申請函數,支持如果當前尺寸的pool用完了,可以分配更大的池。內存堆就是用來應對大小不定的內存分配場合的,當人LWIP支持用堆實現pool也支持用pool實現堆,同時還支持用戶池,這些功能都可以通過宏簡單 的配置具體如下
MEM_LIBC_MALLOC 使用C庫
MEMP_MEM_MALLOC 使用內存堆替換內襯池。
MEM_USE_POOLS 使用內存池替換內存堆
MEMP_USE_CUSTOM_POOLS 使用用戶定義的內存池,這個實現需要用戶提供一個文件lwippools.h,並按如下形式定義字節的內存池,要求內存池的大小要依次增大。
LWIP_MALLOC_MEMPOOL_START LWIP_MALLOC_MEMPOOL(20, 256) LWIP_MALLOC_MEMPOOL(10, 512) LWIP_MALLOC_MEMPOOL(5, 1512) LWIP_MALLOC_MEMPOOL_END
好了,到此LWIP的內存管理部分算是簡單的學習了一下了,內存管理完。
2019-06-16 17:58:42