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

在Ubuntu 22.04(Focal Fossa)系统上安装特定版本的glibc(如2.3.3),通常不是直接推荐的操作,因为默认情况下,Ubuntu会使用最新的稳定版库。然而,如果你确实需要这样做,比如为了兼容性或其他特殊需求,你可以通过从源代码编译来实现。以下是大致步骤: 1. **下载源码**: - 访问glibc的官方GitHub仓库(https://github.com/gnu/glibc)下载对应版本的源代码包。 2. **构建依赖**: - 首先,确保你的系统已经安装了必要的编译工具,例如`build-essential`、`automake`、`autoconf`等。运行`sudo apt-get install build-essential automake autoconf libtool`. 3. **解压并配置**: ```sh tar -xvf glibc-x.y.z.tar.gz cd glibc-x.y.z ./configure --prefix=/usr/local/glibc_2.3.3 ``` 将`x.y.z`替换为实际的glibc版本号。 4. **编译与安装**: ```sh make && sudo make install ``` 这将编译glibc并将其安装到自定义目录`/usr/local/glibc_2.3.3`,而不是系统的标准位置。 5. **更新动态链接器**: - 使用新的glibc替换旧的动态链接器,这可能会涉及到修改一些系统链接和环境变量,建议有充分了解后再操作。 6. **测试**: 安装完成后,确认glibc已生效可以运行一些基本的命令或者程序,看看是否与预期的行为一致。 **注意:** - 这种做法可能存在风险,因为它可能导致系统不稳定,尤其是当你不是基于官方发行版的定制时。因此,在生产环境,除非必要,一般不推荐手动编译glibc。 - 如果只是为了某个特定软件包或应用的需求,尝试使用软件包管理器(如apt或aptitude)寻找对应的版本,它可能会提供预编译的包或特定的解决方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值