嵌入式系统动态内存的使用技巧

一. 常见错误与预防

1. 分配后忘记释放内存

void func(void)

{

p = malloc(len);

do_something(p);

return; /*错误!退出程序时没有释放内存*/

}

预防: 编写代码时malloc()和free()保证成对出现,避免忘记资源回收。

int func(void)

{

p = malloc(len);

if (condition)

return -1; /*错误!退出程序时没有释放内存*/

free(p);

return 0;

}

预防: 一旦使用动态内存分配,请仔细检查程序的退出分支是否已经释放该动态内存。

2. 释放内存调用错误指针

void func(void)

{

p = malloc(len);

val = *p++; /*错误!动态内存句柄不可移动*/

free(p);

}

预防: 千万不要修改动态内存句柄!可以另外赋值给其他指针变量,再对该动态内存进行访问操作。

3. 分配内存不够导致溢出

void func(void)

{

len = strlen(str);

p = malloc(len);

strcpy(p, str); /*错误!str的’’写到动态内存外*/

}

预防: 分配内存前仔细思考长度是否足够,千万注意字符串拷贝占用内存比字符串长度大1。

二. 自动查错机制

尽管在开发过程中坚守原则和谨慎编程甚至严格测试,然而内存泄露的错误还是难以杜绝,如何让系统自动查出内存泄露的错误呢?

一种比较好的方法是建立日志块,即每次分配内存时记录该内存块的指针和大小,释放时再去除该日志块,如果有内存泄露就会有对应的日志块记录这些内存没有释放,这样就可以提醒程序员进行查错。

有了上述日志块操作函数,再来实现动态内存分配与释放函数就很容易了。只有当处于DEBUG版本和打开内存调试DMEM_DBG时才进行日志登录,否则MallocExt()和FreeExt()函数与malloc()和free()是等价的,这样保证了系统处于发布版本时的性能。

(代码已经过严格测试,但这不是盈利的商业代码,即没有版权。但如果因代码错误带来的任何损失作者具有免责权利)

代码部分:

首先定义日志块结构体:

/* Log of dynamic memory usage */

typedef struct _dmem_log

{

struct _dmem_log *p_stNext; /* Point to next log */

const void *p_vDMem; /* Point to allocated memory by this pointer */

INT32S iSize; /* Size of the allocated memory */

} DMEM_LOG;

然后为该结构体开辟内存:

static DMEM_LOG *s_pstFreeLog; /* Point to free log pool by this pointer */

static INT8U s_byNumUsedLog;

static DMEM_LOG *s_pstHeadLog; /* Point to used log chain by this pointer */

/* Pool of dynamic memory log */

#define NUM_DMEM_LOG 20

static DMEM_LOG s_astDMemLog[NUM_DMEM_LOG];

下面是内存日志块的操作函数:初始化、插入日志和移除日志:

/********************************************************** * Initialize DMem Log

* Description : Initialize log of dynamic memory

* Arguments : void

* Returns : void

* Notes :

**********************************************************/

static void InitDMemLog(void)

{

INT16S nCnt;

/* Initialize pool of log */

for (nCnt = 0; nCnt < NUM_DMEM_LOG; ++nCnt)

{

/* Point to next one */

s_astDMemLog[nCnt].p_stNext = &s_astDMemLog[nCnt + 1];

}

s_astDMemLog[NUM_DMEM_LOG – 1].p_stNext = NULL;

s_pstFreeLog = &s_astDMemLog[0]; /* Point to the 1th log */

return;

}

/********************************************************** * Log DMem

* Description : Join an allocated memory into log pool

* Arguments : const void *p_vAddr point to address of this allocated memory by this pointer

* INT32S iSize size of this allocated memory

* Returns : void

* Notes :

**********************************************************/

static void LogDMem(const void *p_vAddr, INT32S iSize)

{

ASSERT(p_vAddr && iSize > 0);

DMEM_LOG *p_stLog;

#if OS_CRITICAL_METHOD == 3

OS_CPU_SR cpu_sr;

#endif

/* Get a log from free pool */

OS_ENTER_CRITICAL(); /* Avoid race condition on s_pstFreeLog */

if (!s_pstFreeLog)

{

OS_EXIT_CRITICAL();

PRINTF(“Allocate DMemLog failed.

“);

return;

}

p_stLog = s_pstFreeLog;

s_pstFreeLog = s_pstFreeLog->p_stNext;

OS_EXIT_CRITICAL();

/* Don’t need to protect this log that is free one currently */

p_stLog->p_vDMem = p_vAddr;

p_stLog->iSize = iSize;

/* Put this log into used chain */

OS_ENTER_CRITICAL(); /* Avoid race condition */

p_stLog->p_stNext = s_pstHeadLog;

s_pstHeadLog = p_stLog;

++s_byNumUsedLog;

OS_EXIT_CRITICAL();

return;

}

/********************************************************** * Unlog DMem

* Description : Remove an allocated memory from log pool

* Arguments : const void *p_vAddr point to address of this allocated memory by this pointer

* Returns : void

* Notes :

**********************************************************/

static void UnlogDMem(const void *p_vAddr)

{

ASSERT(p_vAddr);

DMEM_LOG *p_stLog, *p_stPrev;

#if OS_CRITICAL_METHOD == 3

OS_CPU_SR cpu_sr;

#endif

/* Search the log */

OS_ENTER_CRITICAL(); /*Avoid race condition */

p_stLog = p_stPrev = s_pstHeadLog;

while (p_stLog)

{

if (p_vAddr == p_stLog->p_vDMem)

{

break; /* Have found */

}

p_stPrev = p_stLog;

p_stLog = p_stLog->p_stNext; /* Move to next one */

}

if (!p_stLog)

{

OS_EXIT_CRITICAL();

PRINTF(“Search Log failed.

“);

return;

}

/* Remove from used pool */

if (p_stLog == s_pstHeadLog)

{

s_pstHeadLog = s_pstHeadLog->p_stNext;

}

else

{

p_stPrev->p_stNext = p_stLog->p_stNext;

}

–s_byNumUsedLog;

OS_EXIT_CRITICAL();

/* Don’t need to protect this log that is free one currently */

p_stLog->p_vDMem = NULL;

p_stLog->iSize = 0;

/* Add into free pool */

OS_ENTER_CRITICAL(); /* Avoid race condition */

p_stLog->p_stNext = s_pstFreeLog;

s_pstFreeLog = p_stLog;

OS_EXIT_CRITICAL();

return;

}

带日志记录功能的内存分配MallocExt()和内存释放FreeExt()函数:

/*********************************************************

* Malloc Extension

* Description : Malloc a block of memory and log it if need

* Arguments : INT32S iSize size of desired allocate memory

* Returns: void *NULL= failed, otherwise=pointer of allocated memory

* Notes :

**********************************************************/

void *MallocExt(INT32S iSize)

{

ASSERT(iSize > 0);

void *p_vAddr;

p_vAddr = malloc(iSize);

if (!p_vAddr)

{

PRINTF(“malloc failed at %s line %d.

“, __FILE__, __LINE__);

}

else

{

#if (DMEM_DBG && DBG_VER)

memset(p_vAddr, 0xA3, iSize); /* Fill gargage for debug */

LogDMem(p_vAddr, iSize); /* Log memory for debug */

#endif

}

return p_vAddr;

}

/**********************************************************

* Free Extension

* Description : Free a block of memory and unlog it if need

* Arguments : void * p_vMem point to the memory by this pointer

* Returns : void

* Notes :

**********************************************************/

void FreeExt(void *p_vMem)

{

ASSERT(p_vMem);

free(p_vMem);

#if (DMEM_DBG && DBG_VER)

UnlogDMem(p_vMem); /* Remove memory from log */

#endif

return;

}

编辑:黄飞

发表评论