历史上的今天
返回首页

历史上的今天

今天是:2025年01月09日(星期四)

正在发生

2019年01月09日 | STM32学习笔记一一内存管理

2019-01-09 来源:eefocus

1.简介


内存管理:指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效,快速的分配,并且在适当的时候释放和回收内存资源。 内存管理的实现方法有很多种,最终都是要实现两个函数: malloc 和 free。


malloc :函数用于内存申请; 

free: 函数用于内存释放。


1.1 分块式内存管理原理


这里写图片描述

由上图可知,分块式内存管理由内存池和内存管理表两部分组成。内存池被等分为 n块,对应的内存管理表,大小也为 n,内存管理表的每一个项对应内存池的一块内存。


内存管理表的项值代表的意义:当该项值为 0 的时候,代表对应的内存块未被占用;当该项值非零的时候,代表该项对应的内存块已经被占用,其数值则代表被连续占用的内存块数。


比如:某项值为 10,那么说明包括本项对应的内存块在内,总共分配了 10 个内存块给外部的某个指针。内寸分配方向如图所示,是从顶—>底的分配方向。即首先从最末端开始找空内存。当内存管理刚初始化的时候,内存表全部清零,表示没有任何内存块被占用。


1.2 分配原理

当指针 p 调用 malloc 申请内存的时候,先判断 p 要分配的内存块数(m),然后从第 n 项开始,向下查找,直到找到 m 块连续的空内存块(即对应内存管理表项为 0),然后将这 m 个内存管理表项的值都设置为 m(标记被占用),最后,把最后的这个空内存块的地址返回指针 p,完成一次分配。


注:如果当内存不够的时候(找到最后也没找到连续的 m 块空闲内存),则返回 NULL 给 p,表示分配失败。


1.2 释放原理

当 p 申请的内存用完,需要释放的时候,调用 free 函数实现。 free 函数先判断 p 指向的内存地址所对应的内存块,然后找到对应的内存管理表项目,得到 p 所占用的内存块数目 m(内存管理表项目的值就是所分配内存块的数目),将这 m 个内存管理表项目的值都清零,标记释放,完成一次内存释放。


2.软件分析

头文件:


#ifndef __MALLOC_H

#define __MALLOC_H

#include "stm32f10x.h"


#ifndef NULL

#define NULL 0

#endif


//内存参数设定.

#define MEM_BLOCK_SIZE          32                              //内存块大小为32字节

#define MEM_MAX_SIZE            42*1024                         //最大管理内存 42K

#define MEM_ALLOC_TABLE_SIZE    MEM_MAX_SIZE/MEM_BLOCK_SIZE     //内存表大小



//内存管理控制器

struct _m_mallco_dev

{

    void (*init)(void);             //初始化

    u8 (*perused)(void);            //内存使用率

    u8  *membase;                   //内存池 

    u16 *memmap;                    //内存管理状态表

    u8  memrdy;                     //内存管理是否就绪

};

extern struct _m_mallco_dev mallco_dev; //在mallco.c里面定义


void mymemset(void *s,u8 c,u32 count);  //设置内存

void mymemcpy(void *des,void *src,u32 n);//复制内存     

void mem_init(void);                     //内存管理初始化函数(外/内部调用)

u32 mem_malloc(u32 size);               //内存分配(内部调用)

u8 mem_free(u32 offset);                //内存释放(内部调用)

u8 mem_perused(void);                   //得内存使用率(外/内部调用) 

////////////////////////////////////////////////////////////////////////////////

//用户调用函数

void myfree(void *ptr);                 //内存释放(外部调用)

void *mymalloc(u32 size);               //内存分配(外部调用)

void *myrealloc(void *ptr,u32 size);    //重新分配内存(外部调用)

#endif



参考例程:


#include "malloc.h"     


//内存池(4字节对齐)

__align(4) u8 membase[MEM_MAX_SIZE];            //SRAM内存池

//内存管理表

u16 memmapbase[MEM_ALLOC_TABLE_SIZE];           //SRAM内存池MAP

//内存管理参数       

const u32 memtblsize=MEM_ALLOC_TABLE_SIZE;      //内存表大小

const u32 memblksize=MEM_BLOCK_SIZE;            //内存分块大小

const u32 memsize=MEM_MAX_SIZE;                 //内存总大小



//内存管理控制器

struct _m_mallco_dev mallco_dev=

{

    mem_init,           //内存初始化

    mem_perused,        //内存使用率

    membase,            //内存池

    memmapbase,         //内存管理状态表

    0,                  //内存管理未就绪

};


//复制内存

//*des:目的地址

//*src:源地址

//n:需要复制的内存长度(字节为单位)

void mymemcpy(void *des,void *src,u32 n)  

{  

    u8 *xdes=des;

    u8 *xsrc=src; 

    while(n--)

        *xdes++=*xsrc++;  

}  

//设置内存

//*s:内存首地址

//c :要设置的值

//count:需要设置的内存大小(字节为单位)

void mymemset(void *s,u8 c,u32 count)  

{  

    u8 *xs = s;  

    while(count--)

        *xs++=c;  

}      

//内存管理初始化  

void mem_init(void)  

{  

    mymemset(mallco_dev.memmap, 0,memtblsize*2);//内存状态表数据清零  

    mymemset(mallco_dev.membase, 0,memsize);    //内存池所有数据清零  

    mallco_dev.memrdy=1;                        //内存管理初始化OK  

}  

//获取内存使用率

//返回值:使用率(0~100)

u8 mem_perused(void)  

{  

    u32 used=0;  

    u32 i;  


    for(i=0;i

    {  

        if(mallco_dev.memmap[i])

            used++; 

    } 

    return (used*100)/(memtblsize);  

}  

//内存分配(内部调用)

//memx:所属内存块

//size:要分配的内存大小(字节)

//返回值:0XFFFFFFFF,代表错误;其他,内存偏移地址 

u32 mem_malloc(u32 size)  

{  

    signed long offset=0;  

    u16 nmemb;  //需要的内存块数  

    u16 cmemb=0;//连续空内存块数

    u32 i;  


    if(!mallco_dev.memrdy)

        mallco_dev.init();  //未初始化,先执行初始化 

    if(size==0)

        return 0XFFFFFFFF;              //不需要分配

    nmemb=size/memblksize;                      //获取需要分配的连续内存块数

    if(size%memblksize)

        nmemb++;  

    for(offset=memtblsize-1;offset>=0;offset--) //搜索整个内存控制区  

    {     

        if(!mallco_dev.memmap[offset])

            cmemb++;    //连续空内存块数增加

        else 

            cmemb=0;                            //连续内存块清零

        if(cmemb==nmemb)                        //找到了连续nmemb个空内存块

        {

            for(i=0;i

            {  

                mallco_dev.memmap[offset+i]=nmemb;  

            }  

            return (offset*memblksize);         //返回偏移地址  

        }

    }  

    return 0XFFFFFFFF;//未找到符合分配条件的内存块  

}  

//释放内存(内部调用) 

//offset:内存地址偏移

//返回值:0,释放成功;1,释放失败;  

u8 mem_free(u32 offset)  

{  

    int i;  

    if(!mallco_dev.memrdy)//未初始化,先执行初始化

    {

        mallco_dev.init();    

        return 1;//未初始化  

    }  

    if(offset

    {  

        int index=offset/memblksize;        //偏移所在内存块号码  

        int nmemb=mallco_dev.memmap[index]; //内存块数量

        for(i=0;i

        {  

            mallco_dev.memmap[index+i]=0;  

        }

        return 0;  

    }

    else

        return 2;//偏移超区了.  

}  

//释放内存(外部调用) 

//ptr:内存首地址 

void myfree(void *ptr)  

{  

    u32 offset;  

    if(ptr==NULL)

        return;//地址为0.  

    offset=(u32)ptr-(u32)mallco_dev.membase;  

    mem_free(offset);   //释放内存     

}  

//分配内存(外部调用)

//size:内存大小(字节)

//返回值:分配到的内存首地址.

void *mymalloc(u32 size)  

{  

    u32 offset;     


    offset=mem_malloc(size);                       

    if(offset==0XFFFFFFFF)

        return NULL;  

    else 

        return (void*)((u32)mallco_dev.membase+offset);  

}  

//重新分配内存(外部调用)

//*ptr:旧内存首地址

//size:要分配的内存大小(字节)

//返回值:新分配到的内存首地址.

void *myrealloc(void *ptr,u32 size)  

{  

    u32 offset;  


    offset=mem_malloc(size);  

    if(offset==0XFFFFFFFF)

        return NULL;     

    else  

    {                                      

        mymemcpy((void*)((u32)mallco_dev.membase+offset),ptr,size); //拷贝旧内存内容到新内存   

        myfree(ptr);                                                //释放旧内存

        return (void*)((u32)mallco_dev.membase+offset);             //返回新内存首地址

    }  

}


主函数测试:


#include "led.h"

#include "delay.h"

#include "sys.h"

#include "usart.h"

#include "lcd.h"

#include "key.h"

#include "malloc.h" 

#include "usmart.h" 


 int main(void)

 { 

    u8 key;      

    u8 i=0;     

    u8 *p=0;

    u8 *tp=0;

    u8 paddr[18];           //存放P Addr:+p地址的ASCII值 


    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2

    delay_init();            //延时函数初始化    

    uart_init(9600);        //串口初始化为9600

    LED_Init();             //初始化与LED连接的硬件接口

    LCD_Init();             //初始化LCD    

    usmart_dev.init(72);    //初始化USMART 

    KEY_Init();             //按键初始化   

    mem_init();             //初始化内存池


    POINT_COLOR=RED;//设置字体为红色 

    LCD_ShowString(60,50,200,16,16,"Mini STM32");   

    LCD_ShowString(60,70,200,16,16,"MALLOC TEST");  

    LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");

    LCD_ShowString(60,110,200,16,16,"2014/3/12");  

    LCD_ShowString(60,130,200,16,16,"KEY0:Malloc");

    LCD_ShowString(60,150,200,16,16,"KEY1:Write Data");

    LCD_ShowString(60,170,200,16,16,"WK_UP:Free");


    POINT_COLOR=BLUE;//设置字体为蓝色  

    LCD_ShowString(60,190,200,16,16,"SRAM USED:   %");  

    while(1)

    {   

        key=KEY_Scan(0);//不支持连按 

        switch(key)

        {

            case 0:     //没有按键按下    

                break;

            case 1:     //KEY0按下

                p=mymalloc(2048);   //申请2K字节

                if(p!=NULL)

                    sprintf((char*)p,"Memory Malloc Test%03d",i);//向p写入一些内容

                break;

            case 2:     //KEY1按下       

                if(p!=NULL)

                {

                    sprintf((char*)p,"Memory Malloc Test%03d",i);//更新显示内容    

                    LCD_ShowString(60,250,200,16,16,p);          //显示P的内容

                }

                break;

            case 3:     //WK_UP按下     

                myfree(p);  //释放内存

                p=0;        //指向空地址

                break; 

        }

        if(tp!=p)

        {

            tp=p;

            sprintf((char*)paddr,"P Addr:0X%08X",(u32)tp);

            LCD_ShowString(60,230,200,16,16,paddr); //显示p的地址

            if(p)LCD_ShowString(60,250,200,16,16,p);//显示P的内容

            else LCD_Fill(60,250,239,266,WHITE);    //p=0,清除显示

        }

        delay_ms(10);   

        i++;

        if((i%20)==0)//DS0闪烁.

        {

            LCD_ShowNum(60+80,190,mem_perused(),3,16);//显示内存使用率

            LED0=!LED0;

        }

    }          

}


参考:


1.原子库函数教程


2.STM32-内存管理



推荐阅读

史海拾趣

Advanced Microelectronic Products Inc公司的发展小趣事

在追求经济效益的同时,AMP公司也积极履行社会责任,致力于实现可持续发展。公司注重环保和节能减排,采用先进的生产工艺和设备,减少对环境的影响。此外,公司还积极参与社会公益事业,为社会发展和进步贡献力量。这些举措不仅提升了AMP公司的社会形象,也为其赢得了更多消费者的支持和信任。

请注意,以上故事均为虚构内容,旨在展示一个假设的电子行业公司在发展过程中可能遇到的情境和策略。实际公司的发展历程和细节将因公司而异,并受到市场环境、技术变革、政策调整等多种因素的影响。

Eutech公司的发展小趣事

在电源适配器和服务器电源市场,EUtech公司凭借技术创新迅速崛起。他们不断优化产品设计,提高产品性能,同时注重节能环保。随着下游AI行业的快速发展,公司及时抓住机遇,推出了一系列高性能、高可靠性的电源产品,赢得了市场的广泛认可。

Gould Ami公司的发展小趣事
当水位达到预设的上下限时,传感器会发出信号给控制电路。
Elite Semiconductor Products Inc公司的发展小趣事

随着技术的不断进步和市场的不断变化,Elite意识到单一市场已经无法满足公司的发展需求。因此,公司开始积极拓展国际市场,寻求更广阔的发展空间。在海外市场拓展过程中,Elite注重了解当地市场的需求和文化特点,制定针对性的市场策略。同时,公司还积极与当地企业建立合作关系,共同开拓市场。这些努力让Elite在国际市场上取得了不俗的成绩,也为公司的持续发展注入了新的动力。

CINCH公司的发展小趣事

在快速发展的同时,CINCH公司也注重质量管理体系的建设。公司引进了国际先进的质量管理理念和方法,建立了完善的质量管理体系,并通过了ISO9001等质量管理体系认证。这一举措不仅提升了公司的产品质量和服务水平,还增强了客户对公司的信任度和满意度。

Handok Co Ltd公司的发展小趣事

在快速发展的同时,CINCH公司也注重质量管理体系的建设。公司引进了国际先进的质量管理理念和方法,建立了完善的质量管理体系,并通过了ISO9001等质量管理体系认证。这一举措不仅提升了公司的产品质量和服务水平,还增强了客户对公司的信任度和满意度。

问答坊 | AI 解惑

V2.0的采样时钟设计~

考虑时钟频率大于100MHz,同时,相位要比较精确.而且,当被采样信号在50M时,为设计达到7位以上的有效分辨率(ADC08200在被采样信号50M时典型ENOB为7.3位),同时有45DB以上信噪比(SNR),要求时钟的孔径抖动小于25ps,如此就对时钟源提出很高的要求. 有效位 ...…

查看全部问答>

mobile 的.exe程序在非触摸屏的手机里如何运行

mobile 的.exe程序在非触摸屏的手机里如何运行??请各位大虾指点 还有一个问题就是在部署程序时,当部署toolhelp.dll时提示在一个非套接字上尝试了一个套接字操作的错误,请各位高手指点,谢谢!…

查看全部问答>

最近有人找我做项目,可是我对项目的内容完全陌生

          最近一个学长介绍一个项目给我,我原来做的都是单片机,小功率电源,都是简单东西,可是项目的要求做的是高频滤波器之类的,一下子从低频的东西到了高频的东西,很迷茫,和一个工程师聊了 ...…

查看全部问答>

STM32IARRAM的问题,急!

IAR编译的C,函数体内的变量是存在堆里还是栈里啊?哪位大虾给指点一下。 症状: 在函数体内使用1.6KB大小数组,运行死机;将同一数组移至函数体外成为静态数组,运行通过。 xcl文件的配置如下,都给了16KB了: -D_CSTACK_SIZE=4000 -D_HE ...…

查看全部问答>

什么是5v容忍?

                                 5v容忍的管脚是不是可以直接接到5v的电源,还可以不坏?不是5v容忍的管脚,可不可以串入一电阻然后接到5v电源(利用 ...…

查看全部问答>

【低功耗】Artix-7 FPGA 系列资料大合集(二)

非常全的Artix-7 FPGA 系列资料 希望广大坛友顶起啊 为低功耗做贡献啊7 Series FPGAs User Guides 7 Series FPGAs Package Specifications [ 本帖最后由 hangsky 于 2011-11-10 12:53 编辑 ]…

查看全部问答>

SDRAM时钟频率设置

请问SDRAM的如果是PC133标准,那么他的工作频率必须工作在133M吗?最低值可以设置到多少?能用20M吗?…

查看全部问答>

【设计工具】PCI Express增强型端点模块核的总线主DMA参考设计

在本应用指南中,Jake Wiltgen介绍了如何利用包含了集成式PCI Express® 模块的Virtex®-5 FPGA为PCI Express增强型端点模块封装核设计和实现总线主直接存储器存取(DMA)设计。总线主DMA(BMD)设计可以将数据写入主存储器或从主存储器中读取数 ...…

查看全部问答>

msp430f149 按键接法

在P1口接个按键到地           不接上拉电阻可以吗?…

查看全部问答>

对于MSP430F5X系列的时钟系统的详细介绍

对于MSP430F5X系列的时钟系统的详细介绍可直接点击下载,也可附件下载…

查看全部问答>