1. 程式人生 > >基於STM32F429的記憶體管理

基於STM32F429的記憶體管理

1.記憶體管理介紹

  記憶體管理,是指軟體執行時對計算機記憶體資源的分配和使用的技術。其最主要的目的是如
何高效,快速的分配,並且在適當的時候釋放和回收記憶體資源。 記憶體管理的實現方法有很多種,
他們其實最終都是要實現 2 個函式: malloc 和 free; malloc 函式用於記憶體申請, free 函式用於
記憶體釋放。
             

   從上圖可以看出,分塊式記憶體管理由記憶體池和記憶體管理表兩部分組成。記憶體池被等分為 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 個記憶體管理表專案的值都清零,標記釋
放,完成一次記憶體釋放。

該原理解釋要結合下面的內部呼叫函式理解

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