ptmalloc源码分析 - ptmalloc的初始化实现(01)

目录

一、ptmalloc的简介

二、入口函数__libc_malloc

1. 初始化的原子模式atomic_forced_read

2. 初始化的调用顺序malloc_hook_ini

3. 初始化的核心逻辑ptmalloc_init 


前几年阅读过华庭的《glibc内存管理ptmalloc源代码分析》文章,并做过一篇笔记:《Linux c 开发 - 内存管理器ptmalloc》

今年打算重点阅读一下glibc里面,ptmalloc部分的具体实现机制。

一、ptmalloc的简介


Linux早期的版本,是由Doug Lea实现的,但是早期的版本有一个问题,就是没办法处理多线程下并发分配和回收的高效和正确性。Wolfram Gloger在Doug Lea的基础上改进使得Glibc的malloc可以支持多线程,ptmalloc。在glibc-2.3.x.中已经集成了ptmalloc2,所以我们平时使用Linux系统的时候,使用到的内存分配器就是ptmalloc。

ptmalloc实现了malloc(),free()以及一组其它的函数,提供系统级别的内存管理。

ptmalloc内存分配器处于用户程序和内核之间,通过malloc/free等函数响应上层使用者的内存分配请求。ptmalloc向操作系统申请内存,然后返回到上层用户层。

为了保持高效的分配,分配器一般都会预先分配一块大于用户请求的内存,并通过某种算法管理这块内存。来满足用户的内存分配要求,用户释放掉的内存也并不是立即就返回给操作系统,相反,分配器会管理这些被释放掉的空闲空间,以应对用户以后的内存分配要求。也就是说,分配器不但要管理已分配的内存块,还需要管理空闲的内存块,当响应用户分配要求时,分配器会首先在空闲空间中寻找一块合适的内存给用户,在空闲空间中找不到的情况下才分配一块新的内存。

通过下载glic2.31源码包,我们就能看到ptmalloc的源码实现。

二、入口启动函数__libc_malloc


ptmalloc的源码在glibc/malloc文件夹下。malloc函数的入口在malloc/malloc.c文件中。

但是我们没法直接找到malloc的函数,这里glibc就是通过别名机制string_alias,从malloc映射到__libc_malloc函数。

所以,当用户调用malloc(xx)分配内存的时候,实际调用了malloc.c文件中的__libc_malloc函数。


strong_alias (__libc_calloc, __calloc) weak_alias (__libc_calloc, calloc)
strong_alias (__libc_free, __free) strong_alias (__libc_free, free)
strong_alias (__libc_malloc, __malloc) strong_alias (__libc_malloc, malloc)
strong_alias (__libc_memalign, __memalign)
weak_alias (__libc_memalign, memalign)
strong_alias (__libc_realloc, __realloc) strong_alias (__libc_realloc, realloc)
strong_alias (__libc_valloc, __valloc) weak_alias (__libc_valloc, valloc)
strong_alias (__libc_pvalloc, __pvalloc) weak_alias (__libc_pvalloc, pvalloc)
strong_alias (__libc_mallinfo, __mallinfo)
weak_alias (__libc_mallinfo, mallinfo)
strong_alias (__libc_mallopt, __mallopt) weak_alias (__libc_mallopt, mallopt)

weak_alias (__malloc_stats, malloc_stats)
weak_alias (__malloc_usable_size, malloc_usable_size)
weak_alias (__malloc_trim, malloc_trim)

1. 初始化的原子模式atomic_forced_read


我们先看一下入口函数__libc_malloc的核心代码

/**
 * malloc() 主函数入口
 */
void *
__libc_malloc (size_t bytes)
{
  mstate ar_ptr;
  void *victim;

  _Static_assert (PTRDIFF_MAX <= SIZE_MAX / 2,
                  "PTRDIFF_MAX is not more than half of SIZE_MAX");

  /**
   * 1. __malloc_hook 值是 malloc_hook_ini ,初始化ptmalloc
   * 2. atomic_forced_read 汇编语句,原子读操作,读取__malloc_hook
   * 3. weak_variable 相当于属性赋值操作attribute,将malloc_hook_ini转到__malloc_hook
   * 4. malloc_hook_ini 中调用的是ptmalloc_init()函数,初始化ptmalloc
   */
  void *(*hook) (size_t, const void *)
    = atomic_forced_read (__malloc_hook);
  if (__builtin_expect (hook != NULL, 0))
    return (*hook)(bytes, RETURN_ADDRESS (0));
  • mstate ar_ptr指向全局内存分配器的指针,说白了就是全局内存分配器状态机。具体的数据结构第二章详细讲解。
  • atomic_forced_read 是汇编语句,用于原子读操作,每次只会读取一次。例如调用malloc_hook_ini初始化只会调用一次
  • __malloc_hook指向malloc_hook_ini,该函数为ptmalloc的初始化函数。主要用于初始化全局状态机+chunk的数据结构
//---------原子读操作
#ifndef atomic_forced_read
# define atomic_forced_read(x) \
  ({ __typeof (x) __x; __asm ("" : "=r" (__x) : "0" (x)); __x; })
#endif

//__malloc_hook指向malloc_hook_ini函数
void *weak_variable (*__malloc_hook)
  (size_t __size, const void *) = malloc_hook_ini; //设置__malloc_hook函数

2. 初始化的调用顺序malloc_hook_ini


先看一下malloc_hook_ini函数:

/**
 * 初始化
 */
static void *
malloc_hook_ini (size_t sz, const void *caller)
{
  __malloc_hook = NULL;
  ptmalloc_init ();
  return __libc_malloc (sz);
}

先将__malloc_hook的值设置为NULL,然后调用ptmalloc_init函数,最后竟然又回调了__libc_malloc函数

在我们第一次调用 malloc 申请堆空间的时候,首先会进入 malloc_hook_ini 函数里面进行对 ptmalloc 的初始化工作,然后再次进入 __libc_malloc 的时候,此时钩子 __malloc_hook 已经被置空了,从而继续执行剩余的代码,即转入 _int_malloc 函数

可以看一下整体的调用逻辑和顺序:

3. 初始化的核心逻辑ptmalloc_init 


ptmalloc初始化过程核心就是初始化:全局内存分配器的状态机

  • 通过__malloc_initialized全局变量,来记录初始化的状态。0=未初始化,1-初始化。如果已经初始化,则直接返回
  • main_arena是全局内存分配器状态机的主线程结构,数据结构:mstate
  • malloc_init_state是核心初始化mstate状态机数据结构
/*
 * ptmalloc_init 初始化过程
 */
static void
ptmalloc_init (void)
{
	/**
	 * 1. 判断是否已经初始化,如果初始化过了,则不再执行;
	 * 2. 如果等于0,则正在初始化,如果等于1,则初始化完成
	 */
  if (__malloc_initialized >= 0)
    return;

  __malloc_initialized = 0;

#ifdef SHARED
  /* In case this libc copy is in a non-default namespace, never use brk.
     Likewise if dlopened from statically linked program.  */
  Dl_info di;
  struct link_map *l;

  if (_dl_open_hook != NULL
      || (_dl_addr (ptmalloc_init, &di, &l, NULL) != 0
          && l->l_ns != LM_ID_BASE))
    __morecore = __failing_morecore;
#endif

  /**
   * 1. main_arena为主分配区域
   * 2. malloc_init_state 初始化主分配区数据
   */
  thread_arena = &main_arena;

  malloc_init_state (&main_arena);
......
......
......
//设置hook值
#if HAVE_MALLOC_INIT_HOOK
  void (*hook) (void) = atomic_forced_read (__malloc_initialize_hook);
  if (hook != NULL)
    (*hook)();
#endif
  /* 初始化完毕,则设置为1 */
  __malloc_initialized = 1;
}

 

下一章,我们核心讲讲malloc_init_state初始化逻辑和状态机的数据结构。

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 代码科技 设计师:Amelia_0503 返回首页