基於STM32F429的記憶體管理
1.記憶體管理介紹
記憶體管理,是指軟體執行時對計算機記憶體資源的分配和使用的技術。其最主要的目的是如
何高效,快速的分配,並且在適當的時候釋放和回收記憶體資源。 記憶體管理的實現方法有很多種,
他們其實最終都是要實現 2 個函式: malloc 和 free; malloc 函式用於記憶體申請, free 函式用於
記憶體釋放。
從上圖可以看出,分塊式記憶體管理由記憶體池和記憶體管理表兩部分組成。記憶體池被等分為 n
塊,對應的記憶體管理表,大小也為 n,記憶體管理表的每一個項對應記憶體池的一塊記憶體。
該項值非零的時候,代表該項對應的記憶體塊已經被佔用,其數值則代表被連續佔用的記憶體塊數。
比如某項值為 10,那麼說明包括本項對應的記憶體塊在內,總共分配了 10 個記憶體塊給外部的某
個指標。
內寸分配方向如圖所示,是從頂底的分配方向。即首先從最末端開始找空記憶體。當記憶體
管理剛初始化的時候,記憶體表全部清零,表示沒有任何記憶體塊被佔用。
分配原理
當指標 p 呼叫 malloc 申請記憶體的時候,先判斷 p 要分配的記憶體塊數(m),然後從第 n 項開
始,向下查詢,直到找到 m 塊連續的空記憶體塊(即對應記憶體管理表項為 0),然後將這 m 個內
完成一次分配。注意,如果當記憶體不夠的時候(找到最後也沒找到連續的 m 塊空閒記憶體),則
返回 NULL 給 p,表示分配失敗。
釋放原理
當 p 申請的記憶體用完,需要釋放的時候,呼叫 free 函式實現。 free 函式先判斷 p 指向的內
存地址所對應的記憶體塊,然後找到對應的記憶體管理表專案,得到 p 所佔用的記憶體塊數目 m(內
存管理表專案的值就是所分配記憶體塊的數目),將這 m 個記憶體管理表專案的值都清零,標記釋
放,完成一次記憶體釋放。
該原理解釋要結合下面的內部呼叫函式理解
2.程式碼介紹
該次程式 只實現了內部記憶體池的記憶體管理,還有外部記憶體池(SDRAM) 和CCM 記憶體池(此部分 SRAM 僅僅 CPU 可以訪問)未實現,
但一般程式只要前者就夠了。內部記憶體池是STM32F4內部的處理晶片的RAM,STM32F429 本身自帶的 256K 位元組記憶體 ,普通記憶體
(地址從: 0X2000 0000 開始,共 192KB),這部分記憶體任何外設都可以訪問 。
記憶體管理的巨集定義引數:即把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 //記憶體表大小
管理結構體:因為是靜態分配的記憶體管理,所以用巨集定義的結構體,動態分配的能力有限,弄不了。
struct _m_mallco_dev { uint8_t *membase; //記憶體池 管理SRAMBANK個區域的記憶體 uint32_t *memmap; //記憶體管理狀態表 uint8_t memrdy; //記憶體管理是否就緒 }; extern struct _m_mallco_dev malloc_handle2;
記憶體管理函式
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; } }
測試結果
4,注意事項
申請的記憶體使用完後,一定要釋放掉,不然記憶體池會被寫爆。
有不足之處請指正,謝謝閱讀,麻煩點贊支援。
&n