在应用程序中替换Linux中Glibc的malloc的四种方法

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

在应⽤程序中替换Linux中Glibc的malloc的四种⽅法
打算优化系统的内存分配,接管glibc提供的内存管理,但是整个⼯程的代码量很⼤,使⽤malloc、realloc、calloc和free的地⽅到处都是,如果⾃⼰写好的接⼝需要重命名所有的调⽤,先不说⼯作量,部分没有权限查看代码的.a⽂件就搞不定了。

所以需要替换掉系统的malloc,保证原有调⽤的名称不变。

经过尝试,共有四种⽅法可以替换,各有优缺点吧。

⽅案1 使⽤环境变量LD_PRELOAD
环境变量LD_PRELOAD指定程序运⾏时优先加载的动态连接库,这个动态链接库中的符号优先级是最⾼的。

标准C的各种函数都是存放在libc.so.6的⽂件中,在程序运⾏时⾃动链接。

使⽤LD_PRELOAD后,⾃⼰编写的malloc的加载顺序⾼于glibc中的malloc,这样就实现了替换。

⽤法:
[littlefang]$ LD_PRELOAD=" ./mymalloc.so"
这是最实⽤的替换⽅法,动态链接库加载过程中提供了初始化函数,可以轻易的获得系统malloc的句柄,再将它做进⼀步的管理,Hoard(参见深⼊Linux 的内存管理,关于PTMalloc3、Hoard和TCMalloc)的就是这样实现的。

⽅案2 malloc调试变量
__malloc_hook是⼀组glibc提供的malloc调试变量中的⼀个,这组变量包括:
[cpp:nogutter] view plaincopyprint?
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_hook = my_malloc_hook;”,之后的malloc调⽤都会使⽤my_malloc_hook函数,⽅便易⾏。

但是这组调试变量不是线程安全的,当你想⽤系统malloc的时候不得不把他们改回来,多线程调⽤就得上锁了。

因此⽅案2不很适⽤于系统内存优化,勉强⽤来简单管理线程内存使⽤。

详细⽤法请猛击这⾥
⽅案3 编译⾃⼰的libmalloc.a
关于重载glibc的malloc库,ChinaUnix上有这样的讨论:
如果我⽤cc -o myprog myprog.c -lmylib,⽽不想修改缺省的ld的命令⾏参数或者linker脚本,不知可不可以?
这个⽅法确实⽐较理想,只需要make⼀次就OK了,不⽤更改环境变量,省得担⼼后台运⾏的问题。

后⾯有⼈回复让楼主试试,不知道楼主试了没有,我试了⼀下。

若要把系统内存管理起来,⾸先还是要向操作系统申请内存,这个问题对于LD_PRELOAD⽅案很简单,链接库加载时就可以把glibc中的malloc加载进来,以后直接调⽤就可以了,如:
real_malloc = dlsym(RTLD_NEXT, "malloc");
但是你如果使⽤⾃⼰编译的malloc库,在你调⽤dlsym这个函数时,dlsym会调⽤dlerror,dlerror会调⽤calloc,calloc要调⽤malloc,⽽你的malloc正在初始化等待dlsym返回中,于是死循环了。

有⼈说,在调⽤没有初始化完毕的malloc时,返回NULL,我试了dlsym不认账,加载可耻的失败了。

在满世界的寻找dlsym 的替代品未果后,我把⽬光瞄住了tcmalloc(参见深⼊Linux的内存管理,关于PTMalloc3、Hoard和TCMalloc)。

Tcmalloc使⽤时需要在链接时加上-ltcmalloc 即可,它代码⾥也没使⽤dlsym,⼤略了看了下它的代码,它使⽤mmap从系统获取的内存。

void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);
这种⽅法从页⾯级就要对系统内存进⾏管理,Glibc中的malloc就是使⽤mmap和brk两个函数从程序堆中获得内存的。

⽆疑,这⽐起⽤malloc分配的内存复杂了很多。

⽅案4 链接过程控制
ld中有⼀个选项 –wrap,当查找某个符号时,它优先先解析__wrap_symbol, 解析不到才去解析symbol。

例如:
[cpp:nogutter] view plaincopyprint?
void *__wrap_malloc (size_t c)
{
printf ("malloc called with %zu/n", c);
return __real_malloc (c);
}
当其它⽂件与你实现__wrap_malloc函数的⽂件链接时使⽤--wrap malloc ,则所有到malloc的调⽤都是会链接到__wrap_malloc上。

只有调⽤__reall_malloc时才会调⽤真正的malloc。

[cpp:nogutter] view plaincopyprint?
#include <stdio.h>
#include <stdlib.h>
void *__real_malloc(size_t);
void *__wrap_malloc(size_t c)
{
printf("My MALLOC called: %d/n", c);
return __real_malloc(c);
}
int main (int argc, char *argv[])
{
void *ptr = malloc(12);
return 0;
}
编译
[littlefang]$ gcc wrap.c -o wrap -Wl,-wrap,malloc
运⾏
[littlefang]$ ./wrap
My MALLOC called: 12
Gcc或g++编译使⽤ –Wl选项,以指定链接器参数,⽐如同时替换malloc,free,realloc就要⽤
gcc wrap.c -o wrap -Wl,-wrap,malloc -Wl,-wrap,free -Wl,-wrap,realloc。

特别需要注意的是,如果你的__wrap_malloc是⽤C++实现的,千万不要忘记加上extern “C”做修饰,不然会出现"undefine reference to __wrap_malloc"。

相关文档
最新文档