1、前言
2、簡單的內存管理器示例
3、GNU malloc算法
4、Kernel Buddy夥伴系統算法
5、Kernel Slab/Slub高速緩存算法html
此次課程最初的題目叫《linux內存管理》, 但是寫着寫着就感受這個題目起的太大了, VM(virtul memory)是操做系統中最抽象最複雜的子系統, 想經過一次課把它所有講清楚有點不現實。 因此我把此次課程的名字改爲內存管理初探,先講講linux內存的分配算法, 後續課程中在陸續涉及內存映射與回收機制。另外,因爲本人水平有限, 有講的不對的地方, 請你們多多扔磚頭, 固然爲了支持環保, 請扔銀行卡。linux
c/c++程序員對glibc的malloc在熟悉不過了,你們在平時的開發中都會用到malloc進行內存分配,或者在對malloc進行一層包裝, 如xmalloc,在sshd等開源軟件中常常會碰見。 那麼你們有沒有想過glibc的malloc是如何實現的呢。glibc的內存管理器很是複雜, 若是想要經過分析源碼來了解內存管理器的實現將會花不少時間。本小節咱們將會經過編寫一個簡陋的內存管理器來替代GNU glibc的內存管理器, 以此學習下基本的內存管理器設計方法。c++
前面說道VM(virtual memory)是內核中最複雜的子系統, 它複雜是因爲要和進程,文件系統,網絡這些子系統都有聯繫。 一個進程在建立的時候會因COW(copy on write)機制來複制父進程的頁表,當進程在運行中因爲某些代碼或數據不常用, 還會被OS將這部分數據寫入到磁盤中, 這個過程是VM的內存回收機制,它有不少策略來決定回收哪些進程的哪些數據到磁盤上以便來節省內存空間。 當進程又須要那部分數據的時候, 又會由VM把它從磁盤上加載回來, 這些都是與文件系統的交互。可是有一點咱們要清楚, 內存是計算機中的一個硬件設備, 它是很是簡單的, 不像硬盤,鍵盤,顯示器等等還須要OS來寫專門的驅動程序來驅動它。因此VM管理的本質實際就是管理虛擬地址,VM只須要按照CPU的規則, 將物理地址與虛擬地址創建好關係,真正讀寫內存的是CPU。VM的任務就是要配合進程,文件系統來管理好這些虛擬地址。 VM中的內存分配就是按照必定的算法將一部分線性地址劃分出來給某些kernel代碼使用;VM的映射機制本質上就是物理地址與線性地址的轉化,只不過要遵循CPU硬件設計上的規則而已;VM的回收不過就是按照必定的規則將某些線性地址對應的內存回收到磁盤而已。 內存管理的入口點有兩個, 一個是前面說的內核入口點, 它是內存管理的核心。 一個是應用層的glibc,glibc的內存管理器用來從內核批發一塊內存,而後按照必定的規則來管理這塊內存, 進程用到內存的時候, 就從這塊內存分割處一塊內存給進程, 進程不用的時候在把這塊內存回收到那塊大內存中去。這樣作的目的是提升內存分配的效率,因此glibc的內存管理器實際上不是必須的, 進程每次須要內存的時候, 能夠本身經過brk()函數來擴展進程的heap區達到內存分配的目的。 glibc其實也是經過brk()來擴展進程的heap區, 只不過它一次會請求很大的內存,之後進程須要內存的時候就從那個大內存中分配。這有點相似你們常常聽到的高速緩存的概念。 這裏要注意一點進程的堆棧也會進行內存引用, 堆棧區域的內存是由編譯器在編譯的時候分配的。下面這個圖會幫助你們理清上述關係。程序員
————— —————– —————————
| User Space | –> | Glibc | –> | Kernel(buddy & slab)|
————— —————– —————————算法
glibc充當了一個批發商的做用, c程序在最初運行的時候不是從main函數開始, 而是從glibc的一個入口函數開始, 它會初始化程序的運行環境, 好比拷貝參數,初始化io管理器,初始化內存管理器。 當運行環境都初始化好以後,在跳轉到main函數去運行。 程序運行結束後, 在跳轉到glibc的結束函數那裏, 清理以前初始化的程序運行環境。 初始化內存管理器, 也是初始化所謂的heap區。進程的heap區就是由內存管理器來管理的, 它經過sys_brk系統調用向內核申請一塊空閒的內存, 而後將這塊內存分紅一個個能夠維護的塊,malloc()函數的做用就是從內存管理器中按照必定的算法選擇一個合適的塊返回給程序。free()的時候在將這個塊歸還給內存管理器。 GNU glibc的內存管理器也是這個原理, 只不過它的管理算法很複雜, 本小節咱們將本身實現一個簡單的內存管理器, 牛刀小試一把,看看內存管理是怎麼設計出來的, 而後咱們在下面的章節中在看看工業級的內存管理器應該如何設計。api
內存管理器其實就是管理sys_brk分配的進程heap區, 爲了簡單,咱們將heap區分紅大小相同的若干塊,而後將他們用雙向鏈表鏈接起來,分配內存的時候只要順序的尋找一個沒被分配的塊便可, 那麼它的結構就很是的簡單了:數組
| heap |
—————————————————————
| chunk | chunk | chunk | … | chunk |
—————————————————————緩存
chunk的結構以下:安全
——————————–
| memory | struct heap_chunk |
——————————–網絡
一個chunk分爲2部分, 前面指向實際的內存區, 也就是malloc返回的地址, 後面是管理結構,用於將每一個chunk鏈接起來。
struct heap_chunk {
char *chunk_pos;
int inuse;
struct list_head list;
};
chunk_pos指向實際的內存塊。 inuse表示chunk是否被分配。list用於雙向鏈表鏈接。
前面提到內存管理器是由glibc在程序運行的時候初始化的, 它首先調用sys_brk設置進程的heap區,
咱們的內存管理器暫時只管理4M內存。
#define HEAP_SIZE (1024 * 1024 * 4)
#define HEAP_CHUNK_STRUCT_SIZE sizeof(struct heap_chunk)
#define DEFAULT_CHUNK_SIZE 128
int w_crt_heap_init(void)
{
void *heap_end;
INIT_LIST_HEAD(&heap_inactive_list);
INIT_LIST_HEAD(&heap_active_list);
heap_base = (void *)brk(0);
heap_end = (void *)brk(heap_base + HEAP_SIZE);
if (!heap_end)
return -1;
heap_chunk_init(heap_base);
return 0;
}
注意咱們如今不使用glibc,因此也無法調用glibc的brk函數,brk()函數須要本身包裝一下:
static int brk(void *end_data_segment)
{
int ret;
asm(「movl $45, %%eax\n\t」
「movl %1, %%ebx\n\t」
「int $0×80\n\t」
「movl %%eax, %0″
:」=r」(ret):」m」(end_data_segment));
}
經過c內嵌彙編的方式, 給eax寄存器賦值爲45, 它是sys_brk的系統調用號。 ebx是第一個參數。
用int $0×80直接對sys_brk進行調用。 其實glibc也是這麼來作的, 不過它還有不少複雜的設計。
經過brk()將進程的heap區設置爲4m大小。 而後在將這個heap區分割成若干大小的chunk:
void heap_chunk_init(void *heap_base)
{
int idx;
for (idx = 0; idx < CHUNK_NUM; idx += CHUNK_SIZE) {
struct heap_chunk *heap_chunk;
heap_chunk = (struct heap_chunk *)(heap_base +
DEFAULT_CHUNK_SIZE + idx);
heap_chunk->inuse = 0;
heap_chunk->chunk_pos = heap_base + idx;
list_add_tail(&(heap_chunk->list), &heap_inactive_list);
}
}
每一個塊都被加入到雙向鏈表中。
malloc只是簡單的遍歷鏈表找到第一個沒被分配的chunk, 將它的inuse結構置1後返回。
void *malloc(unsigned int size)
{
struct heap_chunk *s = NULL;
struct list_head *p = NULL;
if (!size || size > DEFAULT_CHUNK_SIZE)
return NULL;
if (list_empty(&heap_inactive_list))
return NULL;
list_for_each(p, (&heap_inactive_list)) {
s = list_entry(p, struct heap_chunk, list);
if (s && !s->inuse) {
s->inuse = 1;
list_del(p);
list_add_tail(p, &heap_active_list);
return s->chunk_pos;
}
}
return NULL;
}
free()只須要把addr對應的chunk塊中的inuse清0, 而後加入到鏈表的末端。FIFO的方式:)
void free(void *addr)
{
struct heap_chunk *s = NULL;
s = container_of(addr + DEFAULT_CHUNK_SIZE, struct heap_chunk, chunk_pos);
s->inuse = 0;
list_del(&(s->list));
list_add_tail(&(s->list), &heap_inactive_list);
}
咱們這個簡單的內存管理器已經寫好了, 下面寫個程序測試先:
#include 「w_crt.h」
int main(int argc, char **argv)
{
char *s;
s = malloc(64);
strcpy(s, 「abcd」);
printf(「%s」, s);
free(s);
}
這個內存管理器包含在我本身寫的一個c lib中, 因此不須要glibc的頭文件, 完整的源代碼在附件中。
編譯一下:
wzt@program:~/code/crt$ cat Makefile
all: w_crt.a test
w_crt.a:
gcc -c -fno-builtin -nostdlib -fno-stack-protector entry.c fork.c malloc.c printf.c stdio.c string.c
ar -rs w_crt.a malloc.o printf.o stdio.o string.o fork.o
test:
gcc -c -fno-builtin -nostdlib -fno-stack-protector test.c
ld -static -e w_crt_entry entry.o test.o w_crt.a -o test
clean:
rm -f test *.o *.a
運行:
wzt@program:~/code/crt$ ./test
abcd
經過前面的幾個小節咱們已經知道如何來寫一個簡單的內存管理器了, 可是它與工業級的內存管理器仍是有很大差距的, 一個優秀的內存管理要有以下的優勢:
兼容性 – 遵循posix標準
分配/回收速度 – 這是系統性能的熱點
線程安全 – SMP處理器上支持多線程的安全分配
支持緩存 – 爲了加快分配速度
Doug Lea Malloc:Doug Lea Malloc其實是完整的一組分配程序,其中包括Doug Lea的原始分配程序,GNU libc分配程序和ptmalloc。Doug Lea的分配程序加入了索引,這使得搜索速度更快,而且能夠將多個沒有被使用的塊組合爲一個大的塊。它還支持緩存,以便更快地再次使用最近釋放的內存。ptmalloc是Doug Lea Malloc 的一個擴展版本,支持多線程。在本文後面的部分詳細分析ptamlloc2的源代碼實現。
BSD Malloc:BSD Malloc是隨4.2 BSD發行的實現,包含在FreeBSD之中,這個分配程序能夠從預先確實大小的對象構成的池中分配對象。它有一些用於對象大小的 size類,這些對象的大小爲2的若干次冪減去某一常數。因此,若是您請求給定大小的一個對象,它就簡單地分配一個與之匹配的size類。這樣就提供了一個快速的實現,可是可能會浪費內存。
Hoard:編寫Hoard的目標是使內存分配在多線程環境中進行得很是快。所以,它的構造以鎖的使用爲中心,從而使全部進程沒必要等待分配內存。它能夠顯著地加快那些進行不少分配和回收的多線程進程的速度。
TCMalloc:(Thread-Caching Malloc)是google開發的開源工具──「google-perftools」中的成員。與標準的Glibc庫的malloc相比,TCMalloc在內存的分配上效率和速度要高得多。
上面的描述摘自下《Glibc內存管理-ptmalloc2源碼分析》一文, 更多關於內存分配器介紹的知識請參考此文。
GNU glibc使用了ptmalloc2內存管理器, 它是由Wolfram Gloger在Doug Lea的基礎上改進來的。ptmalloc2能夠理解成pthread malloc, 它提供了多線程之間安全分配內存的能力。
ptmalloc2的空間結構以下:
|unsorted bin| small bins | large bins |
——————————————-
| |16|24|32|…|512|576|640|…|
——————————————-
| | | |
—- —- —- —-
| | | | | | | |
—- —- —- —-
| |
—- —-
| | | |
—- —-
ptmalloc2將類似大小的chunk用鏈表連接起來, 這樣的鏈表稱爲一個bin。ptmalloc2一共維護了128個bin。 前64個bins稱爲small bins用來分配小內存。 後64個bins稱爲large bins用來分配大內存。 第一個稱爲unsorted bin, 至關於cache的功能。
此處省略N個字, 因爲ptmalloc2的內存分配策略有點複雜, 用簡短的文字描述不清,在給你們分享的時候, 我會用一些形象的語言來描述, 請你們必定要去聽哦。
Linux kernel的內存分配算法是由Buddy夥伴系統和Slab/Slub/Slob共同來控制的。Buddy是內存管理的最上層結構,它分配若干個連續的物理內存頁, 一個物理頁大小爲4kb, 也是說經過Buddy系統分配獲得的內存最少是4kb,它管理的是大內存塊。Slab基於Buddy, 將一個物理內存頁分紅若干小塊進行管理, 因此slab用於管理小塊。本小節和接下來的關於slab算法描述的小節不會剖析linux kernel的代碼實現,由於這樣的paper實在太多了, 你們有須要能夠看文後的參考文章。再者應用層程序員也沒必要理解kernel的內存分配算法具體實現。針對buddy/slab內存分配算法, 我根據linux kernel對buddy和slab實現的原理山寨了一個超小型的buddy和slab內存分配器,麻雀雖小,五臟俱全,它們能很好的工做在我本身寫的os中了。os的部分源碼提供在附件中(主要是buddy.c和slab.c)。 經過這兩個簡單的示例代碼但願能幫助程序員瞭解buddy/slab算法的基本原理, 在之後關於cache設計的項目中均可以用到。
linux kernel buddy用於分配N個連續的物理內存頁, 一個物理頁4k大小。 爲了快速分配, buddy
將空閒物理頁劃分紅了11個鏈表, 每一個鏈表依次保存1,2,4,8,16,32,64,128,256,512,1024個連續的物理頁。1024個物理頁表明kernel一次最多能夠分配4M字節的空閒內存。它的結構以下:
+—-+
|1024|–> …
+—-+
|512 |
+—-+
|256 |
+—-+
|128 |
+—-+
| 64 |
+—-+ +———————————–+
| 32 |–>| PAGE_SIZE*32 | –> …
+—-+ +———————————–+
| 16 |
+—-+
| 8 |
+—-+ +—————————+ +—————————-+
| 4 |–>| PAGE_SIZE*4 |–>| PAGE_SIZE*4 |
+—-+ +—————————+ +—————————-+
| 2 |
+—-+ +———–+ +———–+
| 1 |–>| PAGE_SIZE |–>| PAGE_SIZE |
+—-+ +———–+ +———–+
咱們用一個struct mm_buddy數組來管理這些內存塊,結構以下:
struct mm_buddy {
int size;
int chunk_num;
int order;
int free_num;
int total_num;
int obj_map[MAX_BUDDY_CHUNK_NUM];
void *obj[MAX_BUDDY_CHUNK_NUM];
};
obj_map和obj用來定爲buddy上的一個空閒塊,obj保存了buddy上的全部空閒塊的地址, obj_map是對應空閒塊的標誌。
每一個內存塊由struct mm_buddy_chunk來描述:
struct mm_buddy_chunk {
void *chunk_pos;
int inuse;
struct list_head list;
};
爲了快速定爲struct mm_buddy結構, 咱們用mm_buddy_array[11]數組來管理全部struct mm_buddy結構。
初始化buddy系統就是對這個數組進行賦值。
void init_buddy_list(void)
{
void *base;
int i, j;
base = mm_buddy_base;
for (i = 0; i < BUDDY_CHUNK_NUM; i++) {
mm_buddy_array[i].size = PAGE_SIZE * (1 << i) * buddy_chunk_num;
mm_buddy_array[i].chunk_num = buddy_size[i];
mm_buddy_array[i].order = i;
mm_buddy_array[i].free_num = buddy_chunk_num;
mm_buddy_array[i].total_num = buddy_chunk_num;
for (j = 0; j < buddy_chunk_num; j++)
mm_buddy_array[i].obj[j] = base + j * (PAGE_SIZE * (1 << i));
for (j = 0; j < MAX_BUDDY_CHUNK_NUM; j++)
mm_buddy_array[i].obj_map[j] = 0;
base += mm_buddy_array[i].size;
}
}
假設如今要分配一個連續1個物理頁的空閒內存, 那麼首先找到數組下標爲0的struct mm_buddy結構,而後搜索這個內存塊鏈表, 找到一個還有剩餘內存的struct mm_buddy結構, 從中選取一個空閒塊返回。 若是數組下標爲0的struct mm_buddy結構沒有空閒內存了, 這時就逐層向上搜索有剩餘的struct mm_buddy結構。假設如今在下標爲2的struct mm_buddy結構中找到了還有空閒內存的struct mm_buddy_chunk結構, 它首先將這個塊劃分一個物理頁出來, 而後就要對剩餘的3個物理頁進行分解。依次劃分2個物理頁給數組下標1的struct mm_buddy結構, 劃分最後1個物理頁給下標爲0的struct mm_buddy結構。經過算法來達到減小內存碎片的目的, 這個就是夥伴系統的核心算法。
alloc_page函數用來分配2^order數目的連續物理頁, 好比order爲0表示就分配一個物理頁,order爲1表示分配2個物理頁, order爲2,分配4個物理頁。 下面是alloc_page函數的核心代碼實現, 完整源代碼在附件中。釋放算法基本就是逆過程, 下文不在講述, 你們能夠參考源代碼。
void *alloc_page(int order)
{
int idx;
void *addr;
if (mm_buddy_array[order].free_num) {
addr = alloc_buddy_chunk(order);
return addr;
}
for (idx = order + 1; idx < BUDDY_CHUNK_NUM; idx++) {
if (mm_buddy_array[idx].free_num) {
//printk(「alloc page from order: %d\n」, idx);
addr = __alloc_page(order, idx);
if (addr)
return addr;
}
}
return NULL;
}
void *__alloc_page(int old_order, int new_order)
{
int i, next;
void *base, *new_base, *addr;
for (i = 0; i < mm_buddy_array[new_order].total_num; i++) {
if (!mm_buddy_array[new_order].obj_map[i]) {
mm_buddy_array[new_order].obj_map[i] = 1;
next = i;
break;
}
}
if (next >= mm_buddy_array[new_order].total_num)
return NULL;
mm_buddy_array[new_order].free_num–;
base = addr = mm_buddy_array[new_order].obj[next];
new_base = base + (1 << new_order) * PAGE_SIZE;
while (new_order > old_order) {
new_order–;
new_base -= (1 << new_order) * PAGE_SIZE;
next = ++mm_buddy_array[new_order].total_num;
mm_buddy_array[new_order].obj_map[next] = 0;
mm_buddy_array[new_order].free_num++;
mm_buddy_array[new_order].obj[next - 1] = new_base;
}
return addr;
Kernel Buddy夥伴系統是用來分配連續物理頁的,由於內核是管理內存的基本單位是物理頁。 若是內核代碼想要分配小內存,好比32字節,buddy就沒辦法提供了。Slab分配算法被用來管理小的內存塊。它的基本思想是將一個物理頁劃分紅多個小的obj塊, 爲了減小內存碎片, slab將每一個物理頁劃分紅了大小爲
8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096的小塊。 slab的結構以下:
——- —— ——
|cache|–> |slab| –> |slab|
——- —— ——
|cache|
—–
|cache| …
—–
|cache| …
—–
|cache| …
——- —— ——
|cache|–> |slab| –> |slab|
——- —— ——
|cache|–>|slab|–>|slab|
——- —— ——
每一個cache由struct slab_cache來描述:
struct slab_cache {
struct slab_obj_cache obj_cache; /* 用來實現obj的本地高速緩存 */
void *slab_page; /* 最後一個slab的地址, kfree的時候用到 */
int slab_size; /* slab的大小 */
int slab_num; /* cache中, slab的數目 */
int free_num; /* cache中剩餘slab的數目 */
int align; /* 用於CPU高速緩存 */
int color_num; /* slab的顏色, 用於CPU L1 cache */
int color_next; /* 下一個slab的顏色 */
char name[SLAB_CACHE_NAME_LEN]; /* slab的名字 */
void (*ctor)(void); /* cache的初始化函數 */
void (*dtor)(void); /* cache的結束函數 */
struct list_head list; /* 用於連接每一個slab */
struct list_head cache_list; /* 用於連接每一個cache */
};
一個slab的結構以下:
+———————————————–+
| struct slab | bufctl | obj | obj | …| color |
+———————————————–+
每一個slab的最前面是這個slab的管理結構, bufctl是個數組,用於將後面的obj連接起來。obj就是一個要分配的對象, color用於CPU cache對齊。它的結構以下:
struct slab {
int obj_num; /* slab中obj的數目 */
int free_num; /* 剩餘obj的數目 */
int free_idx; /* slab中下一個空閒obj的索引 */
void *base; /* slab中第一個obj的地址 */
struct list_head list; /* 用於連接每一個slab */
};
先初始化每一個cache, cache用slab_cache_array數組來管理。
void init_general_slab_cache(void)
{
int i;
printk(「Init genernal slab cache.\n」);
for (i = 0; i < SLAB_SIZE_NUM; i++) {
slab_cache_array[i].slab_size = slab_size[i];
slab_cache_array[i].slab_num = SLAB_NUM;
slab_cache_array[i].free_num = 0;
slab_cache_array[i].ctor = NULL;
slab_cache_array[i].dtor = NULL;
slab_cache_array[i].color_num =
compute_slab_color_num(slab_size[i], PAGE_SIZE);
slab_cache_array[i].color_next = 0;
slab_cache_array[i].obj_cache.limit = 0;
INIT_LIST_HEAD(&slab_cache_array[i].list);
init_slab(&slab_cache_array[i], slab_size[i]);
}
}
每一個cache默認有2個slab, 他們用鏈表連接起來。
int init_slab(struct slab_cache *slab_cache, int size)
{
int i;
for (i = 0; i < SLAB_NUM; i++) {
void *addr;
// alloc_page是buddy系統提供的api, 用來分配一頁物理內存。
addr = alloc_page(PAGE_ORDER_ZERO);
if (!addr) {
printk(「alloc page failed.\n」);
return -1;
}
//printk(「slab alloc page at 0x%x\n」, addr);
// 繼續初始化每一個slab
__init_slab(slab_cache, addr, size);
}
return 0;
}
void __init_slab(struct slab_cache *slab_cache, void *addr, int size)
{
struct slab *new_slab = (struct slab *)addr;
void *base;
int idx;
// 根據size來計算slab中obj的數目。
new_slab->obj_num = compute_slab_obj_num(size, PAGE_SIZE);
new_slab->free_num = new_slab->obj_num;
// slab_bufctl數組中保存的是下一個空閒的obj索引, 至關於用鏈表的形式把obj連接起來。
for (idx = 0; idx < new_slab->obj_num – 1; idx++)
slab_bufctl(new_slab)[idx] = idx + 1;
slab_bufctl(new_slab)[idx] = -1;
if (slab_cache->ctor)
slab_cache->ctor();
slab_cache->slab_page = addr;
slab_cache->free_num += new_slab->free_num;
slab_cache->color_next = get_slab_color(slab_cache);
new_slab->free_idx = 0;
list_add_tail(&(new_slab->list), &(slab_cache->list));
new_slab->base = set_slab_base_addr(addr, new_slab);
new_slab->base = fix_slab_base_addr(new_slab->base,
slab_cache->color_next);
}
先根據size的大小計算對應的slab_cache_array數組下標, 而後遍歷slab鏈表, 找到一個
還有空閒obj的slab。 在經過get_slab_obj函數從中取得一個空閒的obj。
void *get_slab_obj(struct slab *slab, struct slab_cache *slab_cache)
{
void *obj;
obj = slab->base + slab_cache->slab_size * slab->free_idx;
slab->free_idx = slab_bufctl(slab)[slab->free_idx];
slab->free_num–;
slab_cache->free_num–;
return obj;
}
void *kmalloc(int size)
{
struct slab *s = NULL;
struct list_head *p = NULL;
void *obj;
int idx;
if (size < 0 || size > 1024)
return NULL;
idx = check_slab_size(size);
// 當cache中沒有空閒的slab時就擴展這個cache, 從新生成一個slab。
if (!slab_cache_array[idx].free_num) {
printk(「expand slab obj in %d.\n」, idx);
if (!(s = expand_slab(&slab_cache_array[idx]))) {
printk(「expand slab failed.\n」);
return NULL;
}
obj = get_slab_obj(s, &(slab_cache_array[idx]));
return obj;
}
// 遍歷slab鏈表找到一個空閒的slab結構。
list_for_each(p, (&slab_cache_array[idx].list)) {
s = list_entry(p, struct slab, list);
if (s && s->free_num) {
// 找到一個空閒的obj。
obj = get_slab_obj(s, &(slab_cache_array[idx]));
return obj;
}
}
return NULL;
}
6、參考文獻
一、內存管理內幕 – http://www.ibm.com/developerworks/cn/linux/l-memory/二、Glibc內存管理-ptmalloc2源碼分析 – http://mqzhuang.iteye.com/blog/1005909三、ptmalloc分配器的分析 – http://blogold.chinaunix.net/u/8059/showart_1002710.html四、ptmallo2源碼 – http://www.malloc.de/en/五、Linux slab 分配器剖析 – http://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/六、linux kernel source code – http://www.kernel.org