http://blog.csdn.net/Angel_94/article/details/50817918
2016
使用LWIP源碼版本為1.4.1
使用內存池分配內存的優(yōu)點(diǎn)在于速度快、效率高、不會(huì )產(chǎn)生很多內存碎片,但是缺點(diǎn)在于只能分配各種固定大小的內存空間,LWIP必須實(shí)現知道用戶(hù)要使用哪些類(lèi)型的POOL,每種類(lèi)型的POOL數量,然后根據這個(gè)需求建立內存池。
內存池初始化函數memp_init,在內核初始化時(shí),該函數必須被調用,用來(lái)完成內存池的建立;
內存池分配函數memp_malloc,通常被內核調用,以實(shí)現核中固定數據結構的申請;
內存池釋放函數memp_free;
個(gè)人一直是跟老師自學(xué)嵌入式,許多問(wèn)題也是第一次遇到,比如這個(gè)臨界區。下面一些代碼多次遇到了臨界問(wèn)題。
雖然多個(gè)進(jìn)程可以共享系統中的各種資源,但其中許多資源一次只能為一個(gè)進(jìn)程所使用,我們把一次僅允許一個(gè)進(jìn)程使用的資源稱(chēng)為臨界資源。許多物理設備都屬于臨界資源,比如打印機,輸入機等。此外,還有許多變量、數據等都可以被若干進(jìn)程共享,也屬于臨界資源。
對臨界資源的訪(fǎng)問(wèn),必須互斥的進(jìn)行,在每個(gè)進(jìn)程中,訪(fǎng)問(wèn)臨界資源的那段代碼稱(chēng)為臨界區。為了保證臨界資源的正確使用,可以把臨界資源的訪(fǎng)問(wèn)過(guò)程分為四個(gè)部分:
+ 進(jìn)入臨界區:為了進(jìn)入臨界區使用臨界資源,在進(jìn)入區要檢查可否進(jìn)入臨界區,如果可以進(jìn)入,則應設置正在訪(fǎng)問(wèn)臨界區的標志,以組織其他進(jìn)程同時(shí)進(jìn)入臨界區
+ 臨界區:進(jìn)程中訪(fǎng)問(wèn)臨界資源的那段代碼,又稱(chēng)為臨界段。
+ 退出區:將正在訪(fǎng)問(wèn)臨界區的標志清除
+ 剩余區:代碼中的其余部分
struct memp { struct memp *next; //下一個(gè)鏈表#if MEMP_OVERLOW_CHECK /*發(fā)生溢出時(shí)調用函數的文件名,mem_malloc調用者的文件*/ const char *file; /*發(fā)生溢出時(shí)調用函數的行號,mem_malloc調用者的行數*/ int line;#endif /* MEMP_OVERFLOW_CHECK */}全局指針數組,指向每類(lèi)POOL的第一個(gè)POOL,memp.c文件中
static struct memp *memp_tab[MEMP_MAX];全局數組,用來(lái)記錄每個(gè)POOL的大小,memp.c文件中
const u16_t memp_sizes[MEMP_MAX]={ #define LWIP_MEMPOOL(name, num, size, desc) LWIP_MEM_ALIGN_SIZE(size), #include "lwip/memp_std.h"};memp_sizes的真實(shí)面目如下:
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(struct tcp_pcb_listen)), LWIP_MEM_ALIGN_SIZE(sizeof(struct tcp_seg)), …….};memp_sizes中保存了每種類(lèi)型POOL的大小,這里的大小都是進(jìn)行了內存對
齊的。
全局數組,用來(lái)記錄每類(lèi)POOL中POOL的個(gè)數,memp.c文件中
const u16_t memp_num[MEMP_MAX]={ #define LWIP_MEMPOOL(name, num, size, desc) (num), #include "lwip/memp_std.h"};memp_num的真實(shí)面目如下:
const u16_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等等都是又用戶(hù)
定義的,用來(lái)記錄對應的POOL的數量,用戶(hù)可以在lwipopts.h文件中定義,
LWIP在opt.h中已經(jīng)配置了默認值。
全局型指針數組,指向每類(lèi)POOL的描述符,memp.c文件中:
static sonst char *memp_desc[MEMP_MAX]={ #define LWIP_MEMPOOL(name, num, size, desc) (desc), #include "lwip/memp_std.h"};memp_desc的真實(shí)面目如下:
static const char *memp_desc[MEMP_MAX] ={ ("RAW_PCB"), ("UDP_PCB"), ("TCP_PCB"), ("TCP_PCB_LISTEN"), ("TCP_PCB_LISTEN"), …….};memp_desc中的每個(gè)元素指向了一個(gè)字符串,這些字符串在統計信息輸出中
可能用到。
這是一個(gè)數組,這才是真正的內存池,memp.c文件中
static u8_t memp_memory[MEM_ALIGNMENT - 1 #define LWIP_MEMPOOL(name,num,size,desc) + ((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size))) #include "lwip/memp_std.h"];memp_memory的真實(shí)面目如下:
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表示需要在每個(gè)POOL頭部預留的空間,LWIP中在某些特殊
場(chǎng)合使用該空間中的值來(lái)對POOL進(jìn)行特殊處理,這里不使用該項功能,所以
MEMP_SIZE為0,。如果使用到MEMP_SIZE的話(huà)也需要對這個(gè)大小進(jìn)行內存對
齊!
內存池的初始化,主要是為每種內存池建立鏈表memp_tab,其鏈表是逆序的,此外,如果有統計功能使能的話(huà),也記錄了各種內存池的數目
通過(guò)struct memp作為節點(diǎn),以單鏈表的形式串聯(lián)起來(lái)
voidmemp_init(void){ struct memp *memp; u16_t i, j; for (i = 0; i < MEMP_MAX; ++i) { MEMP_STATS_AVAIL(used, i, 0); MEMP_STATS_AVAIL(max, i, 0); MEMP_STATS_AVAIL(err, i, 0); MEMP_STATS_AVAIL(avail, i, memp_num[i]); }#if !MEMP_SEPARATE_POOLS memp = (struct memp *)LWIP_MEM_ALIGN(memp_memory); //將內存池起始空間進(jìn)行對齊#endif /* !MEMP_SEPARATE_POOLS */ /* for every pool: */ for (i = 0; i < MEMP_MAX; ++i) { //依次對每種類(lèi)型的POOL進(jìn)行操作 memp_tab[i] = NULL; //初始指針為空#if MEMP_SEPARATE_POOLS memp = (struct memp*)memp_bases[i];#endif /* MEMP_SEPARATE_POOLS */ /* create a linked list of memp elements */ for (j = 0; j < memp_num[i]; ++j) { //將該類(lèi)所有POOL組成鏈表的形式 memp->next = memp_tab[i]; memp_tab[i] = memp; memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + memp_sizes[i]#if MEMP_OVERFLOW_CHECK + MEMP_SANITY_REGION_AFTER_ALIGNED#endif ); //將每個(gè)POOL的起始處轉換為memp類(lèi)型,以實(shí)現鏈表連接 } }#if MEMP_OVERFLOW_CHECK memp_overflow_init(); /* check everything a first time to see if it worked */ memp_overflow_check_all();#endif /* MEMP_OVERFLOW_CHECK */}系統初始化時(shí),函數,memp_init是必須被調用的,否則內存池空間將無(wú)效。
下圖是初始化內存池空間示意圖
分配過(guò)程是如果memp_tab[]數組中相應鏈表的指針為空,說(shuō)明該類(lèi)型的POOL已經(jīng)沒(méi)有了,分配失??;否則選擇鏈表中的第一個(gè)POOL,并在POOL最開(kāi)始處預留出MEMP_SIZE(這里為0)的空間,最后將有效地址返回給函數調用者。
void *#if !MEMP_OVERFLOW_CHECKmemp_malloc(memp_t type)#else/* memp_t type 輸入參數為需要分配的POOL的類(lèi)型*/memp_malloc_fn(memp_t type, const char* file, const int line)#endif{ struct memp *memp; SYS_ARCH_DECL_PROTECT(old_level); //聲明一個(gè)臨界區保護變量 LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;); SYS_ARCH_PROTECT(old_level); //進(jìn)入臨界區#if MEMP_OVERFLOW_CHECK >= 2 memp_overflow_check_all();#endif /* MEMP_OVERFLOW_CHECK >= 2 */ memp = memp_tab[type]; //獲得對應頭指針指向的POOL if (memp != NULL) { //不為空,則說(shuō)明還有空閑的POOL memp_tab[type] = memp->next; //頭指針指向下一個(gè)節點(diǎn)#if MEMP_OVERFLOW_CHECK memp->next = NULL; memp->file = file; memp->line = line;#endif /* MEMP_OVERFLOW_CHECK */ MEMP_STATS_INC_USED(used, type); //增加內存池分配相關(guān)統計量 LWIP_ASSERT("memp_malloc: memp properly aligned", ((mem_ptr_t)memp % MEM_ALIGNMENT) == 0); memp = (struct memp*)(void *)((u8_t*)memp + MEMP_SIZE);//留出預留空間,這里MEMP_SIZE為0,不預留任何空間 } else { LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", memp_desc[type])); MEMP_STATS_INC(err, type); //增加內存池分配出錯統計量 } SYS_ARCH_UNPROTECT(old_level); //退出臨界區 return memp; //返回可用空間起始地址}voidmemp_free(memp_t type, void *mem)//兩個(gè)參數,釋放的POOL的類(lèi)型及起始地址{ struct memp *memp; //聲明臨界區保護變量 SYS_ARCH_DECL_PROTECT(old_level); if (mem == NULL) { //釋放地址如果為空,直接返回 return; } LWIP_ASSERT("memp_free: mem properly aligned", ((mem_ptr_t)mem % MEM_ALIGNMENT) == 0); memp = (struct memp *)(void *)((u8_t*)mem - MEMP_SIZE);//得到POOL的起始地址 SYS_ARCH_PROTECT(old_level); //進(jìn)入臨界區#if MEMP_OVERFLOW_CHECK#if MEMP_OVERFLOW_CHECK >= 2 memp_overflow_check_all();#else memp_overflow_check_element_overflow(memp, type); memp_overflow_check_element_underflow(memp, type);#endif /* MEMP_OVERFLOW_CHECK >= 2 */#endif /* MEMP_OVERFLOW_CHECK */ MEMP_STATS_DEC(used, type); //減少內存池分配相關(guān)的統計量 memp->next = memp_tab[type]; //將POOL插入到memp_tab[頭部] memp_tab[type] = memp;#if MEMP_SANITY_CHECK LWIP_ASSERT("memp sanity", memp_sanity());#endif /* MEMP_SANITY_CHECK */ SYS_ARCH_UNPROTECT(old_level); //退出臨界區}聯(lián)系客服