基于STM32F429的内存管理

偶尔善良 提交于 2020-11-18 20:09:05

1.内存管理介绍

  内存管理,是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如
何高效,快速的分配,并且在适当的时候释放和回收内存资源。 内存管理的实现方法有很多种,
他们其实最终都是要实现 2 个函数: malloc freemalloc 函数用于内存申请, 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,注意事项

   申请的内存使用完后,一定要释放掉,不然内存池会被写爆。

  有不足之处请指正,谢谢阅读,麻烦点赞支持。

 

原文出处:https://www.cnblogs.com/feiniaoliangtiangao/p/11060674.html

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!