Glibc中关于内存的函数
__malloc_hook
glibc中定义了函数指针变量__malloc_hook等。 根据glibc中malloc的实现(malloc/malloc.c),如果这个变量不为空则执行其指向的函数,否则进行内存分配。 因此使用这种技术可以实现内存分配的跟踪等功能。例如,设置全局变量记录程序使用的动态内存的大小,代码如下:
#include <malloc.h>
#include <stdlib.h>
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
size_t malloc_usage;
size_t const malloc_align=8;
void* malloc_hook(size_t s, void const* x)
{
__malloc_hook = 0;
size_t* p = (size_t*)malloc(s + malloc_align);
__malloc_hook = malloc_hook;
if(likely(p))
{
*p = s;
malloc_usage += s;
return (char*)p + malloc_align;
}
return 0;
}
void free_hook(void* p, void const* x)
{
if(likely(p))
{
p = (char*)p - malloc_align;
malloc_usage -= *(size_t*)p;
}
__free_hook = 0;
free(p);
__free_hook = free_hook;
}
void* realloc_hook(void* q, size_t t, void const* x)
{
if(q)
{
q = (char*)q - malloc_align;
malloc_usage -= *(size_t*)q;
}
size_t s = t;
if(s)
s += malloc_align;
__realloc_hook = 0;
__malloc_hook = 0;
__free_hook = 0;
size_t* p = (size_t*)realloc(q, s);
__free_hook = free_hook;
__malloc_hook = malloc_hook;
__realloc_hook = realloc_hook;
if(likely(p))
{
*p = t;
malloc_usage += t;
return (char*)p + malloc_align;
}
return 0;
}
void* memalign_hook(size_t y, size_t z, void const* x)
{
abort(); // not implemented
}
void init_malloc_hook()
{
__malloc_hook = malloc_hook;
__realloc_hook = realloc_hook;
__memalign_hook = memalign_hook;
__free_hook = free_hook;
}
void(* __MALLOC_HOOK_VOLATILE __malloc_initialize_hook)(void) = init_malloc_hook;
int main()
{
void * ptr= malloc(100);
int i=0;
for(i=0;i<10;i++)
{
ptr=malloc(123);
}
free(ptr);
printf("%d\n", malloc_usage );
}
上面的代码编译会报错warning: '__malloc_hook' is deprecated (declared at /usr/include/malloc.h:153) [-Wdeprecated-declarations]
因为其不适用与多线程的环境。
若果在单线程环境使用该技术,可以仿照malloc的实现,
利用__builtin_return_address(0)
与__libc_malloc
实现__malloc_hook
的功能
__libc_malloc等函数
Glibc中malloc等函数实际上是调用了__libc_malloc
等函数;
可以结合LD_PRELOAD用来实现自己的malloc以"重载"标准库中的malloc,可以测试时使用。
需要注意的是
- free必须支持free(NULL)。否则程序可能尚未执行即coredump。
- 使用
__libc_malloc
函数实现malloc,若其中需要读写文件不能使用C标准库函数,需要使用open
系统调用。
mallopt函数
MMAP_THRESHOLD
决定malloc时使用sbrt还是mmap
设置内存分配参数,控制内存分配函数的行为
mcheck、mtrace
可以使用mtrace
简单的进行内存泄漏检测,mtrace hook malloc(), realloc(), memalign(), calloc() 和 free(),
对分配和释放内存的操作进行配对检测,如果发现有内存泄漏的情况, 会记录导致内存泄漏的分配函数调用所在的位置,
并将记录保存到环境变量 MALLOC_TRACE
指定的文件中,然后就可以使用 mtrace 命令来查看日志了。man mtrace
info mtrace
查阅glic manual (info libc): Memory(Virtual Memory Allocation And Paging):Memory Allocation: Unconstrained Allocation: Heap Consistency Checking
注意使用mtrace函数必须在malloc等函数调用之前调用,需引用头文件#include <mcheck.h>
另,可以-lmcheck
连接mcheck
库。
环境变量MALLOC_CHECK_
提供了类似的功能。
libmemusage 统计内存使用
glibc 自带了一个 libmemusage 的库,用于收集应用程序运行时的内存使用情况。
使用起来很简单,只要在编译的时候添加 -lmemusage
即可。
它使用 api hook 技术对 malloc等的调用进行监视,统计相应大小内存块的使用比率,
并可给出简单的内存申请与释放的统计信息,可以用于简单的判断是否有内存泄漏。
malloc_info函数
将malloc的状态信息输出到FILE*流中。
- mallinfo将内存信息输出到一个结构中, 不适合64位的情况(输出结构中的字段定义为int)
- malloc_info将信息输出为自描述的格式(目前为XML)
- malloc_info的输出可能随glibc的变化而变化
如何查看glibc的版本
- ldd progname_查看其依赖的libc(如
/lib64/libc.so.6
);执行/lib64/libc.so.6
将显示相关信息 - SUSE:
getconf -a |grep -i lib
- 程序中使用Glibc提供的函数
#include <stdio.h> #include <gnu/libc-version.h> int main () { printf("%s\n",gnu_get_libc_version ()); return 0; }
备注
- 关于malloc_info参见glibc 2.10 news