Keil C动态内存管理机制分析及改进(转)
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)。
Keil C动态内存管理机制分析及改进(转)的更多相关文章
- Keil C动态内存管理机制分析及改进
Keil C是常用的嵌入式系统编程工具,它通过init_mempool.mallloe.free等函数,提供了动态存储管理等功能.本文通过对init_mempool.mallloe和free这3个Ke ...
- memcached内存管理机制分析
memached是高性能分布式内存对象系统,通过在内存中存储数据对象来减少对磁盘的数据读取次数,提高服务速度. 从业务需求出发.我们通过一条命令(如set)将一条键值对(key,value)插入mem ...
- iOS中引用计数内存管理机制分析
在 iOS 中引用计数是内存的管理方式,虽然在 iOS5 版本中,已经支持了自动引用计数管理模式,但理解它的运行方式有助于我们了解程序的运行原理,有助于 debug 程序. 操作系统的内存管理分成堆和 ...
- 【Cocos2d-x 3.x】内存管理机制与源码分析
侯捷先生说过这么一句话 : 源码之前,了无秘密. 要了解Cocos2d-x的内存管理机制,就得阅读源码. 接触Cocos2d-x时, Cocos2d-x的最新版本已经到了3.2的时代,在学习Coco ...
- Java虚拟机内存管理机制
自动内存管理机制 Java虚拟机(JVM)在执行Java程序过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有的区 ...
- 浅析java内存管理机制
内存管理是计算机编程中的一个重要问题,一般来说,内存管理主要包括内存分配和内存回收两个部分.不同的编程语言有不同的内存管理机制,本文在对比C++和Java语言内存管理机制的不同的基础上,浅析java中 ...
- Spark内存管理机制
Spark内存管理机制 Spark 作为一个基于内存的分布式计算引擎,其内存管理模块在整个系统中扮演着非常重要的角色.理解 Spark 内存管理的基本原理,有助于更好地开发 Spark 应用程序和进行 ...
- 【JVM】5、JVM内存管理机制
转自:http://blog.csdn.net/lengyuhong/article/details/5953544 近期看了看Java内存泄露的一些案例,跟原来的几个哥们讨论了一下,深入研究发现JV ...
- FreeRTOS 动态内存管理
以下转载自安富莱电子: http://forum.armfly.com/forum.php 本章节为大家讲解 FreeRTOS 动态内存管理,动态内存管理是 FreeRTOS 非常重要的一项功能,前面 ...
随机推荐
- 用GDB调试程序的设置 Segmentation fault(Core Dump)调试
在写wifi库的时候碰见一个 Segmentation fault(Core Dump) 所以需要用GDB调试下. 在cmake的时候,修改CMakeLists.txt set(CMAKE_C_FLA ...
- cscope
http://sourceforge.net/p/cscope/bugs/247/ buffer read only? cx cq Here is a simple patch which re-en ...
- jsonarray和jsonobject
JSONArray ja = new JSONArray(); ja.put("11"); ja.put("22"); ja.put("33" ...
- 小蚂蚁搬家<贪心>
题意: 由于预知未来可能会下雨,所以小蚂蚁决定搬家.它需要将它的所有物品都搬到新家,新家的体积为V,小蚂蚁有N件物品需要搬,每件物品的体积为Ai,但他发现:每件物品需要新家剩余体积大于等于Bi才能使它 ...
- CDockablePane使用总结
基于 http://blog.csdn.net/kikaylee/article/details/8936953 CDockablePane的基本布局和用法 新建一个SDI工程,在CMainFrame ...
- nagios安装全过程
Nagios是一个用来监控主机.服务和网络的开放源码软件,可以在发生故障时发送报警短信和邮件,只要Nagios监控的对象发生故障,系统就会自动发送短信到手机上.所以应用十分广泛. Nagios is ...
- SAX,DOM,JAXP,JDOM,DOM4J比较
dom,sax,jdom,dom4j的技术特点: 1: DOMDOM 是用与平台和语言无关的方式表示 XML 文档的官方 W3C 标准.DOM 是以层次结构组织的节点或信息片断的集合.这个层次结构允许 ...
- HDU 5479 Scaena Felix
水题,括号匹配,有几对匹配了,答案就是那个... #include<cstdio> #include<cstring> #include<cmath> #inclu ...
- python 从windows上传文件到linux脚本
import paramiko import datetime import os hostname = '192.168.112.132' username = 'root' password = ...
- CentOS Hadoop安装配置详细
总体思路,准备主从服务器,配置主服务器可以无密码SSH登录从服务器,解压安装JDK,解压安装Hadoop,配置hdfs.mapreduce等主从关系. 1.环境,3台CentOS7,64位,Hadoo ...