<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    堆相關數據結構

    malloc_chunk

    看一下源碼

    /*
      This struct declaration is misleading (but accurate and necessary).
      It declares a "view" into memory allowing access to necessary
      fields at known offsets from a given base. See explanation below.
    */
    struct malloc_chunk {
    
      INTERNAL_SIZE_T      prev_size;  /* Size of previous chunk (if free).  */
      INTERNAL_SIZE_T      size;       /* Size in bytes, including overhead. */
    
      struct malloc_chunk* fd;         /* double links -- used only if free. */
      struct malloc_chunk* bk;
    
      /* Only used for large blocks: pointer to next larger size.  */
      struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
      struct malloc_chunk* bk_nextsize;
    };
    /* INTERNAL_SIZE_T is the word-size used for internal bookkeeping of
       chunk sizes.
       The default version is the same as size_t.
       While not strictly necessary, it is best to define this as an
       unsigned type, even if size_t is a signed type. This may avoid some
       artificial size limitations on some systems.
       On a 64-bit machine, you may be able to reduce malloc overhead by
       defining INTERNAL_SIZE_T to be a 32 bit `unsigned int' at the
       expense of not being able to handle more than 2^32 of malloced
       space. If this limitation is acceptable, you are encouraged to set
       this unless you are on a platform requiring 16byte alignments. In
       this case the alignment requirements turn out to negate any
       potential advantages of decreasing size_t word size.
       Implementors: Beware of the possible combinations of:
         - INTERNAL_SIZE_T might be signed or unsigned, might be 32 or 64 bits,
           and might be the same width as int or as long
         - size_t might have different width and signedness as INTERNAL_SIZE_T
         - int and long might be 32 or 64 bits, and might be the same width
       To deal with this, most comparisons and difference computations
       among INTERNAL_SIZE_Ts should cast them to unsigned long, being
       aware of the fact that casting an unsigned int to a wider long does
       not sign-extend. (This also makes checking for negative numbers
       awkward.) Some of these casts result in harmless compiler warnings
       on some systems.  */
    #ifndef INTERNAL_SIZE_T
    # define INTERNAL_SIZE_T size_t
    #endif
    
    /* The corresponding word size.  */
    #define SIZE_SZ (sizeof (INTERNAL_SIZE_T))
    
    /* The corresponding bit mask value.  */
    #define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1)
    

    在程序的執行過程中,我們稱由 malloc 申請的內存為 chunk 。這塊內存在 ptmalloc 內部用 malloc_chunk 結構體來表示。當程序申請的 chunk 被 free 后,會被加入到相應的空閑管理列表中。

    非常有意思的是,無論一個 chunk 的大小如何,處于分配狀態還是釋放狀態,它們都使用一個統一的結構。雖然它們使用了同一個數據結構,但是根據是否被釋放,它們的表現形式會有所不同。

    源碼的每個字段的具體的解釋如下

      INTERNAL_SIZE_T      prev_size;  /* Size of previous chunk (if free).  */
      INTERNAL_SIZE_T      size;       /* Size in bytes, including overhead. */ 
    • prev_size, 如果該 chunk 的物理相鄰的前一地址 chunk(兩個指針的地址差值為前一 chunk 大小)是空閑的話,那該字段記錄的是前一個 chunk 的大小 (包括 chunk 頭)。否則,該字段可以用來存儲物理相鄰的前一個 chunk 的數據。這里的前一 chunk 指的是較低地址的 chunk
    • size ,該 chunk 的大小,大小必須是 2 * SIZE_SZ 的整數倍。如果申請的內存大小不是 2 * SIZE_SZ 的整數倍,會被轉換滿足大小的最小的 2 * SIZE_SZ 的倍數。32 位系統中,SIZE_SZ 是 4;64 位系統中,SIZE_SZ 是 8。 該字段的低三個比特位對 chunk 的大小沒有影響,它們從高到低分別表示
      • NON_MAIN_ARENA,記錄當前 chunk 是否不屬于主線程,1 表示不屬于,0 表示屬于。
      • IS_MAPPED,記錄當前 chunk 是否是由 mmap 分配的。
      • PREV_INUSE,記錄前一個 chunk 塊是否被分配。一般來說,堆中第一個被分配的內存塊的 size 字段的 P 位都會被設置為 1,以便于防止訪問前面的非法內存。當一個 chunk 的 size 的 P 位為 0 時,我們能通過 prev_size 字段來獲取上一個 chunk 的大小以及地址。這也方便進行空閑 chunk 之間的合并。
      struct malloc_chunk* fd;         /* double links -- used only if free. */
      struct malloc_chunk* bk;
    • fd,bk。 chunk 處于分配狀態時,從 fd 字段開始是用戶的數據。chunk 空閑時,會被添加到對應的空閑管理鏈表中,其字段的含義如下
      • fd 指向下一個(非物理相鄰)空閑的 chunk
      • bk 指向上一個(非物理相鄰)空閑的 chunk
      • 通過 fd 和 bk 可以將空閑的 chunk 塊加入到空閑的 chunk 塊鏈表進行統一管理
      /* Only used for large blocks: pointer to next larger size.  */
      struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
      struct malloc_chunk* bk_nextsize;
    • fd_nextsize, bk_nextsize,也是只有 chunk 空閑的時候才使用,不過其用于較大的 chunk(large chunk)。
      • fd_nextsize 指向前一個與當前 chunk 大小不同的第一個空閑塊,不包含 bin 的頭指針。
      • bk_nextsize 指向后一個與當前 chunk 大小不同的第一個空閑塊,不包含 bin 的頭指針。
      • 一般空閑的 large chunk 在 fd 的遍歷順序中,按照由大到小的順序排列。這樣做可以避免在尋找合適 chunk 時挨個遍歷。

    一個已經分配的 chunk 的樣子如下。我們稱前兩個字段稱為 chunk header,后面的部分稱為 user data。每次 malloc 申請得到的內存指針,其實指向 user data 的起始處。

    當一個 chunk 處于使用狀態時,它的下一個 chunk 的 prev_size 域無效,所以下一個 chunk 的該部分也可以被當前 chunk 使用。這就是 chunk 中的空間復用。

    堆相關數據結構

    被釋放的 chunk 被記錄在鏈表中(可能是循環雙向鏈表,也可能是單向鏈表)。具體結構如下

    堆相關數據結構

    可以發現,如果一個 chunk 處于 free 狀態,那么會有兩個位置記錄其相應的大小

    1. 本身的 size 字段會記錄,

    2. 它后面的 chunk 會記錄。

    一般情況下,物理相鄰的兩個空閑 chunk 會被合并為一個 chunk 。堆管理器會通過 prev_size 字段以及 size 字段合并兩個物理相鄰的空閑 chunk 塊。

    參考

    本文章首發在 網安wangan.com 網站上。

    上一篇 下一篇
    討論數量: 0
    只看當前版本


    暫無話題~
    亚洲 欧美 自拍 唯美 另类