內存管理實驗


  一、內存管理的原理

  通過本實驗,主要通過介紹程序運行過程中采用到C語言中的那些知識點,其中比較好用的是結構體,比較有趣的是C語言的靈魂“指針”。

  在STM32f103芯片上的內存管理實驗,主要是采用分塊式內存管理,即把內存(也叫內存池)分成整數塊(一般每塊內存取32字節或者64字節),同時在內存池中的每一塊內存都對應着內存管理表的一塊。

  

 

  分配過程:當要分配n塊內存,從內存管理表的頂部往底層尋找,直到找到連續的n塊空內存(非0表示該內存塊已經被占用),以此時的內存管理表的地址為起點(包括這個地址),往上n塊都值成數字“n”,方便以后刪除釋放內存,同時返回一個地址指針(表示內存管理表分配完內存之后所在)。看

mymalloc(memx,size)。

  釋放過程:當要釋放內存時,要用到myfree(memx,fp),其中(u32)fp-(u32)malloc_dev.membase[memx]=offset(偏移量),根據偏移量可得出index=offset/memblksize[memx],即內存管理表所在的塊,malloc_dev.memmapbase[memx][index]等於需要釋放的塊數n,以index塊為起點,把接下來的n塊內存管理表清零。

 

  參數定義:

  既然這里涉及到許多內存參數,學過C語言的同學知道可以用到結構體,其封裝性可以大大提高我們對所對應的參數的屬性的了解。

struct __Malloc_dev                           //此結構體定義在malloc.h中,是對各種參數的定義
{
    void (*init)(u8);                        //函數指針(*init), 此函數帶有u8 類型的形參,返回(void ) 
    u8 (*perused)(u8);                       //函數指針(*perused),此函數帶有u8類型的形參,返回(u8)
    u8     *membase[SRAMBANK];              //一個二維數組(*membase[SRAMBANK]),此數組為u8 類型--->內存池
    u16  *memmapbase[SRAMBANK];           //一個二維數組(*memmapbase[SRAMBANK]),此數組為u16 類型----〉內存管理表
    u8   memready[SRAMBANK];                //一個數組(memready[SRAMBANK]),此數組為u8 類型            ----〉內存狀態
};

在malloc.c文件中,對結構體初始化

struct __Malloc_dev  malloc_dev=
{
    mymalloc_init,                    //(*init)==mymalloc_init  函數名mymalloc_init 本質上也是一個地址指針(這個指針指向一個函數名所代表的函數)
    mymalloc_perused,                //(*perused)==mymalloc_perused  函mymalloc_perused 本質上也是一個地址指針(這個指針指向一個函數名所代表的函數)
    membase1,membase2,            //內存池的個數
    memmapbase1,memmapbase2,//內存管理表的個數
    0,0                                            //每個內存的裝備狀態(0代表還未裝備好,1代表准備好了)
};

各個初始化的變量都要有定義,不然編譯器會報錯!!!

//定義內存池和內存管理表的數組
__align(32) u8 membase1[MEM1_MAX_SIZE];                                                                    //32位對齊,定義內存池1的數組
__align(32) u8 membase2[MEM2_MAX_SIZE]  __attribute__((at(0x68000000)));//32位對齊,定義內存池2的數組,因為起始地址跟SRAM的地址不一致,
                                           //故要重新定位
u16 memmapbase1[MEM1_TABLE_SIZE]; //定義內存管理表1 u16 memmapbase2[MEM2_TABLE_SIZE] __attribute__((at(0x68000000+MEM2_MAX_SIZE)));//定義內存管理表2

//設置內存
//*s:內存首地址
//c :要設置的值
//count:需要設置的內存大小(字節為單位)
void mymemset(void *s,u8 c,u32 count)
{
u8 *xs = s;
while(count--)*xs++=c;
}

void mymalloc_init(u8 memx)
{
    mymemset(malloc_dev.membase[memx],0,memmaxsize[memx]);                  //通過memset函數對內存池的所有塊清零
    mymemset(malloc_dev.memmapbase[memx],0,memtablesize[memx]*2);          //通過memset函數對內存管理表的所有塊清零
    malloc_dev.memready[memx]=1;                                            //把內存狀態置一表示已經初始化了
}

u8 mymalloc_perused(u8 memx)
{
    u32 i;
    u32 used=0;
    for(i=0;i<memtablesize[memx];i++)                                     //遍歷整個內存的內存管理表
    {
        if(malloc_dev.memmapbase[memx][i]!=NULL) used++;                  //找到內存管理表所有非零的項,並統計出總和
    }
    return used*100/memtablesize[memx];                                   //把所有非零項總和/整個內存管理表*100,表示內存管理表的使用率
    
}

 

  從上面的兩個指針函數的應用,就可知指針的威力,

   void (*init)(u8);                        //函數指針(*init), 此函數帶有u8 類型的形參,返回(void ) 
    u8 (*perused)(u8);               //函數指針(*perused),此函數帶有u8類型的形參,返回(u8)
通過指針(此指針指向函數)就可對一個函數完成定義,在此工程中,由於初始化結構體之后,
void (*init)(u8)   相當於    void mymalloc_init(u8 memx)
u8 (*perused)(u8)  相當於  u8 mymalloc_perused(u8 memx)



除了以上的結構體定義之外,還需要對內存塊大小、內存池大小、內存管理表的數量進行定義
#ifndef NULL
#define NULL 0
#endif


//內存的塊數
#define SRAMIN  0      //內部內存池
#define SRAMEX  1      //外部內存池
#define SRAMBANK 2      //內存池的數量

//內存1的具體參數
#define     MEM1_BLK_SIZE     32    //內存塊大小
#define MEM1_MAX_SIZE 40*1024       //內存池大小

#define MEM1_TABLE_SIZE MEM1_MAX_SIZE/MEM1_BLK_SIZE   //內存管理表的數量

//內存2的具體參數

#define MEM2_BLK_SIZE 32         //內存塊大小

#define MEM2_MAX_SIZE 32*1       //內存池大小

#define MEM2_TABLE_SIZE MEM2_MAX_SIZE/MEM2_BLK_SIZE    內存管理表的數量

為了方便調用,把這些分類地定義數組

const u32 memblksize[SRAMBANK]={MEM1_BLK_SIZE,MEM2_BLK_SIZE};    //內存塊大小
const u32 memmaxsize[SRAMBANK]={MEM1_MAX_SIZE,MEM2_MAX_SIZE};     //內存池的大小
const u32 memtablesize[SRAMBANK]={MEM1_TABLE_SIZE,MEM2_TABLE_SIZE};  //內存管理表的數量



二、內存的申請和釋放詳解
內存申請:
//內存分配(內部調用)
//memx:所屬內存塊
//size:要分配的內存大小(字節)
//返回值:0XFFFFFFFF,代表錯誤;其他,內存偏移地址 
u32 my_mem_malloc(u8 memx,u32 size)  
{  
    signed long offset=0;  
    u32 nmemb;    //需要的內存塊數  
        u32 cmemb=0;//連續空內存塊數
    u32 i;  
    if(!mallco_dev.memrdy[memx])mallco_dev.init(memx);//未初始化,先執行初始化 
    if(size==0)return 0XFFFFFFFF;//不需要分配
    nmemb=size/memblksize[memx];      //獲取需要分配的連續內存塊數
    if(size%memblksize[memx])nmemb++;  
    for(offset=memtblsize[memx]-1;offset>=0;offset--)//搜索整個內存控制區  
    {     
        if(!mallco_dev.memmap[memx][offset])cmemb++;//連續空內存塊數增加
        else cmemb=0;                                //連續內存塊清零
        if(cmemb==nmemb)                            //找到了連續nmemb個空內存塊
        {
            for(i=0;i<nmemb;i++)                      //標注內存塊非空 
            {  
                mallco_dev.memmap[memx][offset+i]=nmemb;  
            }  
            return (offset*memblksize[memx]);//返回偏移地址  
        }
    }  
    return 0XFFFFFFFF;//未找到符合分配條件的內存塊  
}  


//分配內存(外部調用)
//memx:所屬內存塊
//size:內存大小(字節)
//返回值:分配到的內存首地址.
void *mymalloc(u8 memx,u32 size)  
{  
    u32 offset;   
    offset=my_mem_malloc(memx,size);                     
    if(offset==0XFFFFFFFF)return NULL;  
    else return (void*)((u32)mallco_dev.membase[memx]+offset);  
} 

內存釋放:

//釋放內存(內部調用) 
//memx:所屬內存塊
//offset:內存地址偏移
//返回值:0,釋放成功;1,釋放失敗;  
u8 my_mem_free(u8 memx,u32 offset)  
{  
    int i;  
    if(!mallco_dev.memrdy[memx])//未初始化,先執行初始化
    {
        mallco_dev.init(memx);    
        return 1;//未初始化  
    }  
    if(offset<memsize[memx])//偏移在內存池內. 
    {  
        int index=offset/memblksize[memx];            //偏移所在內存塊號碼  
        int nmemb=mallco_dev.memmap[memx][index];    //內存塊數量
        for(i=0;i<nmemb;i++)                          //內存塊清零
        {  
            mallco_dev.memmap[memx][index+i]=0;  
        }  
        return 0;  
    }else return 2;//偏移超區了.  
}  


//釋放內存(外部調用) 
//memx:所屬內存塊
//ptr:內存首地址 
void myfree(u8 memx,void *ptr)  
{  
    u32 offset;   
    if(ptr==NULL)return;//地址為0.  
     offset=(u32)ptr-(u32)mallco_dev.membase[memx];     
    my_mem_free(memx,offset);    //釋放內存      
} 

 

三、main函數中的調用

int main(void)
 {     
    u8 key;         
     u8 i=0;        
    u8 *p=0;
    u8 *tp=0;
    u8 paddr[18];                //存放P Addr:+p地址的ASCII值
  
    delay_init();             //延時函數初始化      
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//設置中斷優先級分組為組2:2位搶占優先級,2位響應優先級
    uart_init(115200);         //串口初始化為115200
     LED_Init();                      //初始化與LED連接的硬件接口
    KEY_Init();                    //初始化按鍵
    LCD_Init();                       //初始化LCD   
     my_mem_init(SRAMIN);        //初始化內部內存池 
    POINT_COLOR=RED;            //設置字體為紅色 
    LCD_ShowString(30,50,200,16,16,"ELITE STM32F103 ^_^");    
    LCD_ShowString(30,70,200,16,16,"MALLOC TEST");    
    LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
    LCD_ShowString(30,110,200,16,16,"2015/1/20"); 
    LCD_ShowString(30,130,200,16,16,"KEY0:Malloc  KEY1:Free");
    LCD_ShowString(30,150,200,16,16,"KEY_UP:Write");

     POINT_COLOR=BLUE;//設置字體為藍色 
    LCD_ShowString(30,170,200,16,16,"SRAMIN");
    LCD_ShowString(30,190,200,16,16,"SRAMIN USED:   %");  
      while(1)
    {    
        key=KEY_Scan(0);            //不支持連按    
        switch(key)
        {
            case 0:                    //沒有按鍵按下    
                break;
            case KEY0_PRES:            //KEY0按下
                p=mymalloc(SRAMIN,2048);//申請2K字節
                if(p!=NULL)sprintf((char*)p,"Memory Malloc Test%03d",i);//向p寫入一些內容
                break;
            case KEY1_PRES:            //KEY1按下
                myfree(SRAMIN,p);    //釋放內存
                p=0;                //指向空地址      
                break;
            case WKUP_PRES:            //KEY UP按下 
                if(p!=NULL)
                {
                    sprintf((char*)p,"Memory Malloc Test%03d",i);//更新顯示內容      
                    LCD_ShowString(30,250,200,16,16,p);             //顯示P的內容
                } 
                break; 
        }
        if(tp!=p)
        {
            tp=p;
            sprintf((char*)paddr,"P Addr:0X%08X",(u32)tp);
            LCD_ShowString(30,230,200,16,16,paddr);    //顯示p的地址
            if(p)LCD_ShowString(30,250,200,16,16,p);//顯示P的內容
            else LCD_Fill(30,250,239,266,WHITE);    //p=0,清除顯示
        }
        delay_ms(10);   
        i++;
        if((i%20)==0)//DS0閃爍.
        { 
            LCD_ShowNum(30+96,190,my_mem_perused(SRAMIN),3,16);//顯示內部內存使用率
             LED0=!LED0;
         }
    }       
}

其中值得注意的是sprintf(裝字符串的數組,“字符串”,寫入字符串的內容);

過程:

以sprintf((char*)paddr,"P Addr:0X%08X",(u32)tp)為例子,

tp是一個指針地址,賦給"P Addr:0X%08X"這個字符串,最后賦給paddr這個數組。

sprintf((char*)p,"Memory Malloc Test%03d",i);//更新顯示內容 
這個也是一個道理,i=>"Memory Malloc Test%03d"=>p(數組)


總結:要活用這個sprintf函數,可以快速地把一個帶參數字符串賦給一個數組變量。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM