打印
[STM32F1]

【转】 基于stm32f103zet6的内存管理的学习

[复制链接]
855|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
一灯大神|  楼主 | 2016-11-11 19:37 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
[csharp] view plain copy
print?


  •   


主要是依照原子哥哥的代码来初步了解或者说学习一下内存管理,特别对于我们这个想往嵌入式方向发展的人来说,内存管理应该是一种艺术的。

今天在对原子的代码稍作修改是可以进行内存分配和回收的,所以开始深入分析一下这个代码的实现过程。一、所谓的内存管理内存管理,是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效,快速的分配,并且在适当的时候释放和回收内存资源。

二、代码分析

1、首先了解一下一个数据结构,这是一个声明


[csharp] view plain copy
print?


  • /*************************** 内存管理控制 **********************************************/        
  • typedef struct   
  • {  
  •     void (*init)(u8);                   //初始化  
  •     u8 (*perused)(u8);                  //内存使用率  
  •     u8  *membase[2];                    //内存池 管理2个区域的内存  
  •     u16 *memmap[2];                     //内存管理状态表  
  •     u8  memrdy[2];                      //内存管理是否就绪  
  • }_m_mallco_dev;  


成员包括两个函数指针(该指针指向函数),两个指针数组和一个u8类型的数组,具体分析下这几个成员的含义,那么首先要找到这个


[csharp] view plain copy
print?


  • _m_mallco_dev mallco_dev=  
  • {  
  •     mem_init,               //内存初始化  
  •     mem_perused,                //内存使用率  
  •     mem1base,mem2base,          //内存池  
  •     mem1mapbase,mem2mapbase,        //内存管理状态表  
  •     0,0,                    //内存管理未就绪  
  • };  

这才是真正定义的地方,现在就可以了解这个几个成员的具体功能了。

a、初始化中 mem_init,mem_perused,这是两个函数,为什么可以这样用呢(直接用函数名)?

可以这样理解么,函数名就像数组名一样,只不过函数名是代码段的指针,而数组名是数据段的指针 ,所以这里函数名就是给函数指针赋值了。当然函数指针并不能说是等于指针的,就像数组一样,数组名不等于指针的。总书记和主席还是不一样的。所以暂时可以这样理解,函数名虽然代表了一个地址,但是这个值是确定了的,但是指针是可以指向别的地址的。就这样!这样写只不过是为了方便我们访问罢了。可以按自己的需要修改!
那么这两个函数的作用?这才是我们最关心的,看这个
[csharp] view plain copy
print?


  • 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、注释很明确,那么接下来就是分析这个三句话的作用,没办法,我无法做到,一眼能看出究竟。

mymemset(mallco_dev.memmap[memx], 0,memtblsize[memx]*2);等价于mymemset(mem1base, 0,0x500*2)
它里面的内容很简单就是
[csharp] view plain copy
print?


  • void mymemset(void *s,u8 c,u32 count)   
  • {   
  •     u8 *xs = s;   
  •   while(count--)*xs++=c;   
  • }     

以mem1base为首地址的大小为0xa00的内容清0,那么mem1base又是什么呢?接下来看看

__align(4) u8 mem1base[MEM1_MAX_SIZE];这明显是4字节对齐的内部SRAM的地址,也就是我们的flash里面的地址。所以这就实现了对我们内部flash0xa00的内容清零,好的继续看下面的

c、 mymemset(mallco_dev.membase[memx], 0,memsize[memx]); //内存池所有数据清零  

自然地这个也是一个意思,清零,唯一不同的就是代表的意思不一样,到底是内存池数据清零,还是状态表的清零,我们看不出来,那么只有继续分析了。清零完成就给相应的数组元素填充1表示完成标志。至此我们第一个初始化成员就分析完毕!!
2、下面开始分析第二个成员perused函数
先看函数如何定义的[csharp] view plain copy
print?


  • /**************************************************************************************
  • * 名    称: 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])used++;   
  •         }   
  •     return (used*100)/(memtblsize[memx]);   
  • }   

这里可以看到出现一个这样的表达式,需要仔细分析!mallco_dev.memmap[memx]

分解一下,还是一样,这个是指针数组,也就是数组里面存放的是指针,那么这里给它赋值为一个数组名 mem1mapbase,但是访问的时候还是可以用下表来访问的。那么可以替换为:if(mem1mapbase) used++;看到没,这还是我们之前访问过了的那个数组,只不过这里是当非零的时候执行used++,也就是我们占用了才会进行++。那么作用就是:used表示的是占用了的大小。(used*100)/(memtblsize[memx])表示的就是占用值,memtblsize[memx]使我们分配的总的大小,到这里那么第二个成员也分析完毕。
3、后面这几个成员变量,之前就已经分析过了。[csharp] view plain copy
print?


  • mem1base,mem2base,          //内存池  
  • mem1mapbase,mem2mapbase,        //内存管理状态表  
  • 0,0,         

这里就不详述了。这个数据结果分析至此,那么接下来看我们分配内存的过程究竟如何实现?


沙发
一灯大神|  楼主 | 2016-11-11 19:42 | 只看该作者
三、分配内存
首先看一个核心代码如下

[csharp] view plain copy
print?


  • /**************************************************************************************
  • * 名    称: 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、好的,接下来就是将偏移值转化为所谓的外部指针了。


[csharp] view plain copy
print?


  • else return (void*)((u32)mallco_dev.membase[memx]+offset);   


这行代码,返回一个void 类型的首地址就是mallco_dev.membase[0],这样我们就得到了一个地址了。

3、然后就是


[csharp] view plain copy
print?


  • 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);//释放内存,否则资源难以回收

记得要释放内存呀,看代码函数


[csharp] view plain copy
print?


  • /**************************************************************************************
  • * 名    称: 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;                 //偏移超区了.   
  • }   

就是将内存块清零,与之前的那行代码对应就是

[csharp] view plain copy
print?


  • mallco_dev.memmap[memx][offset+i]=nmemb;  

标注非空了,那么也就是说,我们占用了的那些内存块就会标记为nmenb,否则就是0。

当我们释放完内存后,记得加上这个  P = NULL.

只是为了防止产生野指针,谁能保证,每次运行程序的时候,给变量分配地址的时候,不会使用到这个地址呢??所以这是个好习惯!



使用特权

评论回复
板凳
mmuuss586| | 2016-11-11 19:46 | 只看该作者
谢谢分享

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

65

主题

112

帖子

2

粉丝