基于STM32原子战舰板内存管理源码
*/
//复制内存,作用是将源地址的内容复制到目标地址
//*des:目的地址
//*src:源地址
//n:需要复制的内存长度(字节为单位)
void mymemcpy(void *des,void *src,u32 n)
{ //“void *des”无类型指针,不能指向具体的数据,“void *des”无类型指针指向内存中的数据类型由用户自己确定
u8 *xdes=des;//目标地址,“*xdes”转换成u8类型,也可以理解为把目的地地址des存储到xdes指针中
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;
} //以*s为内存首地址的count个字节中,填充c,即把c写入到*s为首地址的内存中,个数多少由count值决定
//内存管理初始化
//memx:所属内存块,要么SRAMEX==1(外部内存);要么SRAMIN(内部内存)==0
/*
const u32 memtblsize[2]={MEM1_ALLOC_TABLE_SIZE,MEM2_ALLOC_TABLE_SIZE};//内存管理表大小
const u32 memblksize[2]={MEM1_BLOCK_SIZE,MEM2_BLOCK_SIZE}; //内存分块大小
const u32 memsize[2]={MEM1_MAX_SIZE,MEM2_MAX_SIZE}; //内存总大小
*/
void mem_init(u8 memx) //如“mem_init(SRAMIN);”表示内部内存块
{ //memmap,是16位的,mymemset,设置是针对8位的,那么1个16位的数据是不是2个8位组成的啊?!
mymemset(mallco_dev.memmap[memx], 0,memtblsize[memx]*2);//内存状态表数据清零
//把u8类型的数据“0”填充到u16类型指针元素memmap[0]中(根据结构体定义“u16 *memmap[2]; ”),memmap[0]=mem1mapbase==1250,
//也就是说“mallco_dev.memmap[memx]”在这里表示1250个内部内存块用以存储u16类型指针,
//“memtblsize[memx]”是什么呢?memtblsize[memx]即memtblsize[0]==1250个内部内存管理表,
//而mallco_dev.memmap[memx]是16位的,为了将其全部清零,所以乘以2.
mymemset(mallco_dev.membase[memx], 0,memsize[memx]); //内存池所有数据清零
//memsize[0]==40K字节空间, mallco_dev.membase[memx]==40K字节空间,
mallco_dev.memrdy[memx]=1; //内存管理初始化OK
}
/*
*/
//获取内存使用率
//memx:所属内存块,要么SRAMEX==1(外部内存);要么SRAMIN(内部内存)==0
//返回值:使用率(0~100)
u8 mem_perused(u8 memx)
{
u32 used=0;
u32 i;
for(i=0;i {
if(mallco_dev.memmap[memx][i])used++;
} //mallco_dev.memmap[memx][i]是二维数组。当内存块初始化后该值为0,
return (used*100)/(memtblsize[memx]); //used*100,乘以100是将小数变成整数
}
//内存分配(内部调用)
//memx:所属内存块
//size:要分配的内存大小(字节数)
//返回值:0XFFFFFFFF,代表错误;其他,内存偏移地址
//向memx存储器申请size个字节的连续存储空间,并将size个字节中首个字节的地址偏移值标注出来,注意是地址偏移值而不是地址。
u32 mem_malloc(u8 memx,u32 size)
{
signed long offset=0;
u16 nmemb; //需要的内存块数
u16 cmemb=0;//连续空内存块数
u32 i;
if(!mallco_dev.memrdy[memx])mallco_dev.init(memx);//未初始化,先执行初始化
/*
“mallco_dev.init(memx);”是什么意思?mallco_dev.init(memx)是结构体变量mallco_dev的一个成员,本句中就是对结构体成员的引用,即执行
mem_init(u8 memx)函数的意思;如何引用结构体中指向函数的指针变量成员?既然是指向函数的指针变量且有赋值,在引用时按照格式:
结构体变量名.指向函数的指针变量名(形参);
*/
if(size==0)return 0XFFFFFFFF;//不需要分配 memblksize[memx]==32
nmemb=size/memblksize[memx]; //获取需要分配的连续内存块数
/*
c语言规定:除法的运算结果与运算对象的数据类型有关,两个数都是int则商(即结果)是int,若商(即结果)有小数则省略掉小数点部分。本例中
size和memblksize[memx]都是int,所以结果只能是int。假设size<32,则nmemb==0;
c语言规定取余运算的运算对象必须是int。当小数对大数取余时余(即结果)是小数本身;例如,在“if(size%memblksize[memx])nmemb++;”中 ,
假设size<32,则size%memblksize[memx]的结果是size值本身,所以执行“nmemb++;”运算,这时运算结果是nmemb==1;如果size是32的整数倍则不执行
“nmemb++;”运算;
memtblsize[0]==1250,memtblsize[1]==6250,
mallco_dev.memmap[memx][offset]是什么意思?
*/
if(size%memblksize[memx])nmemb++;
for(offset=memtblsize[memx]-1;offset>=0;offset--)//搜索整个内存控制区
{
if(!mallco_dev.memmap[memx][offset])cmemb++;//连续空内存块数增加,offset从1249->0变化
/*
如,{ memmap[0][149],memmap[0][148],...memmap[0][1],memmap[0][0]};实际上可以把“mallco_dev.memmap[memx][offset]”视为具有1250个变量的
一维数组,每个元素对应的实际意义是对应的一个内存块,顺序是offset从1249(高)->0(低)变化;如果哪个变量等于0(即空闲)就执行
“cmemb++;”操作,这样就可以计算出连续空闲内存块数cmemb;切记!目的是要获取连续的空闲的内存块数!这样就必须结合下一句
“else cmemb=0;”来分析;如果没有出现连续的空闲内存块(即数组顺序相连的变量值没有出现类似“0,0,0,0,0”这样的情况),程序会执行下一语
句“else cmemb=0;”即把上面的“cmemb”统计值清零,这样程序就会在for循环里面继续寻找符合“if(cmemb==nmemb)”条件的状态出现,
如果for循环执行完了还没有出现符合“if(cmemb==nmemb)”条件的状态,则返回0XFFFFFFFF结束本函数表示没有找到符合条件的内存块。假
设:size=65,那么nmemb就是3即需要获取连续3个内存块来存放65个字节,再假设数组顺序相连的变量值出现了类似“0,0,0,0,0”这样的情况(即有
连续4个空闲的内存块),这时就出现了符合“if(cmemb==nmemb)”条件的状态,即当cmemb计数计到3的时候(即出现了连续相连的3个内存块)就
符合“cmemb==nmemb”了,程序就自然进入“if(cmemb==nmemb)”语句。
offset*memblksize[memx]代表什么呢?offset的取值范围是0-1249,memblksize[memx]代表每个内存块的字节数即32,offset*memblksize[memx]就
是返回偏移地址值;也就是把连续空闲的内存块对应的地址的首地址值标注出来。
*/
else cmemb=0; //连续内存块清零
if(cmemb==nmemb) //找到了连续nmemb个空内存块
{
for(i=0;i {
mallco_dev.memmap[memx][offset+i]=nmemb;
}
return (offset*memblksize[memx]);//返回偏移地址
}
}
return 0XFFFFFFFF;//未找到符合分配条件的内存块
}
//释放内存(内部调用)
//memx:所属内存块
//offset:内存地址偏移
//返回值:0,释放成功;1,释放失败;
u8 mem_free(u8 memx,u32 offset)
{
int i;
if(!mallco_dev.memrdy[memx])//未初始化,先执行初始化
{
mallco_dev.init(memx); //本句等价于“mem_init(memx);”
return 1;//未初始化
}
if(offset {
int index=offset/memblksize[memx]; //偏移所在内存块号码 memblksize[memx]==32,
int nmemb=mallco_dev.memmap[memx][index]; //内存块数量
for(i=0;i {
mallco_dev.memmap[memx][index+i]=0;
}
return 0;
}else return 2;//偏移超区了.
}
//释放内存(外部调用)
//memx:所属内存块
//ptr:内存首地址
void myfree(u8 memx,void *ptr)
{
u32 offset;
if(ptr==NULL)return;//地址为0.
offset=(u32)ptr-(u32)mallco_dev.membase[memx];
mem_free(memx,offset);//释放内存
}
//分配内存(外部调用)
//memx:所属内存块
//size:内存大小(字节)
//返回值:分配到的内存首地址.
//在memx存储器中,找出size个字节的连续空闲的内存空间,并将连续空闲的内存空间指针值标注出来;返回值就是这个指针值
/*
mallco_dev.membase[memx]即mallco_dev.membase[0]代表MCU内部存储器的40K字节中的第一个字节变量的地址,是u8类型指针变量,也就是说一个字节占用一个地址;换句话说,把内部存储器的40K字节的地址定义为一个“u8 mem1base[MEM1_MAX_SIZE]”数组,指针类型数组“u8 *membase[2];”的赋值是{mem1base,mem2base},而“mem1base,mem2base”分别是内部内存池和外部内存池的数组名,各自首元素的地址亦是个指针常量;因为事先已经定义
“u8 mem1base[MEM1_MAX_SIZE]”即“u8 mem1base[40K];”。如何理解“(void*)((u32)mallco_dev.membase[memx]+offset); ”呢?
1),已经说过mallco_dev.membase[memx]是首个变量的地址即40k字节中首个字节的地址值;
2),“offset”是:向memx存储器申请size个字节的连续空闲存储空间,这个找到的连续空闲空间当中首个字节的地址偏移值就是offset,offset==32(将32个字节空间组成一个内存块)*内存块号(如,假设向内部存储器申请64个字节的连续空闲存储空间,通过“mem_malloc(memx,size); ”函数得到在第五个存储块开始有连续2个存储快空闲可供使用(假设是5号和4号存储快),因为每个存储快有32个字节即有32个地址编号,4*32==128(这里的4是指第四块),5*32==160(这里的5是指第五块),那么这个160就是40K个字节编号当中的地址偏移值offset,即128-192号就是第四块和第五块内存块所对应的指针编号);注意offset是地址偏移值而不是地址;为什么要引入地址偏移值这个概念呢?假设第一个字节的地址值是0x0000 6800,那么就知道(0x0000 6800+160)的值就是第五块内存的指针。
3),“(u32)mallco_dev.membase[memx]”代表指针类型数组,意义是内部存储器40K字节中的第一个字节变量的地址,原来存放的是u8类型数据的地址,现在强制类型转换扩展为u32类型;
4),(void*)((u32)mallco_dev.membase[memx]+offset); 转换为无类型指针,指针值是32位,由此可知,“void *mymalloc(u8 memx,u32 size)”函数的返回值就是一个指针,即形参size所指向的由高向低的首个指针值;“void *mymalloc(u8 memx,u32 size)”是个指针类型函数,只能赋给指针。
*/
void *mymalloc(u8 memx,u32 size) //p=mymalloc(sramx,2048)
{
u32 offset;
offset=mem_malloc(memx,size);
if(offset==0XFFFFFFFF)return NULL;
else return (void*)((u32)mallco_dev.membase[memx]+offset);
}
//重新分配内存(外部调用)
//memx:所属内存块
//*ptr:旧内存首地址
//size:要分配的内存大小(字节)
//返回值:新分配到的内存首地址.
void *myrealloc(u8 memx,void *ptr,u32 size)
{
u32 offset;
offset=mem_malloc(memx,size);
if(offset==0XFFFFFFFF)return NULL;
else
{
mymemcpy((void*)((u32)mallco_dev.membase[memx]+offset),ptr,size); //拷贝旧内存内容到新内存
// 把size个字节指针ptr复制到“((u32)mallco_dev.membase[memx]+offset)”,
myfree(memx,ptr); //释放旧内存,因为在mem_malloc(memx,size)中已经将连续空闲内存块标注为1(已被占用),清除掉原来的标记
return (void*)((u32)mallco_dev.membase[memx]+offset); //返回新内存首地址,无类型指针
}
}
头文件:
#ifndef __MALLOC_H
#define __MALLOC_H
typedef unsigned long u32;
typedef unsigned short u16;
typedef unsigned char u8;
#ifndef NULL
#define NULL 0
#endif
#define SRAMIN 0 //内部内存池
#define SRAMEX 1 //外部内存池
//mem1内存参数设定.mem1完全处于内部SRAM里面
#define MEM1_BLOCK_SIZE 32 //内存块大小为32字节
#define MEM1_MAX_SIZE 40*1024 //最大管理内存 40K
#define MEM1_ALLOC_TABLE_SIZE MEM1_MAX_SIZE/MEM1_BLOCK_SIZE //内存表大小
//mem2内存参数设定.mem2的内存池处于外部SRAM里面,其他的处于内部SRAM里面
#define MEM2_BLOCK_SIZE 32 //内存块大小为32字节
#define MEM2_MAX_SIZE 200*1024 //最大管理内存200K
评论