目录

glibc源码分析

目录

Heap

结构

malloc_par

malloc.c中,记录堆管理器的相关参数

struct malloc_par
{
  unsigned long trim_threshold; // 收缩阈值 默认128KB
  /*
  	用于控制main_arena中保留的内存量
  	当释放的chunk为mmap获得的,同时大小大于mmap_threshold,更新mmap_threshold同时将trim_threshold乘2;
  	当释放的chunk大小在 fast bin 范围内,合并完 size 大于 FASTBIN_CONSOLIDATION_THRESHOLD:0x10000,根据该字段缩小 top chunk
  */
  INTERNAL_SIZE_T top_pad;			// 初始化或扩展堆时申请内存是否添加额外pad,默认为0
    								// 调用sbrk函数时在原有请求大小上添加的一个值,是一个填充
  INTERNAL_SIZE_T mmap_threshold;	// mmap分配阈值
  /* 
  	决定sysmalloc用mmap还是sbrk分配内存界限, >则mmap, <则sbrk,
  	若释放的内存通过mmap得到的, 则mmap_threshold与该内存大小取max, 且该值最大不超过DEFAULT_MMAP_THRESHOLD_MAX:0x2000000
  */
  INTERNAL_SIZE_T arena_test; // 最小分配区
  INTERNAL_SIZE_T arena_max;  // 最大分配区
 
  int n_mmaps;			// mmap分配的内存数量, mmap一次+1, munmap一次-1
  int n_mmaps_max;		// 最多能mmap的内存数量
  int max_n_mmaps;		// n_mmaps达到过的最大值

  int no_dyn_threshold;	// 是否开启mmap分配阈值动态调整,默认为0开启
 
  INTERNAL_SIZE_T mmapped_mem;		// 当前 mmap 分配的内存大小总和
  /*INTERNAL_SIZE_T  sbrked_mem;*/
  /*INTERNAL_SIZE_T  max_sbrked_mem;*/
  INTERNAL_SIZE_T max_mmapped_mem;	// mmap 的内存大小总和达到过的最大值
  INTERNAL_SIZE_T max_total_mem;  // 单线程情况下统计进程分配的内存总数
 
  char *sbrk_base; // brk系统调用申请的heap区域的起始地址
};

该结构体类型实例mp_来记录ptmalloc参数

#define DEFAULT_TOP_PAD 131072 // 0x20000
#define DEFAULT_MMAP_MAX       (65536) // 0x10000
#define DEFAULT_MMAP_THRESHOLD_MIN (128 * 1024)
#define DEFAULT_MMAP_THRESHOLD DEFAULT_MMAP_THRESHOLD_MIN // 0x20000
#define DEFAULT_TRIM_THRESHOLD (128 * 1024) // 0x20000

static struct malloc_par mp_ =
{
  .top_pad = DEFAULT_TOP_PAD,
  .n_mmaps_max = DEFAULT_MMAP_MAX,
  .mmap_threshold = DEFAULT_MMAP_THRESHOLD,
  .trim_threshold = DEFAULT_TRIM_THRESHOLD,
#define NARENAS_FROM_NCORES(n) ((n) * (sizeof (long) == 4 ? 2 : 8))
  .arena_test = NARENAS_FROM_NCORES (1)
};

heap_info

  • 位于堆块的开头,记录通过mmap从Memory Mapping Segment处申请到的内存块信息,arena.c
  • 非主线程分配内存使用,因为主分配区可以直接使用sbrk扩展,只有一个堆,非主线程的堆是提前分配好的,当该资源用完时需要重新申请内存空间,不连续所以需要记录不同的堆的链接结构
typedef struct _heap_info
{
  mstate ar_ptr; /* 指向管理该堆块的arena分配区 */
  struct _heap_info *prev; /* 上一个heap_info,单链表形式记录一个线程所有堆结构 */
  size_t size;   /* 该堆块大小 */
  size_t mprotect_size; /* 记录该堆块被mprotected保护的大小*/

  char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK]; // 在SIZE_SZ不正常时填充来对其, 正常pad占用0字节
} heap_info;

malloc

2.23

SIZE_SZ				// sizeof( size_t ) 1字节大小
MALLOC_ALIGN_MASK	// ( 2 * SIZE_SZ ) - 1 = 2 * 8 - 1 = 0x10 - 1
MINSIZE 			// ( MIN_CHUNK_SIZE + MALLOC_ALIGN_MASK ) & ~MALLOC_ALIGN_MASK 向下取整
MIN_CHUNK_SIZE 		// offsetof( struct malloc_chunk, fd_nextsize ) 0x20字节
__libc_malloc
void *__libc_malloc (size_t bytes)
{
  mstate ar_ptr; // 保存指向分配区main_arena的指针
  void *victim; // 保存获得的堆块内存指针: chunk_addr + 0x10

  // 获取 __malloc_hook
  void *(*hook) (size_t, const void *) = atomic_forced_read (__malloc_hook); 
  if (__builtin_expect (hook != NULL, 0)) // 检查__malloc_hook值是否被设置
      return (*hook)(bytes, RETURN_ADDRESS (0)); // 若被设置则调用其指向的函数, 参数为申请的内存大小
  
  arena_get (ar_ptr, bytes); // 若未被设置则获取本线程一个可用分配区thread_arena
  victim = _int_malloc (ar_ptr, bytes); // 申请内存并返回

  if (!victim && ar_ptr != NULL) // 堆块未申请成功且arena的指针不为空
    {
      LIBC_PROBE (memory_malloc_retry, 1, bytes);
      ar_ptr = arena_get_retry (ar_ptr, bytes); // 获取下一个分配区
      victim = _int_malloc (ar_ptr, bytes); // 再次申请分配
    }

  if (ar_ptr != NULL) 
      (void) mutex_unlock (&ar_ptr->mutex); // 操作完成, 解锁分配区使其他线程能够访问
	
    // 只有3种情况
  assert (!victim || // 未申请到内存
          chunk_is_mmapped (mem2chunk (victim)) || // mmap获取的内存
          ar_ptr == arena_for_chunk (mem2chunk (victim))); // 内存从当前线程对应的thread_arena管理的内存中获取
  return victim;
}
_int_malloc

CAS:从内存位置读取值与期望值比较,相等则更新,不相等则失败重新尝试

ABA:一个值在经过多次修改后又回到原始值

static void *_int_malloc (mstate av, size_t bytes)
{
  INTERNAL_SIZE_T nb;               /* 请求的chunk_size */
  unsigned int idx;                 /* 对应bin数组中的index索引 */
  mbinptr bin;                      /* 指向对应bin的指针 */

  mchunkptr victim;                 /* 指向分配的chunk */
  INTERNAL_SIZE_T size;             /* 分配的chunk的size */
  int victim_index;                 /* 分配的chunk的bin的index */

  mchunkptr remainder;              /* 指向分割后剩下的那块chunk */
  unsigned long remainder_size;     /* 分割后剩下那块chunk的size */

  unsigned int block;               /* bit map traverser */
  unsigned int bit;                 /* bit map traverser */
  unsigned int map;                 /* current word of binmap */

  mchunkptr fwd;                    /* 链表操作 */
  mchunkptr bck;                    /* 链表操作 */

  const char *errstr = NULL;

  checked_request2size (bytes, nb); // 检查并将申请内存转换为适合内存分配的块大小

  if (__glibc_unlikely (av == NULL)) // 没有可用arena即arena未初始化
  {
    void *p = sysmalloc (nb, av); // 通过sysmalloc系统调用从mmap获取堆块
    if (p != NULL)
      alloc_perturb (p, bytes); // 用memset清理空间数据
    return p;
  }

  // 在fastbin大小内
  if ((unsigned long)(nb) <= (unsigned long)(get_max_fast())) // global_max_fast:0x80
  {
    idx = fastbin_index(nb); // 获取fastbin中的索引,无任何检查,改global_max_fast可使idx极大
    mfastbinptr *fb = &fastbin(av, idx);
    // #define fastbin(ar_ptr, idx) ((ar_ptr)->fastbinsY[idx]) 即fb指向fastbin中对应的bin的地址
    mchunkptr pp = *fb; // pp指向该对应fastbin中第一个chunk
    do
    {
      victim = pp; // 取出第一个空闲chunk来分配 【victim】
      if (victim == NULL) // fastbin中无chunk,跳出,去申请相应大小的smallbin
        break;
        	// 等价于*fb = victim->fd, 链表头指向该空闲chunk的下一个chunk
    } while ((pp = catomic_compare_and_exchange_val_acq(fb, victim->fd, victim)) != victim);
      // # define catomic_compare_and_exchange_val_acq(mem, newval, oldval) 
      // CAS(Compareand-Swap)原子操作, 避免多线程的ABA问题
      
    // 存在可使用的chunk
    if (victim != 0)
    {
      if (__builtin_expect(fastbin_index(chunksize(victim)) != idx, 0))
          // 检测该chunk的size是否符合该bin的index
      {
        errstr = "malloc(): memory corruption (fast)";
      errout:
        malloc_printerr(check_action, errstr, chunk2mem(victim), av);
        return NULL;
      }
      check_remalloced_chunk(av, victim, nb); // 对chunk标志位检查、是否是malloc_state所表示的分配区中的
        									  // 检查是否已分配,是否重复分配和大小检查
      void *p = chunk2mem(victim); // p 指向 chunk 的 fd 字段地址即data区域
      alloc_perturb(p, bytes);
      return p;
    }
  }

  // 在 small bin 大小范围内
  if (in_smallbin_range(nb)) 
  {
    idx = smallbin_index(nb); // 获取smallbin的下标索引
    bin = bin_at(av, idx);	// 取出对应的bin

    if ((victim = last(bin)) != bin)
        // #define last(b) ((b)->bk) 即 bin->bk != bin说明small bin非空
        // 【victim为取出的表尾第一个chunk】
    {
      if (victim == 0) /* main_arena未初始化时victim为0,表示smallbin还未初始化为双向循环链表 */
        malloc_consolidate(av); // 初始化
      else
      {
        bck = victim->bk; // 取出victim之后的一个chunk检查
        if (__glibc_unlikely(bck->fd != victim)) // 安全检查: 该chunk的fd应指回victim
        {
          errstr = "malloc(): smallbin double linked list corrupted";
          goto errout;
        }
        set_inuse_bit_at_offset(victim, nb); // 设置物理相邻的下一个chunk inuse位, 表示victim被使用
        // 使链表头 bin 与 bck 的bk与fd相互连接, 将 victim 脱离双向循环链表
        bin->bk = bck;
        bck->fd = bin;

        if (av != &main_arena) // 若是非住分配区将标志位清零
          victim->size |= NON_MAIN_ARENA; // 只有申请出的chunk才会置该位, bin中chunk不置位 0x4
          
        check_malloced_chunk(av, victim, nb);
        void *p = chunk2mem(victim); // p 指向 chunk 的 fd 字段地址
        alloc_perturb(p, bytes);
        return p;
      }
    }
  }
  // 否则在 large bin 范围中,先不查找而是对fastbin进行处理
  else
  {
    idx = largebin_index(nb); // 获取largebin中索引
    if (have_fastchunks(av)) // ((M)->flags & FASTCHUNKS_BIT) == 0 即是否已初始化main_arena
      malloc_consolidate(av); // 对fastbin中所有chunk进行遍历、合并,将空闲chunk放入unsorted bin
  }
  // 在 unsorted bin中找,并将相应的bin按照大小放入small bin和large bin中
  for (;;)
  {
    int iters = 0;
    // 取unsorted bin中最后一个chunk victim, 反向遍历unsorted bin直到unsorted bin为空
    while ((victim = unsorted_chunks(av)->bk) != unsorted_chunks(av))
    {
      bck = victim->bk; // victim的前一个chunk
      if (__builtin_expect(victim->size <= 2 * SIZE_SZ, 0) || 
          __builtin_expect(victim->size > av->system_mem, 0))
          // 若小于0x10或大于arena管理的最大内存,报错
        malloc_printerr(check_action, "malloc(): memory corruption",
                        chunk2mem(victim), av);
      size = chunksize(victim); // 获取chunk大小
	  
      // 需要切割情况
      if (in_smallbin_range(nb) &&	// 申请大小在small bin范围
          bck == unsorted_chunks(av) && // unsorted bin中只有一个chunk victim
          victim == av->last_remainder && // victim刚好是last_remainder
          (unsigned long)(size) > (unsigned long)(nb + MINSIZE)) // victim大小 > 申请大小 + 0x20
      {
        remainder_size = size - nb; 
        remainder = chunk_at_offset(victim, nb); // 切出一个remainder_size的chunk
          
        unsorted_chunks(av)->bk = unsorted_chunks(av)->fd = remainder; // 切出的chunk放入unsorted bin
        av->last_remainder = remainder; // 设置新的remainder
        remainder->bk = remainder->fd = unsorted_chunks(av); // 维护双向链表
          
        if (!in_smallbin_range(remainder_size))
        { // 若是large bin则设置两个nextsize
          remainder->fd_nextsize = NULL;
          remainder->bk_nextsize = NULL;
        }

        set_head(victim, nb | PREV_INUSE | (av != &main_arena ? NON_MAIN_ARENA : 0));
        set_head(remainder, remainder_size | PREV_INUSE);
        set_foot(remainder, remainder_size);

        check_malloced_chunk(av, victim, nb);
        void *p = chunk2mem(victim); // 转换为内存指针返回
        alloc_perturb(p, bytes);
        return p;
      }

      /* 不满足切割的条件,将 victim 从 unsorted bin 中取出 */
      unsorted_chunks(av)->bk = bck;
      bck->fd = unsorted_chunks(av);

      if (size == nb) // 若victim大小刚好为用户申请的大小, 直接取出
      {
        set_inuse_bit_at_offset(victim, size);
        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;
      }
      // 到这说明该victim会被放入对应大小的bin链表中,分别获得bck和fwd用于插入
	  // 若 victim 大小属于 small bin
      if (in_smallbin_range(size))
      {
        victim_index = smallbin_index(size);
        bck = bin_at(av, victim_index); // bck赋值为smallbin的链表表头
        fwd = bck->fd;	// fwd指向small bin第一个chunk,victim将插入到bck和fwd中作为第一个chunk
      }
      // 若 victim 大小属于 large bin
      else
      {
        victim_index = largebin_index(size);
        bck = bin_at(av, victim_index); // bck赋值为largebin的链表表头
        fwd = bck->fd;	// fwd指向large bin第一个chunk

        if (fwd != bck) // large bin中非空,即其中有空闲chunk存在
        {
          size |= PREV_INUSE; // 当前chunk的size的inuse置位,便于加快chunk大小比较
          assert((bck->bk->size & NON_MAIN_ARENA) == 0); // 多次判断确保NON_MAIN_ARENA为0,主线程
          if ((unsigned long)(size) < (unsigned long)(bck->bk->size))
              // fd一般指向比自己小的,bck的bk指向的是最小的size,此时victim为最小size
          {
            // 交换
            fwd = bck; // fwd 指向 largebin 链表表头
            bck = bck->bk; // bck 指向largebin中最小size的chunk
			
            // 更新victim的2个nextsize,使得victim插入largebin的末尾
            // fd_nextsize指向最大的chunk
            victim->fd_nextsize = fwd->fd; 
            // bk_nextsize指向最大chunk的bk_nextsize, 即最小size的第一个chunk(因为最小chunk可能多个)
            victim->bk_nextsize = fwd->fd->bk_nextsize; 
            fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim; // 对应反向指
          }
          else // victim不为最小size
          {
            assert((fwd->size & NON_MAIN_ARENA) == 0);
            // 遍历找到第一个小于等于victim size的chunk
            while ((unsigned long)size < fwd->size)
            {
              fwd = fwd->fd_nextsize; // 不断遍历使得fwd->size非严格递减
              assert((fwd->size & NON_MAIN_ARENA) == 0);
            }

            if ((unsigned long)size == (unsigned long)fwd->size)
              fwd = fwd->fd; // 插入第二个位置,则不需要更新fd_nextsize和bk_nextsize
            else
            { // 此时victim > fwd(同样大小第一个),更新将victim插入fwd前
              victim->fd_nextsize = fwd;
              victim->bk_nextsize = fwd->bk_nextsize;
              fwd->bk_nextsize = victim;
              victim->bk_nextsize->fd_nextsize = victim;
            }
            bck = fwd->bk; // bck 更新为找到的fwd的上一个chunk
          }
        }
        else // largebin 为空直接插入更新nextsize
          victim->fd_nextsize = victim->bk_nextsize = victim;
      }
	  // 统一维护fd和bk指针将victim插入bck和fwd中间
      mark_bin(av, victim_index); 
        /* 
       		#define mark_bin(m, i) ((m)->binmap[idx2block(i)] |= idx2bit(i))
        	将对应map里该index对应的标志位置1 
      	*/
      victim->bk = bck;
      victim->fd = fwd;
      fwd->bk = victim;
      bck->fd = victim; // 将当前chunk插入到对应bin中

#define MAX_ITERS 10000
      if (++iters >= MAX_ITERS) // 循环10000次处理unsorted bin中的chunk
        break;
    }

    // 此时unsorted bin 链表已经处理完成,在 large bin 中查找
    if (!in_smallbin_range(nb))
    {
      bin = bin_at(av, idx); // 对应large bin

      if ((victim = first(bin)) != bin && // large bin不为空,victim设为largebin最大cunk
          (unsigned long)(victim->size) >= (unsigned long)(nb)) // 申请大小小于最大chunk
      {
        victim = victim->bk_nextsize; // 最大chunk的bk_nextsize指向最小chunk, 此时victim最小
        // 遍历找到比申请的nb大小小的最近的
        while (((unsigned long)(size = chunksize(victim)) < (unsigned long)(nb)))
          victim = victim->bk_nextsize; // 最小chunk的bk_nextsize的chunk size 不断变大
		// victim有效 且 有至少两个size相同的chunk
        if (victim != last(bin) && victim->size == victim->fd->size)
          victim = victim->fd; // 再往下跳一步避免维护两个nextsize指针
          
		// 分割
        remainder_size = size - nb; // 不一定完全合适,计算remainder_size
        unlink(av, victim, bck, fwd); // 将找到的victim 脱链

        // 比MINSIZE还小则不能切割,将整个victim返回,实际分配的chunk比所需chunk大一些
        if (remainder_size < MINSIZE)
        {
          set_inuse_bit_at_offset(victim, size); // 设置下一个chunk的prev_inuse
          if (av != &main_arena)
            victim->size |= NON_MAIN_ARENA; // 非主线程设置non_main_arena位
        }
		// 否则需要切割
        else
        { // 从victim中切分出所需的chunk,剩余部分作为新chunk加入unsorted bin中
          remainder = chunk_at_offset(victim, nb); // 剩余chunk
          bck = unsorted_chunks(av); // 获取unsorted bin
          fwd = bck->fd; // 指向 unsorted bin 第一个 chunk
          if (__glibc_unlikely(fwd->bk != bck)) // 检测是否指针相互指向
          {
            errstr = "malloc(): corrupted unsorted chunks";
            goto errout;
          }
          // 将remainder其放入unsorted bin中
          remainder->bk = bck;
          remainder->fd = fwd;
          bck->fd = remainder;
          fwd->bk = remainder;
          if (!in_smallbin_range(remainder_size))
          {
            remainder->fd_nextsize = NULL;
            remainder->bk_nextsize = NULL;
          }
          // 设置victim标志
          set_head(victim, nb | PREV_INUSE |
                               (av != &main_arena ? NON_MAIN_ARENA : 0));
          set_head(remainder, remainder_size | PREV_INUSE);
          set_foot(remainder, remainder_size);
        }
        check_malloced_chunk(av, victim, nb);
        void *p = chunk2mem(victim);
        alloc_perturb(p, bytes);
        return p; // 从largebin中取出victim返回
      }
    }

    ++idx; // 正确的idx找不到chunk,加一看下一个索引:比当前binindex大的small/large bin 是否能找到chunk
    bin = bin_at(av, idx);
    block = idx2block(idx); // 将idx/32转移到binmap中的block, 32位一组block
    // #define idx2block(i) ((i) >> BINMAPSHIFT)
    // #define BINMAPSHIFT 5
    map = av->binmap[block]; // binmap用于加速查找bin是否包含空闲chunk
    bit = idx2bit(idx); // 将idx指定的位置1,其他位清零
    // #define idx2bit(i) ((1U << ((i) & ((1U << BINMAPSHIFT) - 1))))

    for (;;)
    {
      // 大于等于该bit位的位都未置1,表示该block没有可用的空闲chunk,需要搜索下一个block
      if (bit > map || bit == 0)
      {
        do
        {     // 加一换到下一组block
          if (++block >= BINMAPSIZE) // 检查是否超过范围
            goto use_top;
        } while ((map = av->binmap[block]) == 0); // 直到找到一个不为0的block

        bin = bin_at(av, (block << BINMAPSHIFT)); // block*32转换为bin的位置
        bit = 1;
      } // 可以确定有可用的chunk

      while ((bit & map) == 0) // 与后为0则表明没有可用chunk
      { // 在一个block中遍历对应的bin直到找到一个bit不为0,退出遍历
        bin = next_bin(bin); // 找下一个bin
        bit <<= 1;
        assert(bit != 0);
      }
	  // 获取bin中的最后一个chunk victim
      victim = last(bin);
      if (victim == bin) // victim与bin链表头相同则说明bin中无空闲chunk,binmap设置有误
      {
        av->binmap[block] = map &= ~bit; // 将binmap的相应bit位清零
        bin = next_bin(bin); // 获取下一个bin
        bit <<= 1; // 将bit移到下一个bit位,即乘以2
      }
      else // bin中有空闲chunk,不为空,基本操作同之前
      {
        size = chunksize(victim); // 获取大小

        /*  We know the first chunk in this bin is big enough to use. */
        assert((unsigned long)(size) >= (unsigned long)(nb));

        remainder_size = size - nb; // 计算切分出所需chunk后剩余部分大小
        unlink(av, victim, bck, fwd); // 从链表取出victim

        // 无法切割
        if (remainder_size < MINSIZE)
        {
          set_inuse_bit_at_offset(victim, size);
          if (av != &main_arena)
            victim->size |= NON_MAIN_ARENA;
        }
        else // 可以切割,将切割出的remainder放入unsored bin
        {
          remainder = chunk_at_offset(victim, nb);
          bck = unsorted_chunks(av);
          fwd = bck->fd;
          if (__glibc_unlikely(fwd->bk != bck))
          {
            errstr = "malloc(): corrupted unsorted chunks 2";
            goto errout;
          }
          remainder->bk = bck;
          remainder->fd = fwd;
          bck->fd = remainder;
          fwd->bk = remainder;
          // 若剩余部分chunk属于smallbin,将分配区的last_remainder chunk设置为remainder
          if (in_smallbin_range(nb))
            av->last_remainder = remainder;
          if (!in_smallbin_range(remainder_size))
          {
            remainder->fd_nextsize = NULL;
            remainder->bk_nextsize = NULL;
          }
          set_head(victim, nb | PREV_INUSE |
                               (av != &main_arena ? NON_MAIN_ARENA : 0));
          set_head(remainder, remainder_size | PREV_INUSE);
          set_foot(remainder, remainder_size);
        }
        check_malloced_chunk(av, victim, nb);
        void *p = chunk2mem(victim);
        alloc_perturb(p, bytes);
        return p;
      }
    }

  // 均找不到可用chunk, 则切top chunk
  use_top:
    victim = av->top;
    size = chunksize(victim);

    if ((unsigned long)(size) >= (unsigned long)(nb + MINSIZE))
    {
      remainder_size = size - nb; // 切割出remainder
      remainder = chunk_at_offset(victim, nb);
      av->top = remainder; // remainder成为新的top chunk
      // 设置标志位
      set_head(victim, nb | PREV_INUSE | (av != &main_arena ? NON_MAIN_ARENA : 0));
      set_head(remainder, remainder_size | PREV_INUSE);

      check_malloced_chunk(av, victim, nb);
      void *p = chunk2mem(victim);
      alloc_perturb(p, bytes);
      return p; // 返回top chunk切割出的victim
    }
    else if (have_fastchunks(av)) // 看是否有fastchunk
    {
      malloc_consolidate(av); // 进一步处理fastchunk放入unsorted bin
      if (in_smallbin_range(nb)) // 再重新赋值idx找
        idx = smallbin_index(nb);
      else
        idx = largebin_index(nb);
    }
    else
    {
      void *p = sysmalloc(nb, av); // 系统调用sysmalloc向操作系统申请内存返回堆块
      if (p != NULL)
        alloc_perturb(p, bytes);
      return p;
    }
  } // 分配不到则死循环,在其他线程中找,要么报错要么找到并返回
} 
checked_request2size
// 可用于prev_size复用
#define checked_request2size(req, sz)
if (REQUEST_OUT_OF_RANGE (req)) { // 看是否超过范围
  __set_errno (ENOMEM);
  return 0;
}
(sz) = request2size (req); // 将申请内存转换为适合内存分配的块大小

#define request2size(req) (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE)  ?
MINSIZE : ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
      // = bytes + sizeof(size_t)*2 + sizeof(size_t) - 1 关于 0x10 向下取整
  • bytes变化堆块大小使得与下一个堆块的prev_size重合,则8+7向下取整0,最终只申请prev_size+size+bytes
  • 若超过下一个堆块的prev_size,则9+7向下取整0x10,最终申请prev_size+size+bytes+prev_size+size
图片无法加载
get_max_fast
#define get_max_fast() global_max_fast

#define set_max_fast(s) 
	global_max_fast = (((s) == 0) ? SMALLBIN_WIDTH : ((s + SIZE_SZ) & ~MALLOC_ALIGN_MASK))

set_max_fast(DEFAULT_MXFAST); // malloc_init_state函数中

#define DEFAULT_MXFAST (64 * SIZE_SZ / 4) // 64*8/4=128=0x80
fastbin_index
#define fastbin_index(sz) ((((unsigned int)(sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2)
// 64位下 申请size右移4位再减2,最小的size为0x20,则0x20/16-2=0索引
chunk_size
#define chunksize(p) ((p)->size & ~(SIZE_BITS)) // 去除3个标志位后得到chunk的size
#define SIZE_BITS (PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
chunk2mem
#define chunk2mem(p) ((void *)((char *)(p) + 2 * SIZE_SZ))
// 将指向 prev_size 的指针偏移2个机器字长后指向 fd
in_smallbin_range
#define in_smallbin_range(sz) ((unsigned long)(sz) < (unsigned long)MIN_LARGE_SIZE)
// 小于 largebin 最小的 size 
#define MIN_LARGE_SIZE ((NSMALLBINS - SMALLBIN_CORRECTION) * SMALLBIN_WIDTH)
// (64 - 0)*(2 * 8) = 0x400 = 1024
#define NSMALLBINS 64
#define SMALLBIN_CORRECTION (MALLOC_ALIGNMENT > 2 * SIZE_SZ)
#define SMALLBIN_WIDTH MALLOC_ALIGNMENT
#define MALLOC_ALIGNMENT (2 * SIZE_SZ)
malloc_consolidate

遍历fastbin,合并并放入unsorted bin中

static void malloc_consolidate(mstate av)
{
  mfastbinptr *fb;
  mfastbinptr *maxfb;
  mchunkptr p;
  mchunkptr nextp;
  mchunkptr unsorted_bin;
  mchunkptr first_unsorted;
    
  mchunkptr nextchunk;
  INTERNAL_SIZE_T size;
  INTERNAL_SIZE_T nextsize;
  INTERNAL_SIZE_T prevsize;
  int nextinuse;
  mchunkptr bck;
  mchunkptr fwd;

  if (get_max_fast() != 0) // 已初始化
  {
    clear_fastchunks(av); // 清除fastchunk的标志位

    unsorted_bin = unsorted_chunks(av);
    // #define unsorted_chunks(M) (bin_at(M, 1)) 获取unsorted_bin

    maxfb = &fastbin(av, NFASTBINS - 1); // 指向最大size的fastbin地址
      // #define NFASTBINS (fastbin_index(request2size(MAX_FAST_SIZE)) + 1)
    fb = &fastbin(av, 0); // 指向最小size的fastbin地址
    do
    {
      p = atomic_exchange_acq(fb, 0); // 将fastbin置为0,而p指向fastbin第一个chunk
      if (p != 0)
      {
        do
        {
          check_inuse_chunk(av, p); // 具体是检查下一个相邻chunk的prev_size位
          nextp = p->fd; // p的下一个chunk

          size = p->size & ~(PREV_INUSE | NON_MAIN_ARENA); // p指向chunk的size
          nextchunk = chunk_at_offset(p, size); // 下一个chunk
            // #define chunk_at_offset(p, s) ((mchunkptr)(((char *)(p)) + (s)))
          nextsize = chunksize(nextchunk); // 下一个chunk的size

          if (!prev_inuse(p)) // 即p相邻上一个chunk空闲: 在small/large/unsorted bin中
          {
            // 前向合并
            prevsize = p->prev_size;
            size += prevsize;	// size变为前一个chunk大小加上当前size大小
            p = chunk_at_offset(p, -((long)prevsize)); // p此时指向相邻上一个chunk
            unlink(av, p, bck, fwd); // 将p从双向链表中取出
          }
		  // 下一个 chunk 不是 top chunk
          if (nextchunk != av->top) 
          {
            nextinuse = inuse_bit_at_offset(nextchunk, nextsize); 
            if (!nextinuse)// 下一个chunk+nextsize偏移的prev_inuse为0,表示下一个chunk空闲
            {
              // 后向合并
              size += nextsize;
              unlink(av, nextchunk, bck, fwd); // 将下一个chunk也脱链
            }
            else
              clear_inuse_bit_at_offset(nextchunk, 0); // 下一个chunk的prev_inuse设为0,即当前chunk此时空闲

            first_unsorted = unsorted_bin->fd; // 第一个unsorted bin
            unsorted_bin->fd = p;
            first_unsorted->bk = p; // 在unsorted bin链表头加入p,此处改unsorted bin原先chunk的指针
			
            // 在 large bin 中将两个nextsize指针置空
            if (!in_smallbin_range(size))
            {
              p->fd_nextsize = NULL;
              p->bk_nextsize = NULL;
            }

            set_head(p, size | PREV_INUSE); // 设置p的size
            p->bk = unsorted_bin;
            p->fd = first_unsorted; // 此处将p的bk和fd指针更改
            set_foot(p, size); // 设置下一个chunk的prev_size为size
          }
          else
          { // 下一个chunk是top chunk,则将当前chunk合并入top chunk
            size += nextsize;
            set_head(p, size | PREV_INUSE);
            av->top = p;
          }

        } while ((p = nextp) != 0); // 内部循环某个fastbin的链表中的chunk,合并+放入unsorted bin中
      }
    } while (fb++ != maxfb); // 循环整个fastbin 
  }
  else // global_max_fast为0则初始化
  {
    malloc_init_state(av);
    check_malloc_state(av);
  }
}
malloc_init_state
static void malloc_init_state(mstate av)
{
  int i;
  mbinptr bin;
    
  for (i = 1; i < NBINS; ++i)
  {
    bin = bin_at(av, i);
    bin->fd = bin->bk = bin; // 初始化创建循环链表
  }

#if MORECORE_CONTIGUOUS
  if (av != &main_arena)
#endif
    set_noncontiguous(av);
  if (av == &main_arena) // 若为主线程
    set_max_fast(DEFAULT_MXFAST); // 设置global_max_fast为0x80,即fastbin最大size
  av->flags |= FASTCHUNKS_BIT; // FASTCHUNKS_BIT = 1U 设置标志位

  av->top = initial_top(av); // 初始化分配区的top chunk
}
#define unlink(AV, P, BK, FD)
  {                                                                                                     
    FD = P->fd; // p在bin中下一个chunk
    BK = P->bk; // p在bin中上一个chunk
    if (__builtin_expect(FD->bk != P || BK->fd != P, 0))
        // 检查fd和bk应该对应相互指向对方
      malloc_printerr(check_action, "corrupted double-linked list", P, AV);
    else
    {
      FD->bk = BK;
      BK->fd = FD; // 将中间的p chunk脱离
      if (!in_smallbin_range(P->size) && __builtin_expect(P->fd_nextsize != NULL, 0))
      { // 若 P 属于 large bin 且 fd->nextsize 不为空
        if (__builtin_expect(P->fd_nextsize->bk_nextsize != P, 0) || 
            __builtin_expect(P->bk_nextsize->fd_nextsize != P, 0))
          // 判断 fd_nextsize 和 bk_nextsize 是否合法: 相互对应指向对方
          malloc_printerr(check_action,"corrupted double-linked list (not small)", P, AV);
          
        // 下一个chunk的fd_nextsize为空
        if (FD->fd_nextsize == NULL)
        {
          if (P->fd_nextsize == P) // p的fd_nextsize指向自己: p和FD size相等
            FD->fd_nextsize = FD->bk_nextsize = FD; // 此时FD替代p将两个nextsize指针指向自己
          else // p的fd_nextsize不指向自己: p和FD size相等
          { // 此时FD替代p将两个nextsize指针指向p原本指向的chunk
            FD->fd_nextsize = P->fd_nextsize;
            FD->bk_nextsize = P->bk_nextsize;
            P->fd_nextsize->bk_nextsize = FD;
            P->bk_nextsize->fd_nextsize = FD; // 相互指
          }
        }
        // 下一个chunk的fd_nextsize不为空: p和FD size不相等
        else
        {
          P->fd_nextsize->bk_nextsize = P->bk_nextsize;
          P->bk_nextsize->fd_nextsize = P->fd_nextsize; 
          // FD的两个nextsize替换为p的两个nextsize
        }
      }
    }
  }
alloc_perturb
static void alloc_perturb(char *p, size_t n)
{
  if (__glibc_unlikely(perturb_byte)) // 若perturb_byte不为0
    memset(p, perturb_byte ^ 0xff, n); // 设置内存为该值的最低1字节,可能导致程序崩溃
}

2.27

__libc_malloc

主要讲解变化

void *__libc_malloc(size_t bytes)
{
  // 增加
#if USE_TCACHE
  size_t tbytes;
  checked_request2size(bytes, tbytes);
  size_t tc_idx = csize2tidx(tbytes); // 获取tcache的索引

  MAYBE_INIT_TCACHE(); // tcache初始化
  /*
  	#define MAYBE_INIT_TCACHE()
  		if (__glibc_unlikely(tcache == NULL))
    		tcache_init();
  */

  DIAG_PUSH_NEEDS_COMMENT;
  // mp_.tcache_bins = TCACHE_MAX_BINS = 64
  if (tc_idx < mp_.tcache_bins && tcache && tcache->entries[tc_idx] != NULL)
  { // 在tcache范围内,且entries指向不为空
    return tcache_get(tc_idx); // 获取tcache并返回
  }
  DIAG_POP_NEEDS_COMMENT;
#endif

  if (SINGLE_THREAD_P)
  {
    victim = _int_malloc(&main_arena, bytes);
    assert(!victim || chunk_is_mmapped(mem2chunk(victim)) ||
           &main_arena == arena_for_chunk(mem2chunk(victim)));
    return victim;
  }

// 变化
/*
  if (ar_ptr != NULL) 
    (void) mutex_unlock (&ar_ptr->mutex)
*/
  if (ar_ptr != NULL)
    __libc_lock_unlock(ar_ptr->mutex); 
}
_int_malloc
// 增加 fastbin中
#if USE_TCACHE
        size_t tc_idx = csize2tidx(nb);
        if (tcache && tc_idx < mp_.tcache_bins) // 若在tcache范围内
        {
          mchunkptr tc_victim;

          while (tcache->counts[tc_idx] < mp_.tcache_count && (tc_victim = *fb) != NULL)
          { // 小于mp_.tcache_count=7即tcache没装满:遍历将fastbin中chunk加到tcache中直到满7个或fastbin为空
            if (SINGLE_THREAD_P) // 单线程
              *fb = tc_victim->fd; // 取出一个空闲chunk
            else
            {
              REMOVE_FB(fb, pp, tc_victim); // 取出一个空闲chunk
              if (__glibc_unlikely(tc_victim == NULL))
                break;
            }
            tcache_put(tc_victim, tc_idx); // 把fastbin中拿出的chunk加入到tcache链表中
          }
        }
#endif

// 增加 small bin中
#if USE_TCACHE
      size_t tc_idx = csize2tidx(nb);
      if (tcache && tc_idx < mp_.tcache_bins)
      {
        mchunkptr tc_victim;
        while (tcache->counts[tc_idx] < mp_.tcache_count && (tc_victim = last(bin)) != bin)
        {
          if (tc_victim != 0)
          {
            bck = tc_victim->bk;
            set_inuse_bit_at_offset(tc_victim, nb); // 设置tc_victim物理相邻的下一个chunk的prev_inuse位
            if (av != &main_arena)
              set_non_main_arena(tc_victim);
            bin->bk = bck;
            bck->fd = bin;

            tcache_put(tc_victim, tc_idx);// 把small bin中拿出的chunk加入到tcache中
          }
        }
      }
#endif
REMOVE_FB
#define REMOVE_FB(fb, victim, pp) // 封装成一个宏,CAS操作
// 从刚刚得到的空闲chunk链表指针中取出第一个空闲的chunk(victim)
// 并将链表头设置为该空闲chunk的下一个chunk(victim->fd)
  do                              
  {                               
    victim = pp;                  
    if (victim == NULL)           
      break;                      
  } while ((pp = catomic_compare_and_exchange_val_acq(fb, victim->fd, victim)) != victim);
tcache_init
static void tcache_init(void)
{
  mstate ar_ptr;
  void *victim = 0;
  const size_t bytes = sizeof(tcache_perthread_struct);

  if (tcache_shutting_down)
    return;

  arena_get(ar_ptr, bytes);
  victim = _int_malloc(ar_ptr, bytes); // 通过_int_malloc获取内存chunk
  if (!victim && ar_ptr != NULL) 
  {
    ar_ptr = arena_get_retry(ar_ptr, bytes); // 获取下一个分配区
    victim = _int_malloc(ar_ptr, bytes); // 再次malloc
  }

  if (ar_ptr != NULL)
    __libc_lock_unlock(ar_ptr->mutex);
  // 申请成功
  if (victim)
  {
    tcache = (tcache_perthread_struct *)victim; // 将victim给tcache,每个线程都有一个tcache缓解竞争
    memset(tcache, 0, sizeof(tcache_perthread_struct)); // 清空数据
  }
}
tcache_get
static __always_inline void *tcache_get(size_t tc_idx)
{
  tcache_entry *e = tcache->entries[tc_idx]; // 从entries中取出入口指针
  assert(tc_idx < TCACHE_MAX_BINS);
  assert(tcache->entries[tc_idx] > 0);
  tcache->entries[tc_idx] = e->next; // 更新entries,指向其下一个chunk
  --(tcache->counts[tc_idx]); // counts减少一个
  return (void *)e;
}
tcache_put
static __always_inline void tcache_put(mchunkptr chunk, size_t tc_idx)
{ // 将其放入tcache中
  tcache_entry *e = (tcache_entry *)chunk2mem(chunk);
  assert(tc_idx < TCACHE_MAX_BINS);
  e->next = tcache->entries[tc_idx];
  tcache->entries[tc_idx] = e;
  ++(tcache->counts[tc_idx]);
}
#define unlink(AV, P, BK, FD) {
// 增加 开头加了一个判断:P的 chunk 大小需要等于下一个 chunk 的 prev_size
    if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))
      malloc_printerr ("corrupted size vs. prev_size");			      

sysmalloc

2.23

sysmalloc
static void *sysmalloc(INTERNAL_SIZE_T nb, mstate av) // 需要申请的大小need_bytes + mainarena
{
  mchunkptr old_top;        /* av->top的原始值 */
  INTERNAL_SIZE_T old_size; /* av->top大小 */
  char *old_end;            /* av->top结束地址 */

  long size; /* 给MORECORE或mmap调用的参数 */
  char *brk; /* MORECORE返回值 */

  long correction; /* 给第二个MORECORE调用的参数 */
  char *snd_brk;   /* MORECORE第二个返回值 */

  INTERNAL_SIZE_T front_misalign; /* 新空间前的不可用字节 */
  INTERNAL_SIZE_T end_misalign;   /* partial page left at end of new space */
  char *aligned_brk;              /* aligned offset into brk */

  mchunkptr p;                  /* the allocated/returned chunk */
  mchunkptr remainder;          /* remainder from allocation */
  unsigned long remainder_size; /* its size */

  size_t pagesize = GLRO(dl_pagesize);
  bool tried_mmap = false; // 标记是否尝试过mmap
  
  if (av == NULL || // 无arena,则也没有top chunk
     ((unsigned long)(nb) >= (unsigned long)(mp_.mmap_threshold) && // 大于mmap阈值则使用mmap
     (mp_.n_mmaps < mp_.n_mmaps_max))) // 且mmap的次数小于最大的nmap的次数
  {
    char *mm; /* mmap调用的返回值 */
  // 走mmap调用
  try_mmap:
/* 
	mmap直接分配内存,不需要添加到管理free bin的链表中,所以不存在chunk前后关系
	当chunk被使用时无法借用后一个chunk的prev_size字段,需要将prev_size的SIZE_SZ加上进行内存向上取整对齐
	nb 向上对齐为 size
*/
    if (MALLOC_ALIGNMENT == 2 * SIZE_SZ)
      size = ALIGN_UP(nb + SIZE_SZ, pagesize);
    else
      size = ALIGN_UP(nb + SIZE_SZ + MALLOC_ALIGN_MASK, pagesize);
    tried_mmap = true;

    if ((unsigned long)(size) > (unsigned long)(nb))
    { 
      // 若size>nb 调用MMAP
      mm = (char *)(MMAP(0, size, PROT_READ | PROT_WRITE, 0));

      if (mm != MAP_FAILED) // MMAP成功
      {
        if (MALLOC_ALIGNMENT == 2 * SIZE_SZ)
        { // 若对齐,进行检查,不可用字节设为0
          assert(((INTERNAL_SIZE_T)chunk2mem(mm) & MALLOC_ALIGN_MASK) == 0);
          front_misalign = 0;
        }
        else // 未对齐获取不可用字节大小
          front_misalign = (INTERNAL_SIZE_T)chunk2mem(mm) & MALLOC_ALIGN_MASK;
        if (front_misalign > 0)
        {
          correction = MALLOC_ALIGNMENT - front_misalign; // 进行纠正
          p = (mchunkptr)(mm + correction);
          p->prev_size = correction;
          set_head(p, (size - correction) | IS_MMAPPED);
        }
        else
        {
          p = (mchunkptr)mm;
          set_head(p, size | IS_MMAPPED); // 设置size中标志
        }

        int new = atomic_exchange_and_add(&mp_.n_mmaps, 1) + 1; // mmap的数量加1
        atomic_max(&mp_.max_n_mmaps, new); // 取最大更新max_n_mmaps

        unsigned long sum;
        sum = atomic_exchange_and_add(&mp_.mmapped_mem, size) + size; // mmap的内存大小加上size
        atomic_max(&mp_.max_mmapped_mem, sum); // 取最大值更新max_mmapped_mem

        check_chunk(av, p);
        return chunk2mem(p); // 返回内存
      }
    }
  }

  if (av == NULL)
    return 0; // mmap失败且无arena,系统调用失败

  // 有arena则有top chunk,需要扩展top chunk,切割内存返回
  old_top = av->top; // 获取原top chunk
  old_size = chunksize(old_top); // 获取原top chunk 大小
  old_end = (char *)(chunk_at_offset(old_top, old_size)); // 获取原top chunk结束地址

  brk = snd_brk = (char *)(MORECORE_FAILURE); // 初始化brk和snd_brk为0,#define MORECORE_FAILURE 0

  assert((old_top == initial_top(av) && old_size == 0) ||
         // 1. arena第一次结构体初始化时,top chunk未被分配,此时top chunk指向自己, size为0
         ((unsigned long)(old_size) >= MINSIZE &&
         prev_inuse(old_top) &&
         ((unsigned long)old_end & (pagesize - 1)) == 0));
    	 // 2. 非第一次,top chunk已有,检查size大于等于0x20,prev_inuse置位,且与页面对齐

  assert((unsigned long)(old_size) < (unsigned long)(nb + MINSIZE));
  // top chunk 不够用:原top chunk 大小 < 申请大小+0x20 

  // 非主线程
  if (av != &main_arena)
  {
    heap_info *old_heap, *heap;
    size_t old_heap_size;

    old_heap = heap_for_ptr(old_top); // 获取原始heap堆段的起始地址,heap_info在堆块开头
    old_heap_size = old_heap->size;	// 获取原始堆大小
    // 尝试扩展堆块
    if ((long)(MINSIZE + nb - old_size) > 0 && grow_heap(old_heap, MINSIZE + nb - old_size) == 0)
    {// 堆块扩展成功
      // 更新
      av->system_mem += old_heap->size - old_heap_size;
      arena_mem += old_heap->size - old_heap_size;
      // 新堆顶块大小: old_heap + old_heap->size - old_top
      set_head(old_top, (((char *)old_heap + old_heap->size) - (char *)old_top) | PREV_INUSE);
    } // 或创建新堆
    else if ((heap = new_heap(nb + (MINSIZE + sizeof(*heap)), mp_.top_pad)))
    {
      // 创建新堆后更新数据
      heap->ar_ptr = av; // 更新 arena 管理分配区
      heap->prev = old_heap; 
      av->system_mem += heap->size;
      arena_mem += heap->size;
        
	  // 更新新的top chunk位置,之前的top chunk作废
      top(av) = chunk_at_offset(heap, sizeof(*heap));
      // top chunk大小: heap大小减去开头heap结构体大小
      set_head(top(av), (heap->size - sizeof(*heap)) | PREV_INUSE);

      /* 释放旧的top chunk,  */
      old_size = (old_size - MINSIZE) & ~MALLOC_ALIGN_MASK; // 预留两个chunk和align来作为标记,防止错误
      set_head(chunk_at_offset(old_top, old_size + 2 * SIZE_SZ), 0 | PREV_INUSE);
      if (old_size >= MINSIZE)
      {
        set_head(chunk_at_offset(old_top, old_size), (2 * SIZE_SZ) | PREV_INUSE);
        set_foot(chunk_at_offset(old_top, old_size), (2 * SIZE_SZ));
        set_head(old_top, old_size | PREV_INUSE | NON_MAIN_ARENA);
        _int_free(av, old_top, 1);
      }
      else
      { // 不够大小直接设置标记,不释放了
        set_head(old_top, (old_size + 2 * SIZE_SZ) | PREV_INUSE);
        set_foot(old_top, (old_size + 2 * SIZE_SZ));
      }
    }
    else if (!tried_mmap)
      goto try_mmap; // 还是不行,只能调mmap
  }
  else// 主线程:main_arena
  {
    size = nb + mp_.top_pad + MINSIZE; // 请求足够的空间来扩展top chunk,nb需要申请走
      
    if (contiguous(av)) // 若top chunk 连续
      size -= old_size; // 实际涨一点大小即可,不需要再申请nb那么大的size,nb比top chunk还大
    size = ALIGN_UP(size, pagesize);

    if (size > 0)
    {
      brk = (char *)(MORECORE(size)); // 通过brk来扩展
        // 系统调用的__brk是将最高地址指针向高地址推,参数为最终地址,返回最终地址
        // glibc的sbrk参数是大小,将扩展多少大小向高地址,返回原来未扩展时的顶
      LIBC_PROBE(memory_sbrk_more, 2, brk, size);
    }

    if (brk != (char *)(MORECORE_FAILURE))
    { // brk调用成功,将top chunk 扩展了size大小
      void (*hook)(void) = atomic_forced_read(__after_morecore_hook);
      if (__builtin_expect(hook != NULL, 0))
        (*hook)(); // after_morecore_hook不为空则执行
    }
    else
    { // 若brk调用失败,说明不能再维护连续内存
      if (contiguous(av)) // 由于之前连续减去过old_size,此处要加回来
        size = ALIGN_UP(size + old_size, pagesize);

      if ((unsigned long)(size) < (unsigned long)(MMAP_AS_MORECORE_SIZE))
        // #define MMAP_AS_MORECORE_SIZE (1024 * 1024) 小于则使用mmap最小的size
        size = MMAP_AS_MORECORE_SIZE;

      if ((unsigned long)(size) > (unsigned long)(nb))
      { // 若size可以包含申请的大小
        char *mbrk = (char *)(MMAP(0, size, PROT_READ | PROT_WRITE, 0)); // mmap系统调用

        if (mbrk != MAP_FAILED)
        { // mmap未失败
          brk = mbrk; // 新mmap的内存起始位置
          snd_brk = brk + size; // 新的mmap的内存结束位置
          set_noncontiguous(av); // 设置arena为不连续
        }
      }
    }
      
	// 若brk不再是0,已经获取了内存地址
    if (brk != (char *)(MORECORE_FAILURE))
    {
      if (mp_.sbrk_base == 0)
        mp_.sbrk_base = brk; // 更新 sbrk_base
      av->system_mem += size;

      // 【1】: topchunk通过brk向下扩展了一小段,nb申请后也够用
      if (brk == old_end && snd_brk == (char *)(MORECORE_FAILURE))
        set_head(old_top, (size + old_size) | PREV_INUSE);
      // 【2】: arena连续则不是mmap出来的,old_size排除了未初始化情况,越brk却越小的情况
      else if (contiguous(av) && old_size && brk < old_end)
      { // 崩溃
        malloc_printerr(3, "break adjusted to free malloc space", brk, av);
      }
      else
      {
        front_misalign = 0;
        end_misalign = 0;
        correction = 0;
        aligned_brk = brk;
		// 【3】: 新分配的内存地址大于原来top chunk结束地址,不连续但分配区连续标志位置位 
        if (contiguous(av)) // 说明是其他线程调用了brk在堆上分配了内存
        {
          if (old_size)
            av->system_mem += brk - old_end; // 其他线程分配的内存一并计入
	      // 对齐操作
          front_misalign = (INTERNAL_SIZE_T)chunk2mem(brk) & MALLOC_ALIGN_MASK;
          // 假设两个top chunk 1,2, 计算1和2中间不对齐的部分
          if (front_misalign > 0)
          {
            // 获取校正,即2开头需要加上该校正,得到地址才和MALLOC_ALIGN_MASK对齐
            correction = MALLOC_ALIGNMENT - front_misalign; 
            aligned_brk += correction; // 不向上对齐,向下对齐,此时aligned_brk指向top chunk
          }

          correction += old_size; // top chunk的大小
		  // 2结尾也同样对齐,correction为 top chunk结束位置到新分配内存空间的大小
          end_misalign = (INTERNAL_SIZE_T)(brk + size + correction);
          correction += (ALIGN_UP(end_misalign, pagesize)) - end_misalign;

          assert(correction >= 0);
          snd_brk = (char *)(MORECORE(correction)); // 再次调用brk补充correction的内存
            
          if (snd_brk == (char *)(MORECORE_FAILURE)) // 失败
          {
            correction = 0;
            snd_brk = (char *)(MORECORE(0)); // 重置为原来分配内存的brk结束地址
          }
          else
          {
            void (*hook)(void) = atomic_forced_read(__after_morecore_hook);
            if (__builtin_expect(hook != NULL, 0))
              (*hook)(); // 提供hook点
          }
        }
        // 【4】: 新分配的内存地址大于原来top chunk结束地址,均不连续
        else
        {
          if (MALLOC_ALIGNMENT == 2 * SIZE_SZ)
            assert(((unsigned long)chunk2mem(brk) & MALLOC_ALIGN_MASK) == 0);
          else
          {
            front_misalign = (INTERNAL_SIZE_T)chunk2mem(brk) & MALLOC_ALIGN_MASK;
            if (front_misalign > 0)
            {
              aligned_brk += MALLOC_ALIGNMENT - front_misalign;
            }
          }
          if (snd_brk == (char *)(MORECORE_FAILURE))
          {
            snd_brk = (char *)(MORECORE(0)); // 同样为了对齐进行brk调用申请来补充新top chunk
          }
        }

        if (snd_brk != (char *)(MORECORE_FAILURE)) // 表示申请成功
        { // 需要对不连续的原先top chunk进行处理
          av->top = (mchunkptr)aligned_brk; // 上一个不连续的top chunk
          set_head(av->top, (snd_brk - aligned_brk + correction) | PREV_INUSE);
          av->system_mem += correction;
            
          if (old_size != 0)
          {
            old_size = (old_size - 4 * SIZE_SZ) & ~MALLOC_ALIGN_MASK;
            set_head(old_top, old_size | PREV_INUSE);
			// 设置标记防止后续需要后续堆块prev_size情况的错误
            chunk_at_offset(old_top, old_size)->size = (2 * SIZE_SZ) | PREV_INUSE;
            chunk_at_offset(old_top, old_size + 2 * SIZE_SZ)->size = (2 * SIZE_SZ) | PREV_INUSE;

            if (old_size >= MINSIZE)
            {
              _int_free(av, old_top, 1); // 释放掉之前的 top chunk
            }
          }
        }
      }
    }
  }
  // 更新
  if ((unsigned long)av->system_mem > (unsigned long)(av->max_system_mem))
    av->max_system_mem = av->system_mem;
  check_malloc_state(av);

  p = av->top;
  size = chunksize(p);
  // 进行 top chunk 的分配,切一块分配给nb,剩余为top chunk
  if ((unsigned long)(size) >= (unsigned long)(nb + MINSIZE))
  {
    remainder_size = size - nb;
    remainder = chunk_at_offset(p, nb);
    av->top = remainder;
    set_head(p, nb | PREV_INUSE | (av != &main_arena ? NON_MAIN_ARENA : 0));
    set_head(remainder, remainder_size | PREV_INUSE);
    check_malloced_chunk(av, p, nb);
    return chunk2mem(p);
  }

  __set_errno(ENOMEM); // 抓取所有未申请到内存情况
  return 0;
}
MMAP
#define MMAP(addr, size, prot, flags)
	__mmap((addr), (size), (prot), (flags) | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)
initial_top
// 为方便,unsorted bin在第一次调用时可作为虚假的top chunk
#define initial_top(M) (unsorted_chunks(M))
#define unsorted_chunks(M) (bin_at(M, 1))
heap_for_ptr
#define heap_for_ptr(ptr) 
	((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))
/* 
	非主线程的堆都按照 HEAP_MAX_SIZE对齐分配,
	ptr & ~0xfffff 即将ptr的后5位置0,可以获取 heap_info 结构体的起始地址
*/
#  define HEAP_MAX_SIZE (1024 * 1024) /* 0x100000,必须是2的幂 */
grow_heap
static int grow_heap (heap_info *h, long diff) // diff为差的size
{
  size_t pagesize = GLRO (dl_pagesize);
  long new_size;

  diff = ALIGN_UP (diff, pagesize);
  new_size = (long) h->size + diff; // 扩展堆后 新的size
  if ((unsigned long) new_size > (unsigned long) HEAP_MAX_SIZE)
    return -1; // 若大于HEAP_MAX_SIZE则失败

  if ((unsigned long) new_size > h->mprotect_size) // 若新堆块大小超出mprotect保护的大小
  {
    if (__mprotect ( // 调用mprotect将超过的部分设置为可读可写
      (char *) h + h->mprotect_size,
      (unsigned long) new_size - h->mprotect_size,
      PROT_READ | PROT_WRITE)
  != 0)
      return -2;// 设置失败

    h->mprotect_size = new_size; // 设置成功后将mprotect_size更新
  }

  h->size = new_size; // 更新堆大小
  LIBC_PROBE (memory_heap_more, 2, h, h->size);
  return 0;
}
new_heap
static heap_info *internal_function new_heap (size_t size, size_t top_pad)
{
  size_t pagesize = GLRO (dl_pagesize);
  char *p1, *p2;
  unsigned long ul;
  heap_info *h;

  if (size + top_pad < HEAP_MIN_SIZE)
    size = HEAP_MIN_SIZE; // 大小加填充后小于最小size则用HEAP_MIN_SIZE
  else if (size + top_pad <= HEAP_MAX_SIZE)
    size += top_pad; // 小于最大size则用【大小+填充】
  else if (size > HEAP_MAX_SIZE)
    return 0; // 大于最大size则创建失败
  else
    size = HEAP_MAX_SIZE; 
  size = ALIGN_UP (size, pagesize); // 确定创建的对齐堆大小

  p2 = MAP_FAILED;
  if (aligned_heap_area) // aligned_heap_area记录了上一次分配的堆
    { // mmap申请,其与HEAP_MAX_SIZE对齐
      p2 = (char *) MMAP (aligned_heap_area, HEAP_MAX_SIZE, PROT_NONE,MAP_NORESERVE);
      aligned_heap_area = NULL;
      // mmap成功但未对齐
      if (p2 != MAP_FAILED && ((unsigned long) p2 & (HEAP_MAX_SIZE - 1)))
        {
          __munmap (p2, HEAP_MAX_SIZE); // 取消分配,删除地址区域的对象映射
          p2 = MAP_FAILED;
        }
    }
  if (p2 == MAP_FAILED)
    { // mmap申请只保证页面对齐,于是申请两倍HEAP_MAX_SIZE,<<1即乘以2,总有关于HEAP_MAX_SIZE对齐的地方
      p1 = (char *) MMAP (0, HEAP_MAX_SIZE << 1, PROT_NONE, MAP_NORESERVE);
      if (p1 != MAP_FAILED)
        {
          // 从p1出发截取对齐HEAP_MAX_SIZE的位置p2
          p2 = (char *) (((unsigned long) p1 + (HEAP_MAX_SIZE - 1)) & ~(HEAP_MAX_SIZE - 1));
          ul = p2 - p1; 
          if (ul)
            __munmap (p1, ul); // 将多出来那段映射删除
          else
            aligned_heap_area = p2 + HEAP_MAX_SIZE; // 否则刚好可以申请,更新aligned_heap_area留后续使用
          __munmap (p2 + HEAP_MAX_SIZE, HEAP_MAX_SIZE - ul); // 记录后再将后面这块映射删除
          // 此时获得p2与HEAP_MAX_SIZE对齐,大小为HEAP_MAX_SIZE的堆块
        }
      else
        { // 尝试只申请一倍,刚好已经与HEAP_MAX_SIZE对齐得堆块
          p2 = (char *) MMAP (0, HEAP_MAX_SIZE, PROT_NONE, MAP_NORESERVE);
          if (p2 == MAP_FAILED) // 分配失败返回0
            return 0;

          if ((unsigned long) p2 & (HEAP_MAX_SIZE - 1)) // 未对齐返回0
            {
              __munmap (p2, HEAP_MAX_SIZE); // 取消分配
              return 0;
            }
        }
    }
  if (__mprotect (p2, size, PROT_READ | PROT_WRITE) != 0)
      // mprotect只将HEAP_MAX_SIZE前面的size大小设置可读可写
    {
      __munmap (p2, HEAP_MAX_SIZE);
      return 0;
    }
  h = (heap_info *) p2; // 最终返回p2指向的内存
  h->size = size;
  h->mprotect_size = size;
  LIBC_PROBE (memory_heap_new, 2, h, h->size);
  return h;
}
MORECORE
#define MORECORE (*__morecore)

void *(*__morecore)(ptrdiff_t) = __default_morecore;

void * __default_morecore (ptrdiff_t increment)
{
  // 调用 __sbrk 增加或减少堆内存
  void *result = (void *) __sbrk (increment);
  if (result == (void *) -1)
    return NULL;

  return result;
}

void * __sbrk (intptr_t increment)
{
  void *oldbrk;

  if (__curbrk == NULL || __libc_multiple_libcs)
  // 若__curbrk为空:即尚未设置堆起始地址
    if (__brk (0) < 0) // 调用__brk(0)获取当前堆顶__curbrk
      return (void *) -1;

  if (increment == 0)
    return __curbrk; // 返回当前堆顶地址

  oldbrk = __curbrk;
  if (increment > 0 // 检测溢出
      ? ((uintptr_t) oldbrk + (uintptr_t) increment < (uintptr_t) oldbrk) // 扩展正值后反而大小变小
      : ((uintptr_t) oldbrk < (uintptr_t) -increment)) // 扩展后变为负值
    {
      __set_errno (ENOMEM);
      return (void *) -1;
    }

  if (__brk (oldbrk + increment) < 0) // 最终调用__brk扩展地址
    return (void *) -1;

  return oldbrk;
}

int __brk (void *addr) // 目标堆顶地址
{
  void *newbrk;

  __curbrk = newbrk = (void *) INLINE_SYSCALL (brk, 1, addr); 
  // 内核brk系统调用调整堆顶地址,此时__curbrk为当前堆顶地址

  if (newbrk < addr)
    {
      __set_errno (ENOMEM); // 操作失败返回-1
      return -1;
    }

  return 0; // 成功返回0
}

free

2.23

__libc_free
void __libc_free(void *mem)
{
  mstate ar_ptr;
  mchunkptr p;

  void (*hook)(void *, const void *) = atomic_forced_read(__free_hook); // 原子读free_hook
  if (__builtin_expect(hook != NULL, 0)) // 查看free_hook是否被设置
  {
    (*hook)(mem, RETURN_ADDRESS(0)); // 非空则调用该hook
    return;
 
  if (mem == 0) /* 需要释放的内存为0,free(0)无效 */
    return;

  p = mem2chunk(mem); // 用户内存指针转换为chunk指针

  if (chunk_is_mmapped(p)) /* 若是mmap申请的内存 */
  {
    /* 
    	no_dyn_threshold初始默认为0, free的堆大小大于mmap阈值且小于默认最大的mmap阈值
    	说明mmap需求量大,但耗时大,于是调节阈值mmap_threshold来使得倾向于用brk而非mmap
    	#define DEFAULT_MMAP_THRESHOLD_MAX (4 * 1024 * 1024 * sizeof(long))
    	trim_threshold 为是否 systrim 减少 ptmalloc 保留内存的参考值
    */
    if (!mp_.no_dyn_threshold && p->size > mp_.mmap_threshold && p->size <= DEFAULT_MMAP_THRESHOLD_MAX)
    {
      mp_.mmap_threshold = chunksize(p);
      mp_.trim_threshold = 2 * mp_.mmap_threshold; // 收缩阈值也提高
      LIBC_PROBE(memory_mallopt_free_dyn_thresholds, 2, mp_.mmap_threshold, mp_.trim_threshold);
    }
    munmap_chunk(p); // 调用了__munmap释放映射
    return;
  }
      
  // 内存由ptmalloc申请的而非mmap申请
  ar_ptr = arena_for_chunk(p); // 获取arena
  _int_free(ar_ptr, p, 0); // 调用_int_free进行堆块释放
}
_int_free
static void _int_free(mstate av, mchunkptr p, int have_lock)
{
  INTERNAL_SIZE_T size;     /* 释放的chunk的大小 */
  mfastbinptr *fb;          /* 对应的fastbin */
  mchunkptr nextchunk;      /* 内存空间中下一个连续的chunk */
  INTERNAL_SIZE_T nextsize; /* 下一个chunk大小 */
  int nextinuse;            /* 下一个chunk是否在使用 */
  INTERNAL_SIZE_T prevsize; /* 内存空间中上一个连续的chunk */
  mchunkptr bck;            /* 存储bin链表指针 */
  mchunkptr fwd;            /* 存储bin链表指针 */

  const char *errstr = NULL;
  int locked = 0;

  size = chunksize(p); // 获取chunk大小
  // 检查1:-size强制转换为无符号整型会发生模运算转换为接近地址空间的最大值,通过判断p和-size防止指针越界溢出
  if (__builtin_expect((uintptr_t)p > (uintptr_t)-size, 0) || __builtin_expect(misaligned_chunk(p), 0))
  {
      // 检查2:是否与MALLOC_ALIGN_MASK对齐
    errstr = "free(): invalid pointer";
  errout:
    if (!have_lock && locked)
      (void)mutex_unlock(&av->mutex);
    malloc_printerr(check_action, errstr, chunk2mem(p), av);
    return;
  }
  // 若大小比 MINSIZE小或size不对齐,则不能free
  if (__glibc_unlikely(size < MINSIZE || !aligned_OK(size)))
  {
    errstr = "free(): invalid size";
    goto errout;
  }

  check_inuse_chunk(av, p);
    
  // 在 fastbin 范围内
  if ((unsigned long)(size) <= (unsigned long)(get_max_fast())
#if TRIM_FASTBINS
      && (chunk_at_offset(p, size) != av->top) // 且下一个chunk不是top chunk
#endif
  )
  {		// 若下一个chunk大小小于2*0x8=0x10或大于系统可用的内存,进入报错部分
    if (__builtin_expect(chunk_at_offset(p, size)->size <= 2 * SIZE_SZ, 0) || 
        __builtin_expect(chunksize(chunk_at_offset(p, size)) >= av->system_mem, 0))
    {
      if (have_lock || // 有互斥锁则直接进入报错
         	({assert(locked == 0);mutex_lock(&av->mutex);locked = 1; // 无锁则显式上锁之后再检查判断
            		chunk_at_offset(p, size)->size <= 2 * SIZE_SZ || 
            		chunksize(chunk_at_offset(p, size)) >= av->system_mem;
             })
         )
      {
        errstr = "free(): invalid next size (fast)";
        goto errout;
      }
      if (!have_lock)
      { // 读取分配区所分配的内存总量需要对分配区加锁,检查完以后,释放分配区的锁
        (void)mutex_unlock(&av->mutex); // 解锁
        locked = 0;
      }
    }

    free_perturb(chunk2mem(p), size - 2 * SIZE_SZ);

    set_fastchunks(av); // 设置arena fastchunk标志位表明有使用fastbin
    unsigned int idx = fastbin_index(size); // 获取fastbin索引
    fb = &fastbin(av, idx); // fb指向fastbin的地址

    mchunkptr old = *fb, old2; // old为fastbin中第一个chunk
    unsigned int old_idx = ~0u;
    do
    {
      if (__builtin_expect(old == p, 0))
      { // 释放的chunk p和fastbin中第一个chunk是同一个chunk,报错double free
        errstr = "double free or corruption (fasttop)";
        goto errout;
      }
      if (have_lock && old != NULL)
        old_idx = fastbin_index(chunksize(old));
      p->fd = old2 = old; // p的fd指向old,即将p插到第一个chunk位置
      // *fb = p,即将fastbin的fd指向p
    } while ((old = catomic_compare_and_exchange_val_rel(fb, p, old2)) != old2);

    if (have_lock && old != NULL && __builtin_expect(old_idx != idx, 0))
    { // 判断:old的索引是否也是该fastbin对应索引
      errstr = "invalid fastbin entry (free)";
      goto errout;
    }
  }
  // 再次判断,若不是mmap的,则是ptmalloc,前向后向合并放入unsorted bin
  else if (!chunk_is_mmapped(p))
  {
    if (!have_lock)
    {
      (void)mutex_lock(&av->mutex);
      locked = 1;
    }

    nextchunk = chunk_at_offset(p, size); // 找到其下一个chunk

    if (__glibc_unlikely(p == av->top)) // 检查是不是释放top chunk
    {
      errstr = "double free or corruption (top)";
      goto errout;
    }
    if (__builtin_expect(contiguous(av) && (char *)nextchunk >= ((char *)av->top + chunksize(av->top)), 0))// 若arena连续且下一个chunk地址大于top chunk的结束地址,即已经到top chunk外
    {
      errstr = "double free or corruption (out)";
      goto errout;
    } // 下一个chunk的prev_inuse未置位表示p是已经释放了的
    if (__glibc_unlikely(!prev_inuse(nextchunk)))
    {
      errstr = "double free or corruption (!prev)";
      goto errout;
    }
	// 获取下一个chunk的大小
    nextsize = chunksize(nextchunk);
    if (__builtin_expect(nextchunk->size <= 2 * SIZE_SZ, 0) || __builtin_expect(nextsize >= av->system_mem, 0))
    { // 大小小于0x10或大于系统内存大小,报错
      errstr = "free(): invalid next size (normal)";
      goto errout;
    }
    free_perturb(chunk2mem(p), size - 2 * SIZE_SZ);

    // 若p的前一个chunk是空闲的,向前合并
    if (!prev_inuse(p))
    {
      prevsize = p->prev_size;
      size += prevsize; // 合并后chunk大小
      p = chunk_at_offset(p, -((long)prevsize)); // p指向前一个chunk
      unlink(av, p, bck, fwd); // 将两个chunk合并脱链
    }
    if (nextchunk != av->top)
    { // 若下一个chunk不是 top chunk
      nextinuse = inuse_bit_at_offset(nextchunk, nextsize); // 下一个chunk的下一个chunk的prev_inuse
      if (!nextinuse) // 表示下一个chunk未使用,空闲,则后向合并
      {
        unlink(av, nextchunk, bck, fwd); // 将下一个chunk脱链
        size += nextsize;
      }
      else // 下一个chunk不空闲
        clear_inuse_bit_at_offset(nextchunk, 0); // 则清除下一个chunk的prev_inuse
	  
      // 准备插入合并的chunk到unsorted bin
      bck = unsorted_chunks(av); // 获取unsorted bin地址
      fwd = bck->fd; // 获取unsorted bin的fd指向的第一个chunk
      if (__glibc_unlikely(fwd->bk != bck)) // 判断:fwd的bk是否反向指回bck
      {
        errstr = "free(): corrupted unsorted chunks";
        goto errout;
      }
      // 将 p 插入到unsorted bin中
      p->fd = fwd;
      p->bk = bck;
      if (!in_smallbin_range(size))
      {
        p->fd_nextsize = NULL;
        p->bk_nextsize = NULL; // large bin要设置两个nextsize
      }
      bck->fd = p;
      fwd->bk = p; // 此时 p 插入到bin 的 fd 指向的第一个 chunk 位置

      set_head(p, size | PREV_INUSE); // 更新
      set_foot(p, size);

      check_free_chunk(av, p);
    }
    else
    { // 若下一个chunk是 top chunk
      size += nextsize;
      set_head(p, size | PREV_INUSE); // 更新标志
      av->top = p; // 与p后的top chunk合并,更新p为 top chunk地址
      check_chunk(av, p);
    }
    
    // 大小大于 65536,进行堆收缩操作
    if ((unsigned long)(size) >= FASTBIN_CONSOLIDATION_THRESHOLD)
    {
      if (have_fastchunks(av))
        malloc_consolidate(av); // 有fast chunk则将fastbin中chunk合并放入unsorted bin中

      if (av == &main_arena)
      { // 主线程中,brk申请的
#ifndef MORECORE_CANNOT_TRIM // top chunk 大小大于 收缩阈值
        if ((unsigned long)(chunksize(av->top)) >= (unsigned long)(mp_.trim_threshold))
          systrim(mp_.top_pad, av); // 进行top chunk收缩操作
#endif
      }
      else // 非主线程中,mmap申请的
      {
        heap_info *heap = heap_for_ptr(top(av)); // 先找到heap结构体
        assert(heap->ar_ptr == av);
        heap_trim(heap, mp_.top_pad);// 进行堆收缩操作
      }
    }

    if (!have_lock)
    {
      assert(locked);
      (void)mutex_unlock(&av->mutex); // 解锁
    }
  }
  else // 若是mmap的chunk,则释放映射,类似__libc_free中的检查
  {
    munmap_chunk(p);
  }
}
munmap_chunk
static void internal_function munmap_chunk(mchunkptr p)
{
  INTERNAL_SIZE_T size = chunksize(p); // 获取chunk大小
  assert(chunk_is_mmapped(p)); // 检查是mmap分配
    
  // mmap分配的chunk一般为独立的即p->prev_size为0,因此还是释放一个chunk
  uintptr_t block = (uintptr_t)p - p->prev_size;  // 获取前一个chunk的指针block
  size_t total_size = p->prev_size + size; // 计算两个chunk的总大小

  if (__builtin_expect(((block | total_size) & (GLRO(dl_pagesize) - 1)) != 0, 0))
  { // 检查是否页对齐
    malloc_printerr(check_action, "munmap_chunk(): invalid pointer", chunk2mem(p), NULL);
    return;
  }
  atomic_decrement(&mp_.n_mmaps); // 减少mmap内存快的计数
  atomic_add(&mp_.mmapped_mem, -total_size); // 更新mmap分配的总内存量

  __munmap((char *)block, total_size);
}
arena_for_chunk
#define arena_for_chunk(ptr)
  (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena) 
// 是main_arena直接返回,否则用heap_for_ptr宏

#define chunk_non_main_arena(p) ((p)->size & NON_MAIN_ARENA)

#define heap_for_ptr(ptr)
  ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1))) // 对ptr对齐找arena
systrim
static int
systrim(size_t pad, mstate av)
{
  long top_size;     /* Amount of top-most memory */
  long extra;        /* Amount to release */
  long released;     /* Amount actually released */
  char *current_brk; /* address returned by pre-check sbrk call */
  char *new_brk;     /* address returned by post-check sbrk call */
  size_t pagesize;
  long top_area;

  pagesize = GLRO(dl_pagesize);
  top_size = chunksize(av->top);

  top_area = top_size - MINSIZE - 1; // top chunk 大小 - 0x20 - 1
  if (top_area <= pad) // 若小于则说明 top chunk 本来就没啥空间,直接返回
    return 0;

  extra = ALIGN_DOWN(top_area - pad, pagesize); // 将主分配区中可以缩小的大小对页面对齐后保存在extra中

  if (extra == 0) // 无可收缩则退出
    return 0;

  current_brk = (char *)(MORECORE(0)); // 0 即返回当前的堆顶地址
  if (current_brk == (char *)(av->top) + top_size) // 判断当前堆顶地址就是top chunk地址加上大小后的结束地址(堆顶)
  {
    MORECORE(-extra); // 将堆顶往回收缩extra大小
    void (*hook)(void) = atomic_forced_read(__after_morecore_hook);
    if (__builtin_expect(hook != NULL, 0))
      (*hook)();
    // 新堆顶地址
    new_brk = (char *)(MORECORE(0));
    LIBC_PROBE(memory_sbrk_less, 2, new_brk, extra);

    if (new_brk != (char *)MORECORE_FAILURE) // 若brk成功
    { 
      released = (long)(current_brk - new_brk); // 获取收缩了的部分,即释放归还给操作系统的内存
      if (released != 0)
      {
        av->system_mem -= released; // 更新系统内存大小
        set_head(av->top, (top_size - released) | PREV_INUSE); // 更新top chunk的头
        check_malloc_state(av);
        return 1;
      }
    }
  }
  return 0;
}
heap_trim
static int internal_function heap_trim (heap_info *heap, size_t pad)
{
  mstate ar_ptr = heap->ar_ptr;
  unsigned long pagesz = GLRO (dl_pagesize);
  mchunkptr top_chunk = top (ar_ptr), p, bck, fwd;
  heap_info *prev_heap;
  long new_size, top_size, top_area, extra, prev_size, misalign;

  while (top_chunk == chunk_at_offset (heap, sizeof (*heap)))
    { // heap结构体地址加上结构体大小若是 top chunk 地址则说明:后续的 top chunk 均为空闲,考虑释放整个 heap
      // 但需要检查该heap的前一个heap是否有足够空间,否则删除后剩余空间太小
      /*	结合sysmalloc中非主线程 top chunk 添加的两个chunk+align
      		【1】[prev_size]
      		【2】[size]		> (2*SIZE_SZ) | PREV_INUSE
      		【3】[prev_size]	> (2*SIZE_SZ)
      		【4】[size]		> 0 | PREV_INUSE
      		【5】[align]
      */
      prev_heap = heap->prev; // 上一个heap堆块
      prev_size = prev_heap->size - (MINSIZE - 2 * SIZE_SZ); // 上一个堆块大小 - 0x10
      p = chunk_at_offset (prev_heap, prev_size); 
      misalign = ((long) p) & MALLOC_ALIGN_MASK;
      p = chunk_at_offset (prev_heap, prev_size - misalign); // 通过对齐操作使得p指向【3,4】chunk
      assert (p->size == (0 | PREV_INUSE)); // 判断一下
      p = prev_chunk (p); // 通过取前一个chunk使得p指向【1,2】chunk
      
      // 计算【1,2,3,4,5】大小
      new_size = chunksize (p) + (MINSIZE - 2 * SIZE_SZ) + misalign;
      assert (new_size > 0 && new_size < (long) (2 * MINSIZE)); // 安全检查 0 < new_size < 0x40
      if (!prev_inuse (p)) // 前一个chunk空闲则加上大小
        new_size += p->prev_size; // 作为新堆块 top chunk大小
      assert (new_size > 0 && new_size < HEAP_MAX_SIZE); // 安全大小检查
      if (new_size + (HEAP_MAX_SIZE - prev_heap->size) < pad + MINSIZE + pagesz)
        break; // 若空间不足够则退出不再释放
      // 更新
      ar_ptr->system_mem -= heap->size;
      arena_mem -= heap->size;
      LIBC_PROBE (memory_heap_free, 2, heap, heap->size);
      
      delete_heap (heap); // 调用宏释放heap
      heap = prev_heap; // 此时heap变为前一个堆heap chunk
      if (!prev_inuse (p))
        {
          p = prev_chunk (p);
          unlink (ar_ptr, p, bck, fwd); // 前向合并 脱链
        }
      assert (((unsigned long) ((char *) p + new_size) & (pagesz - 1)) == 0);
      assert (((char *) p + new_size) == ((char *) heap + heap->size));
      top (ar_ptr) = top_chunk = p; // 更新 top chunk
      set_head (top_chunk, new_size | PREV_INUSE);
    }

  top_size = chunksize (top_chunk); // 获取top chunk大小
  if ((unsigned long)(top_size) < (unsigned long)(mp_.trim_threshold))
    return 0; // 小于阈值无法收缩,退出

  top_area = top_size - MINSIZE - 1;
  if (top_area < 0 || (size_t) top_area <= pad) 
    return 0; // 可收缩值过小,退出

  extra = ALIGN_DOWN(top_area - pad, pagesz);
  if (extra == 0)
    return 0; // 对齐后的区域不足以收缩,退出

  if (shrink_heap (heap, extra) != 0) // 释放刚计算的对齐extra
    return 0;

  ar_ptr->system_mem -= extra;
  arena_mem -= extra; // 更新

  set_head (top_chunk, (top_size - extra) | PREV_INUSE); // 设置标志
  return 1;
}
shrink_heap
static int shrink_heap (heap_info *h, long diff)
{
  long new_size;

  new_size = (long) h->size - diff; // 减去 diff 后的新堆的大小
  if (new_size < (long) sizeof (*h))
    return -1; // 小于heap结构体大小则退出
    
  if (__glibc_unlikely (check_may_shrink_heap ())) // 检查当前系统环境是否支持通过重新映射的方式释放堆空间
    {
      if ((char *) MMAP ((char *) h + new_size, diff, PROT_NONE, MAP_FIXED) == (char *) MAP_FAILED)
        return -2; // 尝试 mmap 将新释放的内存段重新映射为不可访问的内存区域

      h->mprotect_size = new_size; // 更新mprotect记录的映射大小
    }
  else // 不支持mmap重新映射则调用madvise系统调用,指示OS回收这些内存
    __madvise ((char *) h + new_size, diff, MADV_DONTNEED);

  h->size = new_size; // 更新堆大小
  LIBC_PROBE (memory_heap_less, 2, h, h->size);
  return 0;
}

2.27

__libc_free
// 增加
MAYBE_INIT_TCACHE(); // tcache初始化
MAYBE_INIT_TCACHE
# define MAYBE_INIT_TCACHE()
  if (__glibc_unlikely (tcache == NULL)) 
    tcache_init();

static void tcache_init(void)
{
  mstate ar_ptr; // arena指针
  void *victim = 0; // 用于存储分配得到的内存块(tcache_perthread_struct)
  const size_t bytes = sizeof (tcache_perthread_struct); // 分配的字节数
  
  // static __thread bool tcache_shutting_down = false;
  if (tcache_shutting_down) // 若tcache系统关闭则不初始化
    return;

  arena_get (ar_ptr, bytes); // 获取一个arena用于分配内存
  victim = _int_malloc (ar_ptr, bytes); // 从 arena 中分配bytes字节的内存块
  if (!victim && ar_ptr != NULL)
    {
      ar_ptr = arena_get_retry (ar_ptr, bytes); // 分配失败重新尝试
      victim = _int_malloc (ar_ptr, bytes);
    }

  if (ar_ptr != NULL)
    __libc_lock_unlock (ar_ptr->mutex); // 使用了某个arena,解锁允许其他线程访问

  if (victim) // 内存分配成功
    {
      tcache = (tcache_perthread_struct *) victim; 
      // victim转换为tcache_perthread_struct指针存在全局变量tcache中
      memset (tcache, 0, sizeof (tcache_perthread_struct)); // 初始化内存为0
    }
}
_int_free
// 增加
#if USE_TCACHE
{
    size_t tc_idx = csize2tidx (size); // 获取tcache的索引
	// 若 tcache已初始化 + tcache索引在范围内 + tcache的数量此时小于7
    if (tcache && tc_idx < mp_.tcache_bins && tcache->counts[tc_idx] < mp_.tcache_count)
    {
        tcache_put (p, tc_idx);// 将chunk放入tcache中
        return;
    }
}
#endif
tcache_put
static __always_inline void tcache_put (mchunkptr chunk, size_t tc_idx)
{
  tcache_entry *e = (tcache_entry *) chunk2mem (chunk);
  assert (tc_idx < TCACHE_MAX_BINS);
  e->next = tcache->entries[tc_idx]; // chunk插入tcache中
  tcache->entries[tc_idx] = e; // 设置新入口
  ++(tcache->counts[tc_idx]); // 增加tcache的数量
}

2.31

_int_free
// 增加
#if USE_TCACHE
 {
    size_t tc_idx = csize2tidx (size);
    if (tcache != NULL && tc_idx < mp_.tcache_bins) // tcache已初始化且索引在tcache范围内
    {	// 内存块地址转换为 tcache_entry 结构体指针
		tcache_entry *e = (tcache_entry *) chunk2mem (p);

        if (__glibc_unlikely (e->key == tcache)) // tcache为tcacche_perthread_structure的地址
        {
            tcache_entry *tmp;
            LIBC_PROBE (memory_tcache_double_free, 2, e, tc_idx);
            // 循环检测tcache中是否有与e相等的chunk,可能double free
            for (tmp = tcache->entries[tc_idx]; tmp; tmp = tmp->next)
              if (tmp == e)
                malloc_printerr ("free(): double free detected in tcache 2");
         }
        if (tcache->counts[tc_idx] < mp_.tcache_count)
        { // 将当前内存块放入 tcache 中进行缓存
            tcache_put (p, tc_idx);
            return;
        }
     }
}
#endif

calloc

2.23

__libc_calloc

分配一块内存并初始化为零,calloc申请内存不会从tcache中获取,而是直接从堆块中获取

void *__libc_calloc(size_t n, size_t elem_size) // n项,每一项大小为elem_size
{
  mstate av;
  mchunkptr oldtop, p;
  INTERNAL_SIZE_T bytes, sz, csz, oldtopsize;
  void *mem;
  unsigned long clearsize;
  unsigned long nclears;
  INTERNAL_SIZE_T *d;

  bytes = n * elem_size; // 相乘将需要申请的内存大小转换为以字节为单位
  // 判断溢出
#define HALF_INTERNAL_SIZE_T (((INTERNAL_SIZE_T)1) << (8 * sizeof(INTERNAL_SIZE_T) / 2))
  if (__builtin_expect((n | elem_size) >= HALF_INTERNAL_SIZE_T, 0))
  {
    if (elem_size != 0 && bytes / elem_size != n)
    {
      __set_errno(ENOMEM); // 发生整数溢出,退出
      return 0;
    }
  }

  void *(*hook)(size_t, const void *) = atomic_forced_read(__malloc_hook); 
  if (__builtin_expect(hook != NULL, 0)) // 若malloc_hook被定义
  {
    sz = bytes;
    mem = (*hook)(sz, RETURN_ADDRESS(0)); // 调用malloc_hook
    if (mem == 0) // 失败则退出
      return 0;
    return memset(mem, 0, sz); // 并将内存清零
  }
  sz = bytes; // 大小赋值

  arena_get(av, sz); // 获取arena
  if (av)
  {
#if MORECORE_CLEARS
    /* 
    	由于无论是main_arena控制的堆通过sbrk扩展还是非main_arena通过heap_info向后扩展受保护的内存区域
      	新扩展的内存区初始值为0,不需要清空,因此后续需要清理的内存大小只清理与 top chunk 重合区域,提升效率
    */
    oldtop = top(av); // 获取top chunk
    oldtopsize = chunksize(top(av)); // 获取 top chunk 头之后可控制的内存大小
#if MORECORE_CLEARS < 2
    // 主线程 + top chunk需要清空的内存大小为 top chunk 到原先 heap 区域末尾位置
    if (av == &main_arena && oldtopsize <  mp_.sbrk_base + av->max_system_mem - (char *)oldtop)
      oldtopsize = (mp_.sbrk_base + av->max_system_mem - (char *)oldtop);
#endif
    // 非主线程
    if (av != &main_arena)
    { // top chunk 需要清空的内存大小为 top chunk 到原先 heap_info 受保护区域末尾位置
      heap_info *heap = heap_for_ptr(oldtop); // 获取heap_info
      if (oldtopsize < (char *)heap + heap->mprotect_size - (char *)oldtop)
        oldtopsize = (char *)heap + heap->mprotect_size - (char *)oldtop;
    }
#endif
  }
  else 
  { // 无可用的arena,后续_int_malloc会直接mmap获取内存,而mmap获取内存初始值为0,不需要清零
    oldtop = 0;
    oldtopsize = 0;
  }
  mem = _int_malloc(av, sz); // 在 arena 中尝试分配内存

  assert(!mem || chunk_is_mmapped(mem2chunk(mem)) || // 未申请到内存或mmap获取的内存
         av == arena_for_chunk(mem2chunk(mem))); // 内存从当前线程对应的arena管理的内存中获取
  // 未申请到内存且arena不为空
  if (mem == 0 && av != NULL)
  {
    LIBC_PROBE(memory_calloc_retry, 1, sz);
    av = arena_get_retry(av, sz); // 再次获取arena
    mem = _int_malloc(av, sz); // 再次申请分配内存
  }

  if (av != NULL)
    (void)mutex_unlock(&av->mutex);

  if (mem == 0)
    return 0; // 申请为0则退出

  p = mem2chunk(mem); // 将申请到的内存转换为chunk地址

  if (chunk_is_mmapped(p))
  {
    if (__builtin_expect(perturb_byte, 0))
      return memset(mem, 0, sz);

    return mem; // 直接返回,因为mmap的内存默认初始化为0
  }
  csz = chunksize(p); // 需要清空的堆大小

#if MORECORE_CLEARS
  if (perturb_byte == 0 && (p == oldtop && csz > oldtopsize))
  { // 如果是从 top chunk 上切下来的,申请比原先top chunk大小大,则说明原来top chunk扩展
    // 只需要清零 top chunk 范围的内存
    csz = oldtopsize;
  }
#endif

  d = (INTERNAL_SIZE_T *)mem;
  clearsize = csz - SIZE_SZ;
  nclears = clearsize / sizeof(INTERNAL_SIZE_T);
  assert(nclears >= 3);

  if (nclears > 9)
    return memset(d, 0, clearsize);// 清零字节数较多,直接调用 memset
  else // 字节数较少,使用循环展开手动清零,以优化性能
  {
    *(d + 0) = 0;
    *(d + 1) = 0;
    *(d + 2) = 0;
    if (nclears > 4)
    {
      *(d + 3) = 0;
      *(d + 4) = 0;
      if (nclears > 6)
      {
        *(d + 5) = 0;
        *(d + 6) = 0;
        if (nclears > 8)
        {
          *(d + 7) = 0;
          *(d + 8) = 0;
        }
      }
    }
  }

  return mem;
}

realloc

2.23

__libc_realloc
void *__libc_realloc(void *oldmem, size_t bytes)
{
  mstate ar_ptr;
  INTERNAL_SIZE_T nb; /* padded request size */

  void *newp; /* 返回的堆块 */

  void *(*hook)(void *, size_t, const void *) = atomic_forced_read(__realloc_hook);
  if (__builtin_expect(hook != NULL, 0))
    return (*hook)(oldmem, bytes, RETURN_ADDRESS(0)); // 若realloc_hook被设置则调用

#if REALLOC_ZERO_BYTES_FREES
  if (bytes == 0 && oldmem != NULL) // 若大小为0即 realloc(0)
  {
    __libc_free(oldmem); // 相当于free,将oldmem指针对应堆块释放
    return 0;
  }
#endif
  if (oldmem == 0) // 若 oldmem 为 NULL,相当于 malloc(bytes)
    return __libc_malloc(bytes);

  const mchunkptr oldp = mem2chunk(oldmem); // 将chunk转换为对应内存大小
  const INTERNAL_SIZE_T oldsize = chunksize(oldp); // 获取chunk大小

  if (chunk_is_mmapped(oldp))
    ar_ptr = NULL; // 若是mmap申请,则arena指针为空
  else
    ar_ptr = arena_for_chunk(oldp); // 否则通过该chunk获取arena地址

  if (__builtin_expect((uintptr_t)oldp > (uintptr_t)-oldsize, 0) || // 不超过内存空间,判断溢出
      __builtin_expect(misaligned_chunk(oldp), 0)) // 该堆块必须关于0x10对齐
  {
    malloc_printerr(check_action, "realloc(): invalid pointer", oldmem, ar_ptr);
    return NULL;
  }
  checked_request2size(bytes, nb); // 将申请内存转换为适合内存分配的块大小, 转换为nb大小

  // 若该chunk是mmap的
  if (chunk_is_mmapped(oldp))
  {
    void *newmem;

#if HAVE_MREMAP
    newp = mremap_chunk(oldp, nb); // 将oldp原来的chunk的大小调整为nb
    if (newp)
      return chunk2mem(newp); // 调整成功返回
#endif
    if (oldsize - SIZE_SZ >= nb) // 减去头的用户数据大小要大于申请的堆块大小
      return oldmem;

    // 若 mremap失败,则通过malloc获取内存
    newmem = __libc_malloc(bytes);
    if (newmem == 0)
      return 0;
	
    // 将原先内存的数据复制到新内存中
    memcpy(newmem, oldmem, oldsize - 2 * SIZE_SZ);
    munmap_chunk(oldp); // 将原先内存释放掉
    return newmem;
  }
    
  // 若不是mmap的则为ptmalloc申请 
  (void)mutex_lock(&ar_ptr->mutex); // 上锁

  newp = _int_realloc(ar_ptr, oldp, oldsize, nb); // 调用 _int_realloc 核心函数

  (void)mutex_unlock(&ar_ptr->mutex); // 解锁
  // 判断三种情况
  assert(!newp || chunk_is_mmapped(mem2chunk(newp)) ||
         ar_ptr == arena_for_chunk(mem2chunk(newp)));
  // realloc 申请没调整成功
  if (newp == NULL)
  {
    LIBC_PROBE(memory_realloc_retry, 2, bytes, oldmem);
    newp = __libc_malloc(bytes); // 尝试malloc
    if (newp != NULL)
    {
      memcpy(newp, oldmem, oldsize - SIZE_SZ); // 复制数据到新内存
      _int_free(ar_ptr, oldp, 0); // 释放旧内存
    }
  }
  return newp;
}
int_realloc

用于重新分配内存块,尝试更改内存块大小

// av指向内存状态的指针,oldp指向内存状态的指针,oldsize当前块的大小,nb请求的新大小
void * _int_realloc(mstate av, mchunkptr oldp, INTERNAL_SIZE_T oldsize, INTERNAL_SIZE_T nb)
{
  mchunkptr newp;          /* 新分配的内存块指针 */
  INTERNAL_SIZE_T newsize; /* 新内存块大小 */
  void *newmem;            /* 对应用户内存的指针 */

  mchunkptr next; /* 指向oldp后面的连续内存块 */

  mchunkptr remainder;          /* 新分配内存后剩余的内存块 */
  unsigned long remainder_size; /* 剩余内存块大小 */

  mchunkptr bck; /* 链表临时变量 */
  mchunkptr fwd; /* 链表临时变量 */

  unsigned long copysize; /* 需要复制的字节数 */
  unsigned int ncopies;   /* 需要复制的INTERNAL_SIZE_T字数 */
  INTERNAL_SIZE_T *s;     /* 复制源的指针 */
  INTERNAL_SIZE_T *d;     /* 复制目标的指针 */

  const char *errstr = NULL;
  // 若大小小于0x10或大于系统内存
  if (__builtin_expect(oldp->size <= 2 * SIZE_SZ, 0) || __builtin_expect(oldsize >= av->system_mem, 0))
  {
    errstr = "realloc(): invalid old size";
  errout:
    malloc_printerr(check_action, errstr, chunk2mem(oldp), av); // 报错
    return NULL;
  }

  check_inuse_chunk(av, oldp);
  assert(!chunk_is_mmapped(oldp)); // 检查该chunk不是mmap申请的

  next = chunk_at_offset(oldp, oldsize); // 获取下一个chunk
  INTERNAL_SIZE_T nextsize = chunksize(next); // 下一个chunk大小
  if (__builtin_expect(next->size <= 2 * SIZE_SZ, 0) || __builtin_expect(nextsize >= av->system_mem, 0))
  {
    errstr = "realloc(): invalid next size"; // 安全检查下一个chunk大小
    goto errout;
  }

  if ((unsigned long)(oldsize) >= (unsigned long)(nb))
  { // chunk大小足够大
    newp = oldp;
    newsize = oldsize;
  }
  else
  { // chunk大小不足够申请的大小nb,尝试扩展内存
    if (next == av->top && // 下一个chunk是top chunk
        (unsigned long)(newsize = oldsize + nextsize) >= (unsigned long)(nb + MINSIZE))
    { // 且两个chunk大小大于nb+MINSIZE
      set_head_size(oldp, nb | (av != &main_arena ? NON_MAIN_ARENA : 0));
      av->top = chunk_at_offset(oldp, nb); // 设置top chunk为oldp偏移nb,即切割一片内存与原堆块合并
      set_head(av->top, (newsize - nb) | PREV_INUSE);
      check_inuse_chunk(av, oldp);
      return chunk2mem(oldp); // 返回合并后堆块
    } 
    else if (next != av->top && // 若下一个chunk不是 top chunk
             !inuse(next) && // 下一个chunk空闲
             (unsigned long)(newsize = oldsize + nextsize) >= (unsigned long)(nb)) // 两个chunk大小大于nb
    {
      newp = oldp; // 后向合并 next
      unlink(av, next, bck, fwd); // 把下一个chunk脱链,还未返回,后续还需要切割后面的chunk
    }
    else // 下一个chunk不是空闲的
    {
      newmem = _int_malloc(av, nb - MALLOC_ALIGN_MASK); // 新申请一块内存
      if (newmem == 0)
        return 0;

      newp = mem2chunk(newmem); // 内存转换为chunk指针
      newsize = chunksize(newp); // 获取chunk大小

      if (newp == next) // 之前判断空闲不能判断是否在fastbin,所以此处申请可能为fastbin相连
      {
        newsize += oldsize; // 相邻则直接加在一起
        newp = oldp;
      }
      else // 否则拷贝+释放操作
      {
        copysize = oldsize - SIZE_SZ; // 复制的大小
        s = (INTERNAL_SIZE_T *)(chunk2mem(oldp));
        d = (INTERNAL_SIZE_T *)(newmem);
        ncopies = copysize / sizeof(INTERNAL_SIZE_T);
        assert(ncopies >= 3);

        if (ncopies > 9)
          memcpy(d, s, copysize); // 大于9直接向d中拷贝copysize大小的s中数据

        else // 否则手动拷贝,一次拷贝8字节优化
        {
          *(d + 0) = *(s + 0);
          *(d + 1) = *(s + 1);
          *(d + 2) = *(s + 2);
          if (ncopies > 4)
          {
            *(d + 3) = *(s + 3);
            *(d + 4) = *(s + 4);
            if (ncopies > 6)
            {
              *(d + 5) = *(s + 5);
              *(d + 6) = *(s + 6);
              if (ncopies > 8)
              {
                *(d + 7) = *(s + 7);
                *(d + 8) = *(s + 8);
              }
            }
          }
        }

        _int_free(av, oldp, 1); // 释放之前的chunk
        check_inuse_chunk(av, newp);
        return chunk2mem(newp); // 直接返回了
      }
    }
  }

  assert((unsigned long)(newsize) >= (unsigned long)(nb));
  // 用于切割
  remainder_size = newsize - nb;

  if (remainder_size < MINSIZE)// 无法切割出一个chunk
  {
    set_head_size(newp, newsize | (av != &main_arena ? NON_MAIN_ARENA : 0));
    set_inuse_bit_at_offset(newp, newsize); // 设置标志位
  }
  else// 剩余部分还可以切割一个chunk
  {
    remainder = chunk_at_offset(newp, nb); // 切割
    set_head_size(newp, nb | (av != &main_arena ? NON_MAIN_ARENA : 0));
    set_head(remainder, remainder_size | PREV_INUSE | (av != &main_arena ? NON_MAIN_ARENA : 0));
    set_inuse_bit_at_offset(remainder, remainder_size);
    _int_free(av, remainder, 1); // 释放remainder
  }

  check_inuse_chunk(av, newp);
  return chunk2mem(newp);
}
mremap_chunk

调整mmap分配的内存块大小

static mchunkptr internal_function mremap_chunk(mchunkptr p, size_t new_size)
{ // 调整由mmap分配的内存块 p 的大小到 new_size
  size_t pagesize = GLRO(dl_pagesize); // 当前系统页大小
  INTERNAL_SIZE_T offset = p->prev_size; // 当前块的偏移量,prev_size
  INTERNAL_SIZE_T size = chunksize(p); // 当前块大小
  char *cp;

  assert(chunk_is_mmapped(p)); // 确保是mmap分配
  assert(((size + offset) & (GLRO(dl_pagesize) - 1)) == 0); // 确保块大小加上偏移量是页大小整数倍对齐

  new_size = ALIGN_UP(new_size + offset + SIZE_SZ, pagesize); // 将new_size调整为与系统页对齐

  if (size + offset == new_size) // 若刚好等于调整后则返回
    return p;
  // 否则调用系统调用__mremap重新映射内存块,mremap重新分配一块内存并将之前的数据复制到新的内存上
  cp = (char *)__mremap((char *)p - offset, size + offset, new_size,MREMAP_MAYMOVE);

  if (cp == MAP_FAILED)
    return 0; // 调整失败

  p = (mchunkptr)(cp + offset); // mremap返回地址cp加上偏移得到新的块指针p

  assert(aligned_OK(chunk2mem(p))); // 地址对齐
  assert((p->prev_size == offset)); // 偏移量未变
  set_head(p, (new_size - offset) | IS_MMAPPED); // 更新头信息

  INTERNAL_SIZE_T new; // 更新mmaped_mem信息
  new = atomic_exchange_and_add(&mp_.mmapped_mem, new_size - size - offset) + new_size - size - offset;
  atomic_max(&mp_.max_mmapped_mem, new);
  return p;
}

IO_FILE

结构

_IO_list_all

// 指向_IO_FILE单链表的链表头
extern struct _IO_FILE_plus *_IO_list_all;

_IO_FILE_plus

struct _IO_FILE_plus
{
  _IO_FILE file;
  // vtable: 实现文件流操作的虚函数表,包含一组函数指针,指向实现各种IO操作的函数,不同的对象指向函数可能不同
  const struct _IO_jump_t *vtable;
};

_IO_FILE

struct _IO_FILE
{
  int _flags;		/* High-order word is _IO_MAGIC; rest is flags. */

  /* The following pointers correspond to the C++ streambuf protocol. */
  char *_IO_read_ptr;	/* 指针指向当前读到的位置 */
  char *_IO_read_end;	/* get area的结束,系统读取数据的结尾 */
  char *_IO_read_base;	/* putback+get area的开始,系统读取数据的开头 */
  char *_IO_write_base;	/* IO文件缓冲区开头 */
  char *_IO_write_ptr;	/* 指针指向当前写到的位置 */
  char *_IO_write_end;	/* IO文件缓冲区结尾 */
  char *_IO_buf_base;	/* buf缓冲区的开始 */
  char *_IO_buf_end;	/* buf缓冲区的结束 */

  /* The following fields are used to support backing up and undo. */
  char *_IO_save_base; /* Pointer to start of non-current get area. */
  char *_IO_backup_base;  /* Pointer to first valid character of backup area */
  char *_IO_save_end; /* Pointer to end of non-current get area. */

  struct _IO_marker *_markers;

  struct _IO_FILE *_chain; // 指向下一个_IO_FILE结构体

  int _fileno;
  int _flags2;
  __off_t _old_offset;
  unsigned short _cur_column;
  signed char _vtable_offset;
  char _shortbuf[1];

  _IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

偏移

0x0:'_flags',
0x8:'_IO_read_ptr',
0x10:'_IO_read_end',
0x18:'_IO_read_base',
0x20:'_IO_write_base',
0x28:'_IO_write_ptr',
0x30:'_IO_write_end',
0x38:'_IO_buf_base',
0x40:'_IO_buf_end',
0x48:'_IO_save_base',
0x50:'_IO_backup_base',
0x58:'_IO_save_end',
0x60:'_markers',
0x68:'_chain',
0x70:'_fileno',
0x74:'_flags2',
0x78:'_old_offset',
0x80:'_cur_column',
0x82:'_vtable_offset',
0x83:'_shortbuf',
0x88:'_lock',
0x90:'_offset',
0x98:'_codecvt',
0xa0:'_wide_data',
0xa8:'_freeres_list',
0xb0:'_freeres_buf',
0xb8:'__pad5',
0xc0:'_mode',
0xc4:'_unused2',
0xd8:'vtable'

_IO_FILE_complete

struct _IO_FILE_complete
{
  struct _IO_FILE _file;
  __off64_t _offset;
  struct _IO_codecvt *_codecvt; // house of apple 3利用
  struct _IO_wide_data *_wide_data; // house of apple 2劫持这个变量
  struct _IO_FILE *_freeres_list;
  void *_freeres_buf;
  size_t __pad5;
  int _mode;
  char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
};

_IO_codecvt

struct _IO_codecvt
{
  _IO_iconv_t __cd_in;
  _IO_iconv_t __cd_out;
};

_IO_iconv_t

typedef struct
{
  struct __gconv_step *step;
  struct __gconv_step_data step_data;
} _IO_iconv_t;

__gconv_step

struct __gconv_step
{
  struct __gconv_loaded_object *__shlib_handle;
  const char *__modname;
  int __counter;

  char *__from_name;
  char *__to_name;

  __gconv_fct __fct;
  __gconv_btowc_fct __btowc_fct;
  __gconv_init_fct __init_fct;
  __gconv_end_fct __end_fct;
    
  int __min_needed_from;
  int __max_needed_from;
  int __min_needed_to;
  int __max_needed_to;

  int __stateful;
  void *__data;
};

_IO_wstrnfile

typedef struct{
  _IO_strfile f;
  wchar_t overflow_buf[64]; // overflow_buf
} _IO_wstrnfile;

_IO_strnfile

typedef struct{
  _IO_strfile f;
  char overflow_buf[64];
} _IO_strnfile;

_IO_strfile

typedef struct _IO_strfile_
{
  struct _IO_streambuf _sbf;
  struct _IO_str_fields _s;
} _IO_strfile;

_IO_streambuf

struct _IO_streambuf
{
  FILE _f;
  const struct _IO_jump_t *vtable;
};

_IO_str_fields

struct _IO_str_fields
{
  _IO_alloc_type _allocate_buffer_unused;
  _IO_free_type _free_buffer_unused;
};

_IO_wide_data

// 结构与_IO_FILE很像
struct _IO_wide_data
{
  wchar_t *_IO_read_ptr;	/* Current read pointer */
  wchar_t *_IO_read_end;	/* End of get area. */
  wchar_t *_IO_read_base;	/* Start of putback+get area. */
  wchar_t *_IO_write_base;	/* Start of put area. */
  wchar_t *_IO_write_ptr;	/* Current put pointer. */
  wchar_t *_IO_write_end;	/* End of put area. */
  wchar_t *_IO_buf_base;	/* Start of reserve area. */
  wchar_t *_IO_buf_end;		/* End of reserve area. */
  /* The following fields are used to support backing up and undo. */
  wchar_t *_IO_save_base;	/* Pointer to start of non-current get area. */
  wchar_t *_IO_backup_base;	/* Pointer to first valid character of
				   backup area */
  wchar_t *_IO_save_end;	/* Pointer to end of non-current get area. */

  __mbstate_t _IO_state;
  __mbstate_t _IO_last_state;
  struct _IO_codecvt _codecvt;
  wchar_t _shortbuf[1];
  const struct _IO_jump_t *_wide_vtable; // vtable表
};

_IO_jump_t

不同的实例对象指向的函数可能不同

struct _IO_jump_t
{
    JUMP_FIELD(size_t, __dummy);
    JUMP_FIELD(size_t, __dummy2);
    JUMP_FIELD(_IO_finish_t, __finish);
    JUMP_FIELD(_IO_overflow_t, __overflow);
    JUMP_FIELD(_IO_underflow_t, __underflow);
    JUMP_FIELD(_IO_underflow_t, __uflow);
    JUMP_FIELD(_IO_pbackfail_t, __pbackfail);
    JUMP_FIELD(_IO_xsputn_t, __xsputn);
    JUMP_FIELD(_IO_xsgetn_t, __xsgetn);
    JUMP_FIELD(_IO_seekoff_t, __seekoff);
    JUMP_FIELD(_IO_seekpos_t, __seekpos);
    JUMP_FIELD(_IO_setbuf_t, __setbuf);
    JUMP_FIELD(_IO_sync_t, __sync);
    JUMP_FIELD(_IO_doallocate_t, __doallocate);
    JUMP_FIELD(_IO_read_t, __read);
    JUMP_FIELD(_IO_write_t, __write);
    JUMP_FIELD(_IO_seek_t, __seek);
    JUMP_FIELD(_IO_close_t, __close);
    JUMP_FIELD(_IO_stat_t, __stat);
    JUMP_FIELD(_IO_showmanyc_t, __showmanyc);
    JUMP_FIELD(_IO_imbue_t, __imbue);
#if 0
    get_column;
    set_column;
#endif
};

STD

  • 初始情况_IO_list_all指向_IO_2_1_stdin__IO_2_1_stdout__IO_2_1_stderr_三个_IO_FILE结构体,开在libc的数据段上
// stdfiles.c
DEF_STDFILE(_IO_2_1_stdin_, 0, 0, _IO_NO_WRITES);
DEF_STDFILE(_IO_2_1_stdout_, 1, &_IO_2_1_stdin_, _IO_NO_READS);
DEF_STDFILE(_IO_2_1_stderr_, 2, &_IO_2_1_stdout_, _IO_NO_READS+_IO_UNBUFFERED);

struct _IO_FILE_plus *_IO_list_all = &_IO_2_1_stderr_;
libc_hidden_data_def (_IO_list_all)
// stdio.c
// 三个全局指针指向三个结构体
_IO_FILE *stdin = (FILE *) &_IO_2_1_stdin_;
_IO_FILE *stdout = (FILE *) &_IO_2_1_stdout_;
_IO_FILE *stderr = (FILE *) &_IO_2_1_stderr_;
图片无法加载

DEF_STDFILE

// stdfiles.c
# define DEF_STDFILE(NAME, FD, CHAIN, FLAGS) // 文件流对象名、文件描述符、文件链、标志[只读只写]
// 锁,保护文件流的并发访问
 static _IO_lock_t _IO_stdfile_##FD##_lock = _IO_lock_initializer; 
// 宽字符数据结构
 static struct _IO_wide_data _IO_wide_data_##FD = { ._wide_vtable = &_IO_wfile_jumps }; 
// 文件流结构体
 struct _IO_FILE_plus NAME = {FILEBUF_LITERAL(CHAIN, FLAGS, FD, &_IO_wide_data_##FD), &_IO_file_jumps};
// 有文件读写操作,为对应文件创建_IO_FILE结构体,头插法链接到_IO_list_all链表上
void _IO_link_in (struct _IO_FILE_plus *fp)
{
  // _IO_LINKED 标志未被设置, 执行插入操作
  if ((fp->file._flags & _IO_LINKED) == 0)
    {
      // 设置标志位
      fp->file._flags |= _IO_LINKED;
#ifdef _IO_MTSAFE_IO
      // 线程安全操作
      _IO_cleanup_region_start_noarg (flush_cleanup);
      _IO_lock_lock (list_all_lock);
      run_fp = (_IO_FILE *) fp;
      _IO_flockfile ((_IO_FILE *) fp);
#endif
      // 链接到开头
      fp->file._chain = (_IO_FILE *) _IO_list_all;
      _IO_list_all = fp;
#ifdef _IO_MTSAFE_IO
      // 线程安全操作
      _IO_funlockfile ((_IO_FILE *) fp);
      run_fp = NULL;
      _IO_lock_unlock (list_all_lock);
      _IO_cleanup_region_end (0);
#endif
    }
}
libc_hidden_def (_IO_link_in)
struct _IO_cookie_file
{
  struct _IO_FILE_plus __fp; // 包含一个FILE结构体和一个_IO_jump_t指针vtable
  void *__cookie;
  cookie_io_functions_t __io_functions; // 包含4个函数指针
};
typedef struct _IO_cookie_io_functions_t
{
  cookie_read_function_t *read;		/* Read bytes.  */
  cookie_write_function_t *write;	/* Write bytes.  */
  cookie_seek_function_t *seek;		/* Seek/tell file position.  */
  cookie_close_function_t *close;	/* Close file.  */
} cookie_io_functions_t;

fopen

fopen

# define fopen(fname, mode) _IO_new_fopen (fname, mode)

_IO_new_fopen

FILE *_IO_new_fopen (const char *filename, const char *mode)
{
  return __fopen_internal (filename, mode, 1);
}

__fopen_internal

FILE *__fopen_internal (const char *filename, const char *mode, int is32)
{
  struct locked_FILE
  {
    struct _IO_FILE_plus fp;
    // 若定义了 _IO_MTSAFE_IO 宏,结构体将包含 lock 成员,
    // 实现条件性编译,针对是否支持多线程安全生成不同的结构体布局
#ifdef _IO_MTSAFE_IO
    _IO_lock_t lock;
#endif
    struct _IO_wide_data wd;
  } *new_f = (struct locked_FILE *) malloc (sizeof (struct locked_FILE));
    // malloc创建locked_FILE结构体

  if (new_f == NULL)
    return NULL;
#ifdef _IO_MTSAFE_IO
  // 初始化多线程锁
  new_f->fp.file._lock = &new_f->lock;
#endif
  // NULL初始化
  _IO_no_init (&new_f->fp.file, 0, 0, &new_f->wd, &_IO_wfile_jumps);
  // 设置vtable
  _IO_JUMPS (&new_f->fp) = &_IO_file_jumps;
  // 将结构体插入链表中
  _IO_new_file_init_internal (&new_f->fp);
  // 执行系统调用打开文件
  if (_IO_file_fopen ((FILE *) new_f, filename, mode, is32) != NULL)
    return __fopen_maybe_mmap (&new_f->fp.file);

  _IO_un_link (&new_f->fp);
  free (new_f);
  return NULL;
}

_IO_no_init

// 初始化操作
void _IO_no_init (FILE *fp, int flags, int orientation,
	     struct _IO_wide_data *wd, const struct _IO_jump_t *jmp)
{
  _IO_old_init (fp, flags);
  fp->_mode = orientation;
  if (orientation >= 0)
    {
      fp->_wide_data = wd;
      fp->_wide_data->_IO_buf_base = NULL;
      fp->_wide_data->_IO_buf_end = NULL;
      fp->_wide_data->_IO_read_base = NULL;
      fp->_wide_data->_IO_read_ptr = NULL;
      fp->_wide_data->_IO_read_end = NULL;
      fp->_wide_data->_IO_write_base = NULL;
      fp->_wide_data->_IO_write_ptr = NULL;
      fp->_wide_data->_IO_write_end = NULL;
      fp->_wide_data->_IO_save_base = NULL;
      fp->_wide_data->_IO_backup_base = NULL;
      fp->_wide_data->_IO_save_end = NULL;

      fp->_wide_data->_wide_vtable = jmp;
    }
  else
    fp->_wide_data = (struct _IO_wide_data *) -1L;
  fp->_freeres_list = NULL;
}

_IO_old_init

void _IO_old_init (FILE *fp, int flags)
{
  fp->_flags = _IO_MAGIC|flags; // #define _IO_MAGIC 0xFBAD0000 魔数
  fp->_flags2 = 0;
  if (stdio_needs_locking)
    fp->_flags2 |= _IO_FLAGS2_NEED_LOCK;
  fp->_IO_buf_base = NULL;
  fp->_IO_buf_end = NULL;
  fp->_IO_read_base = NULL;
  fp->_IO_read_ptr = NULL;
  fp->_IO_read_end = NULL;
  fp->_IO_write_base = NULL;
  fp->_IO_write_ptr = NULL;
  fp->_IO_write_end = NULL;
  fp->_chain = NULL;

  fp->_IO_save_base = NULL;
  fp->_IO_backup_base = NULL;
  fp->_IO_save_end = NULL;
  fp->_markers = NULL;
  fp->_cur_column = 0;
#if _IO_JUMPS_OFFSET
  fp->_vtable_offset = 0;
#endif
#ifdef _IO_MTSAFE_IO
  if (fp->_lock != NULL)
    _IO_lock_init (*fp->_lock);
#endif
}

_IO_new_file_init_internal

void _IO_new_file_init_internal (struct _IO_FILE_plus *fp)
{
  // 设置一些标志
  fp->file._offset = _IO_pos_BAD;
  fp->file._flags |= CLOSED_FILEBUF_FLAGS;

  _IO_link_in (fp); // 头插法加到链表中
  fp->file._fileno = -1;
}

_IO_new_file_fopen

FILE *_IO_new_file_fopen (FILE *fp, const char *filename, const char *mode, int is32not64)
{
  int oflags = 0, omode;
  int read_write;
  int oprot = 0666;
  int i;
  FILE *result;
  const char *cs;
  const char *last_recognized;

  if (_IO_file_is_open (fp))
    return 0;
  switch (*mode)
    {
    case 'r':
      omode = O_RDONLY;
      read_write = _IO_NO_WRITES;
      break;
    case 'w':
      omode = O_WRONLY;
      oflags = O_CREAT|O_TRUNC;
      read_write = _IO_NO_READS;
      break;
    case 'a':
      omode = O_WRONLY;
      oflags = O_CREAT|O_APPEND;
      read_write = _IO_NO_READS|_IO_IS_APPENDING;
      break;
    default:
      __set_errno (EINVAL);
      return NULL;
    }
  last_recognized = mode;
  for (i = 1; i < 7; ++i)
    {
      switch (*++mode)
	{
	case '\0':
	  break;
	case '+':
	  omode = O_RDWR;
	  read_write &= _IO_IS_APPENDING;
	  last_recognized = mode;
	  continue;
	case 'x':
	  oflags |= O_EXCL;
	  last_recognized = mode;
	  continue;
	case 'b':
	  last_recognized = mode;
	  continue;
	case 'm':
	  fp->_flags2 |= _IO_FLAGS2_MMAP;
	  continue;
	case 'c':
	  fp->_flags2 |= _IO_FLAGS2_NOTCANCEL;
	  continue;
	case 'e':
	  oflags |= O_CLOEXEC;
	  fp->_flags2 |= _IO_FLAGS2_CLOEXEC;
	  continue;
	default:
	  continue;
	}
      break;
    }

  result = _IO_file_open (fp, filename, omode|oflags, oprot, read_write, is32not64);

  if (result != NULL)
    {
      cs = strstr (last_recognized + 1, ",ccs=");
      if (cs != NULL)
	{
	  struct gconv_fcts fcts;
	  struct _IO_codecvt *cc;
	  char *endp = __strchrnul (cs + 5, ',');
	  char *ccs = malloc (endp - (cs + 5) + 3);

	  if (ccs == NULL)
	    {
	      int malloc_err = errno;
	      (void) _IO_file_close_it (fp);
	      __set_errno (malloc_err);
	      return NULL;
	    }

	  *((char *) __mempcpy (ccs, cs + 5, endp - (cs + 5))) = '\0';
	  strip (ccs, ccs);

	  if (__wcsmbs_named_conv (&fcts, ccs[2] == '\0' ? upstr (ccs, cs + 5) : ccs) != 0)
	    {
	      (void) _IO_file_close_it (fp);
	      free (ccs);
	      __set_errno (EINVAL);
	      return NULL;
	    }

	  free (ccs);

	  assert (fcts.towc_nsteps == 1);
	  assert (fcts.tomb_nsteps == 1);

	  fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_end;
	  fp->_wide_data->_IO_write_ptr = fp->_wide_data->_IO_write_base;

	  memset (&fp->_wide_data->_IO_state, '\0', sizeof (__mbstate_t));
	  memset (&fp->_wide_data->_IO_last_state, '\0', sizeof (__mbstate_t));

	  cc = fp->_codecvt = &fp->_wide_data->_codecvt;

	  cc->__cd_in.step = fcts.towc;

	  cc->__cd_in.step_data.__invocation_counter = 0;
	  cc->__cd_in.step_data.__internal_use = 1;
	  cc->__cd_in.step_data.__flags = __GCONV_IS_LAST;
	  cc->__cd_in.step_data.__statep = &result->_wide_data->_IO_state;

	  cc->__cd_out.step = fcts.tomb;

	  cc->__cd_out.step_data.__invocation_counter = 0;
	  cc->__cd_out.step_data.__internal_use = 1;
	  cc->__cd_out.step_data.__flags = __GCONV_IS_LAST | __GCONV_TRANSLIT;
	  cc->__cd_out.step_data.__statep = &result->_wide_data->_IO_state;

	  _IO_JUMPS_FILE_plus (fp) = fp->_wide_data->_wide_vtable;
	  result->_mode = 1;
	}
    }

  return result;
}
libc_hidden_ver (_IO_new_file_fopen, _IO_file_fopen)

_IO_file_open

FILE *_IO_file_open (FILE *fp, const char *filename, int posix_mode, int prot,
	       int read_write, int is32not64)
{
  int fdesc;
  if (__glibc_unlikely (fp->_flags2 & _IO_FLAGS2_NOTCANCEL))
    fdesc = __open_nocancel (filename, posix_mode | (is32not64 ? 0 : O_LARGEFILE), prot);
  else
    fdesc = __open (filename, posix_mode | (is32not64 ? 0 : O_LARGEFILE), prot);
  if (fdesc < 0)
    return NULL;
  fp->_fileno = fdesc;
  _IO_mask_flags (fp, read_write,_IO_NO_READS+_IO_NO_WRITES+_IO_IS_APPENDING);
  if ((read_write & (_IO_IS_APPENDING | _IO_NO_READS)) == (_IO_IS_APPENDING | _IO_NO_READS))
    {
      off64_t new_pos = _IO_SYSSEEK (fp, 0, _IO_seek_end);
      if (new_pos == _IO_pos_BAD && errno != ESPIPE)
        {
          __close_nocancel (fdesc);
          return NULL;
        }
    }
  _IO_link_in ((struct _IO_FILE_plus *) fp);
  return fp;
}
libc_hidden_def (_IO_file_open)

fread

图片无法加载

fread

#define fread(p, m, n, s) _IO_fread (p, m, n, s)

_IO_fread

size_t _IO_fread (void *buf, size_t size, size_t count, FILE *fp)
{
  size_t bytes_requested = size * count;
  size_t bytes_read;
  CHECK_FILE (fp, 0);
  if (bytes_requested == 0)
    return 0;
  _IO_acquire_lock (fp);
  bytes_read = _IO_sgetn (fp, (char *) buf, bytes_requested);
  _IO_release_lock (fp);
  return bytes_requested == bytes_read ? count : bytes_read / size;
}
libc_hidden_def (_IO_fread)

_IO_sgetn

#define _IO_XSGETN(FP, DATA, N) JUMP2 (__xsgetn, FP, DATA, N)
#define JUMP2(FUNC, THIS, X1, X2) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS, X1, X2)
# define _IO_JUMPS_FUNC(THIS) (IO_validate_vtable (_IO_JUMPS_FILE_plus (THIS)))

size_t _IO_sgetn (FILE *fp, void *data, size_t n)
{ // 会通过vtable调用不同结构体的函数 _IO_file_xsgetn
  return _IO_XSGETN (fp, data, n);
}
libc_hidden_def (_IO_sgetn)

_IO_file_xsgetn

// 通过文件流fp从文件系统读取最多n字节数据到用户缓冲区data中
size_t _IO_file_xsgetn(FILE *fp, void *data, size_t n)
{
  size_t want, have;
  ssize_t count;
  char *s = data; // 指向用户缓冲区data
  want = n; // 剩余需要读取的字节数,初始为n

  if (fp->_IO_buf_base == NULL)
  // 文件流缓冲区未初始化
  {
    if (fp->_IO_save_base != NULL)
    {
      free(fp->_IO_save_base); // 释放备份缓冲区
      fp->_flags &= ~_IO_IN_BACKUP; // 清除备份模式标志
    }
    // 初始化文件流缓冲区
    _IO_doallocbuf(fp);
  }
  
  // 仍有数据需要读取
  while (want > 0)
  {
    have = fp->_IO_read_end - fp->_IO_read_ptr; // 文件流缓冲区剩余数据大小
    // 文件流缓冲区大小足够满足需要读取的字节数want
    if (want <= have)
    {
      memcpy(s, fp->_IO_read_ptr, want); // 从文件流缓冲区复制到用户缓冲区
      fp->_IO_read_ptr += want; // 更新指针
      want = 0; // 标记读取完成
    }
    // 若文件流缓冲区数据不足
    else
    {
      if (have > 0) // 文件流缓冲区中还有部分数据可用
      {
        // 先尽可能多的从文件流缓冲区复制到用户缓冲区
        s = __mempcpy(s, fp->_IO_read_ptr, have);
        want -= have; // 更新剩余需求
        fp->_IO_read_ptr += have; // 更新文件流缓冲区读取指针
      }
	  // 处于备份模式
      if (_IO_in_backup(fp))
      {
        _IO_switch_to_main_get_area(fp);
        continue; // 切换备份缓冲区重新尝试读取
      }
      // 若文件流缓冲区未满 且 需要读取的数据小于文件流缓冲区总容量
      if (fp->_IO_buf_base && want < (size_t)(fp->_IO_buf_end - fp->_IO_buf_base))
      {
        // underflow 调用系统函数将数据读入文件流缓冲区, 实际调用 _IO_UNDERFLOW
        // 最终调用 _IO_new_file_underflow 函数
        if (__underflow(fp) == EOF)
          break;
        continue; // 填充完文件流缓冲区,继续尝试读取
      }
      // 所需读取的数据大于文件流缓冲区总容量
      // 重置文件流缓冲区指针
      _IO_setg(fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
      _IO_setp(fp, fp->_IO_buf_base, fp->_IO_buf_base);
	  
      // 对齐,若文件流缓冲区大小不小于128字节,则读入长度为文件流缓冲区长度整数倍
      count = want;
      if (fp->_IO_buf_base)
      {
        size_t block_size = fp->_IO_buf_end - fp->_IO_buf_base;
        if (block_size >= 128)
          count -= want % block_size;
      }
	  // 直接系统调用读取文件中数据到用户缓冲区中
      count = _IO_SYSREAD(fp, s, count);
      if (count <= 0)
      {
        if (count == 0)
          fp->_flags |= _IO_EOF_SEEN;
        else
          fp->_flags |= _IO_ERR_SEEN;

        break;
      }
	  // 更新
      s += count;
      want -= count;
      if (fp->_offset != _IO_pos_BAD)
        _IO_pos_adjust(fp->_offset, count);
    }
  }

  return n - want;
}
libc_hidden_def(_IO_file_xsgetn)

_IO_doallocbuf

void _IO_doallocbuf (FILE *fp)
{
  if (fp->_IO_buf_base)
    return;
  if (!(fp->_flags & _IO_UNBUFFERED) || fp->_mode > 0)
    if (_IO_DOALLOCATE (fp) != EOF)
      return;
  _IO_setb (fp, fp->_shortbuf, fp->_shortbuf+1, 0);
}
libc_hidden_def (_IO_doallocbuf)

_IO_new_file_underflow

int _IO_new_file_underflow(_IO_FILE *fp)
{
  _IO_ssize_t count; // 存储从文件中读取的字节数
#if 0
  if (fp->_flags & _IO_EOF_SEEN)
    return (EOF);
#endif

  if (fp->_flags & _IO_NO_READS) // 文件末尾已经被读取, 表示不允许读取数据
  {
    fp->_flags |= _IO_ERR_SEEN;
    __set_errno(EBADF); // 文件描述符无效
    return EOF;
  }
  // 文件流缓冲区已有数据,直接返回_IO_read_ptr指向的数据
  if (fp->_IO_read_ptr < fp->_IO_read_end)
    return *(unsigned char *)fp->_IO_read_ptr;

  // 若文件流没有为读取分配缓冲区
  if (fp->_IO_buf_base == NULL)
  {
    if (fp->_IO_save_base != NULL) // 之前有分配过备份的缓冲区
    {
      free(fp->_IO_save_base); // 释放这些缓冲区
      fp->_flags &= ~_IO_IN_BACKUP;
    }
    _IO_doallocbuf(fp); // 分配新文件流缓冲区
  }
  // 是否设置 行缓冲 或 无缓冲
  if (fp->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
  {
#if 0
      _IO_flush_all_linebuffered ();
#else
    _IO_acquire_lock(_IO_stdout);

    if ((_IO_stdout->_flags & 
        (_IO_LINKED | _IO_NO_WRITES | _IO_LINE_BUF)) == (_IO_LINKED | _IO_LINE_BUF))
      _IO_OVERFLOW(_IO_stdout, EOF);// 调用_IO_OVERFLOW函数处理标准输出的溢出情况

    _IO_release_lock(_IO_stdout);
#endif
  }

  _IO_switch_to_get_mode(fp); // 切换为读取模式
  
  // 设置读写指针均为缓冲区基地址
  fp->_IO_read_base = fp->_IO_read_ptr = fp->_IO_buf_base;
  fp->_IO_read_end = fp->_IO_buf_base;
  fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_write_end = fp->_IO_buf_base;
  // 调用系统SYSREAD调用从文件读取数据存储到缓冲区中,返回实际读取的字节数
  count = _IO_SYSREAD(fp, fp->_IO_buf_base, fp->_IO_buf_end - fp->_IO_buf_base);
  if (count <= 0)
  {
    if (count == 0)
      fp->_flags |= _IO_EOF_SEEN;
    else
      fp->_flags |= _IO_ERR_SEEN, count = 0;
  }
  // 读取结束指针移动count字节
  fp->_IO_read_end += count;
  if (count == 0) // 未成功读取任何数据
  {
    fp->_offset = _IO_pos_BAD;
    return EOF;
  }
  if (fp->_offset != _IO_pos_BAD)
    _IO_pos_adjust(fp->_offset, count); // 调整文件流的偏移量
  return *(unsigned char *)fp->_IO_read_ptr; // 返回当前读取指针指向字节
}
libc_hidden_ver(_IO_new_file_underflow, _IO_file_underflow)

fwrite

图片无法加载

fwrite

#define fwrite(buf, size, count, fp) _IO_fwrite (buf, size, count, fp)

_IO_fwrite

size_t _IO_fwrite (const void *buf, size_t size, size_t count, FILE *fp)
{
  size_t request = size * count;
  size_t written = 0;
  CHECK_FILE (fp, 0);
  if (request == 0)
    return 0;
  _IO_acquire_lock (fp);
  if (_IO_vtable_offset (fp) != 0 || _IO_fwide (fp, -1) == -1)
    written = _IO_sputn (fp, (const char *) buf, request);
  _IO_release_lock (fp);
  if (written == request || written == EOF)
    return count;
  else
    return written / size;
}
libc_hidden_def (_IO_fwrite)

_IO_new_file_xsputn

// 从用户缓冲区data写入文件流f中
size_t _IO_new_file_xsputn(FILE *f, const void *data, size_t n)
{
  const char *s = (const char *)data; // 用户缓冲区指针
  size_t to_do = n; // 写入数据长度
  int must_flush = 0; // 标志是否需要立即刷新文件流缓冲区
  size_t count = 0; 

  if (n <= 0)
    return 0;
  // 判断是否为行缓冲模式,即遇到换行符立即刷新文件流缓冲区
  if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING))
  {
    // 文件流缓冲区剩余空间大小
    count = f->_IO_buf_end - f->_IO_write_ptr;
    if (count >= n) // 若文件流缓冲区能容纳所有数据data
    {
      const char *p;
      for (p = s + n; p > s;) // 从用户数据末尾向前查找换行符
      {
        if (*--p == '\n') // 找到换行符
        {
          count = p - s + 1; // 只写入到换行符前数据
          must_flush = 1; // 立即刷新输出数据
          break;
        }
      }
    }
  }
  // 非行缓冲模式,文件流缓冲区仍有可用空间,获取剩余空间count
  else if (f->_IO_write_end > f->_IO_write_ptr)
    count = f->_IO_write_end - f->_IO_write_ptr;

  if (count > 0)
  {
    if (count > to_do)
      count = to_do; // 若文件流缓冲区能容纳所有剩余数据
    // 将数据data写入到文件流缓冲区中
    f->_IO_write_ptr = __mempcpy(f->_IO_write_ptr, s, count);
    // 更新
    s += count;
    to_do -= count;
  }
  // 若需要刷新缓冲区或仍有数据未写
  if (to_do + must_flush > 0)
  {
    size_t block_size, do_write;
	// 强制输出并刷新清空文件流缓冲区
    // 实际调用了_IO_new_file_overflow
    if (_IO_OVERFLOW(f, EOF) == EOF)
      return to_do == 0 ? EOF : n - to_do;
	// 对齐
    block_size = f->_IO_buf_end - f->_IO_buf_base;
    do_write = to_do - (block_size >= 128 ? to_do % block_size : 0);

    if (do_write)
    {
      // 调用new_do_write直接输出数据
      count = new_do_write(f, s, do_write);
      to_do -= count;
      if (count < do_write)
        return n - to_do;
    }
    if (to_do) // 将剩余数据复制到文件流缓冲区
      to_do -= _IO_default_xsputn(f, s + do_write, to_do);
  }
  return n - to_do;
}
libc_hidden_ver(_IO_new_file_xsputn, _IO_file_xsputn)

_IO_new_file_overflow

int _IO_new_file_overflow(_IO_FILE *f, int ch)
{
  // 检查是否允许写入
  if (f->_flags & _IO_NO_WRITES)
  {
    f->_flags |= _IO_ERR_SEEN;
    __set_errno(EBADF); // 操作失败,写入错误
    return EOF;
  }
  if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0 || f->_IO_write_base == NULL)
  { 
    // 若未分配写入缓冲区
    if (f->_IO_write_base == NULL)
    {
      _IO_doallocbuf(f); // 初始化分配缓冲区
      _IO_setg(f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base); // 设置缓冲区起点
    }
    // 处理备份缓冲区
    if (__glibc_unlikely(_IO_in_backup(f)))
    {
      size_t nbackup = f->_IO_read_end - f->_IO_read_ptr;
      _IO_free_backup_area(f);
      f->_IO_read_base -= MIN(nbackup, f->_IO_read_base - f->_IO_buf_base);
      f->_IO_read_ptr = f->_IO_read_base;
    }
	
    // 若读取指针到达缓冲区末尾,重置为缓冲区起始位置
    if (f->_IO_read_ptr == f->_IO_buf_end)
      f->_IO_read_end = f->_IO_read_ptr = f->_IO_buf_base;
    // 写入指针和缓冲区指针统一到读取指针位置
    f->_IO_write_ptr = f->_IO_read_ptr;
    f->_IO_write_base = f->_IO_write_ptr;
    f->_IO_write_end = f->_IO_buf_end;
    f->_IO_read_base = f->_IO_read_ptr = f->_IO_read_end;

    // 文件流进入写入模式
    f->_flags |= _IO_CURRENTLY_PUTTING;
    // 文件流处于行缓冲或无缓冲模式
    if (f->_mode <= 0 && f->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
      f->_IO_write_end = f->_IO_write_ptr;
  }
  if (ch == EOF) // 直接将缓冲区内容写入文件,实际调用new_do_write
    return _IO_do_write(f, f->_IO_write_base, f->_IO_write_ptr - f->_IO_write_base);
  // 写入指针已到达缓冲区末尾,表示缓冲区已满,调用_IO_do_flush刷新缓冲区
  if (f->_IO_write_ptr == f->_IO_buf_end)
    if (_IO_do_flush(f) == EOF)
      return EOF; // 刷新失败
  // 将ch写入当前的写入指针位置,写入指针移动
  *f->_IO_write_ptr++ = ch;
  // 无缓冲模式/行缓冲模式且写入\n,立即将缓冲区数据写入文件
  if ((f->_flags & _IO_UNBUFFERED) || ((f->_flags & _IO_LINE_BUF) && ch == '\n'))
    if (_IO_do_write(f, f->_IO_write_base, f->_IO_write_ptr - f->_IO_write_base) == EOF)
      return EOF;
  // 返回写入的字符
  return (unsigned char)ch;
}
libc_hidden_ver(_IO_new_file_overflow, _IO_file_overflow)

new_do_write

// 调用了系统调用 将data写入fp
static size_t new_do_write(FILE *fp, const char *data, size_t to_do)
{
  size_t count;
  // 文件流处于追加模式
  if (fp->_flags & _IO_IS_APPENDING)
    fp->_offset = _IO_pos_BAD; // 偏移量设置为无效值
  
  else if (fp->_IO_read_end != fp->_IO_write_base)
  { // 读取指针和写入缓冲区基址不相同,需要调整文件偏移量
    off64_t new_pos = _IO_SYSSEEK(fp, fp->_IO_write_base - fp->_IO_read_end, 1); // 移到写缓冲区基址
    if (new_pos == _IO_pos_BAD)
      return 0;
    fp->_offset = new_pos;
  }
  // 系统调用,data指向的to_do字节数据写入文件流fp
  count = _IO_SYSWRITE(fp, data, to_do);
  // 若当前流的列信息存在且成功写入数据
  if (fp->_cur_column && count)
    // 调整列信息
    fp->_cur_column = _IO_adjust_column(fp->_cur_column - 1, data, count) + 1;
  // 重置读缓冲区
  _IO_setg(fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
  // 初始化写入缓冲区
  fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_buf_base;
  fp->_IO_write_end = (fp->_mode <= 0 && (fp->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
                           ? fp->_IO_buf_base
                           : fp->_IO_buf_end);
  return count; // 返回写入字节数
}

fclose

fclose

#define fclose(fp) _IO_new_fclose (fp)

_IO_new_fclose

int _IO_new_fclose (FILE *fp)
{
  int status;

  CHECK_FILE(fp, EOF);

#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1)
  if (_IO_vtable_offset (fp) != 0)
    return _IO_old_fclose (fp);
#endif
  if (fp->_flags & _IO_IS_FILEBUF)
    _IO_un_link ((struct _IO_FILE_plus *) fp); // 将文件结构体从_IO_list_all链表中取下

  _IO_acquire_lock (fp);
  if (fp->_flags & _IO_IS_FILEBUF)
    status = _IO_file_close_it (fp); // 关闭文件,释放文件流缓冲区
  else
    status = fp->_flags & _IO_ERR_SEEN ? -1 : 0;
  _IO_release_lock (fp);
  _IO_FINISH (fp);
  if (fp->_mode > 0)
    {
      struct _IO_codecvt *cc = fp->_codecvt;

      __libc_lock_lock (__gconv_lock);
      __gconv_release_step (cc->__cd_in.step);
      __gconv_release_step (cc->__cd_out.step);
      __libc_lock_unlock (__gconv_lock);
    }
  else
    {
      if (_IO_have_backup (fp))
	_IO_free_backup_area (fp);
    }
  _IO_deallocate_file (fp);
  return status;
}
void _IO_un_link (struct _IO_FILE_plus *fp)
{
  if (fp->file._flags & _IO_LINKED)
    {
      FILE **f;
#ifdef _IO_MTSAFE_IO
      _IO_cleanup_region_start_noarg (flush_cleanup);
      _IO_lock_lock (list_all_lock);
      run_fp = (FILE *) fp;
      _IO_flockfile ((FILE *) fp);
#endif
      if (_IO_list_all == NULL)
	;
      else if (fp == _IO_list_all)
	_IO_list_all = (struct _IO_FILE_plus *) _IO_list_all->file._chain;
      else
	for (f = &_IO_list_all->file._chain; *f; f = &(*f)->_chain)
	  if (*f == (FILE *) fp)
	    {
	      *f = fp->file._chain;
	      break;
	    }
      fp->file._flags &= ~_IO_LINKED;
#ifdef _IO_MTSAFE_IO
      _IO_funlockfile ((FILE *) fp);
      run_fp = NULL;
      _IO_lock_unlock (list_all_lock);
      _IO_cleanup_region_end (0);
#endif
    }
}
libc_hidden_def (_IO_un_link)

_IO_new_file_close_it

int _IO_new_file_close_it(FILE *fp)
{
  int write_status;
  if (!_IO_file_is_open(fp))
    return EOF;

  if ((fp->_flags & _IO_NO_WRITES) == 0 && (fp->_flags & _IO_CURRENTLY_PUTTING) != 0)
    write_status = _IO_do_flush(fp);
  else
    write_status = 0;

  _IO_unsave_markers(fp);

  int close_status = ((fp->_flags2 & _IO_FLAGS2_NOCLOSE) == 0
                          ? _IO_SYSCLOSE(fp) // 系统调用关闭文件
                          : 0);
  // 释放文件流缓冲区
  if (fp->_mode > 0)
  {
    if (_IO_have_wbackup(fp))
      _IO_free_wbackup_area(fp);
    _IO_wsetb(fp, NULL, NULL, 0);
    _IO_wsetg(fp, NULL, NULL, NULL);
    _IO_wsetp(fp, NULL, NULL);
  }
  _IO_setb(fp, NULL, NULL, 0);
  _IO_setg(fp, NULL, NULL, NULL);
  _IO_setp(fp, NULL, NULL);

  _IO_un_link((struct _IO_FILE_plus *)fp);
  fp->_flags = _IO_MAGIC | CLOSED_FILEBUF_FLAGS;
  fp->_fileno = -1;
  fp->_offset = _IO_pos_BAD;

  return close_status ? close_status : write_status;
}
libc_hidden_ver(_IO_new_file_close_it, _IO_file_close_it)

puts

_IO_puts

int _IO_puts(const char *str)
{
  int result = EOF;
  _IO_size_t len = strlen(str);
  _IO_acquire_lock(_IO_stdout);

  if ((_IO_vtable_offset(_IO_stdout) != 0 || _IO_fwide(_IO_stdout, -1) == -1) 
    && _IO_sputn(_IO_stdout, str, len) == len
    && _IO_putc_unlocked('\n', _IO_stdout) != EOF)
    result = MIN(INT_MAX, len + 1);

  _IO_release_lock(_IO_stdout);
  return result;
}

_IO_sputn

// 2.27 宏展开
((IO_validate_vtable (
	*(struct _IO_jump_t **) ( // 转换为_IO_jump_t * 类型指针最终传入IO_validate_vtable
		(void *) &(
            // 由 char * 转换为 vtable 对应类型
            *(__typeof__ (((struct _IO_FILE_plus){}).vtable) *)(
                // 获取_IO_stdout基地址,计算出vtable成员在_IO_stdout结构体地址
                ((char *) ((_IO_stdout))) + __builtin_offsetof (struct _IO_FILE_plus, vtable)
            )
        ) 
  		+ (_IO_stdout)->_vtable_offset // 一般为0,应对特殊情况偏移
  	)
))->__xsputn) (_IO_stdout, str, len);

IO_validate_vtable

// 2.27 检查传入的 vtable 指针是否指向 libc 库中定义的有效的 I/O 操作函数表
static inline const struct _IO_jump_t *IO_validate_vtable(const struct _IO_jump_t *vtable)
{
   // 计算 I/O 操作函数表在内存中总大小
   uintptr_t section_length = __stop___libc_IO_vtables - __start___libc_IO_vtables;
   const char *ptr = (const char *)vtable; // 强制转换类型以计算偏移
   uintptr_t offset = ptr - __start___libc_IO_vtables; // 计算偏移量
   if (__glibc_unlikely(offset >= section_length)) // 判断是否超过总大小
      _IO_vtable_check(); // 最终报错
   return vtable;
}

_IO_vtable_check

void attribute_hidden _IO_vtable_check (void)
{
#ifdef SHARED // 仅共享库模式下编译
  void (*flag) (void) = atomic_load_relaxed (&IO_accept_foreign_vtables);
#ifdef PTR_DEMANGLE // 处理函数指针的混淆或解混淆
  PTR_DEMANGLE (flag); // 解码函数指针flag值
#endif
  if (flag == &_IO_vtable_check)
    return;
  {
    Dl_info di; // 存储动态链接器返回的信息,例如符号名和地址
    struct link_map *l;
    if (!rtld_active () // 检查运行时动态链接器是否处于活动状态
        || (_dl_addr (_IO_vtable_check, &di, &l, NULL) != 0
            && l->l_ns != LM_ID_BASE))
      return;
  }

#else // 非共享库模式
  if (__dlopen != NULL)
    return;
#endif
  __libc_fatal ("Fatal error: glibc detected an invalid stdio handle\n");
}

rtld_active

#  define GLRO(name) _rtld_local_ro._##name

static inline bool rtld_active (void)
{
  return GLRO(dl_init_all_dirs) != NULL;
}

_dl_addr

int _dl_addr(const void *address, Dl_info *info,
             struct link_map **mapp, const ElfW(Sym) * *symbolp)
{
  const ElfW(Addr) addr = DL_LOOKUP_ADDRESS(address);
  int result = 0;
/*
    libc-lockP.h 关键exit-hook
    # define __rtld_lock_lock_recursive(NAME) \
      __libc_maybe_call (__pthread_mutex_lock, (&(NAME).mutex), 0)
     替换:
    (({
        __typeof(__pthread_mutex_lock) *_fn = (__pthread_mutex_lock);
        _fn != ((void *) 0) ? (*_fn)(&(_dl_load_lock).mutex) : 0;
    }))
*/
  __rtld_lock_lock_recursive(GL(dl_load_lock));

  struct link_map *l = _dl_find_dso_for_object(addr);

  if (l)
  {
    determine_info(addr, l, info, mapp, symbolp);
    result = 1;
  }

  __rtld_lock_unlock_recursive(GL(dl_load_lock));

  return result;
}
libc_hidden_def(_dl_addr)

flush

_IO_flush_all_lockp

// 程序退出时会调用
int _IO_flush_all_lockp(int do_lock)
{
  int result = 0;
  struct _IO_FILE *fp;
  int last_stamp;

#ifdef _IO_MTSAFE_IO
  __libc_cleanup_region_start(do_lock, flush_cleanup, NULL);
  if (do_lock)
    _IO_lock_lock(list_all_lock);
#endif

  last_stamp = _IO_list_all_stamp;
  fp = (_IO_FILE *)_IO_list_all; // 获取fp
  while (fp != NULL)
  {
    run_fp = fp;
    if (do_lock)
      _IO_flockfile(fp);

    if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
         || (_IO_vtable_offset(fp) == 0 && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base))
#endif
             ) &&
        _IO_OVERFLOW(fp, EOF) == EOF) // 调用_IO_OVERFLOW, 参数为fp,即_IO_FILE结构体指针
      result = EOF;

    if (do_lock)
      _IO_funlockfile(fp);
    run_fp = NULL;

    if (last_stamp != _IO_list_all_stamp)
    {
      /* Something was added to the list.  Start all over again.  */
      fp = (_IO_FILE *)_IO_list_all;
      last_stamp = _IO_list_all_stamp;
    }
    else
      fp = fp->_chain;
  }

#ifdef _IO_MTSAFE_IO
  if (do_lock)
    _IO_lock_unlock(list_all_lock);
  __libc_cleanup_region_end(0);
#endif

  return result;
}

_IO_str_overflow

int _IO_str_overflow (FILE *fp, int c)
{
  int flush_only = c == EOF;
  size_t pos;
  if (fp->_flags & _IO_NO_WRITES)
      return flush_only ? 0 : EOF;
  if ((fp->_flags & _IO_TIED_PUT_GET) && !(fp->_flags & _IO_CURRENTLY_PUTTING))
    {
      fp->_flags |= _IO_CURRENTLY_PUTTING;
      fp->_IO_write_ptr = fp->_IO_read_ptr;
      fp->_IO_read_ptr = fp->_IO_read_end;
    }
  pos = fp->_IO_write_ptr - fp->_IO_write_base;
  if (pos >= (size_t) (_IO_blen (fp) + flush_only))
    {
      if (fp->_flags & _IO_USER_BUF)
	return EOF;
      else
	{
	  char *new_buf;
	  char *old_buf = fp->_IO_buf_base;
	  size_t old_blen = _IO_blen (fp); // _IO_buf_end - _IO_buf_base
	  size_t new_size = 2 * old_blen + 100; // 获取新大小
	  if (new_size < old_blen)
	    return EOF;
	  new_buf = malloc (new_size); // 使用malloc申请新的内存,从tcache中获取
	  if (new_buf == NULL)
	    {
	      return EOF;
	    }
	  if (old_buf)
	    {
	      memcpy (new_buf, old_buf, old_blen); // 从old_buf内容拷贝到新的buf中
	      free (old_buf); // 释放old_buf
	      fp->_IO_buf_base = NULL;
	    }
	  memset (new_buf + old_blen, '\0', new_size - old_blen);

	  _IO_setb (fp, new_buf, new_buf + new_size, 1);
	  fp->_IO_read_base = new_buf + (fp->_IO_read_base - old_buf);
	  fp->_IO_read_ptr = new_buf + (fp->_IO_read_ptr - old_buf);
	  fp->_IO_read_end = new_buf + (fp->_IO_read_end - old_buf);
	  fp->_IO_write_ptr = new_buf + (fp->_IO_write_ptr - old_buf);

	  fp->_IO_write_base = new_buf;
	  fp->_IO_write_end = fp->_IO_buf_end;
	}
    }

  if (!flush_only)
    *fp->_IO_write_ptr++ = (unsigned char) c;
  if (fp->_IO_write_ptr > fp->_IO_read_end)
    fp->_IO_read_end = fp->_IO_write_ptr;
  return c;
}
libc_hidden_def (_IO_str_overflow)

_IO_fflush

int _IO_fflush(FILE *fp)
{
  if (fp == NULL)
    return _IO_flush_all(); // 刷新所有打开可用的文件流
  else
  { // 处理单个流
    int result;
    CHECK_FILE(fp, EOF); // 验证是否有效
    _IO_acquire_lock(fp);
/*
#define _IO_SYNC(FP) JUMP0 (__sync, FP)
扩展到:
((IO_validate_vtable ((*(__typeof__ (((struct _IO_FILE_plus){}).vtable) *)(((char *) ((fp))) + __builtin_offsetof (struct _IO_FILE_plus, vtable)))))->__sync) (fp)
*/
    result = _IO_SYNC(fp) ? EOF : 0; // 调用vtable中的函数: 文件流的 __sync 函数指针
    _IO_release_lock(fp);
    return result;
  }
}
libc_hidden_def(_IO_fflush)

printf

__fxprintf

int __fxprintf(FILE *fp, const char *fmt, ...)
{
  va_list ap;
  va_start(ap, fmt);
  int res = __vfxprintf(fp, fmt, ap, 0);
  va_end(ap);
  return res;
}

__vfxprintf

int __vfxprintf(FILE *fp, const char *fmt, va_list ap,
                unsigned int mode_flags)
{
  if (fp == NULL)
    fp = stderr;
  _IO_flockfile(fp);
  int res = locked_vfxprintf(fp, fmt, ap, mode_flags);
  _IO_funlockfile(fp);
  return res;
}

locked_vfxprintf

// # define vfprintf	__vfprintf_internal

static int
locked_vfxprintf(FILE *fp, const char *fmt, va_list ap,
                 unsigned int mode_flags)
{
  if (_IO_fwide(fp, 0) <= 0)
    return __vfprintf_internal(fp, fmt, ap, mode_flags);

  wchar_t *wfmt;
  mbstate_t mbstate;
  int res;
  int used_malloc = 0;
  size_t len = strlen(fmt) + 1;

  if (__glibc_unlikely(len > SIZE_MAX / sizeof(wchar_t)))
  {
    __set_errno(EOVERFLOW);
    return -1;
  }
  if (__libc_use_alloca(len * sizeof(wchar_t)))
    wfmt = alloca(len * sizeof(wchar_t));
  else if ((wfmt = malloc(len * sizeof(wchar_t))) == NULL)
    return -1;
  else
    used_malloc = 1;

  memset(&mbstate, 0, sizeof mbstate);
  res = __mbsrtowcs(wfmt, &fmt, len, &mbstate);

  if (res != -1)
    res = __vfwprintf_internal(fp, wfmt, ap, mode_flags);

  if (used_malloc)
    free(wfmt);

  return res;
}

__printf

int __printf (const char *format, ...)
{
  va_list arg;
  int done;

  va_start (arg, format);
  done = __vfprintf_internal (stdout, format, arg, 0);
  va_end (arg);

  return done;
}

__register_printf_function

int __register_printf_function(int spec, printf_function converter, 
                               printf_arginfo_function arginfo){
	return __register_printf_specifier(spec, converter,
                                       (printf_arginfo_size_function *)arginfo);
}
weak_alias(__register_printf_function, register_printf_function)

__register_printf_specifier

int __register_printf_specifier(int spec, printf_function converter,
                                printf_arginfo_size_function arginfo){
  // spec: fmt的ascii码, converter与arginfo: 要绑定的函数与参数指针
  if (spec < 0 || spec > (int)UCHAR_MAX) // UCHAR_MAX: SCHAR_MAX[0x7f] * 2 + 1 = 0xff
  {
    __set_errno(EINVAL);
    return -1;
  }

  int result = 0;
  __libc_lock_lock(lock);
  
  // 若该表是空的
  if (__printf_function_table == NULL)
  {
    // calloc分配一块内存对arginfo表进行初始化,一次性分配两个指针份的堆块
    __printf_arginfo_table = (printf_arginfo_size_function **)
        calloc(UCHAR_MAX + 1, sizeof(void *) * 2);// void * 为8字节,共申请0x10 * 0x100
    if (__printf_arginfo_table == NULL)
    {
      result = -1;
      goto out;
    }
	// 初始化function表,紧邻arginfo表
    __printf_function_table = (printf_function **)(__printf_arginfo_table + UCHAR_MAX + 1);
  }
  // 绑定
  __printf_function_table[spec] = converter;
  __printf_arginfo_table[spec] = arginfo;

out:
  __libc_lock_unlock(lock);

  return result;
}
libc_hidden_def(__register_printf_specifier)
    weak_alias(__register_printf_specifier, register_printf_specifier)

assert

__malloc_assert

static void
__malloc_assert (const char *assertion, const char *file, unsigned int line,
		 const char *function)
{
  (void) __fxprintf (NULL, "%s%s%s:%u: %s%sAssertion `%s' failed.\n",
		     __progname, __progname[0] ? ": " : "",
		     file, line,
		     function ? function : "", function ? ": " : "",
		     assertion);
  fflush (stderr);
  abort ();
}
static ssize_t
_IO_cookie_read(FILE *fp, void *buf, ssize_t size)
{
  // 将fp转换为_IO_cookie_file *类型的cfile结构体
  struct _IO_cookie_file *cfile = (struct _IO_cookie_file *)fp;
  // 通过偏移取出存放在read对应对象中的函数指针
  cookie_read_function_t *read_cb = cfile->__io_functions.read;
#ifdef PTR_DEMANGLE
  PTR_DEMANGLE(read_cb); // 进行了加密
#endif

  if (read_cb == NULL)
    return -1;

  return read_cb(cfile->__cookie, buf, size); // 调用该指针,传入参数为__cookie
}
static ssize_t
_IO_cookie_write(FILE *fp, const void *buf, ssize_t size)
{
  struct _IO_cookie_file *cfile = (struct _IO_cookie_file *)fp;
  cookie_write_function_t *write_cb = cfile->__io_functions.write;
#ifdef PTR_DEMANGLE
  // 对指针进行加密:取出对应函数指针,将函数指针循环右移11位,并与`fs:[0x30]`异或得到真正函数地址
  PTR_DEMANGLE(write_cb); 
#endif

  if (write_cb == NULL)
  {
    fp->_flags |= _IO_ERR_SEEN;
    return 0;
  }

  ssize_t n = write_cb(cfile->__cookie, buf, size);
  if (n < size)
    fp->_flags |= _IO_ERR_SEEN;

  return n;
}
static int
_IO_cookie_close(FILE *fp)
{
  struct _IO_cookie_file *cfile = (struct _IO_cookie_file *)fp;
  cookie_close_function_t *close_cb = cfile->__io_functions.close;
#ifdef PTR_DEMANGLE
  PTR_DEMANGLE(close_cb);
#endif

  if (close_cb == NULL)
    return 0;

  return close_cb(cfile->__cookie);
}
static off64_t
_IO_cookie_seek(FILE *fp, off64_t offset, int dir)
{
  struct _IO_cookie_file *cfile = (struct _IO_cookie_file *)fp;
  cookie_seek_function_t *seek_cb = cfile->__io_functions.seek;
#ifdef PTR_DEMANGLE
  PTR_DEMANGLE(seek_cb);
#endif

  return ((seek_cb == NULL || (seek_cb(cfile->__cookie, &offset, dir) == -1) || offset == (off64_t)-1)
              ? _IO_pos_BAD
              : offset);
}

house of apple

记录相关一些函数

_IO_wstrn_overflow

static wint_t _IO_wstrn_overflow (FILE *fp, wint_t c)
{
  // 先将 fp 强制转换为_IO_wstrnfile *指针
  _IO_wstrnfile *snf = (_IO_wstrnfile *) fp;

  if (fp->_wide_data->_IO_buf_base != snf->overflow_buf) // 一般都成立
    {
      _IO_wsetb (fp, snf->overflow_buf,
		 snf->overflow_buf + (sizeof (snf->overflow_buf)
				      / sizeof (wchar_t)), 0);
	  // 成立会对以下四个值赋值为`snf->overflow_buf`或相关偏移值
      fp->_wide_data->_IO_write_base = snf->overflow_buf;
      fp->_wide_data->_IO_read_base = snf->overflow_buf;
      fp->_wide_data->_IO_read_ptr = snf->overflow_buf;
      fp->_wide_data->_IO_read_end = (snf->overflow_buf
				      + (sizeof (snf->overflow_buf)
					 / sizeof (wchar_t)));
    }
  // 赋值
  fp->_wide_data->_IO_write_ptr = snf->overflow_buf;
  fp->_wide_data->_IO_write_end = snf->overflow_buf;

  return c;
}

_IO_wsetb

void _IO_wsetb (FILE *f, wchar_t *b, wchar_t *eb, int a)
{
  if (f->_wide_data->_IO_buf_base && !(f->_flags2 & _IO_FLAGS2_USER_WBUF))
    free (f->_wide_data->_IO_buf_base); // 其不为0的时候不要执行到这里
  f->_wide_data->_IO_buf_base = b;
  f->_wide_data->_IO_buf_end = eb;
  if (a)
    f->_flags2 &= ~_IO_FLAGS2_USER_WBUF;
  else
    f->_flags2 |= _IO_FLAGS2_USER_WBUF;
}

__libio_codecvt_in

enum __codecvt_result
__libio_codecvt_in (struct _IO_codecvt *codecvt, __mbstate_t *statep,
		    const char *from_start, const char *from_end,
		    const char **from_stop,
		    wchar_t *to_start, wchar_t *to_end, wchar_t **to_stop)
{
  enum __codecvt_result result;

  struct __gconv_step *gs = codecvt->__cd_in.step; // // gs 源自第一个参数
  int status;
  size_t dummy;
  const unsigned char *from_start_copy = (unsigned char *) from_start;

  codecvt->__cd_in.step_data.__outbuf = (unsigned char *) to_start;
  codecvt->__cd_in.step_data.__outbufend = (unsigned char *) to_end;
  codecvt->__cd_in.step_data.__statep = statep;

  __gconv_fct fct = gs->__fct;
  // 若其不为空,用__pointer_guard解密,设置为NULL可绕过解密
  if (gs->__shlib_handle != NULL)
    PTR_DEMANGLE (fct);
  // 函数指针调用,该宏实际调用fct(gs, ...)
  status = DL_CALL_FCT (fct,
			(gs, &codecvt->__cd_in.step_data, &from_start_copy,
			 (const unsigned char *) from_end, NULL,
			 &dummy, 0, 0));

  *from_stop = (const char *) from_start_copy;
  *to_stop = (wchar_t *) codecvt->__cd_in.step_data.__outbuf;

  switch (status)
    {
    case __GCONV_OK:
    case __GCONV_EMPTY_INPUT:
      result = __codecvt_ok;
      break;

    case __GCONV_FULL_OUTPUT:
    case __GCONV_INCOMPLETE_INPUT:
      result = __codecvt_partial;
      break;

    default:
      result = __codecvt_error;
      break;
    }

  return result;
}

ld.so

结构

rtld_global

struct rtld_global
{
#endif
#ifdef SHARED
# define DL_NNS 16
#else
# define DL_NNS 1
#endif
  EXTERN struct link_namespaces
  {
    struct link_map *_ns_loaded;
    unsigned int _ns_nloaded;
    struct r_scope_elem *_ns_main_searchlist;

    unsigned int _ns_global_scope_alloc;
    unsigned int _ns_global_scope_pending_adds;

    struct link_map *libc_map;
    struct unique_sym_table
    {
      __rtld_lock_define_recursive (, lock)
      struct unique_sym
      {
	uint32_t hashval;
	const char *name;
	const ElfW(Sym) *sym;
	const struct link_map *map;
      } *entries;
      size_t size;
      size_t n_elements;
      void (*free) (void *);
    } _ns_unique_sym_table;

    struct r_debug_extended _ns_debug;
  } _dl_ns[DL_NNS];

  EXTERN size_t _dl_nns;
  ......

_rtld_global

可用 gdb 查看p _rtld_global

// glibc-2.38 rtld.c 该变量在 ld.so 内存地址范围内

struct rtld_global _rtld_global =
  {
#include <dl-procruntime.c>
    ._dl_stack_flags = DEFAULT_STACK_PERMS,
#ifdef _LIBC_REENTRANT
    ._dl_load_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER,
    ._dl_load_write_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER,
    ._dl_load_tls_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER,
#endif
    ._dl_nns = 1,
    ._dl_ns =
    {
#ifdef _LIBC_REENTRANT
      [LM_ID_BASE] = { ._ns_unique_sym_table
		       = { .lock = _RTLD_LOCK_RECURSIVE_INITIALIZER } }
#endif
    }
  };

_dl_ns

// _dl_ns 为 link_namespaces 结构体 16 项数组
pwndbg> p &_rtld_global._dl_ns
$1 = (struct link_namespaces (*)[16]) 0x7ffff7ffd040 <_rtld_local>

pwndbg> p _rtld_global._dl_ns
$2 = {{
    _ns_loaded = 0x7ffff7ffe168,
    _ns_nloaded = 4,
    _ns_main_searchlist = 0x7ffff7ffe420,
    _ns_global_scope_alloc = 0,
    _ns_unique_sym_table = {
      lock = {
        mutex = {
          __data = {
            __lock = 0,
            __count = 0,
            __owner = 0,
            __nusers = 0,
            __kind = 1,
            __spins = 0,
            __elision = 0,
            __list = {
              __prev = 0x0,
              __next = 0x0
            }
          },
          __size = '\000' <repeats 16 times>, "\001", '\000' <repeats 22 times>,
          __align = 0
        }
      },
      entries = 0x0,
      size = 0,
      n_elements = 0,
      free = 0x0
    },
    _ns_debug = {
      r_version = 0,
      r_map = 0x0,
      r_brk = 0,
      r_state = RT_CONSISTENT,
      r_ldbase = 0
    }
  }, {
    _ns_loaded = 0x0,
    _ns_nloaded = 0,
    _ns_main_searchlist = 0x0,
    _ns_global_scope_alloc = 0,
    _ns_unique_sym_table = {
      lock = {
        mutex = {
          __data = {
            __lock = 0,
            __count = 0,
            __owner = 0,
            __nusers = 0,
            __kind = 0,
            __spins = 0,
            __elision = 0,
            __list = {
              __prev = 0x0,
              __next = 0x0
            }
          },
          __size = '\000' <repeats 39 times>,
          __align = 0
        }
      },
      entries = 0x0,
      size = 0,
      n_elements = 0,
      free = 0x0
    },
    _ns_debug = {
      r_version = 0,
      r_map = 0x0,
      r_brk = 0,
      r_state = RT_CONSISTENT,
      r_ldbase = 0
    }
  } <repeats 15 times>}

第 0 项

pwndbg> p _rtld_global._dl_ns[0]
$9 = {
  _ns_loaded = 0x7ffff7ffe168, // (struct link_map *) 指向 link_map 结构体的指针
  _ns_nloaded = 4, // 表示 _ns_nloaded 有多少 link_map 结构体
  _ns_main_searchlist = 0x7ffff7ffe420,
  _ns_global_scope_alloc = 0,
  _ns_unique_sym_table = {
    lock = {
      mutex = {
        __data = {
          __lock = 0,
          __count = 0,
          __owner = 0,
          __nusers = 0,
          __kind = 1,
          __spins = 0,
          __elision = 0,
          __list = {
            __prev = 0x0,
            __next = 0x0
          }
        },
        __size = '\000' <repeats 16 times>, "\001", '\000' <repeats 22 times>,
        __align = 0
      }
    },
    entries = 0x0,
    size = 0,
    n_elements = 0,
    free = 0x0
  },
  _ns_debug = {
    r_version = 0,
    r_map = 0x0,
    r_brk = 0,
    r_state = RT_CONSISTENT,
    r_ldbase = 0
  }
}

_dl_nns

pwndbg> p &_rtld_global._dl_nns
$3 = (size_t *) 0x7ffff7ffd940 <_rtld_local+2304>
pwndbg> p _rtld_global._dl_nns
$4 = 1 // 表明 _dl_ns 数组中有效的项,一般为第0项
struct link_map
{
   ElfW(Addr) l_addr; // 程序加载的内存基址
   char *l_name;
   ElfW(Dyn) * l_ld;
   struct link_map *l_next, *l_prev; // 双向链表结构,分别指向下一个和前一个link_map结构体

   struct link_map *l_real; // 标识该共享库的真实映射

   Lmid_t l_ns;

   struct libname_list *l_libname;
   // l_info 指向 ELF 文件的Dynamic节,包括指向 DT_INIT、DT_FINI、DT_FINI_ARRAY 和其他Dynamic节的指针
   ElfW(Dyn) * l_info[DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM + DT_ADDRNUM];
   const ElfW(Phdr) * l_phdr;
   ElfW(Addr) l_entry;
   ElfW(Half) l_phnum;
   ElfW(Half) l_ldnum;

   struct r_scope_elem l_searchlist;

   struct r_scope_elem l_symbolic_searchlist;

   struct link_map *l_loader;

   struct r_found_version *l_versions;
   unsigned int l_nversions;

   Elf_Symndx l_nbuckets;
   Elf32_Word l_gnu_bitmask_idxbits;
   Elf32_Word l_gnu_shift;
   const ElfW(Addr) * l_gnu_bitmask;
   union
   {
      const Elf32_Word *l_gnu_buckets;
      const Elf_Symndx *l_chain;
   };
   union
   {
      const Elf32_Word *l_gnu_chain_zero;
      const Elf_Symndx *l_buckets;
   };

   unsigned int l_direct_opencount;
   enum
   {
      lt_executable,
      lt_library,
      lt_loaded
   } l_type : 2;
   unsigned int l_dt_relr_ref : 1;
   unsigned int l_relocated : 1;
   unsigned int l_init_called : 1;
   unsigned int l_global : 1;
   unsigned int l_reserved : 2;
   unsigned int l_main_map : 1;
   unsigned int l_visited : 1;
   unsigned int l_map_used : 1;
   unsigned int l_map_done : 1;
   unsigned int l_phdr_allocated : 1;
   unsigned int l_soname_added : 1;
   unsigned int l_faked : 1;
   unsigned int l_need_tls_init : 1;
   unsigned int l_auditing : 1;
   unsigned int l_audit_any_plt : 1;
   unsigned int l_removed : 1;
   unsigned int l_contiguous : 1;
   unsigned int l_free_initfini : 1;
   unsigned int l_ld_readonly : 1;
   unsigned int l_find_object_processed : 1;
   bool l_nodelete_active;
   bool l_nodelete_pending;

#include <link_map.h>

   struct r_search_path_struct l_rpath_dirs;

   struct reloc_result
   {
      DL_FIXUP_VALUE_TYPE addr;
      struct link_map *bound;
      unsigned int boundndx;
      uint32_t enterexit;
      unsigned int flags;
      unsigned int init;
   } *l_reloc_result;

   ElfW(Versym) * l_versyms;

   const char *l_origin;

   ElfW(Addr) l_map_start, l_map_end;

   ElfW(Addr) l_text_end;

   struct r_scope_elem *l_scope_mem[4];

   size_t l_scope_max;
   struct r_scope_elem **l_scope;

   struct r_scope_elem *l_local_scope[2];

   struct r_file_id l_file_id;
   struct r_search_path_struct l_runpath_dirs;

   struct link_map **l_initfini;
   struct link_map_reldeps
   {
      unsigned int act;
      struct link_map *list[];
   } *l_reldeps;
   unsigned int l_reldepsmax;

   unsigned int l_used;

   ElfW(Word) l_feature_1;
   ElfW(Word) l_flags_1;
   ElfW(Word) l_flags;

   int l_idx;

   struct link_map_machine l_mach;

   struct
   {
      const ElfW(Sym) * sym;
      int type_class;
      struct link_map *value;
      const ElfW(Sym) * ret;
   } l_lookup_cache;

   void *l_tls_initimage;
   size_t l_tls_initimage_size;
   size_t l_tls_blocksize;
   size_t l_tls_align;
   size_t l_tls_firstbyte_offset;
#ifndef NO_TLS_OFFSET
#define NO_TLS_OFFSET 0
#endif
#ifndef FORCED_DYNAMIC_TLS_OFFSET
#if NO_TLS_OFFSET == 0
#define FORCED_DYNAMIC_TLS_OFFSET -1
#elif NO_TLS_OFFSET == -1
#define FORCED_DYNAMIC_TLS_OFFSET -2
#else
#error "FORCED_DYNAMIC_TLS_OFFSET is not defined"
#endif
#endif
   ptrdiff_t l_tls_offset;
   size_t l_tls_modid;
   size_t l_tls_dtor_count;
   ElfW(Addr) l_relro_addr;
   size_t l_relro_size;

   unsigned long long int l_serial;
};

2.38

_dl_fini

程序退出或动态库卸载时调用所有已加载共享库( .so 文件)的析构函数

void _dl_fini (void)
{
#ifdef SHARED
  int do_audit = 0; // 标记是否进行审计操作
 again: // 用于重试的逻辑
#endif
  // GL(dl_nns): 命名空间的数量,一般为 1,此时 ns 为 0
  for (Lmid_t ns = GL(dl_nns) - 1; ns >= 0; --ns) // 从最后一个命名空间开始遍历所有命名空间
    {
      __rtld_lock_lock_recursive (GL(dl_load_lock)); // 加锁

      unsigned int nloaded = GL(dl_ns)[ns]._ns_nloaded; // 当前命名空间中加载的共享对象数量, 即多少个link_map
      if (nloaded == 0
#ifdef SHARED
	  || GL(dl_ns)[ns]._ns_loaded->l_auditing != do_audit
#endif
	  )
	__rtld_lock_unlock_recursive (GL(dl_load_lock)); // 释放锁
      else
	{
#ifdef SHARED
	  _dl_audit_activity_nsid (ns, LA_ACT_DELETE); // 审计活动标记为删除(LA_ACT_DELETE), 	记录审计日志
#endif

	  struct link_map *maps[nloaded]; // maps 数组收集当前命名空间中所有加载的共享对象

	  unsigned int i;
	  struct link_map *l;
	  assert (nloaded != 0 || GL(dl_ns)[ns]._ns_loaded == NULL);
      // 遍历链表中每一个link_map结构体
	  for (l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next)
	    if (l == l->l_real) // 需要指向自身
	      {
		assert (i < nloaded);

		maps[i] = l; // 将指针存到 maps 中初始化
		l->l_idx = i; // 赋值索引
		++i; // 索引自增
		++l->l_direct_opencount;
	      }
      // 检查	ns = 0, LM_ID_BASE = 0
	  assert (ns != LM_ID_BASE || i == nloaded);
	  assert (ns == LM_ID_BASE || i == nloaded || i == nloaded - 1);
	  unsigned int nmaps = i;

      // 对 maps 数组中共享对象进行排序,确保按正确的顺序调用析构函数
	  _dl_sort_maps (maps, nmaps, (ns == LM_ID_BASE), true); // 伪造变化可能卡在此处

	  __rtld_lock_unlock_recursive (GL(dl_load_lock));

	  for (i = 0; i < nmaps; ++i) // 遍历maps数组
	    {
	      struct link_map *l = maps[i]; // 取出结构体

	      if (l->l_init_called)
		{
		  _dl_call_fini (l); // 调用每个加载对象的析构函数
#ifdef SHARED
		  _dl_audit_objclose (l);
#endif
		}
	      --l->l_direct_opencount;
	    }

#ifdef SHARED
	  _dl_audit_activity_nsid (ns, LA_ACT_CONSISTENT);
#endif
	}
    }

#ifdef SHARED
  if (! do_audit && GLRO(dl_naudit) > 0)
    {
      do_audit = 1;
      goto again;
    }

  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_STATISTICS))
    _dl_debug_printf ("\nruntime linker statistics:\n"
		      "           final number of relocations: %lu\n"
		      "final number of relocations from cache: %lu\n",
		      GL(dl_num_relocations),
		      GL(dl_num_cache_relocations));
#endif
}

_dl_call_fini

void _dl_call_fini(void *closure_map)
{
  struct link_map *map = closure_map;

  if (__glibc_unlikely(GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS))
    _dl_debug_printf("\ncalling fini: %s [%lu]\n\n", map->l_name, map->l_ns);

  map->l_init_called = 0;

  ElfW(Dyn) *fini_array = map->l_info[DT_FINI_ARRAY]; // 取出第26项
  if (fini_array != NULL)
  {
    ElfW(Addr) *array = (ElfW(Addr) *)(map->l_addr + fini_array->d_un.d_ptr); // 相加找到array指针数组
    size_t sz = (map->l_info[DT_FINI_ARRAYSZ]->d_un.d_val / sizeof(ElfW(Addr))); // 第28项获取大小size

    while (sz-- > 0) // 从后往前依次调用array数组中的函数
      ((fini_t)array[sz])();
  }

  ElfW(Dyn) *fini = map->l_info[DT_FINI];
  if (fini != NULL)
    DL_CALL_DT_FINI(map, ((void *)map->l_addr + fini->d_un.d_ptr));
}

TLS

结构

tcbhead_t

typedef struct {
  void *tcb;    /* 指向TCB */
  dtv_t *dtv;       /* 指向dtv数组 */
  void *self;   /* 指向自身  */
  int multiple_threads;
  int gscope_flag;
  uintptr_t sysinfo;
  uintptr_t stack_guard;    /* canary值 */
  uintptr_t pointer_guard;  /* 用于保护指针 */
  //...
} tcbhead_t;