1.内存管理介绍
内存管理,是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如
何高效,快速的分配,并且在适当的时候释放和回收内存资源。 内存管理的实现方法有很多种,
他们其实最终都是要实现 2 个函数: malloc 和 free; malloc 函数用于内存申请, 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,注意事项
申请的内存使用完后,一定要释放掉,不然内存池会被写爆。
有不足之处请指正,谢谢阅读,麻烦点赞支持。