1内存泄漏
1.1.1 示例
(1)问题描述
通过 malloc 等函数动态申请的内存在使用后必须相应地调用 free 等函数释放,否 则这块内存就不能被再次使用,出现内存泄漏,示例如下:
示例一
在这里插入代码片`
int my_example(int c)
{
void *p = malloc(10);
if(c)
{
return -1; /*"p" 指向的内存被泄漏*/
}
free(p);
return 0;
}
示例二:
int wrong_check()
{
void *p = malloc(10);
void *q = malloc(20);
if((NULL == p) || (NULL == q))
{
return -1;
/*"p" or "q" 其中之一为 NULL 时,另外一个指向的内存被泄漏*/
}
free(q);
free(p);
return 0;
}
示例三:
int test(int i)
{
void *p = malloc(10);
void *q = malloc(4);
if(i > 0)
{
p = q; /* “p”被重新赋值,指向新的地址,之前指向的内存被泄漏*/
} else
{
free(q);
}
free(p);
return 0;
}
(2)防范措施
- 每次申请内存前必须明确由谁负责释放,何时释放,在何处释放;
- 在异常分支中,保持清醒,一定要在 return 语句前考虑是否要释放内存;
- 内存申请后立即检查是否申请成功,不要多个指针用同一个 if 语句判断; 4) 申请内存成功后,禁止对指向给内存地址的指针重新赋值。
2, 句柄泄漏
2.2.1 示例
1)问题描述
通过 open 等函数获得的文件句柄在使用完后必须通过 close 等函数释放,否则 就会 造成句柄泄漏(handle leak),此时没有关闭句柄 fd,就会出现句柄泄漏,示例如下。
示例一:
int leak_example(int c)
{
int fd = open(“my_file”, MY_OPEN_OPTIONS);
if(c)
{
return -1; // 文件句柄“fd”,发生泄漏
}
close(fd);
return 0;
}
示例二:
void gpio_read(int pin_no, unsigned char * val)
{
int fd = -1;
fd = open(GPIO_DEV, O_RDWR);
if (fd == -1)
{
perror("GPIO Driver open");
printf("# Make sure GPIO Module Loaded\n");
return -1;
}
struct io_param gpio_io_param;
memset((char *)&gpio_io_param,'\0',sizeof(gpio_io_param));
gpio_io_param.cmd = GPIO_DRV_READ;
gpio_io_param.pin_no = pin_no;
if (ioctl(fd,gpio_io_param.cmd,&gpio_io_param) == -1)
{
perror("GPIO ioctl");
return ;
}
close(fd);
*val = gpio_io_param.value;
return ;
}
2) 影响
这是一个典型的异常分支处理不当引起的资源泄漏问题,在正常情况下可能不会出现问题,但是一个非常危险的隐患,一旦走入异常分支,就会出现句柄泄漏,而且此函数是一个读取 GPIO 的公用函数,很可能会反复大量的调用,最终会因资源耗尽而导致系统崩溃,
3)防范措施
只要获得了句柄,无论是正常分支还是异常处理分支,一定要在 return 语句前释放句柄
3,其他泄露
上述提到的两类问题是最常见的资源泄漏问题,除此之外,还有文件指针泄漏(fopen等函数打开文件,在使用完后必须通过 close 等函数关闭导致的资泄漏),信号量的泄漏(即创建信号使用完之后没有销毁导致的资源泄漏)等多种形式。
示例一:
void test(int c)
{
FILE *p = fopen("foo.c", "rb");
if(c)
{
return; // 文件指针“p”没有关闭,发生泄漏
}
fclose(p);
}
如下的代码块所示,是一个创建线程的函数,为了保证下一个线程之前,上次创建的线程的某些动作必须执行完成。在线程创建前创建一个信号量,然后 wait,在创建的线程中完成相应动作之后,执行 sem_post 操作。此时程序可以继续往下执行了。 其中 init_sync 为局部变量,函数执行完成之后,该信号量已经用完了,之后未进行信号量的删除操作,进而导致每创建一个线程,就会引起一个信号量的泄漏.
示例二:
int pal_create_task(char *name, int priority, int stack, void *func_ptr)
{
Ipcom_sem init_sync = NULL; //信号量的指针
f (!init_sync)
{
ipcom_sem_create(&init_sync, 0);//创建互斥信号量
}
/* Create and start the shell process. */
ipcom_proc_attr_init(&attr);
attr.stacksize = stack;
attr.priority = priority;
attr.arg = init_sync; //将信号量作为入参,传入要创建的线程中,进行 sem_post
if (ipcom_proc_acreate(name, (Ipcom_proc_func)func_ptr, &attr, &pid))
{
goto exit;
}
ipcom_sem_wait(init_sync); //等待线程创建之后,执行 sem_pos 操作
exit: return RESULT_OK; }