方案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調試變量中的一個,這組變量包括:
- 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。例如:
- 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。
- #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"。