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

转自:http://www.xuebuyuan.com/2223733.html


打算优化系统的内存分配,接管

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
调试变量中的一个,这组变量包括:

view plain
copy to clipboard
print
?
  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
。例如:

view plain
copy to clipboard
print
?
  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

view plain
copy to clipboard
print
?
  1. #include 
      
  2.   
  3. #include 
      
  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"。

你可能感兴趣的:(linux下调试)