`
mqzhuang
  • 浏览: 187285 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

Glibc内存管理--ptmalloc2源代码分析(二十)

阅读更多

5.5.3  ptmalloc_lock_all(),ptmalloc_unlock_all(),ptmalloc_unlock_all2()

/* Magic value for the thread-specific arena pointer when
   malloc_atfork() is in use.  */
#define ATFORK_ARENA_PTR ((Void_t*)-1)

/* The following hooks are used while the `atfork' handling mechanism
   is active. */
static Void_t*
malloc_atfork(size_t sz, const Void_t *caller)
{
  Void_t *vptr = NULL;
  Void_t *victim;

  tsd_getspecific(arena_key, vptr);
  if(vptr == ATFORK_ARENA_PTR) {
    /* We are the only thread that may allocate at all.  */
    if(save_malloc_hook != malloc_check) {
      return _int_malloc(&main_arena, sz);
    } else {
      if(top_check()<0)
        return 0;
      victim = _int_malloc(&main_arena, sz+1);
      return mem2mem_check(victim, sz);
    }
  } else {
    /* Suspend the thread until the `atfork' handlers have completed.
       By that time, the hooks will have been reset as well, so that
       mALLOc() can be used again. */
    (void)mutex_lock(&list_lock);
    (void)mutex_unlock(&list_lock);
    return public_mALLOc(sz);
  }
}

 当父进程中的某个线程使用 fork 的机制创建子线程时,如果进程中的线程需要分配内存,将使用 malloc_atfork() 函数分配内存。 malloc_atfork() 函数首先查看自己的线程私有实例中的分配区指针,如果该指针为 ATFORK_ARENA_PTR ,意味着本线程正在 fork 新线程,并锁住了全局锁 list_lock 和每个分配区,当前只有本线程可以分配内存,如果在 fork 线程前的分配函数不是处于 check 模式,直接调用内部分配函数 _int_malloc() 。否则在分配内存的同时做检查。如果线程私有实例中的指针不是 ATFORK_ARENA_PTR ,意味着当前线程只是常规线程,有另外的线程在 fork 子线程,当前线程只能等待 fork 子线程的线程完成分配,于是等待获得全局锁 list_lock ,如果获得全局锁成功,表示 fork 子线程的线程已经完成 fork 操作,当前线程可以分配内存了,于是是释放全局所 list_lock ,并调用 public_mALLOc() 分配内存。

static void
free_atfork(Void_t* mem, const Void_t *caller)
{
  Void_t *vptr = NULL;
  mstate ar_ptr;
  mchunkptr p;                          /* chunk corresponding to mem */

  if (mem == 0)                              /* free(0) has no effect */
    return;

  p = mem2chunk(mem);         /* do not bother to replicate free_check here */

#if HAVE_MMAP
  if (chunk_is_mmapped(p))                       /* release mmapped memory. */
  {
    munmap_chunk(p);
    return;
  }
#endif

#ifdef ATOMIC_FASTBINS
  ar_ptr = arena_for_chunk(p);
  tsd_getspecific(arena_key, vptr);
  _int_free(ar_ptr, p, vptr == ATFORK_ARENA_PTR);
#else
  ar_ptr = arena_for_chunk(p);
  tsd_getspecific(arena_key, vptr);
  if(vptr != ATFORK_ARENA_PTR)
    (void)mutex_lock(&ar_ptr->mutex);
  _int_free(ar_ptr, p);
  if(vptr != ATFORK_ARENA_PTR)
    (void)mutex_unlock(&ar_ptr->mutex);
#endif
}

 当父进程中的某个线程使用 fork 的机制创建子线程时,如果进程中的线程需要释放内存,将使用 free_atfork() 函数释放内存。 free_atfork() 函数首先通过需 free 的内存块指针获得 chunk 的指针,如果该 chunk 是通过 mmap 分配的,调用 munmap() 释放该 chunk ,否则调用 _int_free() 函数释放内存。在调用 _int_free() 函数前,先根据 chunk 指针获得分配区指针,并读取本线程私用实例的指针,如果开启了 ATOMIC_FASTBINS 优化,这个优化使用了 lock-free 的技术优化 fastbins 中单向链表操作。如果没有开启了 ATOMIC_FASTBINS 优化,并且当前 线程没 有正在 fork 新子线程,则 对分配区加锁,然后调用 _int_free() 函数,然后对分配区解锁。而对于正在 fork 子线程的线程来说,是不需要对分配区加锁的,因为该线程已经对所有的分配区加锁了。

/* Counter for number of times the list is locked by the same thread.  */
static unsigned int atfork_recursive_cntr;

/* The following two functions are registered via thread_atfork() to
   make sure that the mutexes remain in a consistent state in the
   fork()ed version of a thread.  Also adapt the malloc and free hooks
   temporarily, because the `atfork' handler mechanism may use
   malloc/free internally (e.g. in LinuxThreads). */
static void
ptmalloc_lock_all (void)
{
  mstate ar_ptr;

  if(__malloc_initialized < 1)
    return;
  if (mutex_trylock(&list_lock))
    {
      Void_t *my_arena;
      tsd_getspecific(arena_key, my_arena);
      if (my_arena == ATFORK_ARENA_PTR)
        /* This is the same thread which already locks the global list.
           Just bump the counter.  */
        goto out;

      /* This thread has to wait its turn.  */
      (void)mutex_lock(&list_lock);
    }
  for(ar_ptr = &main_arena;;) {
    (void)mutex_lock(&ar_ptr->mutex);
    ar_ptr = ar_ptr->next;
    if(ar_ptr == &main_arena) break;
  }
  save_malloc_hook = __malloc_hook;
  save_free_hook = __free_hook;
  __malloc_hook = malloc_atfork;
  __free_hook = free_atfork;
  /* Only the current thread may perform malloc/free calls now. */
  tsd_getspecific(arena_key, save_arena);
  tsd_setspecific(arena_key, ATFORK_ARENA_PTR);
 out:
  ++atfork_recursive_cntr;
}

父进程中的某个线程使用 fork 的机制创建子线程时,首先调用 ptmalloc_lock_all() 函数暂时对全局锁 list_lock 和所有的分配区加锁,从而保证分配区状态的一致性。 ptmalloc_lock_all() 函数首先检查 ptmalloc 是否已经初始化,如果没有初始化,退出,如果已经初始化,尝试对全局锁 list_lock 加锁,直到获得全局锁 list_lock ,接着对所有的分配区加锁,接着保存原有的分配释放函数,将 malloc_atfork() free_atfork() 函数作为 fork 子线程期间所使用的内存分配释放函数,然后保存当前线程的私有实例中的原有分配区指针,将 ATFORK_ARENA_PTR 存放到当前线程的私有实例中,用于标识当前现在正在 fork 子线程。为了保证父线程 fork 多个子线程工作正常,也就是说当前线程需要 fork 多个子线程,当一个子线程已经创建,当前线程继续创建其它子线程时,发现当前线程已经对 list_lock 和所有分配区加锁,于是对全局变量 atfork_recursive_cntr 1 ,表示递归 fork 子线程的层数,保证父线程在 fork 子线程过程中,调用 ptmalloc_unlock_all() 函数加锁的次数与调用 ptmalloc_lock_all() 函数解锁的次数保持一致,同时也保证保证所有的子线程调用 ptmalloc_unlock_all() 函数加锁的次数与父线程调用 ptmalloc_lock_all() 函数解锁的次数保持一致,防止没有释放锁。

static void
ptmalloc_unlock_all (void)
{
  mstate ar_ptr;

  if(__malloc_initialized < 1)
    return;
  if (--atfork_recursive_cntr != 0)
    return;
  tsd_setspecific(arena_key, save_arena);
  __malloc_hook = save_malloc_hook;
  __free_hook = save_free_hook;
  for(ar_ptr = &main_arena;;) {
    (void)mutex_unlock(&ar_ptr->mutex);
    ar_ptr = ar_ptr->next;
    if(ar_ptr == &main_arena) break;
  }
  (void)mutex_unlock(&list_lock);
}

 当进程的某个线程完成 fork 子线程后,父线程和子线程都调用 ptmall_unlock_all() 函数释放全局锁 list_lock ,释放所有分配区的锁。 ptmall_unlock_all() 函数首先检查 ptmalloc 是否初始化,只有初始化后才能调用该函数,接着将全局变量 atfork_recursive_cntr 1 ,如果 atfork_recursive_cntr 0 ,才继续执行,这保证了递归 fork 子线程只会解锁一次。接着将当前线程的私有实例还原为原来的分配区, __malloc_hook __free_hook 还原为由来的 hook 函数。然后遍历所有分配区,依次解锁每个分配区,最后解锁 list_lock

#ifdef __linux__
/* In NPTL, unlocking a mutex in the child process after a
   fork() is currently unsafe, whereas re-initializing it is safe and
   does not leak resources.  Therefore, a special atfork handler is
   installed for the child. */
static void
ptmalloc_unlock_all2 (void)
{
  mstate ar_ptr;

  if(__malloc_initialized < 1)
    return;
#if defined _LIBC || defined MALLOC_HOOKS
  tsd_setspecific(arena_key, save_arena);
  __malloc_hook = save_malloc_hook;
  __free_hook = save_free_hook;
#endif
#ifdef PER_THREAD
  free_list = NULL;
#endif
  for(ar_ptr = &main_arena;;) {
    mutex_init(&ar_ptr->mutex);
#ifdef PER_THREAD
    if (ar_ptr != save_arena) {
      ar_ptr->next_free = free_list;
      free_list = ar_ptr;
    }
#endif
    ar_ptr = ar_ptr->next;
    if(ar_ptr == &main_arena) break;
  }
  mutex_init(&list_lock);
  atfork_recursive_cntr = 0;
}
#else
#define ptmalloc_unlock_all2 ptmalloc_unlock_all
#endif

 函数ptmalloc_unlock_all2() fork 出的子线程调用,在 Linux 系统中,子线程(进程) unlock 从父线程(进程)中继承的 mutex 不安全,会导致资源泄漏,但重新初始化 mutex 是安全的,所有增加了这个特殊版本用于 Linux 下的 atfork handler ptmalloc_unlock_all2() 函数的处理流程跟 ptmalloc_unlock_all() 函数差不多,使用 mutex_init() 代替了 mutex_unlock() ,如果开启了 PER_THREAD 的优化,将从父线程中继承来的分配区加入到 free_list 中,对于子线程来说,无论全局变量 atfork_recursive_cntr 的值是多少,都将该值设置为 0 ,因为 ptmalloc_unlock_all2() 函数只会被子线程调用一次。

 

 

分享到:
评论

相关推荐

    Glibc内存管理--ptmalloc2源代码分析(三十四)

    《Glibc内存管理--ptmalloc2源代码分析》 Glibc是GNU项目提供的C语言标准库,它在Linux系统中扮演着至关重要的角色。其中,内存管理是Glibc中的核心部分,它负责程序运行时的内存分配与释放,对系统的性能有着深远...

    glibc内存管理ptmalloc源代码分析PDF

    在分析glibc内存管理的ptmalloc源代码之前,我们需要先了解一些基础知识,包括操作系统对内存的分配和管理方法,以及glibc内存分配机制。 内存管理是操作系统的一个核心功能,它负责维护和管理计算机系统中的物理和...

    glibc内存管理ptmalloc源代码分析-高清PDF-pdf版

    《glibc内存管理ptmalloc源代码分析》是一份深入探讨Linux系统中glibc库内存管理机制的专业资料。glibc,全称GNU C Library,是Linux操作系统下广泛使用的C语言标准库,其中ptmalloc是glibc中负责动态内存分配的核心...

    glibc内存管理ptmalloc源代码分析@华庭1

    在深入探讨Glibc内存管理的Ptmalloc源代码之前,我们先来了解一下内存管理的基本概念。内存管理是操作系统和编程语言库中的核心组件,它负责有效地分配和回收内存,以确保程序的高效运行和资源的有效利用。 2.1 X86...

    glibc内存管理ptmalloc源代码分析4.pdf

    通过对ptmalloc源代码的深入分析,可以找到潜在的优化点,如调整分配策略、改进数据结构设计等,以解决这些问题。 #### 13. 使用注意事项 虽然ptmalloc提供了强大的内存管理功能,但在使用过程中也需要注意一些事项...

    glibc内存管理ptmalloc源代码分析.pdf

    glibc内存管理ptmalloc源代码分析

    glibc内存管理ptmalloc源代码分析1

    在深入探讨Glibc内存管理的Ptmalloc源代码之前,我们先来了解一下内存管理的基本概念和Glibc中的Ptmalloc2。内存管理是操作系统和应用程序中的核心部分,它负责为程序分配和释放内存,以确保资源的有效利用和避免...

    glibc内存管理ptmalloc源代码分析

    ### glibc内存管理ptmalloc源代码分析 #### 1. 问题 在开发一款NoSQL系统的过程中,我们遇到一个令人头疼的问题:系统中使用的内存管理模块,在高并发、高负载的场景下,虽然已将内存释放给C运行时库(即glibc),...

    glibc内存管理ptmalloc源代码分析-清晰版.pdf

    ### glibc内存管理ptmalloc源代码分析-清晰版 #### 一、背景介绍与文档概览 本文档针对glibc中的ptmalloc2内存管理模块进行了深入的源代码分析,旨在帮助开发者更好地理解ptmalloc的工作原理及其内部机制。文档...

    Python-用于检测glibc堆ptmalloc的gdbpython库

    `glibc`是GNU项目提供的C标准库,而`ptmalloc`是它的一部分,负责程序的内存分配与管理。`ptmalloc`优化了内存分配效率,但在某些情况下也可能成为安全漏洞的来源。 `GDB`(GNU调试器)是一款强大的调试工具,它...

    内存管理与调试详细剖析

    本文将深入探讨内存管理的基本原理及其调试方法,特别关注于Linux环境下的Glibc内存管理库以及Ptmalloc2的具体实现。 #### 二、基础知识 ##### 2.1 X86平台Linux进程内存布局 **2.1.1 32位模式下进程内存经典布局...

    malloc源码分析glibc库

    通过对glibc库中的ptmalloc源代码进行深入分析,我们不仅了解了其内部实现机制,还能够针对实际应用中的内存管理问题提出有效的解决方案。这对于提升程序性能、减少内存碎片以及提高系统的整体稳定性具有重要意义。...

    pwn学习总结(八)—— 堆(持续更新)

    学习自《glibc内存管理ptmalloc源代码分析》庄明强 著 部分资料参考自互联网 chunk 描述: 当用户通过malloc等函数申请空间时,实际上是从堆中分配内存 目前 Linux 标准发行版中使用的是 glibc 中的堆分配器:...

    堆漏洞的利用技巧1

    《堆漏洞的利用技巧》 堆溢出是计算机安全领域中的一个重要...同时,阅读glibc内存管理的源代码分析对于提高理解和实践能力非常有帮助。通过这些知识的学习,不仅可以增强安全意识,也能提升在CTF竞赛中的攻防能力。

Global site tag (gtag.js) - Google Analytics