概述:glibc
提供的mallochook
机制是通过重新指定内存管理的回调函数指针来实现自定义的内存管理方式。
1. 原型
#include <malloc.h>
void *(*__malloc_hook)(size_t size, const void *caller);
void *(*__realloc_hook)(void *ptr, size_t size, const void *caller);
void *(*__memalign_hook)(size_t alignment, size_t size,
const void *caller);
void (*__free_hook)(void *ptr, const void *caller);
void (*__malloc_initialize_hook)(void);
void (*__after_morecore_hook)(void);
将这些函数地址指向自定义的函数,在调用对应的malloc
等函数的时候,将会被自动调用到自定义的函数。
2. 范例
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <malloc.h>
typedef void *(*malloc_hook_fun_t)(size_t size, const void *caller);
typedef void (*free_hook_fun_t)(void *ptr, const void *caller);
void replace_hook(void);
void restore_hook(void);
malloc_hook_fun_t old_malloc_hook = NULL;
free_hook_fun_t old_free_hook = NULL;
int replaced = 0;
void *my_malloc_fun(size_t size, const void *caller)
{
restore_hook();
void *ret = malloc(size);
printf("+%p:[%p]\n", caller - 1, ret);
replace_hook();
return ret;
}
void my_free_fun(void *ptr, const void *caller)
{
restore_hook();
printf("-%p:[%p]\n", caller - 1, ptr);
free(ptr);
replace_hook();
}
void replace_hook(void)
{
assert(!replaced);
replaced = 1;
old_malloc_hook = __malloc_hook;
old_free_hook = __free_hook;
__malloc_hook = my_malloc_fun;
__free_hook = my_free_fun;
}
void restore_hook(void)
{
assert(replaced);
replaced = 0;
__malloc_hook = old_malloc_hook;
__free_hook = old_free_hook;
}
int main(int argc, char **argv)
{
replace_hook();
char *p = malloc(100);
free(p);
restore_hook();
return 0;
}
- 定义
old_malloc_hook
和old_free_hook
用来保存库自带的内存分配函数。当进行实际内存管理时,恢复回来进行实际的内存分配和释放; - 定义
replace_hook
和restore_hook
函数,方便进行回调函数的替换和恢复; - 定义
my_malloc_fun
和my_free_fun
来替换自带的内存分配、释放函数,用于自定义的内存记录、追踪。
3. 编译
$ gcc malloc_hook.c -g
- 编译会有告警,告警信息是:不建议使用这些回调函数。对于单线程使用替换回调,是一种可行的方式,对于多线程的话,就会导致竞争,不适合使用回调函数。如果说是要进行内存泄漏扫描,可以考虑使用一个叫做
valgrind
的工具。 - 使用
-g
参数是为了后续将输出的转换为代码行号。
4. 运行结果
- 执行结果
$ ./a.out
+0x400808:[0x1c19010]
-0x400818:[0x1c19010]
- 地址转换结果
$ addr2line -f -e a.out -a 0x400808
0x0000000000400808
main
/home/centos/gnuc/c3/jianshu_malloc_hook.c:68
$ addr2line -f -e a.out -a 0x400818
0x0000000000400818
main
/home/centos/gnuc/c3/jianshu_malloc_hook.c:70
通过执行输出打印的调用地址,然后将地址转换层代码位置,这就和之前的 mtrace 的功能类似了。
5. 结论
-
malloc_hook
回调机制适用于单线程,可以通过重新定义内存处理函数来进行内存的记录、追踪和管理等功能。 - 对于多线程,
malloc_hook
机制会出现竞争,不建议使用。如果是调试阶段,可以考虑使用valgrind
来进行内存追踪。