深入理解 glibc malloc

wyzsk發表於2020-08-19
作者: 新一葛格 · 2015/06/15 10:18

0x00 前言


近期的在看 pwn 的一些東西,發現無論是實際場景中,還是 CTF 中,堆的利用越來越多,又由於各種環境下堆的實現都不太一樣,因此就讓皂皂翻譯這篇文章, 我也對本文作了潤色修改,以饗讀者。

原文:https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/comment-page-1/

我一直在執著於堆的一些問題。比如以下幾個

  • 堆的記憶體怎樣從核心中申請的?
  • 怎樣有效地進行記憶體管理?
  • 堆記憶體是透過核心,庫還是堆本身進行管理?
  • 堆的一些相關問題能被利用嗎?

雖然之前經常在想這些問題,但是光想並沒有什麼卵用。正好,最近我找到了點時間來好好思考這些問題。所以現在我就來分享一下這些知識的總結。此外,還有很多可用的記憶體分配器:

  • dlmalloc - 通用分配器
  • ptmallac2- glibc
  • jemalloc - FreeBSD & Firefox
  • tcmalloc - Google
  • libumem - Solaris ...

每種記憶體分配器都說他們是最快的、可擴充套件並且具有高效的記憶體使用!!但是並非所有的分配器都適合我們自己的應用程式。記憶體消耗大的應用效能很大程度依賴於記憶體分配器的效能。本文中,我只討論 "glibc malloc” 記憶體分配器。並希望今後能涉及到其他記憶體分配器的討論。本文中為了更好的理解 ”glibc malloc”,我會聯絡它最近的原始碼來談。好,下面繫好你的安全帶,我們開啟探索 glibc malloc 的旅程!!

0x01 歷史


歷史: ptmalloc2 來自於 dlmalloc 的分支。此後,新增執行緒支援並於 2006 年釋出。正式釋出後,patmalloc2 整合到 glibc 原始碼中。隨著原始碼整合,程式碼修改便直接在 glibc malloc 原始碼裡進行。因此 ptmalloc2 與 glibc 之間的 malloc 實現有很多不同。

系統呼叫:在之前的文章可見malloc的內部呼叫不是 brk 就是 mmap 系統呼叫。

執行緒化:在早期的 Linux 裡,dlmalloc 被用做預設的記憶體分配器。但之後因為 ptmalloc2 新增了執行緒支援,ptmalloc2 成為了 Linux 預設記憶體分配器。執行緒支援可幫助提升記憶體分配器以及應用程式的效能。在 dlmalloc 裡,當兩個執行緒同時呼叫 malloc 時,只有一個執行緒能進入到臨界段,因為這裡的空閒列表資料結構是所有可用執行緒共用的。因此記憶體分配器要在多執行緒應用裡耗費時間,從而導致效能降低。然而在 ptmalloc2 裡,當兩個執行緒同時呼叫 malloc 時,會立即分配記憶體。因為每個執行緒維護一個單獨的堆分段,因此空閒列表資料結構正在維護的這些堆也是獨立的。這種維護獨立堆以及每一個執行緒享有空閒列表資料結構的行為被稱為 Per Thread Arena。

0x02 示例:


#!c
/* Per thread arena example. */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>

void* threadFunc(void* arg) {
        printf("Before malloc in thread 1\n");
        getchar();
        char* addr = (char*) malloc(1000);
        printf("After malloc and before free in thread 1\n");
        getchar();
        free(addr);
        printf("After free in thread 1\n");
        getchar();
}

int main() {
        pthread_t t1;
        void* s;
        int ret;
        char* addr;

        printf("Welcome to per thread arena example::%d\n",getpid());
        printf("Before malloc in main thread\n");
        getchar();
        addr = (char*) malloc(1000);
        printf("After malloc and before free in main thread\n");
        getchar();
        free(addr);
        printf("After free in main thread\n");
        getchar();
        ret = pthread_create(&t1, NULL, threadFunc, NULL);
        if(ret)
        {
                printf("Thread creation error\n");
                return -1;
        }
        ret = pthread_join(t1, &s);
        if(ret)
        {
                printf("Thread join error\n");
                return -1;
        }
        return 0;
}

輸出分析:

在主執行緒 malloc 之前:在以下輸出裡我們可以看到,由於 thread1 尚未建立,這裡尚無堆分段,也沒有執行緒棧。

#!bash
[email protected]:~/ptmalloc.ppt/mthread$ ./mthread 
Welcome to per thread arena example::6501
Before malloc in main thread
...
[email protected]:~/ptmalloc.ppt/mthread$ cat /proc/6501/maps
08048000-08049000 r-xp 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
08049000-0804a000 r--p 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
0804a000-0804b000 rw-p 00001000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
b7e05000-b7e07000 rw-p 00000000 00:00 0 
...
[email protected]:~/ptmalloc.ppt/mthread$

在主執行緒 malloc 之後: 在下面的輸出裡我們可以看到,堆正好被建立在資料段附近(0804b000-0806c000),這表明堆的空間是由呼叫高階別的中斷所建立(即,使用 brk 系統呼叫)。還要注意的是,即使使用者只發出了 1000 bytes 請求,就會建立 132 KB 大小的堆記憶體。這個堆記憶體的連續區域被稱為「arena」。這個 arena 是由主執行緒建立,則被稱為main arena。進一步的分配請求會繼續使用這個 arena 直到 arena 空閒空間耗盡。當 arena 耗盡空閒空間 時,它能線上程呼叫高階別的中斷的位置時建立(調整建立開始塊的 size 以包含額外空間之後)。類似地,arena 也能在 top chunk 有大量空閒空間時回收

注:top chunk 是一個 arena 裡最多的塊。關於它的更多細節,請看以下 "Top Chunk" 部分

#!bash
[email protected]:~/ptmalloc.ppt/mthread$ ./mthread 
Welcome to per thread arena example::6501
Before malloc in main thread
After malloc and before free in main thread
...
[email protected]:~/lsploits/hof/ptmalloc.ppt/mthread$ cat /proc/6501/maps
08048000-08049000 r-xp 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
08049000-0804a000 r--p 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
0804a000-0804b000 rw-p 00001000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
0804b000-0806c000 rw-p 00000000 00:00 0          [heap]
b7e05000-b7e07000 rw-p 00000000 00:00 0 
...
[email protected]:~/ptmalloc.ppt/mthread$

在主執行緒 Free 之後: 在以下輸出裡我們可以看到,當分配記憶體區域被釋放時,其後記憶體不會被立即釋放給作業系統。分配記憶體區域(1000 bytes 大小)只釋放給 "glibc malloc" 庫,在這裡的釋放掉的 Chunk 會被新增到 main arenas 中(在 glibc malloc 裡,freelist 這種資料結構被稱為 bins 譯者注:freelist 是一種單向連結串列)。此後當使用者申請記憶體時,glibc malloc 不會從核心中獲得新的堆記憶體,而是儘量在 bins 裡找到一個空閒塊(Free Chunk)。只有當沒有空閒塊存在時,glibc malloc 才會從繼續核心中申請記憶體。

#!bash
[email protected]:~/ptmalloc.ppt/mthread$ ./mthread 
Welcome to per thread arena example::6501
Before malloc in main thread
After malloc and before free in main thread
After free in main thread
...
[email protected]:~/lsploits/hof/ptmalloc.ppt/mthread$ cat /proc/6501/maps
08048000-08049000 r-xp 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
08049000-0804a000 r--p 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
0804a000-0804b000 rw-p 00001000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
0804b000-0806c000 rw-p 00000000 00:00 0          [heap]
b7e05000-b7e07000 rw-p 00000000 00:00 0 
...
[email protected]:~/ptmalloc.ppt/mthread$

線上程1 malloc 之前:在以下輸出中我們可以看到,這裡沒有 thread1 的堆但現在 thread1 的每一執行緒棧已被建立。

#!bash
[email protected]:~/ptmalloc.ppt/mthread$ ./mthread 
Welcome to per thread arena example::6501
Before malloc in main thread
After malloc and before free in main thread
After free in main thread
Before malloc in thread 1
...
[email protected]:~/ptmalloc.ppt/mthread$ cat /proc/6501/maps
08048000-08049000 r-xp 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
08049000-0804a000 r--p 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
0804a000-0804b000 rw-p 00001000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
0804b000-0806c000 rw-p 00000000 00:00 0          [heap]
b7604000-b7605000 ---p 00000000 00:00 0 
b7605000-b7e07000 rw-p 00000000 00:00 0          [stack:6594]
...
[email protected]:~/ptmalloc.ppt/mthread$

線上程1 malloc 之後:在以下輸出裡我們可以看到,thread1 的堆被建立了。且在記憶體對映段區域出現(b7500000-b7521000 大小為 132 KB),這表明堆記憶體透過使用 mmap 系統呼叫而不是主執行緒(使用 sbrk)建立。這裡再一次看到,即使使用者只請求 1000 bytes,1MB 大小的堆記憶體還是會被對映到程式地址空間中。而1 MB 以外只有 132KB 被設定有讀寫許可權,並同時成為該執行緒的堆。這塊記憶體(132KB)區域被稱為thread arena

注:當使用者申請的記憶體大小超過 128KB( malloc(132*1024) )並且當一個 arena 裡沒有足夠的空間來滿足使用者的請求時,記憶體是使用 mmap 系統呼叫來分配的(不使用 sbrk) 無論這個請求是來自於 main arena 還是 thread arena。

#!bash
[email protected]:~/ptmalloc.ppt/mthread$ ./mthread 
Welcome to per thread arena example::6501
Before malloc in main thread
After malloc and before free in main thread
After free in main thread
Before malloc in thread 1
After malloc and before free in thread 1
...
[email protected]:~/ptmalloc.ppt/mthread$ cat /proc/6501/maps
08048000-08049000 r-xp 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
08049000-0804a000 r--p 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
0804a000-0804b000 rw-p 00001000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
0804b000-0806c000 rw-p 00000000 00:00 0          [heap]
b7500000-b7521000 rw-p 00000000 00:00 0 
b7521000-b7600000 ---p 00000000 00:00 0 
b7604000-b7605000 ---p 00000000 00:00 0 
b7605000-b7e07000 rw-p 00000000 00:00 0          [stack:6594]
...
[email protected]:~/ptmalloc.ppt/mthread$

線上程1 free 掉後:在以下輸出我們可以看到,釋放分配記憶體區域的過程中並不會把堆釋放到作業系統。取而代之的是這塊被分配的記憶體區域(1000 bytes 大小)還是 被釋放 到了 "glibc malloc" 裡,並將這個釋放塊新增到 thread arenas 的 bins 裡。

#!bash
[email protected]:~/ptmalloc.ppt/mthread$ ./mthread 
Welcome to per thread arena example::6501
Before malloc in main thread
After malloc and before free in main thread
After free in main thread
Before malloc in thread 1
After malloc and before free in thread 1
After free in thread 1
...
[email protected]:~/ptmalloc.ppt/mthread$ cat /proc/6501/maps
08048000-08049000 r-xp 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
08049000-0804a000 r--p 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
0804a000-0804b000 rw-p 00001000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
0804b000-0806c000 rw-p 00000000 00:00 0          [heap]
b7500000-b7521000 rw-p 00000000 00:00 0 
b7521000-b7600000 ---p 00000000 00:00 0 
b7604000-b7605000 ---p 00000000 00:00 0 
b7605000-b7e07000 rw-p 00000000 00:00 0          [stack:6594]
...
[email protected]:~/ptmalloc.ppt/mthread$

0x03 Arena


Arena的數量:在以上示例中,我們看到了主執行緒包含 main arena 同時 thread 1 包含了它自己的 thread arena。所以不論執行緒數量,線上程與 arena 之間可以有一對一的對映嗎?當然沒有。一個 arena 應用程式可以包含更多數量的執行緒(相較於 Cores 的數量),在這樣的情況下,每個執行緒擁有一個 arena 顯得有些不值。因此,應用程式的arena 數量的限制是基於系統裡現有的 Cores 的數量

#!bash
For 32 bit systems:
     Number of arena = 2 * number of cores.
For 64 bit systems:
     Number of arena = 8 * number of cores.

0x04 Multiple Arena :


示例:我們來看一個多執行緒應用(4執行緒 ---||- 主執行緒 + 3個使用者執行緒)在一個單核的 32 位系統上執行。這裡執行緒數量(4)> 2 * 核心數量(2)。因此,"glibc malloc" 認定 Multiple Arena 被所有可用程式共享。但它是怎樣共享的呢?

當主執行緒,第一次呼叫 malloc 時,在沒有任何競爭的情況下,已經建立了 main arena

當 thread 1 和 thread 2 第一次呼叫 malloc 時,會為這些執行緒建立一個新的 arena 並且是在無任何競爭的情況下被使用。直到所有執行緒和 arena 有一對一對映時。

當 thread 3 第一次呼叫 malloc 時, arena 限制的數量被算出。這裡超過了 arena 數量的限制,因此嘗試重用 現存的 arena (main arena 或 arena 1 或 arena 2)

重用:

一旦遍歷了所有可用的 arena,就會儘量去鎖定可用的 arena。

如果鎖定成功(我們假設說 main arena 被鎖定成功),就向使用者返回該 arena。

如果沒有 arena 是空閒的,請看接下來的 arena 程式碼區。

現在當 thread 3 第二次呼叫 malloc 時,malloc 會儘量使用上次訪問的 arena (main arena)。如果 main arena 是空閒的, thread 3 會一直使用該 arena 並遮蔽其他執行緒的申請直到 main arena 被釋放。 main arena 就是如此這般在主執行緒和 thread 3 間共享。

0x05 Multiple Heaps:


在 “glibc malloc”原始碼裡主要發現以下三種資料結構:

heap_info – 堆頭 – 單個 thread arena 有多個堆。每個堆有它自己的頭。為什麼需要多個堆?每個 thread arena 開始都只有一個堆,但當這個堆耗盡空間時,新的堆(並不連續的)能 mmap 到這個 arena 裡。

malloc_state – Arena 頭 – 單個 thread arena 有多個堆,但是對於所有這些堆來說,只存在一個 arena 頭。arena 頭 包含 bins、top chunk、最後剩餘塊……

malloc_chunk – 塊頭 – 一個堆基於使用者請求被分為很多塊。每個塊有它自己的塊頭。

注:

Main arena 沒有多重堆,因此沒有 heap_info 結構。當main arena 耗盡空間時,sbrk 堆被擴充套件(連續區域)直到它轉儲到記憶體對映分段中。

不像 thread arena,main arena 的 Arena 頭不是 sbrk 堆分段的一部分。它是一個全域性變數,因此可以在 libc 裡找到它以及資料分段。

main arena 和 thread arena 的形象化檢視(單個堆分段):

thread arena 的形象化檢視(多個堆分段):

enter image description here

Chunk :在堆裡的塊可以是以下幾種型別中的一種:

  • Allocated chunk 分配後的塊
  • Free chunk 空閒塊
  • Top chunk 開始塊
  • Last Remainder chunk 最後剩餘塊

0x06 Allocated Chunk:


enter image description here

size: 這部分包含了此處 Allocated 塊的容量大小,該部分最後 3 bits 包含了關鍵資訊。 PREV_INUSE (P) – 這一 bit 在前一個塊被分配時就被設定。 IS_MMAPPED (M) – 這一 bit 在塊即在 mmap 時被設定。 NON_MAIN_ARENA (N) – 這一 bit 在塊分配給 thread arena 時設定。

注意:

其他 malloc_chunk (比如 fd -- forward point,bk-- back point)的部分不會用於分配塊。因此使用者資料在這個區域。

由於儲存 malloc_chunk 需要一些額外的空間,使用者請求的容量大小轉換成了可用的大小。轉化透過不設定可用容量的最後 3 bits 的方式進行,因此它用於儲存關鍵資訊。

Free Chunk(空閒塊):

enter link description here

prev_size: 兩個空閒塊是不能毗連在一起的。當兩個塊都空閒時,它就會與一個單獨的空閒塊連線。因此前一個塊及當前這個釋放的塊會被分配,同時 prev_size 包含前一個塊的使用者資料。 size: 這個部分包含有空閒塊的 size。 fd: 前指標 – 同一容器裡指向下一塊的指標(不是指向實體記憶體內的下一塊)。 Bins:Bins 是 freelist 資料結構。用以支援 Free Chunk。基於塊的大小,有幾種可用的 bins:

Fast bin 快速bin Unsorted bin 無序bin Small bin 小bin Large bin 大bin

被用來支援這些 bin 的資料結構有:

fastbinsY: 該陣列支援 fast bins bins: 該陣列支援 unsorted,small 以及 large 容器。總共 126 個容器被劃分為以下幾種: Bin 1 – Unsorted bin Bin 2 to Bin 63 – Small bin Bin 64 to Bin 126 – Large bin

Fast Bin: 大小在 1680 bytes 的塊叫做 fast chunk 。支援 fast chunk 的 bin 叫做 fast bins。在上述的這些 bin 裡,fast bins 在記憶體分配和重新分配上更快。(譯者注:只有 fast bin 是 LIFO 其他的 bin 都是 FIFO)

bin 數量 - 10

每個 fast bin 包含 free chunk 的一個單向連結串列。既然在 fast bins 裡,在列表中間塊不會被移除,單向連結列表被使用。新增和刪除都在列表末尾之前進行 - LIFO(後進先出)。

Chunk size – 8 bytes apart 塊大小(size) - 8 bytes 累加

Fast bins 包含大小為 8 bytes 累加的塊的 binlist 。即,第一個 fast bin(index 0) 包含 16 bytes 塊的binlist,第二個 fast bin (index 1)包含 24 bytes 以此類推……(譯者注:binlist 是 bin 的一個連結串列) 在一個獨有的 fast bin 內的塊大小是一樣的。

malloc 初始化 過程中,最大的 fast bin 的大小設定64 (!80) bytes。因此透過塊的預設大小設定為 16 到 64 就可以將其劃分為 fast chunks 了。 不能合併 - 空閒的兩個塊可以相鄰,但不能合併為一個空閒塊。不能合併導致產生外儲存碎片,但它可以大大提速!! malloc(fast chunk) –

初始狀態 fast bin 的最大容積 以及 fast bin 目錄是空的,因此即使使用者請求一個 fast chunk,服務它的是 small bin code 而不是 fast bin code

之後當它不再為空時, 計算 fast bin 目錄以檢索其對應的 binlist。

來自以上被檢索 binlist 的第一個塊會被移除返回給使用者。

free(fast chunk) –

計算 Fast bin 目錄以檢索其對應的 binlist。 該空閒塊被新增到以上檢索 binlist 的最前位置。

enter link description here

Unsorted Bin: 當小或者大的塊被釋放而非把他們新增到各自 bin 裡時,它會被新增到 unsorted bin 裡。 該途徑給了 "glibc malloc" 第二個機會再次利用最近釋放的塊。因此記憶體分配以及回收的速度都會有所提升(因為 unsorted bin)由於排除了用於查詢合適容器的時間。

Number of bins – 1

Unsorted bin 包含空閒塊的一個迴圈雙向連結串列(也稱為 binlist 容器列表)。 塊的大小 - 沒有大小限制,任何大小的塊都屬於此容器。

enter image description here

Small Bin: 小於 512 bytes 的塊叫做 small chunk。 支援 small chunks 的容器叫做 small bins。 Small bins 在記憶體分配和回收時比 large bins 快(比 fast bins 慢)。

Number of bins – 62

  • 每個 small bin 含有空閒塊的一個迴圈雙向連結串列(也稱為 binlist 容器列表)。在 small bins 裡, 塊從列表中間斷開連結後,便使用雙向連結串列。在這裡列表頭部實現新增並在列表的後部刪除 - FIFO(先進先出)。

Chunk Size – 8 bytes 遞增

  • Small bin 包含一個塊大小為 8 bytes 遞增(即,第一個 Small Bin(Bin 2)包含塊大小為 16 bytes 的binlist,第一個 Small Bin (Bin 3)包含塊大小為 24 bytes 的 binlist 以此類推)的 binlist。
  • 一個 small bin 裡的塊大小是一樣的,因此不需要分類。

合併 - 空閒的兩個塊不能毗鄰,會被合併為一個空閒塊。合併消除了外儲存碎片但是執行大大減速!!

malloc(small chunk) – * 初始狀態下所有 small bins 都是 NULL,因此即使使用者請求一個 small chunk,提供服務的是 unsorted bin code 而不是 small bin code。 * 在第一次呼叫 malloc 期間,在 malloc_state 裡發現的 small bin 和 large bin 資料結構(bins 容器)被初始化(即,bins 會指向它自己表示他們是空的)。 * 之後當 small bin 處於非空狀態時,其對應 binlist 中的最後一個塊會被移除返回給使用者。 free(small chunk) – * 釋放塊時,檢視其前一個或下一個塊是否空閒,如果空閒就合併(即,從他們各自的連結串列裡解除塊的連結,然後在 unsorted bin 的連結串列最開端新增新的統一的塊)。

Large Bin: 大小大於等於 512 bytes 的塊稱為 large chunk。支援 large chunks 的 bins 叫作 large bins。Large bins 在記憶體分配和回收中比 small bins 要慢。

Number of bins – 63 每個 large bin 包含空閒塊的一個迴圈雙向連結串列(也稱為 binlist )。在 large bins 裡,塊在任何位置(前端或中間或尾部)被新增或移除後,開始使用雙向連結串列。 這 63 bins 以外:

  • 32 bins 包含大小為 64 bytes 累加的塊的 binlist。即,第一個 large bin(Bin 65)包含大小為 512 bytes 到 568 bytes 的塊的 binlist,第二個 large bin(Bin 66)包含大小為 576 到 632 bytes 的塊的 binlist 以此類推……
  • 16 bins 包含著大小為 512 bytes 累加的塊的 binlist。
  • 8 bins 包含著大小為 4096 bytes 累加的塊的 binlist。
  • 4 bins 包含大小為 32768 bytes 累加的塊的 binlist。
  • 2 bins 包含大小為 262144 bytes 累加的塊的 binlist。
  • 1 bin 包含一個擁有剩餘容量大小的塊。

不同於 small bin,在 large bin 裡的塊大小是不同的。因此他們以降序排列。最大的塊在最前端而最小的塊被排到 binlist 的末尾。

合併 - 空閒的兩個塊不能相鄰,而是合併為一個空閒塊。

malloc(large chunk) –

初始化狀態下所有 large bins 都是 NULL,因此即使使用者請求一個 large chunk,是下一個最大的 bin code 提供服務,而不是 large bin code

同樣在第一次呼叫 malloc 期間,在 malloc_state 裡發現的 small bin 和 large bin 資料結構(bins)被初始化。即,bins 會指向它自己表示它們是空的。

此後當 large bin 不為空時,如果最大塊的大小(在它的容器列表裡)比使用者請求的大小還大, binlist 會從尾部到頭部,來查詢一個大小接近或等於使用者請求大小的合適的塊。一旦找到,這個塊將分裂為兩個塊。

使用者塊(擁有使用者請求的大小)-返回給使用者。

剩餘塊(擁有剩餘容量的大小)-新增到 unsorted bin。

如果最大塊的大小(在它的容器列表裡)小於使用者請求的大小,那麼儘量使用下一個最大(非空)bin 為使用者的請求提供服務。下一個最大的 bin code 會掃描容器對映來查詢下一個最大的非空 bin ,如果找到任何一個,從 binlist 裡檢索到了一個合適的塊,分裂並返回給使用者。如果沒找到,嘗試使用Top Chunk為使用者請求提供服務。

free(large chunk) – 它的程式與空閒(small chunk)類似。

Top Chunk: 在一個 arena 頂部邊框的塊叫做開始塊(Top Chunk)。它不屬於任何容器。在任一 bin 裡,Top Chunk被用於在沒有空閒塊時為使用者請求提供服務。

使用者塊(擁有使用者請求的大小) 剩餘塊(擁有剩餘容量的大小)

剩餘塊變成新的頂部。如果開始塊大小比使用者請求的大小要少,開始塊使用 sbrk (main arena) 或 mmap (thread arena)系統呼叫來擴充套件

最後剩餘塊:是來自一個小請求的最近一次分裂的剩餘。最後剩餘塊幫助引用地址(即,small chunks 的連續 malloc 請求可能在被分配時結束)彼此間更靠近。

但是除了在一個 arena 裡可利用的的眾多塊,哪些塊有資格成為最後剩餘塊呢?

當一個使用者請求 small chunk 時, 不能透過 small bin 以及 unsorted bin 提供服務,這時會掃描 bin 對映會以查詢下一個最大(非空)bin 。就像之前所說,一旦找到下一個最大(非空)bin ,它將分裂成兩塊,使用者塊返回給使用者,同時剩餘塊新增到 unsorted bin。此外,它會變成新的最後剩餘塊

引用地址是怎樣歸檔的?

現在當使用者隨後請求的一個 small chunk 並且最後剩餘塊是 unsorted bin 裡唯一的塊時,最後剩餘塊會分裂成兩塊,使用者塊會返回給使用者同時剩餘的那個塊會新增到 unsorted bin。此外,它還會成為新的一個最後剩餘塊。如此後續的記憶體分配會相繼結束。

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!