這這裏是接上一篇內存池管理部分的,這裏若是讀者一打開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的定義以下,看完繼續懵逼中。。。,可是不能慌一個個宏替換出來spa
#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\ };
而後就是這樣子的宏替換,此處未所有列舉code
#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 }; . . .
,同理理解到這裏下面繼續第二個宏就是同理結果以下orm
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是這樣來的blog
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