目錄
3.5.7 內核中不連續頁的分配
3.5.8 內核映射
本專欄文章將有70篇左右,歡迎+關注,訂閱后續文章。
本節講解vmalloc, vmap,kmap原理。
3.5.7 內核中不連續頁的分配
kmalloc函數:分配物理地址和虛擬地址都連續的內存。
????????kmalloc基于slab,而slab基于伙伴系統。
void *vmalloc(unsigned long size);
? ? ? ? 介紹:
????????????????分配一大片連續虛擬內存,但其物理地址不一定連續。
????????使用場景:
????????????????需要分配很大一塊內存。如模塊加載時。
????????優點:
????????????????更有效利用內存,減少內存分配失敗。
????????缺點:
????????????????但TLB表項增多,TLB cache miss增加。
????????應用進程使用的內存:通常不要求物理連續。
vmalloc分配的虛擬內存如下圖:
vmalloc區域之間插入1頁安全隙,其沒有對應物理頁表項。
如果程序無意訪問到安全隙時,觸發段錯誤機制。阻止潛在的安全威脅或系統崩潰。
vmalloc函數會生成一個sturct vm_struct實例。用于描述vmalloc分配的區域。
struct vm_struct {
????????struct vm_struct ????????*next;? ? ? ? ? ? //連接所有實例,表頭為struct vm_struct *vmlist
????????void ????????????????????????*addr;? ? ? ? ? ? ? //起始的虛擬地址。
????????unsigned long ????????size;? ? ? ? ? ? ? ? ?//該區域大小。
????????unsigned long ????????flags;
????????struct page? ? ? ? ? ? ? **pages;? ? ? ? ? ?//指向page數組,映射源自哪些物理頁。
????????unsigned int? ? ? ? ? ? nr_pages; ????????//包含有多少個物理頁。
????????phys_addr_t? ? ? ? ? ?phys_addr; ????????//ioremap使用,其物理地址。
????????const void? ? ? ? ? ? ? *caller;
};
flags:
????????VA_ALLOC:表示該區域由vmalloc函數建立。
????????VA_MAP:表示該區域由vmap函數建立。
????????VM_IOREMAP:表示該區域由ioremap函數建立。
vmap,ioremap,vmalloc三個函數:
? ? ? ? 1. 都用于建立物理地址非連續的映射。
? ? ? ? 2. 都會生成一個struct vm_struct實例,用flag成員區別。
全局變量struct vm_struct *vmlist鏈表頭,鏈接所有vm_struct。
分配內存
vmalloc實現:
get_vm_area_node:
????????創建vm_struct實例,并分配虛擬內存。
__vmalloc_area_node:
????????分配對應物理頁。
????????初始化vm_struct的pages,nr_pages成員。
????????調用map_vm_area:將分散物理頁映射到連續虛擬空間。
vmalloc的實現中使用了__GFP_HIGHMEM:
????????表示盡可能從ZONE_HIGHMEM分配頁。
當分配頁后,應從對應節點伙伴系統中移除。
其他映射方法
下面函數也可創建虛擬連續的映射:
????????vmalloc_32:
????????????????分配的物理內存總是可以用32位指針尋址。
????????vmap函數:
????????????????將一個page數組映射到連續虛擬地址空間。
????????????????該函數不分配頁,需提前分配好。
????????????????用VM_MAP標識。
????????ioremap:
????????????????體系架構自己實現。將硬件IO空間映射到內核空間。
????????????????用VM_IOREMAP標識,驅動中多使用。
釋放內存
vfree:
????????釋放vmalloc,vmalloc_32分配的內存。
????????將頁返回伙伴系統。
vunmap:
????????釋放vmap,ioremap創建的映射。
????????不會將頁返回伙伴系統。
上述兩個函數最終都調用__vunmap
3.5.8 內核映射
除vmalloc,其他將ZONE_HIGHMEM域到內核空間的方式:
永久內核映射(pkmap):
????????pkmap:Permanent Kernel MAPping。
????????作用:為指定高端內存頁創建永久映射到內核空間,直到手動解除映射。
????????內核空間范圍:
????????????????PKMAP_BASE - FIXADDR_START區域
創建一個永久內核映射:
????????void *kmap(struct page *page)
????????{
????????????????if (!PageHighMem(page))
????????????????????????return page_address(page); 沒有高端內存時,簡單的將頁轉換為虛擬地址。
????????
????????????????return kmap_high(page);
????????}
解除永久映射:
????????void kunmap(struct page *page)
????????????????內容:解除映射,刪除頁表,刷出TLB。
過多kmap映射可能導致內存碎片和性能問題。
避免頻繁使用kmap。
kmap和vmap區別:
????????kmap:單頁的映射。
????????????????短時間的映射。
????????vmap:多個頁映射。(將一組不連續物理頁映射到連續虛擬地址)。
????????????????長時間的映射。
kmap:若pkmap數組沒有空閑位置,會睡眠,所以不能在中斷中使用。
此時應使用kmap_atomic與kunmap_atomic。
在64位體系架構沒有高端內存,此時kmap,kunmap,kmap_atomic,kunmap_atomic函數名一樣,但實現不一致。