Linux內存管理初探
作者:王智通
一、前言
二、簡單的內存管理器示例
三、GNU malloc算法
四、Kernel Buddy夥伴係統算法
五、Kernel Slab/Slub高速緩存算法
一、前言
這次課程最初的題目叫《linux內存管理》, 可是寫著寫著就感覺這個題目起的太大了, VM(virtul memory)是操作係統中最抽象最複雜的子係統, 想通過一次課把它全部講清楚有點不現實。 所以我把這次課程的名字改成內存管理初探,先講講linux內存的分配算法, 後續課程中在陸續涉及內存映射與回收機製。另外,由於本人水平有限, 有講的不對的地方, 請大家多多扔磚頭, 當然為了支持環保, 請扔銀行卡。
二、簡單的內存管理器示例
c/c++程序員對glibc的malloc在熟悉不過了,大家在平時的開發中都會用到malloc進行內存分配,或者在對malloc進行一層包裝, 如xmalloc,在sshd等開源軟件中經常會遇見。 那麼大家有沒有想過glibc的malloc是如何實現的呢。glibc的內存管理器非常複雜, 如果想要通過分析源碼來了解內存管理器的實現將會花很多時間。本小節我們將會通過編寫一個簡陋的內存管理器來替代GNU glibc的內存管理器, 以此學習下基本的內存管理器設計方法。
1、內存管理簡介
前麵說道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的內存管理器也是這個原理, 隻不過它的管理算法很複雜, 本小節我們將自己實現一個簡單的內存管理器, 牛刀小試一把,看看內存管理是怎麼設計出來的, 然後我們在下麵的章節中在看看工業級的內存管理器應該如何設計。
2、數據結構
內存管理器其實就是管理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用於雙向鏈表連接。
3、初始化內存塊
前麵提到內存管理器是由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);
}
}
每個塊都被加入到雙向鏈表中。
3、malloc實現
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;
}
4、free實現
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);
}
5、測試
我們這個簡單的內存管理器已經寫好了, 下麵寫個程序測試先:
#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
6、總結
通過前麵的幾個小節我們已經知道如何來寫一個簡單的內存管理器了, 但是它與工業級的內存管理器還是有很大差距的, 一個優秀的內存管理要有如下的優點:
兼容性 – 遵循posix標準
分配/回收速度 – 這是係統性能的熱點
線程安全 – SMP處理器上支持多線程的安全分配
支持緩存 – 為了加快分配速度
三、GNU malloc算法
1、常見的開源內存管理器介紹
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源碼分析》一文, 更多關於內存分配器介紹的知識請參考此文。
2、GNU ptmalloc2簡介
GNU glibc使用了ptmalloc2內存管理器, 它是由Wolfram Gloger在Doug Lea的基礎上改進來的。ptmalloc2可以理解成pthread malloc, 它提供了多線程之間安全分配內存的能力。
3、數據結構
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的功能。
4、分配算法
此處省略N個字, 由於ptmalloc2的內存分配策略有點複雜, 用簡短的文字描述不清,在給大家分享的時候, 我會用一些形象的語言來描述, 請大家一定要去聽哦。
四、Kernel Buddy夥伴係統算法
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設計的項目中都可以用到。
1、數據結構
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;
};
2、初始化:
為了快速定為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;
}
}
3、分配算法:
假設現在要分配一個連續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 Slab高速緩存算法
1、數據結構
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 */
};
2、初始化
先初始化每個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);
}
2、分配算法
先根據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;
}
六、參考文獻
1、內存管理內幕 – https://www.ibm.com/developerworks/cn/linux/l-memory/
2、Glibc內存管理-ptmalloc2源碼分析 – https://mqzhuang.iteye.com/blog/1005909
3、ptmalloc分配器的分析 – https://blogold.chinaunix.net/u/8059/showart_1002710.html
4、ptmallo2源碼 – https://www.malloc.de/en/
5、Linux slab 分配器剖析 – https://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/
6、linux kernel source code – https://www.kernel.org
最後更新:2017-04-03 07:57:03