STM32工作笔记0071---内存管理实验

技术交流QQ群【JAVA,C++,Python,.NET,BigData,AI】:170933152

战舰版参考这个

然后:探索版也是这个

mini版参考下面这个

 

如果咱们用传统的方法,不用内存管理的话,如果要再LCD上,显示文件名的话,就需要建立一个上面说道的

这个数组,二维数组,比如有10000个文件,然后每个文件有255个长度的名称.这里就需要创建上面的那个大的数组

就需要2550k字节的内存,这个对MCU来说是压力很大的.

如果,再小一点,就是如果你有100个文件,每个文件有100个长度的文件名,这样就可以小一点了,

但是,你不知道,有可能有些文件的文件名只要一个两个字符,你还是要给他分配100个长度这样

就太浪费了,这就是不用内存管理的坏处.

如果用内存管理就可以做到,针对每个文件用多少内存就分配多少内存,这样

会更好一些.

有了内存管理以后,就可以针对,文件名的长度,来动态的给每个文件分配内存,也可以根据文件的个数来动态分配内存,

用完了以后,还可以释放内存,如果没有内存管理,可能要针对所有可能的情况去申请内存,这样

对内存是非常浪费的.

然后再看看内存管理的方法

这个分块式内存管理模块,也就是把内存分为很多个内存池,每个内存池由很多个内存块组成,

每个内存块,对应第一个内存管理表,当对应的内存管理表,中值是0的时候,也就是这个对应的内存块,可以使用,如果

内存块,的值不是0,比如说是10的时候,那么相邻的连续的比如说,有3个连续的内存管理表

的值都是10,那么也就是说,这3个内存管理表,对应的3个内存块就分配给了外部的某个指针了.

当内存管理刚初始化的时候,会清空所有的内存块,对应的内存管理表的值,都是清空成0,代表

所有的内存块都是可以被使用的,然后,要注意,这里查找某个内存块能不能使用,是从顶到低的方式来查询的.

malloc的内存分配原理:
说起来就是需要分配的时候,会先计算一下,比如我申请100字节的内容,那么,如果一个内存块是32个字节,那么就需要

4个内存块,那么这个时候,malloc就会从顶到底去查找,找到连续4个内存管理表是0的,也就是没有被占用的内存块

然后把,这4个连续的是0的内存管理表,标记为4,因为这里需要4个内存块,然后返回这4个空内存块的地址,

如果找到了低还是找不到连续的4个,没有被占用的内存块的话,那么就返回一个null,表示分配失败了.

然后看这个内存释放原理.

当调用free的时候,,首先找到需要的内存块的内存地址,找到地址后,找到内存管理表,根据内存管理表,得到占用的内存块

数目比如是m,然后把m个内存管理表,的值都清零,这样完成内存释放.

这里这个SRAMBANK这个是针对开发版不一样,这个值也不一样的,

比如战舰版,因为有2个外置SRAM一个内置的所以就是3,mini版只有内置的SRAM所以就是1

然后这个init这个函数,这参数的作用就是,如果传入的是1的话,那么就是使用第一个SRAM,如果

传入的是2的话,就是使用第二个SRAM.

然后这

可以看到上面右边就是mini版的,他的init和perused,以及下面的结构体都是只有一个的,也就是因为他没有外置的sram.

这里的内存表大小就是指的是,内存管理表的个数,也就是有多少个内存块

这里因为内存管理表用来存一些数据,而且内存管理表还对应内存块,

所以管理所有MEM_MAX_SIZE的内存,需要再MEM_MAX_SIZE内存大小的基础上+MEM_ALLOC_TABLE_SIZE *2 的这么多字节

这里的字节对齐,是什么意思,后边有文章会说.

这里可以这样理解这个字节对齐的意思,

比如STM32,32位的是4个字节

那么4字节对齐就是说

如果我使用,小于等于32位的空间的时候,实际上都会给分配32位的空间

perused这个是内存使用率

可以看到战舰版的内存初始化函数

这里的所属内存块,就是,比如是内置的SRAM,还是外置的SRAM等等.

mini版的内存初始化函数.

可以看到mini版的内存初始化函数中没有传入对应的是哪个内存池.

可以看看,战舰版的内存使用率的使用方法,可以看到他就是找到所有不是0的内存管理表

也就是所有已经使用的管理表,然后去除以,这个sram的总的内存表大小.

然后*100,就得到了这个内存使用率.

这个是mini版的,内存使用率方法,可以看到参数是void.

然后再去看申请内存的方法:

可以看到,返回的是申请的内存首地址,如果没有申请到内存返回NULL

可以看到这里,返回值是,如果不是0XFFFF FFFF ,就说明申请地址成功,那么

就是内存的偏移地址,

这个偏移地址:

就是地址的偏移量.

//内存分配(内部调用)
//memx:所属内存块
//size:要分配的内存大小(字节)
//返回值:0XFFFFFFFF,代表错误;其他,内存偏移地址 
//1.分配内存用的函数
u32 my_mem_malloc(u8 memx,u32 size)  
{  
    signed long offset=0;  
    u32 nmemb;	//需要的内存块数  
	u32 cmemb=0;//连续空内存块数
    u32 i;  
    //2.先去判断内存有没有被初始化,没有的话,就去先初始化
    if(!mallco_dev.memrdy[memx])mallco_dev.init(memx);//未初始化,先执行初始化 
    //3.如果前来申请的内存大小是0的话,就是不需要分配内存,直接就返回0XFFFF FFFF
    if(size==0)return 0XFFFFFFFF;//不需要分配
    //4.否则先去找到,内存的块数,也就是
    //要申请的字节数 / 一个块的最小字节大小 也就是算出来,用几个内存块
    //
    nmemb=size/memblksize[memx];  	//获取需要分配的连续内存块数
    //5.如果算出来要用的内存块,有余数,那么
    //内存块数需要++
    //
    if(size%memblksize[memx])nmemb++;  
    //6.然后再去所有的内存控制区域中去查找,连续的
    //可以使用的内存块数.
    //这里的控制区域指的是,memtblsize,这是拥有的所有的内存块数
    for(offset=memtblsize[memx]-1;offset>=0;offset--)//搜索整个内存控制区  
    {     
        //7.如果找到对应的内存模块,
        //他的第offset个内存表,是0的话,那么就cmemb++
        //如果中间不连续的话,那么cmemb就需要重新开始计数
        //
		if(!mallco_dev.memmap[memx][offset])cmemb++;//连续空内存块数增加
		else cmemb=0;								//连续内存块清零

        //8.如果连续找到了够用的内存块,那么就
		if(cmemb==nmemb)							//找到了连续nmemb个空内存块
		{
            //9.给找到的内存块做上标记,
            //也就是给对应的内存管理表都赋值上需要的内存块数
            //比如申请100个字节的话,一个内存块是32字节的话
            //那么就需要4个内存块,这里就给找到的连续的内存管理表
            //上面都赋值上4
            for(i=0;i<nmemb;i++)  					//标注内存块非空 
            {  
                mallco_dev.memmap[memx][offset+i]=nmemb;  
            }  
            //10.然后返回找到的内存的偏移地址
            //找到的第几个内存块*每个内存块的大小,
            //比如找到第3个内存块了,每个内存块的大小是32
            //那么地址应该是3*32
            return (offset*memblksize[memx]);//返回偏移地址  
		}
    }  
    return 0XFFFFFFFF;//未找到符合分配条件的内存块  
} 

然后看这里就是,返回对应的,分配的内存的地址,

这里:

//分配内存(外部调用)
//memx:所属内存块
//size:内存大小(字节)
//返回值:分配到的内存首地址.
void *mymalloc(u8 memx,u32 size)  
{  
    u32 offset;   
    //1.返回找到的连续的内存块的地址偏移量
	offset=my_mem_malloc(memx,size);  	   	 	   
    if(offset==0XFFFFFFFF)return NULL; 
    //2.基地址,也就是对应的那个SRAM模块的基地址,加上偏移量
    //得到的就是一个完整的分配的内存地址了 
    else return (void*)((u32)mallco_dev.membase[memx]+offset);  
}  

然后再看一下,这里myfree这个函数.

这个函数用来释放内存的.

//释放内存(外部调用) 
//memx:所属内存块
//ptr:内存首地址 
void myfree(u8 memx,void *ptr)  
{  
	u32 offset;   
    //1.第一个参数是,那个SRAM模块设备
    //第二个参数是,要释放的内存地址
    //2.如果传入的要释放的内存地址是个NULL那么就返回就行了
	if(ptr==NULL)return;//地址为0.  
    //3.否者就先找到这个要释放的地址的
    //偏移量,然后,调用my_mem_free,去释放内存
 	offset=(u32)ptr-(u32)mallco_dev.membase[memx];     
    my_mem_free(memx,offset);	//释放内存      
}  

然后看看内存释放这个函数的具体过程:

//释放内存(内部调用) 
//memx:所属内存块
//offset:内存地址偏移
//返回值:0,释放成功;1,释放失败;  
u8 my_mem_free(u8 memx,u32 offset)  
{  
    int i;  
    //1.这个函数也是先判断这个内存能不能用,不能用
    //去初始化内存,然后返回内存释放成功
    if(!mallco_dev.memrdy[memx])//未初始化,先执行初始化
	{
		mallco_dev.init(memx);    
        return 1;//未初始化  
    }  
    //2.然后再看看,偏移量是不是在
    //内存池里面,这里,如果偏移量在内存池中的话,再去
    //处理,也就是这个偏移,是不是再内存总大小之内
    if(offset<memsize[memx])//偏移在内存池内. 
    {  
        //3.在内存总大小之内的话,就先找到
        //这块内存要在第几个内存块中,也就是所在的内存块的号码
        int index=offset/memblksize[memx];			//偏移所在内存块号码  
        //4.找到在第几个内存块以后,
        //然后再在这个内存块,对应的内存管理表中,去找到,这个内存占用了
        //几个内存块.
        int nmemb=mallco_dev.memmap[memx][index];	//内存块数量
        for(i=0;i<nmemb;i++)  						//内存块清零
        {  
            //5.然后从开始位置,把这个占用的这几个内存块
            //对应的内存管理表的值都设置为0
            mallco_dev.memmap[memx][index+i]=0;  
        }  
        return 0;  
    }else return 2;//偏移超区了.  
}  

这里的释放内存实际上就是,把对应的内存管理表给清零就可以了.

然后看看:

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "lcd.h"
#include "usart.h"	  
#include "sram.h"
#include "string.h"
#include "usmart.h"	
#include "malloc.h"
 
/************************************************
 ALIENTEK精英STM32开发板实验34
 内存管理 实验 
 技术支持:www.openedv.com
 淘宝店铺:http://eboard.taobao.com 
 关注微信公众平台微信号:"正点原子",免费获取STM32资料。
 广州市星翼电子科技有限公司  
 作者:正点原子 @ALIENTEK
************************************************/

 
 int main(void)
 {	 
	u8 key;		 
 	u8 i=0;	    
	u8 *p=0;
	u8 *tp=0;
	u8 paddr[18];				//存放P Addr:+p地址的ASCII值
  
   //1.首先做一些初始化
	delay_init();	    	 //延时函数初始化	  
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 	//串口初始化为115200
 	LED_Init();		  			//初始化与LED连接的硬件接口
	KEY_Init();					//初始化按键
	LCD_Init();			   		//初始化LCD   
 	//2.然后初始化内存池,这个内存池就是一个个的内存模块设备
    //一个个的SRAM设备
    my_mem_init(SRAMIN);		//初始化内部内存池 
    //3.然后再LCD显示一些提示
	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按下
                //3.这里去申请2k的字节,然后
                //往这个地址,去写入Memory Malloc 这样的一串内容
				p=mymalloc(SRAMIN,2048);//申请2K字节
				if(p!=NULL)sprintf((char*)p,"Memory Malloc Test%03d",i);//向p写入一些内容
				break;
			case KEY1_PRES:			//KEY1按下
                //5.按下这个KEY1就会去释放内存
                //SRAMIN,这个是第几个内存池,也就是第几个内存设备
				myfree(SRAMIN,p);	//释放内存
				p=0;				//指向空地址	  
				break;
			case WKUP_PRES:			//KEY UP按下 
				if(p!=NULL)
				{
                    //4.按下WKUP按键的时候,去更新显示内容,然后把内容再显示出来.
					sprintf((char*)p,"Memory Malloc Test%03d",i);//更新显示内容 	 
					LCD_ShowString(30,250,200,16,16,p);			 //显示P的内容
				} 
				break; 
		}
		if(tp!=p)
		{
            //5.然后这个tp是用来判断p这个地址,有没有发生变化的
            //如果发生了变化的话,那么
            //就重新显示一下p的地址.显示在lcd上
            //如果p正常就显示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;
 		}
	}	   
}

然后去测试一下

.

要注意使用完内存,一定要及时的进行释放,否则,可能会造成,没有内存可用.

猜你喜欢

转载自blog.csdn.net/lidew521/article/details/108444340