在应用程序中替换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 调试变量中的一个,这组变量包括:

  1. void  *(*__malloc_hook)( size_t  size,  const   void  *caller);   
  2.   
  3. void  *(*__realloc_hook)( void  *ptr,  size_t  size,  const   void  *caller);   
  4.   
  5. void  *(*__memalign_hook)( size_t  alignment,  size_t  size,  const   void  *caller);   
  6.   
  7. void  (*__free_hook)( void  *ptr,  const   void  *caller);   
  8.   
  9. void  (*__malloc_initialize_hook)( void );   
  10.   
  11. 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 。例如:

  1. void  *__wrap_malloc ( size_t  c)  
  2.   
  3. {  
  4.   
  5.      printf ("malloc called with %zu/n" , c);  
  6.   
  7.      return  __real_malloc (c);  
  8.   
  9. }  
 

当其它文件与你实现 __wrap_malloc 函数的文件链接时使用 --wrap malloc ,则所有到 malloc 的调用都是会链接到 __wrap_malloc 上。只有调用 __reall_malloc 时才会调用真正的 malloc

  1. #include <stdio.h>   
  2.   
  3. #include <stdlib.h>   
  4.   
  5.    
  6.   
  7. void  *__real_malloc( size_t );  
  8.   
  9.    
  10.   
  11. void  *__wrap_malloc( size_t  c)  
  12.   
  13. {  
  14.   
  15.         printf("My MALLOC called: %d/n" , c);  
  16.   
  17.         return  __real_malloc(c);  
  18.   
  19. }  
  20.   
  21.    
  22.   
  23. int  main ( int  argc,  char  *argv[])  
  24.   
  25. {  
  26.   
  27.         void  *ptr = malloc(12);  
  28.   
  29.    
  30.   
  31.         return  0;  
  32.   
  33. }  
 

编译 

[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"。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值