介绍一个动态内存管理模块,可以有效地检测C程序中内存泄漏和写内存越界等错误,适用于具有标准C语言开发环境的各种平台。
Fense的具体实现
Fense提供Fense_Malloc、Fense_Free、Fense_Realloc及Fense_Calloc等内存管理函数,功能与C语言中的malloc、free、realloc和calloc保持一致。以下是Fense_Malloc和Fense_Free及其相关函数的大致实现。Fense_Realloc和Fense_Calloc可通过Fense_Malloc来实现,过程相对比较简单,限于篇幅,本文不再赘述。
/* Fense运行时开关 */
static int st_Disabled = 0;
/* 内存分配函数 */
void * Fense_Malloc(size_t size, char *file, unsigned long line)
{
unsigned char *ptr;
struct Head *h;
/* 对链表进行互斥存取,仅在多线程系统中需要 */
FENSE_LOCK();
/* Fense的运行时开关 */
if(st_Disabled) { ptr = malloc(size); FENSE_UNLOCK(); return(ptr); }
if(!size)
{
/* 对零分配的处理 */
#ifdef WARN_ON_ZERO_MALLOC
/* … 输出警告信息 */
#endif
FENSE_UNLOCK();
return(0);
}
/* 分配内存,包括链表节点区域和前/后监测区域 */
ptr = malloc(sizeof(struct Head) +
FENSE_FRONT_SIZE + size + FENSE_END_SIZE);
if(!ptr)
{
FENSE_UNLOCK();
return(0);
}
/* 初始化链表节点,保存分配内存的信息 */
h = (struct Head *)ptr;
h->size = size;
h->file = file;
h->line = line;
/* 将此内存块插入链表st_Head */
h->next = st_Head;
h->prev = 0;
if(st_Head)
{
st_Head->prev = h;
MakeHeaderValid(st_Head);
}
st_Head = h;
/* 为本节点区域计算校验和 */
MakeHeaderValid(h);
/* 初始化已分配的内存区 */
SetFense(ptr + sizeof(struct Head),
FENSE_FRONT_VAL, FENSE_END_SIZE);
SetFense(ptr + sizeof(struct Head) + FENSE_FRONT_SIZE + size,
FENSE_END_VAL, FENSE_END_SIZE);
#ifdef FILL_ON_MALLOC
/* 填充用户内存 */
SetFense(ptr + sizeof(struct Head) + FENSE_FRONT_SIZE,
FILL_ON_MALLOC_VAL, size);
#endif
FENSE_UNLOCK();
/* 返回用户内存区域的起始位置 */
return (ptr + sizeof(struct Head) + FENSE_FRONT_SIZE);
}
/* 为指定内存块计算校验和 */
void MakeHeaderValid(struct Head *h)
{
int c, checksum, *p;
/* 先将数据成员checksum清为0 */
h->checksum = 0;
/* 计算链表节点区域值的累加和 */
for(c = 0, checksum = 0, p = (int *)h; c < sizeof(struct Head)/sizeof(int); c++)
checksum += *p++;
/* 保存累加和的负值到数据成员checksum中 */
h->checksum = -checksum;
}
/* 设置监测区域为预设值 */
void SetFense(unsigned char *ptr, unsigned char value, size_t size)
{
memset(ptr, value, size);
}
/* 内存释放函数 */
void Fense_Free(void *uptr, char *file, unsigned long line)
{
unsigned char *ptr = (unsigned char *)uptr - sizeof(struct Head) - FENSE_FRONT_SIZE;
struct Head *h = (struct Head *)ptr;
FENSE_LOCK();
if(st_Disabled) { free(uptr); FENSE_UNLOCK(); return; }
#ifdef CHECK_ALL_MEMORY_ON_FREE
/* 检查所有Fense管理下的动态内存 */
Fense_CheckAllMemory(file, line);
#endif
/* 判断本内存块是否在st_Head中 */
#ifdef VALIDATE_FREE
if(!IsOnList(h))
{ /* ... 如果不在st_Head上,提示警告信息,退出Fense_Free */ }
#endif
/* 检查当前内存块 */
if(!CheckBlock(h, file, line))
goto fail;
/* 将当前内存块从st_Head中删除 */
if(h->prev)
{
if(!CheckBlock(h->prev, file, line))
goto fail;
h->prev->next = h->next;
/* 重新计算受到影响的前一内存块的校验和 */
MakeHeaderValid(h->prev);
}
else
st_Head = h->next;
if(h->next)
{
if(!CheckBlock(h->next, file, line))
goto fail;
h->next->prev = h->prev;
/* 重新计算受到影响的后一内存块的校验和 */
MakeHeaderValid(h->next);
}
#ifdef FILL_ON_FREE
/* 填充被释放的内存区 */
SetFense(ptr, FILL_ON_FREE_VAL,
sizeof(struct Head) + FENSE_FRONT_SIZE + h->size + FENSE_END_SIZE);
#endif
/* 释放当前内存块 */
free(ptr);
FENSE_UNLOCK();
return;
fail:
/* … 输出错误信息 */
FENSE_UNLOCK();
}
/* 检查指定内存块是否在st_Head上 1 – 在0 – 不在*/
int IsOnList(struct Head *h)
{
struct Head *curr;
curr = st_Head;
while(curr)
{
if(curr == h)
return(1);
curr = curr->next;
}
return(0);
}
/* 检查指定内存块是否发生错误操作 1 – 正确 0 – 出错 */
int CheckBlock(struct Head *h, char *file, unsigned long line)
{
unsigned char *ptr = (unsigned char *)h;
int result = 1;
if(ChecksumHeader (h))
{
/* … 提示出错 */
return(0);
}
/* 检查前端越界情况 */
if(!CheckFense(ptr + sizeof(struct Head),
FENSE_FRONT_VAL, FENSE_FRONT_SIZE))
{
/* … 提示出现前越界错误 */
result = 0;
}
/*检查后端越界情况*/
if(!CheckFense(ptr + sizeof(struct Head) + FENSE_FRONT_SIZE + h->size,
FENSE_END_VAL, FENSE_END_SIZE))
{
/* … 提示出现后越界错误 */
result = 0;
}
return(result);
}
/* 检查本链表节点区域的校验和,0 – 正确, 非0 – 不正确 */
int ChecksumHeader(struct Head *h)
{
int c, checksum, *p;
for(c = 0, checksum = 0, p = (int *)h; c < sizeof(struct Head)/sizeof(int); c++)
checksum += *p++;
return(checksum);
}
/* 检查监测区域的值是否发生改变 0 - 改变 1 - 没有改变 */
int CheckFense(unsigned char *ptr, unsigned char value, size_t size)
{
while(size--)
if(*ptr++ != value)
return(0);
return(1);
}
/* 检查st_Head上的所有内存块 */
int Fense_CheckAllMemory(char *file, unsigned long line)
{
struct Head *curr = st_Head;
int count = 0;
if(st_Disabled)
return(0);
FENSE_LOCK();
while(curr)
{
if(!CheckBlock(curr, file, line))
count++;
curr = curr->next;
}
FENSE_UNLOCK();
return(count);
}
(文中代码在Visual C++ 6.0,Borland C++ 3.1及CrossCode C 7.4环境中编译通过)