堆入门之glibc分析(一)

本文详细剖析了glibc在Linux环境下如何管理堆内存,重点介绍了malloc函数的执行流程,包括fastbin、smallbin和unsortedbin的工作原理,以及如何避免内存碎片化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

堆入门之glibc分析(一)

0x00

我们都知道glibc是linux的底层api,想要理解堆那么必须对glibc有所理解,本文从glibc的malloc函数来展开分析,帮助理解动态内存是如何被分配的。

0x10 malloc

  1. 函数执行流程 glibc_2.27

    __libc_malloc

    void*  __libc_malloc(size_t bytes)  //bytes:用户申请分配的空间
      __malloc_hook //全局变量   
    //如果需要自定义堆分配函数, 在__libc_malloc中调用以下函数:
    void *(*hook) (size_t, const void *)
     = atomic_forced_read (__malloc_hook);
      if (__builtin_expect (hook != NULL, 0))
        return (*hook)(bytes, RETURN_ADDRESS (0));
    //如果我们没有自定义堆分配函数,默认ptmalloc来完成
    void *weak_variable (*__malloc_hook)
      (size_t __size, const void *) = malloc_hook_ini;  //malloc_hook 被赋值为malloc_hook_ini
    
    static void *_int_malloc (mstate av, size_t bytes) //最后调用_int_malloc
      
    

    __malloc_hook_inin

    static void *
    malloc_hook_ini (size_t sz, const void *caller)
    {
      __malloc_hook = NULL; //被置null,利用点:修改__malloc_hook
      ptmalloc_init (); //函数中 全局变量__malloc_initialize 
      //   >0 初始化完成  =0 正在初始化   <0 未初始化
      return __libc_malloc (sz);
    }
    

    用户申请空间时提交的单位是bytes ptmalloc分配是以chunk为单位,bytes计算成chunk的空间大小由checked_request2size完成

    checked_request2size (bytes, nb);  nb为计算的chunk 
    

    _int_malloc 结构体

    victim = _int_malloc (&main_arena, bytes);
    static struct malloc_state main_arena =
    {
      .mutex = _LIBC_LOCK_INITIALIZER,
      .next = &main_arena,
      .attached_threads = 1
    };
    /*    分析malloc_state 结构体   */
    struct malloc_state  
    {
      /* Serialize access.  */
      __libc_lock_define (, mutex);   //0x4
    
      /* Flags (formerly in max_fast).  */
      int flags;      //0x8
    
      /* Set if the fastbin chunks contain recently inserted free blocks.  */
      /* Note this is a bool but not all targets support atomics on booleans.  */
      int have_fastchunks;    //0xc
    
      /* Fastbins */
      mfastbinptr fastbinsY[NFASTBINS];  //mfastbinptr -->point 8bytes  fasbin的bin链管理头   NFASTBINS = 10   //0x10
    
      /* Base of the topmost chunk -- not otherwise kept in a bin */
      mchunkptr top;   //0x60   +96
    
      /* The remainder from the most recent split of a small request */
      mchunkptr last_remainder;
    
      /* Normal bins packed as described above */
      mchunkptr bins[NBINS * 2 - 2];
    
      /* Bitmap of bins */
      unsigned int binmap[BINMAPSIZE];
    
      /* Linked list */
      struct malloc_state *next;
    
      /* Linked list for free arenas.  Access to this field is serialized
         by free_list_lock in arena.c.  */
      struct malloc_state *next_free;
    
      /* Number of threads attached to this arena.  0 if the arena is on
         the free list.  Access to this field is serialized by
         free_list_lock in arena.c.  */
      INTERNAL_SIZE_T attached_threads;
    
      /* Memory allocated from the system in this arena.  */
      INTERNAL_SIZE_T system_mem;
      INTERNAL_SIZE_T max_system_mem;
    };
    
    /*			malloc_chunk 结构体								*/
    struct malloc_chunk {
    
      INTERNAL_SIZE_T      mchunk_prev_size;  /* Size of previous chunk (if free). 8bytes */
      INTERNAL_SIZE_T      mchunk_size;       /* Size in bytes, including overhead. 8bytes*/
    
      struct malloc_chunk* fd;         /* double links -- used only if free.  8bytes*/   //指向前一个物理相邻的chunk   
      struct malloc_chunk* bk;   /*8bytes*/
    
      /* Only used for large blocks: pointer to next larger size.  */
      struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */     //指向前一个largebin
      struct malloc_chunk* bk_nextsize;
    };
    
    

    分配堆块是基于main_arena来寻址的,首先查找fastbin,其次再找bins

    64位chunk的size必须是16字节对齐 2×SIZE_T 32位chunk的size必须是8字节对齐 2×SIZE_T

    _int_malloc 实现——bins结构

    fastbin

     do							
        {							
          victim = pp;					
          if (victim == NULL)				
    	break;						
        }							
      while ((pp = catomic_compare_and_exchange_val_acq (fb, victim->fd, victim)) 
    	 != victim);					
    
      if ((unsigned long) (nb) <= (unsigned long) (get_max_fast ())) //get_max_fast判断nb是否属于fastbin
        {
          idx = fastbin_index (nb);
          mfastbinptr *fb = &fastbin (av, idx); //fastbin 的bin头
          mchunkptr pp;
          victim = *fb;
    
          if (victim != NULL)
    	{
    	  if (SINGLE_THREAD_P)
    	    *fb = victim->fd; 
    	  else
    	    REMOVE_FB (fb, pp, victim);
    	  if (__glibc_likely (victim != NULL))
    	    {
    	      size_t victim_idx = fastbin_index (chunksize (victim));
    	      if (__builtin_expect (victim_idx != idx, 0))
    		malloc_printerr ("malloc(): memory corruption (fast)");
    	      check_remalloced_chunk (av, victim, nb);
    #if USE_TCACHE
    	      /* While we're here, if we see other chunks of the same size,
    		 stash them in the tcache.  */
    	      size_t tc_idx = csize2tidx (nb);
    	      if (tcache && tc_idx < mp_.tcache_bins)
    		{
    		  mchunkptr tc_victim;
    
    		  /* While bin not empty and tcache not full, copy chunks.  */
    		  while (tcache->counts[tc_idx] < mp_.tcache_count
    			 && (tc_victim = *fb) != NULL)
    		    {
    		      if (SINGLE_THREAD_P)
    			*fb = tc_victim->fd;
    		      else
    			{
    			  REMOVE_FB (fb, pp, tc_victim);
    			  if (__glibc_unlikely (tc_victim == NULL))
    			    break;
    			}
    		      tcache_put (tc_victim, tc_idx);
    		    }
    		}
    #endif
    	      void *p = chunk2mem (victim); //chunk2mem返回chunk中数据区的地址
    	      alloc_perturb (p, bytes);
    	      return p;
    	    }
    	}
        }
    
    1. 堆中fastbin的地址范围0x20-0x80
      1. fastbin的prev_size位为0,prev_inuse为1
    2. chunk为空说明fastbin中没有刚好匹配nb大小的空闲chunk
    3. 精准匹配
    4. 单链表结构
    5. set_max_fast(DEFAULT_MXFAST);在malloc_init_state会调用set_max_fast 设置fastbin chunk的最大值
    6. LIFO 从main_arena开始遍历
    7. 当chunk被释放到unsorted bin ,当前chunk内就有libc上的地址lib.address=libc_on-libc.symbols[“main_arena”]-88

    smallbins

     if (in_smallbin_range (nb))
        {
          idx = smallbin_index (nb);
          bin = bin_at (av, idx);
    
          if ((victim = last (bin)) != bin) //判空  为空则为没有合适的空闲chunk
            {
              if (victim == 0) /* initialization check */
                malloc_consolidate (av); //整理
              else
                {
                  bck = victim->bk;  //获得链表尾的chunk
    	if (__glibc_unlikely (bck->fd != victim))
                    {
                      errstr = "malloc(): smallbin double linked list corrupted";
                      goto errout;
                    }
                  set_inuse_bit_at_offset (victim, nb);
                  bin->bk = bck;
                  bck->fd = bin;
    
                  if (av != &main_arena)
                    victim->size |= NON_MAIN_ARENA;
                  check_malloced_chunk (av, victim, nb);
                  void *p = chunk2mem (victim);
                  alloc_perturb (p, bytes);
                  return p;
                }
            }
        }
    
    
    1.small bin 一共有62个bin链,每个链表中的大小都一直  size = 2 * SIZE *index  (0x20 --0x400)642.FIFO
    3.inuse 如果前一个堆块释放置为0
    4. 2-63为small bin 每个small bin中的size 都相同,相邻的两个链中长度相差2个机器字节
    

    **malloc_consolidate **

      /*
         If this is a large request, consolidate fastbins before continuing.
         While it might look excessive to kill all fastbins before
         even seeing if there is space available, this avoids
         fragmentation problems normally associated with fastbins.
         Also, in practice, programs tend to have runs of either small or
         large requests, but less often mixtures, so consolidation is not
         invoked all that often in most programs. And the programs that
         it is called frequently in otherwise tend to fragment.
       */
    
      else
        {
          idx = largebin_index (nb);
          if (have_fastchunks (av))
            malloc_consolidate (av);
        }
    
    1. malloc_consolidate 会整理fastbin, 将能合并的fastbin合并,一起放入到unsorted bin
    2. 在unsorted bin malloc的最后,会将unsorted bin上的堆块进行整理,并放入到对应的bin中(防止碎片化)

    unsorted bin

    1. unsorted bin 没有排序

    整理知识点

    1. 当不能够print()的时候 可以使用 0xdeadbeef ,gdb中使用search -4 0xdeadbeef 来查看
    2. 当top chunk 大小不能满足当前堆块的大小了,会调用sbrk创建top chunk,之前的不够的块被分配到unsorted bin。 HOUSE OF ORANGE
    3.当fastbin中有chunk可以满足需求,再次malloc的时候不会访问unsorted bin
    4.有任意一个块,被放入了除fastbin的一个bin钟,其中就含有libc上的地址
    
    
本文通过Glibc的内存暴增问题,主要介绍了系统的内存管理问题,具体如下: 目录 1. 问题 2. 基础知识 2.1 X86平台Linux进程内存布局 2.1.1 32位模式下进程内存经典布局 2.1.2 32位模式下进程默认内存布局 2.1.3 64位模式下进程内存布局 2.2 操作系统内存分配的相关函数 2.2.1 Heap操作相关函数 2.2.2 Mmap映射区域操作相关函数 3. 概述 3.1 内存管理般性描述 3.1.1 内存管理的方法 3.1.2 内存管理器的设计目标 3.1.3 常见C内存管理程序 3.2 Ptmalloc内存管理概述 3.2.1 简介 3.2.2 内存管理的设计假设 3.2.3 内存管理数据结构概述 3.2.4 内存分配概述 3.2.5 内存回收概述 3.2.6 配置选项概述 3.2.7 使用注意事项 4. 问题分析及解决 5. 源代码分析 5.1 边界标记法 5.2 分箱式内存管理 5.2.1 Small bins 5.2.2 Large bins 5.2.3 Unsorted bin 5.2.4 Fast bins 5.3 核心结构体分析 5.3.1 malloc_state 5.3.2 Malloc_par 5.3.3 分配区的初始化 5.4 配置选项 5.5 Ptmalloc的初始化 5.5.1 Ptmalloc未初始化时分配/释放内存 5.5.2 ptmalloc_init()函数 5.5.3 ptmalloc_lock_all(),ptmalloc_unlock_all(),ptmalloc_unlock_all2() 5.6 多分配区支持 5.6.1 Heap_info 5.6.2 获取分配区 5.6.3 Arena_get2() 5.6.4 _int_new_arena() 5.6.5 New_heap() 5.6.6 get_free_list()和reused_arena() 5.6.7 grow_heap(),shrink_heap(),delete_heap(),heap_trim() 5.7 内存分配malloc 5.7.1 public_mALLOc() 5.7.2 _int_malloc() 5.8 内存释放free 5.8.1 Public_fREe() 5.8.2 _int_free() 5.8.3 sYSTRIm()和munmap_chunk(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值