内存泄漏
常见检测工具 valgrind,mtrace
该组件解决:
A 如何知道内存泄漏?
B 如何定位代码哪一行引起内存泄漏?
解决方案:
终端命令:addr2line -f -e (执行文件名字) -a (这里写caller地址)
版本1
A 简单做法
malloc/calloc/realloc +1
free -1
判断进程结束前是否为0;
C语言有一些宏可以知道哪一行
-file-,-func-,-LINE-
底层是调用了 builtin_return_address()函数
builtin_return_address(),该函数参数表示返回哪一个函数
注意malloc()会调用builtin_return_address()
例:
fun1(){
fun2(){
fun3(){
malloc();
}
}
}
builtin_return_address(0)》返回的是fun3
builtin_return_address(1)》返回的是fun2
builtin_return_address(2)==》返回的是fun1
等等
代码如下
//版本1 调用系统函数
// __libc_malloc(){}//更底层的函数
extern void* __libc_malloc(size_t size);
int enable_malloc_hook = 1;
void *malloc(size_t size){
//递归终止直接打印会递归死循环,是由于printf中也会调用malloc,gbd 可以看出来
if(enable_malloc_hook){
enable_malloc_hook = 0;
void *p = __libc_malloc(size);
//这个函数返回是一个地址,可以通过地址看出来是哪一个文件,哪一行,哪个函数
//而且编译需要加上-g
void *caller = __builtin_return_address(0);
//这里做法是创建一个文件
char buff[128] ={0};
sprintf(buff,"%p.mem",p);
FILE *fp = fopen(buff,"w");
fprintf(fp,"malloc [+%p]-->addr:%p size: %ld\n",caller,p,size);
fflush(fp);
// fclose(fp);//有问题
// printf("malloc [+%p]-->addr:%p size: %ld\n",caller,p,size);
enable_malloc_hook = 1;
return p;
}else{
return __libc_malloc(size);
}
return NULL;
}
void free(void *p){
if(enable_malloc_hook){
enable_malloc_hook =0;
char buff[128] ={0};
sprintf(buff,"%p.mem",p);
if(unlink(buff)<0){//文件不存在,该函数表示删除buff文件,文件不存在,就删除不了
printf("double free:%p\n",p);
}else{
}
__libc_free(p);
enable_malloc_hook=1;
}else{
__libc_free(p);
}
}
版本2
重写malloc设置宏定义
void *malloc_hook(size_t size,const char* file,int line){
void *p = malloc(size);
//这里做法是创建一个文件
char buff[128] ={0};
sprintf(buff,"%p.mem",p);
FILE *fp = fopen(buff,"w");
fprintf(fp,"malloc [+%s:%d]-->addr:%p size: %ld\n",file,line,p,size);
fflush(fp);
fclose(fp);
return p;
}
void free_hook(void *p,const char* file,int line){
char buff[128] ={0};
sprintf(buff,"%p.mem",p);
if(unlink(buff)<0){//文件不存在,该函数表示删除buff文件,文件不存在,就删除不了
printf("double free:%p,File:%s:%d\n",p,file,line);
return ;
}
free(p);
}
//注意下面的宏不可放在上面函数前,需要调试打开IF就行
#if 0
#define malloc(size) malloc_hook(size,__FILE__,__LINE__)
#define free(p) free_hook(p,__FILE__,__LINE__)
#endif
版本3
挂钩子
#include <malloc.h>
typedef void *(*malloc_hook_t)(size_t,const void *caller);
malloc_hook_t malloc_f;
typedef void (*fire_hook_t)(void *p,const void *caller);
fire_hook_t free_f;
int replaced= 0;//是否修改
void mem_trace(void);
void mem_untrace(void);
void *malloc_hook_f(size_t size,const void *caller){
mem_untrace();//将下面malloc还是设置走系统
void* ptr = malloc(size);
char buff[128] ={0};
sprintf(buff,"%p.mem",ptr);
FILE *fp = fopen(buff,"w");
fprintf(fp,"malloc [+%p]-->addr:%p size: %ld\n",caller,ptr,size);
fflush(fp);
fclose(fp);
mem_trace();
return ptr;
}
void *free_hook_f(void *p,const void *caller){
mem_untrace();
char buff[128] ={0};
sprintf(buff,"%p.mem",p);
if(unlink(buff)<0){//文件不存在,该函数表示删除buff文件,文件不存在,就删除不了
printf("double free:%p,File:%p\n",p,caller);
return ;
}
free(p);
mem_trace();
}
//挂钩子
void mem_trace(void){//mtracemalloc会执行
//保存系统的
replaced = 1;
malloc_f = __malloc_hook;
free_f =__free_hook;
//改变系统本身的指向
__malloc_hook = malloc_hook_f;
___free_hook = free_hook_f;
}
void mem_untrace(void){
__malloc_hook= malloc_f;
__free_hook = free_f;
replaced = 0;
}
版本4
使用matrc
mtrace开源库。
注意一定要再终端执行:export MALLOC_TRACE=./test.log
不然日志没办法输出
#include <mcheck.h>
void main(){
mtrace();
void *p1 = malloc(10);
void *p2 = malloc(20);
free(p1);
muntrace();//版本4 mtrace开源库
}
得出来的test.log,文件可以看到有加减,加的地址对应减的地址 剔除掉,
保留下来的就是内存泄漏的
终端命令:addr2line -f -e (执行文件名字) -a (这里写caller地址)
共享内存
再系统空间里 有一块内存专门做共享内存的
不是在堆里也不是在栈里,在进程的虚拟空间中有一块自己的空间,是用于映射在空一块地方的,就是A进程与B进程操作的共享内存是同一块,映射的是同一块物理内存
fork()函数创建时,子进程继承父进程数据,但是随着时间推移,都有自己的独立思想,就是爸爸与儿子一样,儿子继承老爸的血,但是后天有学习能力
方式1
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
void *shm_mmap_alloc(int size){
/*MAP_ANON任何文件*/
void *addr = mmap(NULL,size,PROT_READ|PROT_WRITE,MAP_ANON|MAP_SHARED,-1/*匿名*/,0);
if(addr == MAP_FAILED){//映射失败
return NULL;
}
return addr;
}
//释放
int shm_mmap_free(void* addr,int size){
return munmap(addr,size);
}
int main(){
//创建共享内存
char* addr = (char*)shm_mmap_alloc(1024);
//开进程
pid_t pid =fork();
if(pid ==0){
//子进程
int i = 0;
while(i<26){
addr[i] = 'a'+i++;
addr[i] = '\0';
printf("children:%s\n",addr);
sleep(1);
}
}else if(pid>0){
//父进程
int i = 0;
while(i++<26){
printf("father:%s\n",addr);
sleep(1);
}
}
//释放
shm_mmap_free(addr,1024);
return 0;
}
方式2
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/fcntl.h>
void *shm_mmap_alloc(int size){
/*MAP_ANON任何文件*/
///dev/zero 就是一个黑洞,不管往里面写多少都是0
int fd = open("/dev/zero",O_RDWR);
void *addr = mmap(NULL,size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
close(fd);
if(addr == MAP_FAILED){//映射失败
return NULL;
}
return addr;
}
//释放
int shm_mmap_free(void* addr,int size){
return munmap(addr,size);
}
int main(){
//创建共享内存
char* addr = (char*)shm_mmap_alloc(1024);
//开进程
pid_t pid =fork();
if(pid ==0){
//子进程
int i = 0;
while(i<26){
addr[i] = 'a'+i++;
addr[i] = '\0';
printf("children:%s\n",addr);
sleep(1);
}
}else if(pid>0){
//父进程
int i = 0;
while(i++<26){
printf("father:%s\n",addr);
sleep(1);
}
}
//释放
shm_mmap_free(addr,1024);
return 0;
}
仅供学习,不佞赐教