之前在meego性能优化团队实习的时候,为了提升APP启动速度,我曾计算APP调用malloc的次数和耗时,并以此数据为依据探讨内存池对软件优化的可能性。通俗地讲,我须要将某些函数的调用重定向到咱们本身定义的替代函数中来,这样的替代函数就叫作包装函数(wrapper function)。对malloc这个具体的场景来讲咱们不只须要hack函数的调用,还要获取本来glibc定义的“real malloc”的句柄(函数指针)以使程序正常运行下去,固然若是这一切能够在不须要从新编译软件的前提下完成就更好了。html
我总结了Linux环境下包装函数的几种实现方法:linux
1.LD_PRELOAD+dlsym性能优化
这对组合中,LD_PRELOAD完成了hack,而dlsym能够帮组咱们获取到real malloc。LD_PRELOAD是一个环境变量,它能够影响程序的运行时的连接,让咱们指定优先加载的动态连接库。虽然这个方法没法hack静态连接的符号,不过还好gcc编译的标准C函数大多包含在动态连接库libc.so.6中,因此这个方法仍是可行的。app
my_malloc.cide
#define _GNU_SOURCE #include <stdio.h> #include <dlfcn.h> void* malloc(size_t sz) { void *(*libc_malloc)(size_t) = dlsym(RTLD_NEXT, "malloc"); printf("malloc\n"); return libc_malloc(sz); }
main.c函数
#include<stdio.h> #include <malloc.h> int main(int argc, char** argv) { int* ptr = (int *)malloc(sizeof(int)); free(ptr); return 0; }
gcc -o main.o -Wall -O2 main.c
gcc -o my_malloc.so -shared my_malloc.c -Wall -ldl -fPIC
export LD_PRELOAD=/path/.../my_malloc.so
./main.o性能
注意:
1.my_malloc.c中若是不加"#define _GNU_SOURCE",编译时报错“RTLD_NEXT undeclared”,由于glibc不会自动使用某些GNU extensions,因此须要定义这个宏启用这些extensions。
2.LD_PRELOAD后面的so文件必定要使用绝对路径或者把so文件放入LD_LIBRARY_PATH指定的目录下,不然会找不到so文件。
3.LD_PRELOAD的做用效果是全局的,使用完后要及时恢复。优化
2.ld --wrap指针
适用:Linuxcode
ld有一个wrap选项,linux man的解释是:Use a wrapper function for symbol. Any undefined reference to symbol will be resolved to "__wrap_symbol". Any undefined reference to "__real_symbol" will be resolved to symbol.
void * __wrap_malloc (size_t size) { printf ("malloc\\n"); return __real_malloc (size); }
全部对malloc的调用被解析为调用__wrap_malloc,而__real_malloc则会被解析为原始malloc函数。这种方法在gDEBugger上获得了实际应用,gDEBugger是一个OpenGL Profiler,它能够给出每一个OpenGL函数的调用次数使用时间等数据,它的实现方法就是为全部OpenGL函数定义wrapper函数,在wrapper函数中添加统计代码。
3.GCC Malloc Hook
gcc only malloc only
参考文献:http://www.gnu.org/s/hello/manual/libc/Hooks-for-Malloc.html 这种方法其实有很强的局限性,只针对使用gcc编译的malloc,realloc,free等内存管理函数。gcc定义了一批函数指针变量,例如__malloc_hook指向“malloc”实际调用的函数,初始化时先保存真正的malloc函数指针而后将本身定义的malloc函数指针赋值给__malloc_hook。
/* Prototypes for __malloc_hook, __free_hook */ #include <malloc.h> /* Prototypes for our hooks. */ static void my_init_hook (void); static void *my_malloc_hook (size_t, const void *); static void my_free_hook (void*, const void *); /* Override initializing hook from the C library. */ void (*__malloc_initialize_hook) (void) = my_init_hook; static void my_init_hook (void) { old_malloc_hook = __malloc_hook; old_free_hook = __free_hook; __malloc_hook = my_malloc_hook; __free_hook = my_free_hook; } static void * my_malloc_hook (size_t size, const void *caller) { void *result; /* Restore all old hooks */ __malloc_hook = old_malloc_hook; __free_hook = old_free_hook; /* Call recursively */ result = malloc (size); /* Save underlying hooks */ old_malloc_hook = __malloc_hook; old_free_hook = __free_hook; /* printf might call malloc, so protect it too. */ printf ("malloc (%u) returns %p\\n", (unsigned int) size, result); /* Restore our own hooks */ __malloc_hook = my_malloc_hook; __free_hook = my_free_hook; return result; } static void my_free_hook (void *ptr, const void *caller) { /* Restore all old hooks */ __malloc_hook = old_malloc_hook; __free_hook = old_free_hook; /* Call recursively */ free (ptr); /* Save underlying hooks */ old_malloc_hook = __malloc_hook; old_free_hook = __free_hook; /* printf might call free, so protect it too. */ printf ("freed pointer %p\\n", ptr); /* Restore our own hooks */ __malloc_hook = my_malloc_hook; __free_hook = my_free_hook; } int main () { ... }