在應用程序中替換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"。

發佈了5 篇原創文章 · 獲贊 3 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章