內存管理,是指軟件運行時對計算機內存資源的分配和使用的技術。其最主要的目的是如
何高效,快速的分配,而且在適當的時候釋放和回收內存資源。 內存管理的實現方法有不少種,
他們其實最終都是要實現 2 個函數: malloc 和 free; malloc 函數用於內存申請, free 函數用於
內存釋放。
c++
從上圖能夠看出,分塊式內存管理由內存池和內存管理表兩部分組成。內存池被等分爲 n
塊,對應的內存管理表,大小也爲 n,內存管理表的每個項對應內存池的一塊內存。
內存管理表的項值表明的意義爲:當該項值爲 0 的時候,表明對應的內存塊未被佔用,當
該項值非零的時候,表明該項對應的內存塊已經被佔用,其數值則表明被連續佔用的內存塊數。
好比某項值爲 10,那麼說明包括本項對應的內存塊在內,總共分配了 10 個內存塊給外部的某
個指針。
內寸分配方向如圖所示,是從頂底的分配方向。即首先從最末端開始找空內存。當內存
管理剛初始化的時候,內存表所有清零,表示沒有任何內存塊被佔用。
分配原理函數
當指針 p 調用 malloc 申請內存的時候,先判斷 p 要分配的內存塊數(m),而後從第 n 項開 測試
始,向下查找,直到找到 m 塊連續的空內存塊(即對應內存管理表項爲 0),而後將這 m 個內
存管理表項的值都設置爲 m(標記被佔用),最後,把最後的這個空內存塊的地址返回指針 p,
完成一次分配。注意,若是當內存不夠的時候(找到最後也沒找到連續的 m 塊空閒內存),則
返回 NULL 給 p,表示分配失敗。
釋放原理
當 p 申請的內存用完,須要釋放的時候,調用 free 函數實現。 free 函數先判斷 p 指向的內
存地址所對應的內存塊,而後找到對應的內存管理表項目,獲得 p 所佔用的內存塊數目 m(內
存管理表項目的值就是所分配內存塊的數目),將這 m 個內存管理表項目的值都清零,標記釋
放,完成一次內存釋放。
該原理解釋要結合下面的內部調用函數理解ui
該次程序 只實現了內部內存池的內存管理,還有外部內存池(SDRAM) 和CCM 內存池(此部分 SRAM 僅僅 CPU 能夠訪問)未實現,
但通常程序只要前者就夠了。內部內存池是STM32F4內部的處理芯片的RAM,STM32F429 自己自帶的 256K 字節內存 ,普通內存spa
(地址從: 0X2000 0000 開始,共 192KB),這部份內存任何外設都能夠訪問 。3d
內存管理的宏定義參數:即把160k的內存分爲64塊,每塊大小爲2560B指針
#define MEM1_BLOCK_SIZE 64 //內存塊大小爲64字節 #define MEM1_MAX_SIZE 160*1024 //最大管理內存 160K #define MEM1_ALLOC_TABLE_SIZE MEM1_MAX_SIZE/MEM1_BLOCK_SIZE //內存表大小
管理結構體:由於是靜態分配的內存管理,因此用宏定義的結構體,動態分配的能力有限,弄不了。code
struct _m_mallco_dev { uint8_t *membase; //內存池 管理SRAMBANK個區域的內存 uint32_t *memmap; //內存管理狀態表 uint8_t memrdy; //內存管理是否就緒 }; extern struct _m_mallco_dev malloc_handle2;
內存管理函數blog
extern void malloc_set(void *s,uint8_t c,uint32_t count); //設置內存(基本函數) extern void malloc_cpy(void *des,void *src,uint32_t n); //複製內存(基本函數) extern void malloc_Outfree(void *ptr); //內存釋放(外部調用) extern void *malloc_Outallot(uint32_t size); //內存分配(外部調用) //外部調用內部 extern uint32_t malloc_mem(uint32_t size); //內存分配(內部調用) extern uint8_t malloc_free(uint32_t offset); //內存釋放(內部調用) extern uint16_t malloc_perused(void) ; //得到內存使用率(外/內部調用) extern void malloc_init(void); //內存管理初始化函數(外/內部調用)
malloc.c程序,由原子的內存管理實驗原碼改制而來,有大幅度變化,只支持內部內存池,最好看看原子的實驗原碼,會有更深的瞭解,就不具體講解了。內存
/******************************僅用於內部存儲模塊SRAM*******************************************/ //內存池(32字節對齊) __align(32) uint8_t mem1base[MEM1_MAX_SIZE]; //內部SRAM內存池 //內存管理表 __align(32) uint32_t memmapbase[MEM1_ALLOC_TABLE_SIZE]; //內部SRAM內存池MAP //內存管理參數 const uint32_t memtblsize=MEM1_ALLOC_TABLE_SIZE; //內存表大小 const uint32_t memblksize=MEM1_BLOCK_SIZE; //內存分塊大小 const uint32_t memsize=MEM1_MAX_SIZE; //內存總大小 struct _m_mallco_dev malloc_handle2= { mem1base, memmapbase, 0, }; //內存管理初始化 //malloc_handle1內存管理結構體 void malloc_init() { malloc_set(malloc_handle2.memmap,0,memtblsize*4); //內存狀態表數據清零 malloc_handle2.memrdy=1; //內存管理初始化OK } //獲取內存使用率 //malloc_handle1內存管理結構體 //返回值:使用率(擴大了10倍,0~1000,表明0.0%~100.0%) uint16_t malloc_perused(void) { uint32_t used=0; uint32_t i; for(i=0;i<memtblsize;i++) { if(malloc_handle2.memmap[i]) { used++; } } printf("%u\n",used); return (used*1000)/(memtblsize); } //複製內存 //*des:目的地址 //*src:源地址 //n:須要複製的內存長度(字節爲單位) void malloc_cpy(void *des,void *src,uint32_t n) { uint8_t *xdes=des; uint8_t *xsrc=src; while(n--) *xdes++=*xsrc++; } //設置內存 //*s:內存首地址 //c :要設置的值 //count:須要設置的內存大小(字節爲單位) void malloc_set(void *s,uint8_t c,uint32_t count) { uint8_t *xs = s; while(count--) *xs++=c; } //分配內存(外部調用) //malloc_handle1內存管理結構體 //size:內存大小(字節) //返回值:分配到的內存首地址. void *malloc_Outallot(uint32_t size) { uint32_t offset; offset=malloc_mem(size); if(offset==0XFFFFFFFF) return NULL; else return (void*)((uint32_t)malloc_handle2.membase+offset); } //釋放內存(外部調用) //malloc_handle1內存管理結構體 //ptr:內存首地址 void malloc_Outfree(void *ptr) { uint32_t offset; if(ptr==NULL) return; offset=(uint32_t )ptr-(uint32_t )malloc_handle2.membase; malloc_free(offset); } //內存分配(內部調用) //malloc_handle1內存管理結構體 //size:要分配的內存大小(字節) //返回值:0XFFFFFFFF,表明錯誤;其餘,內存偏移地址 uint32_t malloc_mem(uint32_t size) { signed long offset=0; uint32_t nmemb; //須要的內存塊數 uint32_t cmemb=0; //連續空內存塊數 uint32_t i; if(!malloc_handle2.memrdy) malloc_init(); //未初始化,先執行初始化 if(size==0) return 0XFFFFFFFF; //不須要分配 //malloc_handle1->memmap=mem1mapbase; nmemb=size/memblksize; //獲取須要分配的連續內存塊數 if(size%memblksize) nmemb++; for(offset=memtblsize-1;offset>=0;offset--)//搜索整個內存控制區 { if(!malloc_handle2.memmap[offset]) cmemb++; //連續空內存塊數增長 else cmemb=0; //連續內存塊清零 if(cmemb==nmemb) //找到了連續nmemb個空內存塊 { for(i=0;i<nmemb;i++) //標註內存塊非空 { malloc_handle2.memmap[offset+i]=nmemb; } return (offset*memblksize);//返回偏移地址 } } return 0XFFFFFFFF;//未找到符合分配條件的內存塊 } //釋放內存(內部調用) //malloc_handle1內存管理結構體 //offset:內存地址偏移 //返回值:0,釋放成功;1,釋放失敗; uint8_t malloc_free(uint32_t offset) { int i; int index=offset/memblksize; //偏移所在內存塊號碼 int nmemb=malloc_handle2.memmap[index]; //內存塊數量 if(!malloc_handle2.memrdy)//未初始化,先執行初始化 { malloc_init(); ; return 1; //未初始化 } if(offset<=memsize) //偏移在內存池內. { for(i=0;i<nmemb;i++) //內存塊清零 { malloc_handle2.memmap[index+i]=NULL; } return 0; } else return 2;//偏移超區了. }
3.測試
測試代碼
uint8_t paddr[20]; //存放P Addr:+p地址的ASCII值
uint16_t memused=0;
uint8_t key;
uint8_t *p=0;
uint8_t i=0;
key=KEY_Scan(0); //按鍵掃描 switch(key) { case WKUP_PRES: { //printf("aa"); p=malloc_Outallot(2048);//申請2K字節 if(p!=NULL) sprintf((char*)p,"AAAAAAAA",i);//向p寫入一些內容 printf("寫入:%s", p); memused=malloc_perused(); sprintf((char*)paddr,"%d.%01d%%",memused/10,memused%10); printf("%s",paddr); break; } case KEY2_PRES: { malloc_Outfree(p);//釋放內存 printf("釋放:%s", p); p=0; //指向空地址 memused=malloc_perused(); sprintf((char*)paddr,"%d.%01d%%",memused/10,memused%10); printf("%s",paddr); break; } case KEY1_PRES: { break; } case KEY0_PRES: { break; } }
測試結果
申請的內存使用完後,必定要釋放掉,否則內存池會被寫爆。
有不足之處請指正,謝謝閱讀,麻煩點贊支持。