1. 程式人生 > 實用技巧 >house of orange 原始碼分析

house of orange 原始碼分析

目錄

前言

原來只知道 house of orange 打 unsorted bin ,碰到題目發現還可以打 fast bin ,今天就具體研究一下原始碼(glibc-2.23)。

分析

當所有的 bins 和 top chunk 都不滿足分配要求,且 fast bin 合併後,再次迴圈中也找不到滿足分配要求的 bin ,就會呼叫 sysmalloc 函式來分配空間。

if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)) //使用 top chunk 分配
{
      ......
}

else if (have_fastchunks (av)) //合併 fast bin 後再次迴圈看是否有滿足分配要求的 bin
{
      ......
}

else
{
      void *p = sysmalloc (nb, av);
      if (p != NULL)
            alloc_perturb (p, bytes);
      return p;
}

跟進 sysmalloc 函式看看

if (av == NULL
      || ((unsigned long) (nb) >= (unsigned long) (mp_.mmap_threshold)
	  && (mp_.n_mmaps < mp_.n_mmaps_max))) //使用 mmap 分配記憶體
{
      ......
}

首先如果所需分配的大小滿足 mmap 的閾值,而且 mmap 分配的記憶體塊數小於最大值,就會使用 mmap() 向作業系統申請記憶體進行分配。

  old_top = av->top;
  old_size = chunksize (old_top);
  old_end = (char *) (chunk_at_offset (old_top, old_size));
  brk = snd_brk = (char *) (MORECORE_FAILURE);

儲存當前 top chunk 的指標,大小和結束地址到臨時變數中。

  assert ((old_top == initial_top (av) && old_size == 0) ||
          ((unsigned long) (old_size) >= MINSIZE &&
           prev_inuse (old_top) &&
           ((unsigned long) old_end & (pagesize - 1)) == 0));

  /* Precondition: not enough current space to satisfy nb request */
  assert ((unsigned long) (old_size) < (unsigned long) (nb + MINSIZE));

檢查 top chunk 的合法性,如果第一次呼叫本函式, top chunk 可能沒有初始化,可能 old_size 為 0 ,如果 top chunk 已經初始化,則 top chunk 的大小必須大於等於 MINSIZE 。 prev_inuse 位必須置 1 ,top chunk 的結尾地址必須是頁對齊的。
top chunk 的大小需要小於所需分配大小 + MINSIZE ,這是因為如果 top chunk 的大小需要大於等於所需分配大小 + MINSIZE ,那麼在 _int_malloc 中就應該已經切割 top chunk 來分配 chunk 了。

if (av != &main_arena) //當前分配區不是主分配區時,進入這個分支分配記憶體
{
      ......
}

else //當前分配區為主分配區,進入這個分支分配記憶體
{
      ......

      if (brk != (char *) (MORECORE_FAILURE)) // brk 合法,說明使用 sbrk() 或 mmap() 分配成功。       
      {
          if (mp_.sbrk_base == 0) //如果 sbrk_base 還沒有初始化,更新 sbrk_base 和當前分配區的記憶體分配總量。 
                mp_.sbrk_base = brk;
          av->system_mem += size;            
      }    
      
      if (brk == old_end && snd_brk == (char *) (MORECORE_FAILURE)) //如果 sbrk()分配成功,更新 top chunk 的大小,
                                                                    //並設定 top chunk 的前一個 chunk 處於 inuse 狀態。
             set_head (old_top, (size + old_size) | PREV_INUSE);
      
      else if (contiguous (av) && old_size && brk < old_end) //如果當前分配區可分配連續虛擬記憶體,原 top chunk 的大小大於 0,
      {                                                      //但新的 brk 值小於原 top chunk 的結束地址,則報錯。
             /* Oops!  Someone else killed our space..  Can't touch anything.  */
              malloc_printerr (3, "break adjusted to free malloc space", brk,av);    
      }
     
      else //執行到這個分支,意味著 sbrk()返回的 brk 值大於原 top chunk 的結束地址,
      {    //那麼新的地址與原 top chunk 的地址不連續。
            ......

           if (snd_brk != (char *) (MORECORE_FAILURE) //如果 brk 的結束地址合法  
            {
                  ......
                  
                  if (old_size != 0) //如果原來 top chunk 的大小不為 0
                  {
                        old_size = (old_size - 4 * SIZE_SZ) & ~MALLOC_ALIGN_MASK;
                        set_head (old_top, old_size | PREV_INUSE);
                        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);
                        }
                  }
            }           
      }
      
}

關鍵程式碼在於 if (old_size != 0) 分支,將 top chunk 分為空閒 chunk 和 fencepost 兩部分, fencepost 為 MINISIZE 大小,設定 fencepost 裡的內容,空閒 chunk 部分呼叫 _int_free 函式釋放。
(未完待續...)

內容來源

《 glibc 記憶體管理 ptmalloc 原始碼分析》