本文介紹malloc的實現及其malloc在進行堆擴展操做,並分析了虛擬地址到物理地址是如何實現映射關係。html
ordeder原創,原文連接: http://blog.csdn.net/ordeder/article/details/41654509
git
圖1:來源 http://www.open-open.com/lib/view/open1409716051963.htmlweb
該結構是由進程task_struct.mm_struct進行管理的mm_struct的定義以下:
算法
[cpp] view plaincopy數據結構
struct mm_struct { app
struct vm_area_struct * mmap; /* list of VMAs */ 函數
... atom
pgd_t * pgd; //用於地址映射 spa
atomic_t mm_users; /* How many users with user space? */ .net
atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */
int map_count; /* number of VMAs */
...
//描述用戶空間的段分佈:數據段,代碼段,堆棧段
unsigned long start_code, end_code, start_data, end_data;
unsigned long start_brk, brk, start_stack;
unsigned long arg_start, arg_end, env_start, env_end;
unsigned long rss, total_vm, locked_vm;
...
};
結構中的startxxx與endxxx描述了進程用戶空間數據段的所在地址。對於堆空間而言,start_brk是堆空間的起始地址,堆是向上擴展的。對於進程堆空間的擴展,brk來記錄堆的頂部位置。而進程動態申請的空間的已經使用到的地址空間(正在使用的變量)是被映射的,這些地址空間記錄於鏈表struct vm_area_struct * mmap中。
虛擬地址和物理地址的映射 : http://blog.csdn.net/ordeder/article/details/41630945
malloc用於用戶空間堆擴展的函數接口。該函數是C庫,屬於封裝了相關係統調用(brk())的glibc庫函數。而不是系統調用(系統可沒有sys_malloc()。若是談及malloc函數涉及的系統內核的那些操做,那麼整體能夠分爲用戶空間層面和內核空間層面來討論。
malloc 的源碼可見 http://repo.or.cz/w/glibc.git/blob/HEAD:/malloc/malloc.c
Malloc和free是在用戶層工做的,該接口爲用戶提供一個比較方便管理堆的接口。它的主要工做是維護一個空閒的堆空間緩衝區鏈表。該緩衝區能夠用以下數據結構表述:
[cpp] view plaincopy
struct malloc_chunk {
INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};
簡化版的空閒緩衝區鏈表以下所示,圖中head即爲上述的malloc_chunk結構。而緊接着的size大小的內存區間是該chunk對應的數據區。
【malloc】
每當進程調用malloc,首先會在該堆緩衝區尋找足夠大小的內存塊分配給進程(選擇緩衝區中的那個塊就有首次命中和最佳命中兩種算法)。若是freechunklist已沒法知足需求的chunk時,那麼malloc會經過調用系統調用brk()將進程空間的堆進行擴展,在新擴展的堆空間上創建一個新的chunk並加入到freelist中,這個過程至關於進程批量想系統申請一塊內存(大小可能比實際需求大得多)。
malloc返回的地址是chunk的中用於存儲數據的首地址,即: chunk + sizeof(chunk)
一個簡單的首次命中malloc的僞代碼:
[cpp] view plaincopy
chunk free_list
malloc(size)
foreach(chuck in freelist)
if(chunk.size >size)
return chunk + sizeof(chunk)
//空閒緩衝區沒法知足需求,那麼像系統批發內存
add = sys_brk(brk+(size +sizeof(chunk)))
newchunk = (chunk)add;
newchunk.size = size;
...
return newchunk + sizeof(newchunk)
【free】
free操做是對堆空間的回收,回收的區塊並非當即返還給內核。而是將區塊對應的chunk「標記」爲空閒,加入空閒隊列中。固然,若是空閒隊列中出現相鄰地址的chunk,那麼能夠考慮合併,已解決內存的碎片化,一遍知足以後的大內存申請的需求。
一個簡單的free僞代碼:將釋放的地址空間加入空閒鏈表中
[cpp] view plaincopy
free(add)
pchunk = add - sizeof(chunk)
insert_to_freelist(pchunk)
上文中,malloc的空閒chunk列表沒法知足用戶的需求,那麼要經過sys_brk()進行堆的擴展,這時候才真正算得上進入內核空間。
sys_brk()涉及的主要操做有:
1. 在mm_struct中的堆上界brk延伸到newbrk:即申請一塊vma,vma.start=brk vma.end=newbrk
2. 爲該虛擬區間塊進行物理內存的映射:從虛擬空間vma.start~vma.end中的每一個內存頁進行映射:
[cpp] view plaincopy
addr = vma.start
do{
handle_mm_fault(mm,vma,addr,...)
addr += PAGESIZE
}while(addr< vma.end)
函數handle_mm_fault爲addr所在的內存頁映射物理頁面。實現虛擬空間到物理空間的換算和映射。
1.經過alloc_page申請一個物理頁面;
2.換算addr在進程pdg映射中所在的pte地址;
3.將addr對應的pte設置爲物理頁面的首地址。
當進程讀取堆空間的地址vaddr時,虛擬地址vaddr到物理頁面的映射以下圖所示。
1. 用戶空間的虛擬地址vaddr經過MMU(pgd,pmd,pte)找到對應的頁表項pte記錄的物理地址paddr
2. 頁表項paddr的高20位是物理頁號:index = x >> PAGE_SHIFT,同理,index後面補上12個0就是物理頁表的首地址。
3. 經過物理頁號,咱們能夠再內核中找到該物理頁的描述的指針mem_map[index]。Page結構能夠參考http://blog.csdn.net/ordeder/article/details/41630945。
1 Malloc 和 free 怎麼看着就是個用戶空間的內存池。特別free的實現。
2 堆的擴展依據brk的移動。Vm_area記錄了虛擬空間中已使用的地址塊。
3 每一個進程的虛擬地址到物理地址的映射是有進程mm.pgd決定的,在該結構中記錄了虛擬頁號到物理頁號的映射關係。
內核源碼情景分析
http://blog.csdn.net/kobbee9/article/details/7397010