2016年2月4日 星期四

malloc(), calloc(), realloc() 和 alloca()

可以從兩個所在動態取得記憶體,一般 malloc() 是從 heap,另一款是從 stack。

從 stack 動態配置記憶體,函數結束自動釋出。只是調整 stack 指標,效率足好。

#include <alloca.h>

void *alloca(size_t size);
  • 可簡化 longjmp() 或 siglongjmp() 的記憶體釋放,不鼓勵用在其它地方 (為什麼???)
  • 不能直接用在函數參數內,配置的記憶體夾在函數參數間可能會有問題。
  • stack overflow 時不會傳回 NULL,SIGSEGV

從 heap 動態配置記憶體

#include <stdlib.h>

// 取得 size 大小位元組、未初始化的記憶體空間。
void *malloc(size_t size);

// 從 heap 動態配置的記憶體,都需要用 free() 歸還,或者在程式結束時自動消失。
// 重複釋出可能發生不可預期的結果ptr 可以是 NULL,不進行任何動作。
void free(void *ptr);

// 基於 malloc(),取得 nmemb * size 大小的記憶體空間,並清為 0。
void *calloc(size_t nmemb, size_t size);


// 記憶體空間重新配置為 size 大小,重複部份會相同。
// 加大配置的空間時,位置可能變或不變。位置改變時會複製重複的部份,效能會較差。
void *realloc(void *ptr, size_t size);

其它使用 malloc() 的函式:strdup() 等。

malloc() 細節

heap 一般放在 Uninitialize data (bss) 後面生向高位址,另一端是 stack 生向 heap。

典型 x86-32 Linux layout

Kernel
0xC000 0000
argv, environment

Stack
Top of stack →

(unallocated memory)

Program break →
Heap

Uninitialized data (bss)

Initialized data

Text (program code)
0x0804 8000

0x0000 0000

heap 跟 stack 中間是實際沒用到的記憶體。heap 的盡頭就是 program break,不夠用時會用 sbrk() 調整 program break 來加大 heap,但通常不會縮減 heap。sbrk(0) 傳回目前 program break 位置。

藏在每一個 malloc() 的記憶體前面,會紀錄這段記憶體的大小。當 free() 時,利用記憶體空間形成 doubly link list 歸還回到 free 列表,並嘗試合併相鄰的記憶體區塊。

配置的記憶體區塊 (前置區塊大小)
size可使用的部份

已釋出可使用的列表 (利用可使用的部份存放 link list 指標)
sizeprevnext剩下可使用的部份

每次 malloc() 會先從 free 列表取得記憶體,例如用 first-fit 或 best-fit 等演算法,如取不到則嘗試加大 heap。

當配置的記憶體區塊大於 MMAP_THRESHOLD,glibc 改用系統呼叫 mmap() 配置,在 Linux 4.7 之後會受到 RLIMIT_DATA 限制。MMAP_THRESHOLD 預設 128 kB,可透過 mallopt() 調整。

在 multithread 應用,內部使用 mutex 保護相關資料結構,glibc 並建立額外的 memory allocation arenas。每個 arena 是內部用 brk() 或 mmap() 配置的大記憶體空間。

每個 process 有自己的虛擬記憶體,虛擬記憶體中實際有使用的區塊才會對應到實體記憶體。malloc() 不保證可以取得實體記憶體,萬一沒有實體記憶體體了,有些 process 會被 OOM killer 結束掉 (見 proc 檔案系統 /proc/sys/vm/overcommit_memory 和 /proc/sys/vm/oom_adj 的說明,以及 Linux kernel 的 Documentation/vm/overcommit-accounting)。

malloc() 除錯

mtrace()、muntrace()
mcheck()、mprobe()
malloc() 除錯函式庫:Electric Fence (http://www.perens.com/FreeSoftware/)、dmalloc (http://dmalloc.com/)、Valgrind (http://valgrind.org/)、Insure++ (http://www.parasoft.com/)。
mallopt()、mallinfo()

參考

  1. TLPI §7
  2. man alloca 
  3. malloc() man-page
  • asprintf()
  • mmap()
  • memalign()、posix_memalign()
  • malloc_trim()

沒有留言:

張貼留言

SIP header Via

所有 SIP 訊息 都要有 Via,縮寫 v。一開始的 UAC 和後續途經的每個 proxy 都會疊加一個 Via 放傳送的位址,依序作為回應的路徑。 格式 sent-protocol sent-by [ ;branch= branch ][ ; 參數 ...] s...