主要是依照原子哥哥的代码来初步了解或者说学习一下内存管理,特别对于我们这个想往嵌入式方向发展的人来说,内存管理应该是一种艺术的。
今天在对原子的代码稍作修改是可以进行内存分配和回收的,所以开始深入分析一下这个代码的实现过程。一、所谓的内存管理内存管理,是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效,快速的分配,并且在适当的时候释放和回收内存资源。二、代码分析
1、首先了解一下一个数据结构,这是一个声明
/*************************** 内存管理控制 **********************************************/ typedef struct { void (*init)(u8); //初始化 u8 (*perused)(u8); //内存使用率 u8 *membase[2]; //内存池 管理2个区域的内存 u16 *memmap[2]; //内存管理状态表 u8 memrdy[2]; //内存管理是否就绪 }_m_mallco_dev;
成员包括两个函数指针(该指针指向函数),两个指针数组和一个u8类型的数组,具体分析下这几个成员的含义,那么首先要找到这个
_m_mallco_dev mallco_dev= { mem_init, //内存初始化 mem_perused, //内存使用率 mem1base,mem2base, //内存池 mem1mapbase,mem2mapbase, //内存管理状态表 0,0, //内存管理未就绪 };这才是真正定义的地方,现在就可以了解这个几个成员的具体功能了。
a、初始化中 mem_init,mem_perused,这是两个函数,为什么可以这样用呢(直接用函数名)?
void mem_init(u8 memx) { mymemset(mallco_dev.memmap[memx], 0,memtblsize[memx]*2);//内存状态表数据清零 mymemset(mallco_dev.membase[memx], 0,memsize[memx]); //内存池所有数据清零 mallco_dev.memrdy[memx]=1; //内存管理初始化OK }b、注释很明确,那么接下来就是分析这个三句话的作用,没办法,我无法做到,一眼能看出究竟。
void mymemset(void *s,u8 c,u32 count) { u8 *xs = s; while(count--)*xs++=c; }以mem1base为首地址的大小为0xa00的内容清0,那么mem1base又是什么呢?接下来看看
/************************************************************************************** * 名 称: mem_perused * 功 能: 获取内存使用率 * 参 数: * memx:所属内存块 * 返 回 值: 使用率(0~100) **************************************************************************************/ u8 mem_perused(u8 memx) { u32 used=0; u32 i; for(i=0;i<memtblsize[memx];i++) { if(mallco_dev.memmap[memx][i])used++; } return (used*100)/(memtblsize[memx]); }这里可以看到出现一个这样的表达式,需要仔细分析!mallco_dev.memmap[memx][i]
mem1base,mem2base, //内存池 mem1mapbase,mem2mapbase, //内存管理状态表 0,0,这里就不详述了。这个数据结果分析至此,那么接下来看我们分配内存的过程究竟如何实现?
/************************************************************************************** * 名 称: mem_malloc * 功 能: 内存分配(内部调用) * 参 数: * memx:所属内存块 * size:要分配的内存大小(字节) * 返 回 值: 0XFFFFFFFF,代表错误;其他,内存偏移地址 **************************************************************************************/ 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); //未初始化,先执行初始化 if(size==0)return 0XFFFFFFFF; //不需要分配 nmemb=size/memblksize[memx]; //获取需要分配的连续内存块数 if(size%memblksize[memx])nmemb++; for(offset=memtblsize[memx]-1;offset>=0;offset--) //搜索整个内存控制区 { if(!mallco_dev.memmap[memx][offset]) cmemb++; //连续空内存块数增加 else cmemb=0; //连续内存块清零 if(cmemb==nmemb) //找到了连续nmemb个空内存块 { for(i=0;i<nmemb;i++) //标注内存块非空 { mallco_dev.memmap[memx][offset+i]=nmemb; } return (offset*memblksize[memx]); //返回偏移地址 } } return 0XFFFFFFFF; //未找到符合分配条件的内存块 }
1、首先进行的是一个初始化,初始化的作用上面已经提及,再次不赘述,这里我们假设一块内存为40个block(一个block为32字节,因为内存太小)那么接下来可以看到是通过我们传入的参数计算出了总的内存块数,并且如果不整除的话,还会多分配一个内存块。nmemb = 64。内存管理表内容用于检测该块是否被占用。注意这里的内存块一定是连续的,
内存管理表的项值代表的意义为:当该项值为0的时候,代表对应的内存块未被占用,当该项值非零的时候,代表该项对应的内存块已经被占用,其数值则代表被连续占用的内存块数。比如某项值为10,那么说明包括本项对应的内存块在内,总共分配了10个内存块给外部的某个指针。
之后就是标志代码了,注释很详细,接下来看看这个返回偏移地址的代码:offset*memblksize[memx],这个偏移值就是memblksize【0】 = 0x20*offset
2、好的,接下来就是将偏移值转化为所谓的外部指针了。
else return (void*)((u32)mallco_dev.membase[memx]+offset);
3、然后就是
p=mymalloc(sramx,2048); //申请2K字节 if(p!=NULL)sprintf((char*)p,"This is xiaobing's Memory Malloc Test!!");//向p写入一些内容 printf("%s",p); //显示P的内容
这就是把这个地址传给指针p,那么我们接着就可以给指针p赋值内容了,这回爽到了吧?
打印指针内容。
4、很重要的一步
myfree(sramx,p);//释放内存,否则资源难以回收
记得要释放内存呀,看代码函数
/************************************************************************************** * 名 称: mem_free * 功 能: 释放内存(内部调用) * 参 数: * memx:所属内存块 * offset:内存地址偏移 * 返 回 值: 0,释放成功;1,释放失败; **************************************************************************************/ u8 mem_free(u8 memx,u32 offset) { int i; if(!mallco_dev.memrdy[memx]) //未初始化,先执行初始化 { mallco_dev.init(memx); return 1; //未初始化 } if(offset<memsize[memx]) //偏移在内存池内. { int index=offset/memblksize[memx]; //偏移所在内存块号码 int nmemb=mallco_dev.memmap[memx][index]; //内存块数量 for(i=0;i<nmemb;i++) //内存块清零 { mallco_dev.memmap[memx][index+i]=0; } return 0; }else return 2; //偏移超区了. }就是将内存块清零,与之前的那行代码对应就是
mallco_dev.memmap[memx][offset+i]=nmemb;标注非空了,那么也就是说,我们占用了的那些内存块就会标记为nmenb,否则就是0。
当我们释放完内存后,记得加上这个 P = NULL.
只是为了防止产生野指针,谁能保证,每次运行程序的时候,给变量分配地址的时候,不会使用到这个地址呢??所以这是个好习惯!