Linux Malloc分析-從用戶空間到內核空間

本文介紹malloc的實現及其malloc在進行堆擴展操做,並分析了虛擬地址到物理地址是如何實現映射關係。html

ordeder原創,原文連接: http://blog.csdn.net/ordeder/article/details/41654509
git

1背景知識

1.1 進程的用戶空間


圖1:來源 http://www.open-open.com/lib/view/open1409716051963.htmlweb

該結構是由進程task_struct.mm_struct進行管理的mm_struct的定義以下:
算法

[cpp] view plaincopy在CODE上查看代碼片派生到個人代碼片數據結構

  1. struct mm_struct {  app

  2.     struct vm_area_struct * mmap;   /* list of VMAs */  函數

  3.     ...  atom

  4.     pgd_t * pgd;                //用於地址映射  spa

  5.     atomic_t mm_users;          /* How many users with user space? */  .net

  6.     atomic_t mm_count;          /* How many references to "struct mm_struct" (users count as 1) */  

  7.     int map_count;              /* number of VMAs */  

  8.     ...  

  9.     //描述用戶空間的段分佈:數據段,代碼段,堆棧段  

  10.     unsigned long start_code, end_code, start_data, end_data;  

  11.     unsigned long start_brk, brk, start_stack;  

  12.     unsigned long arg_start, arg_end, env_start, env_end;  

  13.     unsigned long rss, total_vm, locked_vm;  

  14.     ...  

  15. };  

結構中的startxxx與endxxx描述了進程用戶空間數據段的所在地址。對於堆空間而言,start_brk是堆空間的起始地址,堆是向上擴展的。對於進程堆空間的擴展,brk來記錄堆的頂部位置。而進程動態申請的空間的已經使用到的地址空間(正在使用的變量)是被映射的,這些地址空間記錄於鏈表struct vm_area_struct * mmap中。

 1.2 地址映射

虛擬地址和物理地址的映射 : http://blog.csdn.net/ordeder/article/details/41630945

2 malloc 和free

malloc用於用戶空間堆擴展的函數接口。該函數是C庫,屬於封裝了相關係統調用(brk())的glibc庫函數。而不是系統調用(系統可沒有sys_malloc()。若是談及malloc函數涉及的系統內核的那些操做,那麼整體能夠分爲用戶空間層面和內核空間層面來討論。

2.1 用戶層

malloc 的源碼可見 http://repo.or.cz/w/glibc.git/blob/HEAD:/malloc/malloc.c

Malloc和free是在用戶層工做的,該接口爲用戶提供一個比較方便管理堆的接口。它的主要工做是維護一個空閒的堆空間緩衝區鏈表。該緩衝區能夠用以下數據結構表述:

[cpp] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. struct malloc_chunk {  

  2.     INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */  

  3.     INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */  

  4.     struct malloc_chunk* fd; /* double links -- used only if free. */  

  5.     struct malloc_chunk* bk;  

  6.     /* Only used for large blocks: pointer to next larger size. */  

  7.     struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */  

  8.     struct malloc_chunk* bk_nextsize;  

  9. };  

簡化版的空閒緩衝區鏈表以下所示,圖中head即爲上述的malloc_chunk結構。而緊接着的size大小的內存區間是該chunk對應的數據區。



【malloc】

        每當進程調用malloc,首先會在該堆緩衝區尋找足夠大小的內存塊分配給進程(選擇緩衝區中的那個塊就有首次命中和最佳命中兩種算法)。若是freechunklist已沒法知足需求的chunk時,那麼malloc會經過調用系統調用brk()將進程空間的堆進行擴展,在新擴展的堆空間上創建一個新的chunk並加入到freelist中,這個過程至關於進程批量想系統申請一塊內存(大小可能比實際需求大得多)。

       malloc返回的地址是chunk的中用於存儲數據的首地址,即: chunk + sizeof(chunk)


一個簡單的首次命中malloc的僞代碼:

[cpp] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. chunk free_list  

  2. malloc(size)  

  3.   foreach(chuck in freelist)  

  4.     if(chunk.size >size)  

  5.       return chunk + sizeof(chunk)  

  6.   //空閒緩衝區沒法知足需求,那麼像系統批發內存  

  7.   add = sys_brk(brk+(size +sizeof(chunk)))  

  8.   newchunk = (chunk)add;  

  9.   newchunk.size = size;  

  10.   ...  

  11.   return newchunk + sizeof(newchunk)  

【free】

        free操做是對堆空間的回收,回收的區塊並非當即返還給內核。而是將區塊對應的chunk「標記」爲空閒,加入空閒隊列中。固然,若是空閒隊列中出現相鄰地址的chunk,那麼能夠考慮合併,已解決內存的碎片化,一遍知足以後的大內存申請的需求。

一個簡單的free僞代碼:將釋放的地址空間加入空閒鏈表中

[cpp] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. free(add)  

  2.   pchunk = add - sizeof(chunk)  

  3.   insert_to_freelist(pchunk)  

2.2 內核層

上文中,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在CODE上查看代碼片派生到個人代碼片

  1. addr = vma.start  

  2. do{  

  3.   handle_mm_fault(mm,vma,addr,...)  

  4.   addr += PAGESIZE  

  5. }while(addr< vma.end)  

函數handle_mm_fault爲addr所在的內存頁映射物理頁面。實現虛擬空間到物理空間的換算和映射。

1.經過alloc_page申請一個物理頁面;

2.換算addr在進程pdg映射中所在的pte地址;

3.將addr對應的pte設置爲物理頁面的首地址。


2.3 虛擬地址與物理地址

當進程讀取堆空間的地址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

3 總結

1 Malloc 和 free 怎麼看着就是個用戶空間的內存池。特別free的實現。

2 堆的擴展依據brk的移動。Vm_area記錄了虛擬空間中已使用的地址塊。

3 每一個進程的虛擬地址到物理地址的映射是有進程mm.pgd決定的,在該結構中記錄了虛擬頁號到物理頁號的映射關係。

參考

內核源碼情景分析

http://blog.csdn.net/kobbee9/article/details/7397010

http://www.open-open.com/lib/view/open1409716051963.html

相關文章
相關標籤/搜索