• 基于STM32F429的内存管理


    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
    ",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,注意事项

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

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

  • 相关阅读:
    MySQL乱码问题
    Oracle安装错误“程序异常终止
    Hibernate Spring
    快学Scala 第一课 (变量,类型,操作符)
    Scala 学习笔记之隐式参数和隐式转换并用
    Hadoop 学习笔记之 Incompatible namespaceIDs问题
    Kafka 学习笔记之 Topic日志清理
    Zookeeper 学习笔记之 节点个数
    Kafka 学习笔记之 删除Topic
    Kafka 学习笔记之 架构
  • 原文地址:https://www.cnblogs.com/feiniaoliangtiangao/p/11060674.html
Copyright © 2020-2023  润新知