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,"aaa@qq.com");
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;
}
}
}
然后去测试一下
.
要注意使用完内存,一定要及时的进行释放,否则,可能会造成,没有内存可用.
上一篇: less的一些基础用法
下一篇: C# 清理内存