Linux內存管理第七章 -- Non-Contiguous Memory Allocation(vmalloc)

Linux內存管理第七章 – Non-Contiguous Memory Allocation(vmalloc)

因爲cache相關原因和內存訪問延遲的原因,在處理大量內存的時候應該儘量選擇連續pages的物理內存。但不幸的是由於buddy allocator造成的外部碎片的問題,系統中不可能任何時刻都有大塊的連續物理內存。Linux通過vmalloc()提供了一種機制讓非連續的物理內存在虛擬地址中是連續的,可以當做是虛擬地址連續的內存來使用。
在虛擬地址空間中有預留出VMALLOC_START ~ VMALLOC_END之間的一塊虛擬內存區域。VMALLOC_START的具體位置取決於可用物理內存的數量,但是此內存區域的大小始終至少爲VMALLOC_RESERVER大小,在x86中該區域的大小是128MB。該區域的具體大小在第四章中有詳細討論。
該區域中的內核頁表需要按需調整指向通過buddy allocator分配的物理pages。這就意味着每次分配的大小是物理page的整數倍大小。因爲這種分配要求修改內核頁表,因此使用vmalloc()映射多少內存是由限制的因爲虛擬地址空間中之後VMALLOC_START ~ VMALLOC_END之間的虛擬地址可供vmalloc()使用。所以vmalloc()在內核核心區域小心謹慎地被使用。
本章將從內核如何追蹤vamlloc 地址空間中的小region開始描述,然後再來描述這些小的region如何分配和釋放。

Describing Virtual Memory Areas(描述虛擬內存區域)

Linux中使用資源映射allocator來管理vmalloc虛擬地址空間。struct vm_struct用來存放基礎的成對的size。它在<linux/vmalloc.h>中的定義如下:
struct vm_struct {
void *addr;
unsigned long size;
unsigned long flags;
struct page **pages;
unsigned int nr_pages;
unsigned long phys_addr;
struct vm_struct *next;
};
其中字段含義如下:

  • flags: 該flag要麼是VM_ALLOC,要麼是VM_IOREMAP。如果是VM_ALLOC,那麼該區域就是被vmalloc()所使用。如果是VM_IOREMAP,就是將high memory通過ioremap的方式映射到內核虛擬地址空間
  • addr: 當前內存塊的起始地址,應該是虛擬地址
  • size:當前內存的大小,單位是字節
  • next:指向下一個vm_struct.vm_struct通過地址大小順序存放在一個鏈表中,該鏈表被vmlist_lock所保護。

通過上面的描述可以清晰地得知,vm_struct通過next字段連接在一起並且通過地址大小排序方便查找。每個vm_struct中的地址區間有至少一個page的間隔用來阻止內存覆蓋問題。具體實例如下圖:
vmalloc
當內核想分配一個新的區域,vm_struct list將會被get_vm_area()函數掃描。而vm_struct結構體可以通過kmalloc()從通用slab cache Size-N中分配。如果該虛擬內存區域是被用來重映射IO,該函數將直接被直接調用來映射IO數據。

Allocating A Non-Contiguous Area

vmalloc(),vmalloc_dma()和vmalloc_32()被提供用來分配一個虛擬地址連續物理地址不連續的內存區域。這些函數只有一個輸入參數size,該size是與page size 對齊的。也就是通過vmalloc分配的內存大小是page size的整數倍。者三個函數都是返回該區域的虛擬地址。

void * vmalloc(unsigned long size)
從vmalloc虛擬地址空間分配size大小的內存區域
void * vmalloc_dma(unsigned long size)
從ZONE_DMA中分配物理pages,並映射到vmalloc虛擬地址空間
void * vmalloc_32(unsigned long size)
分配適用於32位地址的內存區域。該函數會保證物理pages是從ZONE_NORMAL中進行分配並且要求當前設備是32位的

下面再來看下vmalloc的大致調用流程:
vmalloc_flow
從上圖中可以清晰地看出,vmalloc有三步:

  • first,通過get_vm_area()函數查找一個足夠大的空間的虛擬地址段,然後再通過kmalloc分配一個新的vm_struct結構體
  • second,計算當前分配的內存大小需要佔用多少個page,然後通過kmalloc分配一組struct page指針數組,再通過調用buddy allocator接口alloc_page()每次獲取一個物理頁框填入到vm_struct中的struct page* 數則
  • third,分配PMD,PTE更新內核頁表,返回映射後的虛擬地址。

被vmalloc()更新的頁表不是當前進程的頁表而是全局引用頁表init_mm->pgd.這就意味着一個訪問vmalloc分配的內存區域會引起缺頁異常因爲當前進程的頁表沒有指向正確的區域。在缺頁異常處理代碼中,這是一種特殊的情況。缺頁異常代碼知道哪些缺頁異常是發生在vmalloc區域,並且從master頁表中的信息更新當前進程的頁表。vmalloc()與buddy allocator以及page faulting之間的關係如下圖:
vmalloc page

Freeing A Non-Contiguous Area

函數vfree()用來釋放一個虛擬內存區域。它線性地掃描vm_structS,來尋找期望要釋放的區域然後調用vmfree_area_pages()來釋放該region中對應的物理內存。
vfree

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章