您的位置 首页 电路

Keil C动态内存管理机制剖析及改善

Keil C是常用的嵌入式系统编程工具,它通过init_mempool、mallloe、free等函数,提供了动态存储管理等功能。本文通过对init_mempool、mallloe和free这3个

  Keil C是常用的嵌入式体系编程东西,它通过init_mempool、mallloe、free等函数,供给了动态存储办理等功能。本文通过对init_mempool、mallloe和free这3个KeilC库函数源代码的剖析,提醒其完成的原理和办法,并对其间的缺乏作了改善,以使Keil C编程人员更好地运用动态存储办理。

  1 相关数据结构、变量及阐明

  在Keil C装置目录下的\c5l\lib目录下,有完成init_mempool、mallloe和free这3个函数的C源文件init_mere.c、malloc.e和free.c。下面针对keil C7.5A版,将其间与动态存储办理相关的数据结构介绍如下;

  #define _MALLOC_MEM_xdata /*该行在stdlib.h文件中*/

  struct __mem__ {

  struct __mem__ _MALLOC_MEM_ *next; /*单链表*/

  unsigned int len; /*下一块长度*/

  };

  该结构的next指向堆中的下一闲暇内存块,len标明该闲暇块除掉该块首部的struct__mem__结构所占的字节数后,该块实践可用的字节数。因为next是一个指向XDATA区的指针,故在Keil C中运用程序所界说的堆空间应在XDATA段中界说。

  在Keil C中,堆中的一切闲暇内存块是用一个单链表来办理的,struct_mere_即为该链表结点的结构,后边界说的宏AVAIL为该链表的首结点,为叙说便利,以下将该链表称为AVAIL链表。

 

  typedef struct __mem__ __memt__;

  typedef __memt__ _MALLOC_MEM_ *__memp__;

  __memt__ _MALLOC_MEM_ __mem_avail__ [2] = {

  { NULL, 0 }, / * 堆中闲暇内存块头结点* /

  { NULL, 0 }, / * 未运用,但对f ree 函数避免丢掉堆空间或误将链表头加入到堆空间,却是有必要的* /

  / * 笔者注:__mem_avail__ [1]的存在,并不能避免堆丢掉,见后边free 函数剖析* /

  };

  #define AVAIL (__mem_avail__[0])

 

  大局数组__meM_ avail_实践也是struct__mem__类型,__mem_avail__[O]的next指向堆中首块闲暇块。假如堆中已无闲暇内存块,则__mem_avail__[0]的next为NULL(0值)。为使程序代码简练,界说了宏AVAIL来替代__mem_avail__[O]。

  2 init_mempool函数剖析

  函数int_mempool(void_MALLOC_MEM_*

  pool,unsigned int size)失利时将回来0,成功则回来一1,参数pool指向运用程序界说的堆空间,参数size为堆空间的字节数。假如运用程序供给的堆空间太小(size的值太小),将失掉实践意义,故函数将回来0标明失利。当size参数足够大,则会初始化AVAIL(即_mem_avail__[O]),使其next域指向pool参数所指向的堆空间,len域为pool参数所指向的堆空间的总字节数size。其在KeilC 7.5A库中init_mem.C的源代码如下:

 

  #define HLEN ( sizeof (__memt__) )

  #define MIN_POOL_SIZE ( HLEN * 10)

  int init_mempool (

  void _MALLOC_MEM_ *pool, unsigned int size) {

  if (size < MIN_POOL_SIZE)

  return(0); / * 失利* /

  if (pool = = NULL)

  { pool = 1 ; size – – ; }

  AVAIL.next = pool ;

  AVAIL.len = size ;

  (AVAIL.next) -> next = NULL ;

  (AVAIL.next) -> len = size – HLEN ;

  return( -1); / * 成功* /

  }

 

  在成功履行init_mempool函数后,将得到如图1所示的一个数据结构。别的,链首结点AVAIL的len域记录了整个堆的字节数。链首AVAIL结点的next域指向的是首块闲暇块,当通过屡次的malloe函数而堆中投有闲暇内存块时,AVAIL结点的next域将为NULL值。

  

 

  很明显,从上面的if(pool==NULL){pool=1;size–;)这部分源代码来看,假如运用程序中pool参数为空指针(pool为0)时,明显不能直接将AVAIL,的next域的值赋为空指针的(即赋为O)。将pool的值改为1,再将size的值减l,这样,init_mempool函数会在XDATA区中,从地址l开端,取size一1个字节作为堆来运用。假如源程序有界说在XDATA区的变量,则这些变量所占的存储单元也或许会被当成堆空间的一部分,这无疑是有潜在危险的。

  部分程序员在调用init_mempool函数时,习气将pool参数设为一个形如0xAAAA数字标明的肯定地址,假如不加特别防备,也是不当的,因为Keil C或许会在此办法指定的堆空间中分配暂时变量。好的习气是界说一个字节数组作为堆空间,再将数组名作为pool参数调用init_mempool函数。

  在Keil C的联机文档中,指明晰init_mempool在运用程序中只能被调用一次,那么,假如屡次调用该函数又会有什么结果呢?从该函数的源代码来剖析,屡次调用init_mempoo1函数,会导致从头初始化首结点AVAIL的next域和len域的值,将使AVAIL链表中的原有办理信息丢掉,然后导致一些很难确诊的问题。

  对此问题,可采用如下保护措施。当发现AVAIL链表中已有办理信息时,则回来失利标志,函数直接回来。详细的办法是查看AVAIL结点的len域,因为其被初始化为零,假如发现其值非零,则标明init_mempool函数已被成功调用过,此刻函数直接回来。

  3 malloc函数剖析

  malloc函数的原形是void *malloc(unsigned intsize),size参数为需动态请求的内存块的字节数。

  malloc函数的算法是查找AVAIL链表中各结点next指针所指向的闲暇内存块。假如某块的闲暇字节数≥size参数,则中止查找,并从该块进行内存分配,回来一个指向所分配内存块的指针给运用程序。假如没有找到符合要求的闲暇内存块,则回来空指针给运用程序。

  需求留意的是,AVAIL链表中除首结点AVAIL外,其他各节点坐落堆中各闲暇内存块开端处的一个struct__mem__结构中,其len域为该闲暇块总字节数减去sizeof(stiuct__mem)后的值,即该块实践闲暇的字节数;next域指向堆中下一闲暇内存块。

  设链表节点p指向所找到的闲暇内存块,假如在p闲暇块分配size个字节后,剩下的字节数不多,则将p块从AVAIL链表中删去,然后回来一个指向p块偏移sizeof(struct__mem)处的指针。假如在p闲暇块分配size个字节后,该块仍剩下较多的字节数,则需对该块进行切割,将多出的这一部分保留在AVAIL链表中。(以下部分有省掉,全文请见本刊网站——编者注)

  4 free函数剖析及改善

  free函数的原形是void free(void xdata *memp),参数memp指向所要开释的内存块。

  在AVAIL链表中,各结点是按其所指闲暇内存块开端地址的巨细按升序摆放的。free函数的算法是在AVAIL链表中查一个节点p(其前驱为q),当p节点所指空问内存块的地址大于参数memp所指内存块的开端地址时,则将memp块刺进到该节点之前,如没有找到这样的节点,则memp块插到链尾。在刺进memp块时,还将查看在memp块的前后是否存在地址相邻的闲暇内存块,假如有,则将memp块与相邻块兼并。(free库函数的部分源代码见本刊网站——编者注)

  值得讨论的是最终一段将memp块与前一块(q块)兼并的这部分代码。假如在履行此部分代码之前,q指向首结点AVAIL,而此刻欲将q块与memp块兼并,明显是不合理的。实践上,此刻应当将q的next指针的值设为memp块的开端地址p0。因为KeilC7.5A中,free库函数的源程序中没有考虑这种特殊情况,因而或许会引发严重结果。

  由源代码剖析可知,q指向首结点AVAIL,而此刻假如满意。memp块与q块兼并的断定条件,履行q>1en+=p0一>Len+HL,EN和q一>next=pO一>next后,不光不能收回内存,反而导致memp块丢掉;一起,AVAIL的len域的值也不正确。假如此刻pO一>next又为NULL,则会导致整个堆内存的丢掉。

  笔者特在Keil C7.5 A版中规划了一个示例(见本刊网站),用于引发该过错。要避免这种过错,只需将if((((char_MALLOC_MEM_*)q)+q一>len+HLEN)==pO)断定句子改为if((q!=&AVAIL)&&(((char_MALLOC_MEM_*)q)+q一>len+HLEN)==p0)即可。有爱好的可通过电子邮件与笔者联络(cqdoml@sina.com)。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/fangan/dianlu/298876.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部