Linux进程内存管理(一)

本文主要讲用户态进程的内存管理,而不是内核的内存管理。简单地说,就是和 malloc 和 free 相关的内存管理。

简介

Linux 环境下,进程的内存管理器默认是使用 glibc 实现的 ptmalloc 。另外,还有两个比较有名的内存管理器:google 的 tcmalloc 和
fackbook 的 jemalloc 。总体来说, tcmalloc 和 jemalloc 在多核多线程的场景下,性能要优于 ptmalloc 。

HOOK 机制

我们先简单了解一下, malloc 和 free 如何调用到我们自定义的函数。

在 Linux 下,内存管理器一般通过 HOOK 来实现自定义的malloc函数,具体就是通过覆盖__malloc_hook等函数指针来实现。glibc 提供了__malloc_hook__realloc_hook__free_hook__memalign_hook 四个全局函数hook指针。简单地说,就是 malloc 调用的是 __malloc_hook 指针指向的函数,所以 jemalloc 或者 tcmalloc 通过覆盖 __malloc_hook 使程序调用到它们自定义的malloc。
接下来,我们做个小实验,覆盖掉__malloc_hook和__malloc_free。

#include <malloc.h>

// 两个函数声明
static void *my_malloc_hook (size_t, const void *);
static void my_free_hook (void*, const void *);

// 两个全局变量
void* (*old_malloc_hook) (size_t size, const void *caller);
void (*old_free_hook) (void *ptr, const void *caller);

static void my_init (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;
  __malloc_hook = old_malloc_hook;
  __free_hook = old_free_hook;
  result = malloc (size);
  old_malloc_hook = __malloc_hook;
  old_free_hook = __free_hook;
  printf ("malloc (%u) returns %p\n", (unsigned int) size, result);
  __malloc_hook = my_malloc_hook;
  __free_hook = my_free_hook;
  return result;
}

static void my_free_hook (void *ptr, const void *caller)
{
  __malloc_hook = old_malloc_hook;
  __free_hook = old_free_hook;
  free (ptr);
  old_malloc_hook = __malloc_hook;
  old_free_hook = __free_hook;
  printf ("freed pointer %p\n", ptr);
  __malloc_hook = my_malloc_hook;
  __free_hook = my_free_hook;
}

int main ()
{
  my_init ();
  void* p = malloc(1);
  free(p);
}

输出:

$ ./mem_hook 
malloc (1) returns 0x1ad2010
freed pointer 0x1ad2010

不过编译的时候编译器告警说这些hook指针已经废弃了(还是可以使用)。这里描述了其它调用自定义malloc函数的方法,有兴趣的话可以尝试一下。

jemalloc 的 HOOK 代码 (jemalloc.c),不止覆盖了 *_hook 函数指针,还有与
__lib_* 系列函数绑定匿名关系(这应该也是一种覆盖默认函数的方式)。

#if defined(JEMALLOC_IS_MALLOC) && defined(JEMALLOC_GLIBC_MALLOC_HOOK)
/*
 * glibc provides the RTLD_DEEPBIND flag for dlopen which can make it possible
 * to inconsistently reference libc's malloc(3)-compatible functions
 * (https://bugzilla.mozilla.org/show_bug.cgi?id=493541).
 *
 * These definitions interpose hooks in glibc.  The functions are actually
 * passed an extra argument for the caller return address, which will be
 * ignored.
 */
JEMALLOC_EXPORT void (*__free_hook)(void *ptr) = je_free;
JEMALLOC_EXPORT void *(*__malloc_hook)(size_t size) = je_malloc;
JEMALLOC_EXPORT void *(*__realloc_hook)(void *ptr, size_t size) = je_realloc;
#  ifdef JEMALLOC_GLIBC_MEMALIGN_HOOK
JEMALLOC_EXPORT void *(*__memalign_hook)(size_t alignment, size_t size) =
    je_memalign;
#  endif

#  ifdef CPU_COUNT
/*
 * To enable static linking with glibc, the libc specific malloc interface must
 * be implemented also, so none of glibc's malloc.o functions are added to the
 * link.
 */
#    define ALIAS(je_fn)    __attribute__((alias (#je_fn), used))
/* To force macro expansion of je_ prefix before stringification. */
#    define PREALIAS(je_fn) ALIAS(je_fn)
#    ifdef JEMALLOC_OVERRIDE___LIBC_CALLOC
void *__libc_calloc(size_t n, size_t size) PREALIAS(je_calloc);
#    endif
#    ifdef JEMALLOC_OVERRIDE___LIBC_FREE
void __libc_free(void* ptr) PREALIAS(je_free);
#    endif
#    ifdef JEMALLOC_OVERRIDE___LIBC_MALLOC
void *__libc_malloc(size_t size) PREALIAS(je_malloc);
#    endif
#    ifdef JEMALLOC_OVERRIDE___LIBC_MEMALIGN
void *__libc_memalign(size_t align, size_t s) PREALIAS(je_memalign);
#    endif
#    ifdef JEMALLOC_OVERRIDE___LIBC_REALLOC
void *__libc_realloc(void* ptr, size_t size) PREALIAS(je_realloc);
#    endif
#    ifdef JEMALLOC_OVERRIDE___LIBC_VALLOC
void *__libc_valloc(size_t size) PREALIAS(je_valloc);
#    endif
#    ifdef JEMALLOC_OVERRIDE___POSIX_MEMALIGN
int __posix_memalign(void** r, size_t a, size_t s) PREALIAS(je_posix_memalign);
#    endif
#    undef PREALIAS
#    undef ALIAS
#  endif
#endif

jemalloc

后面,我们以 jemalloc 为例子对进程的内存管理器进行简单的学习。

  • 从 github 下载 jemalloc 的代码。
git clone https://github.com/jemalloc/jemalloc.git
  • 编译安装 jemalloc。
$ ./autogen.sh
$ ./configure --enable-debug
$ make
$ sudo make install_bin install_include install_lib
$ sudo ldconfig # 刷新动态库路径信息
  • 例子
#include <malloc.h>

void func()
{
    void* p = malloc(1);
    free(p);
}

int main(int argc, char ** argv)
{
   func();
}

编译链接到 jemalloc

gcc -g -o mem_test mem_test.c  -ljemalloc  # 链接jemalloc

查看链接到的动态库

$ ldd mem_test
    linux-vdso.so.1 =>  (0x00007ffedc246000)
    libjemalloc.so.2 => /usr/local/lib/libjemalloc.so.2 (0x00007fb32f629000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb32f25f000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fb32ef56000)
    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fb32ebd4000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fb32e9b7000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fb32e7b3000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fb32e59d000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fb32faed000)

加入编译命令没有指定 jemalloc,默认链接到 ptmalloc

gcc -g -o mem_test mem_test.c  # 默认链接ptmalloc

则链接到的动态库为:

$ ldd mem_test
    linux-vdso.so.1 =>  (0x00007ffdb0b09000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f39e832c000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f39e86f6000)

参考文档

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,458评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,030评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,879评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,278评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,296评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,019评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,633评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,541评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,068评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,181评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,318评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,991评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,670评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,183评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,302评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,655评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,327评论 2 358

推荐阅读更多精彩内容