一直對malloc的實現不是很懂,k&r的c程式語言的8.7節講了乙個malloc的基本實現,這篇文章主要把自己的理解記錄一下。
malloc是在堆裡申請空間的,每一次申請的空間叫做記憶體塊,每個記憶體塊包括頭部和資料區,空閒的記憶體塊通過乙個迴圈鍊錶組織在一起,記憶體塊的位址是從低到高排列的,這是由堆的位址是從低到高生長決定的,鍊錶有乙個表頭,表頭不包含資料區。
為了簡化塊的對齊,每個記憶體塊的大小都是頭部的整數倍,頭部通過乙個聯合體來實現,包括指向下乙個塊的指標和當前塊的長度:
typedef long align;/*for alignment to long boundary*/
union header s;
align x; //強制塊的對齊
};typedef union header header;
當第一次使用
malloc
時,首先讓
base
指向自己
申請空間時遍歷每乙個空閒記憶體塊,這裡分為
3種情況: 1)
當申請的空間剛好為塊的長度時,返回該塊的資料區位址,並把塊從鍊錶中移除 2)
當申請的空間小於塊的長度時,從尾部**乙個資料塊,並將資料區位址返回給使用者
3)
當遍歷完整個鍊錶後,不存在記憶體塊滿足申請時,那麼呼叫
morecore()
向作業系統申請乙個更大的記憶體塊插入到記憶體中,實現**如下:
static header base; //煉表表頭
static header *freep = null; //空閒鍊錶的初始指標
void *malloc(unsigned nbytes)
//從freep的下乙個記憶體塊開始遍歷鍊錶
for(p = prevp->s.ptr; ;prevp = p, p= p->s.ptr)
freep = prevp;
return (void*)(p+1);//返回資料區
}if ((p = morecore(nunits)) == null)//向作業系統申請空間
return null; /* none left */}}
為了講解
morecore
的實現,首先簡單介紹一下作業系統的記憶體排布,這個主要摘自以下文章
「根據linux
核心相關文件
描述,linux64
位作業系統僅使用低
47位,高
17位做擴充套件(只能是全0或全
1)。所以,實際用到的位址為空間為
0x0000000000000000 ~ 0x00007fffffffffff
和0xffff800000000000 ~ 0xffffffffffffffff
,其中前面為使用者空間(
user space
),後者為核心空間(
kernel space
)。圖示如下:
對使用者來說,主要關注的空間是
user space
。將user space
放大後,可以看到裡面主要分為如下幾段:
一般來說,
malloc
所申請的記憶體主要從
heap
區域分配(本文不考慮通過
mmap
申請大塊記憶體的情況)。程序所面對的虛擬記憶體位址空間,只有按頁對映到物理記憶體位址,才能真正使用。受物理儲存容量限制,整個堆虛擬記憶體空間不可能全部對映到實際的物理記憶體。
linux
對堆的管理示意如下:
維護乙個
break
指標,這個指標指向堆空間的某個位址。從堆起始位址到
break
之間的位址空間為對映好的,可以供程序訪問;而從
break
往上,是未對映的位址空間,如果訪問這段空間則程式會報錯。
要增加乙個程序實際的可用堆大小,就需要將
break
指標向高位址移動。
linux
通過brk
和sbrk
系統呼叫操作
break
指標。兩個系統呼叫的原型如下:
int brk(void *addr);
void *sbrk(intptr_t increment);
brk將
break
指標直接設定為某個位址,而
sbrk
將break
從當前位置移動
increment
所指定的增量。
brk在執行成功時返回
0,否則返回
-1並設定
errno
為enomem
;sbrk
成功時返回
break
移動之前所指向的位址,否則返回
(void *)-1。
乙個小技巧是,如果將
increment
設定為0
,則可以獲得當前
break
的位址。」
通過上面的講解之後,我們知道向作業系統請求記憶體塊其實就是移動
break
指標,但這是乙個開銷很大的操作,我們不希望每次都呼叫
morecore
函式,基於這個考慮
morecore
函式請求至少
nalloc
個header
單元長度,這個較大的塊將根據需要分成多個較小的塊
#define nalloc 0//1024 /* minimum #units to request */
static header *morecore(unsigned nu)
最後分析
free
函式的實現,由上面
sbrk
的實現可知記憶體塊的位址是從低到高排列的,我們定義最低的位址為鍊錶的開始,最高的位址為鍊錶的末尾,在
free
函式中會遍歷鍊錶確定要釋放的位址處於哪兩個相鄰的空閒塊之間,也可能處於鍊錶的末尾,找到後將其插入到這兩個空閒塊之間,如果被釋放的塊與空閒塊相鄰,那麼將其與空閒塊合併。
void free(void *ap)
else
bp->s.ptr = p->s.ptr;
if (p+p->s.size == bp) else
p->s.ptr = bp;
freep = p;//返回前乙個相鄰的空閒塊
}
這是malloc
的乙個簡單實現,以後可能會回來再看看
glibc和vs
裡的實現
malloc的實現原理
malloc 是 c語言中動態 儲存管理 的一組標準庫函式之一。其作用是在記憶體的動態儲存區中分配乙個長度為size的連續空間。其引數是乙個無符號整形數,返回值是乙個指向所分配的連續儲存域的起始位址的指標。動態記憶體分配 就 是指在程式執行的過程中動態地分配或者 儲存空間的分配記憶體的方法。動態記憶...
malloc的底層實現
每個程序都有乙個虛擬記憶體空間,虛擬記憶體空間通過mmu 儲存器管理單元 對映到真正的物理空間,mmu是乙個硬體,利用儲存在主存中的查詢表翻譯虛擬位址,查詢表由作業系統管理,使用者無法獲取。虛擬位址空間給每個程序乙個假象,就像每個進城擁有4g的執行空間一樣,但是實際在使用記憶體的時候,虛擬位址空間通...
malloc的實現原理
malloc是c語言最常用的標準庫函式之一,用於在程式執行中動態地申請記憶體空間。我們都會使用它,其函式原型為 extern void malloc unsigned int num bytes 那麼它是怎麼實現的呢?不同的編譯環境中對它的實現可能不同。比如glibc the gnu c libra...