記憶體映射結構:
1.32位地址線定址4G的記憶體空間,其中0-3G為使用者程式所專屬,3G-4G為核心佔有。
2.struct page:整個實體記憶體在初始化時,每個4kb頁面產生一個對應的struct page結構,這個page結構就獨一無二的代表這個實體記憶體頁面,並存放在mem_map全域數組中。
3.段式映射:首先根據程式碼片段選擇子cs為索引,以GDT值為起始地址的段描述表中選擇出對應的段描述符,隨後根據段描述符的基址,本段長度,許可權資訊等進行校正,校正成功後。cs:offset中的32位位移量直接與本段基址相累加,得出最終訪問地址。
0-3G與mem_map的映射方式:
因linux中採用的段式映射為flat模式,所以從邏輯地址到線性地址沒有變化。從段式出來進入頁式,每個使用者進程都獨自擁有一個頁目錄表(pdt),運行時存放於CR3。 CR3(頁目錄) + 前10位 => 頁面表基址 + 中10位 => 頁表項 + 後12位 => 物理頁面地址
3G-4G與mem_map的映射方式:
分為三種類型:低端記憶體/普通記憶體/高端記憶體。
低端記憶體:3G-3G+16M 用於DMA __pa線性映射
普通記憶體:3G+16M-3G+896M __pa線性映射 (若實體記憶體<896M,則分界點就在3G+實際記憶體)
高端記憶體:3G+896-4G 採用動態分配方式
4.高端記憶體(假設3G+896為高端記憶體起址)
作用:訪問到1G以外的實體記憶體空間。
線性地址共分為三段:vmalloc段/kmap段/kmap_atomic段(針對與不同的記憶體配置方式)
從記憶體配置函數的結構來看主要分為下面幾個部分:
a.夥伴演算法(最原始的面向頁的分配方式)
alloc_pages 介面:
struct page * alloc_page(unsigned int gfp_mask)——分配一頁實體記憶體並返回該頁實體記憶體的page結構指標。
struct page * alloc_pages(unsigned int gfp_mask, unsigned int order)——分配 個連續的物理頁並返回分配的第一個物理頁的page結構指標。
<釋放函數:__free_page(s)>
核心中定義:#define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0)
最終都是調用 __alloc_pages.
其中MAX_ORDER 11,及最大分配到到頁面個數為2^10(即4M)。
分配頁後還不能直接用,需要得到該頁對應的虛擬位址:
void *page_address(struct page *page);
低端記憶體的映射方式:__va((unsigned long)(page - mem_map) << 12)
高端記憶體到映射方式:struct page_address_map分配一個動態結構來管理高端記憶體。(核心是訪問不到vma的3G以下的虛擬位址的) 具體映射由kmap / kmap_atomic執行。
get_free_page介面:(alloc_pages介面兩步的替代函數)
unsigned long get_free_page(unsigned int gfp_mask)
unsigned long __get_free_page(unsigned int gfp_mask)
Unsigned long __get_free_pages(unsigned int gfp_mask, unsigned int order)
<釋放函數:free_page>
與alloc_page(s)系列最大的區別是無法申請高端記憶體,因為它返回到是一個線性地址,而高端記憶體是需要額外映射才可以訪問的。
b.slab快取(反覆分配很多同一大小記憶體) 註:使用較少
kmem_cache_t* xx_cache;
建立: xx_cache = kmem_cache_create("name", sizeof(struct xx), SLAB_HWCACHE_ALIGN, NULL, NULL);
分配: kmem_cache_alloc(xx_cache, GFP_KERNEL);
釋放: kmem_cache_free(xx_cache, addr);
記憶體池
mempool 不使用。
c.kmalloc(最常用的分配介面) 註:必須小於128KB
GFP_ATOMIC 不休眠,用於中斷處理等情況
GFP_KERNEL 會休眠,一般狀況使用此標記
GFP_USER 會休眠
__GFP_DMA 分配DMA記憶體
kmalloc/kfree
d.vmalloc/vfree
vmalloc採用高端記憶體預留的虛擬空間來收集記憶體片段引起的不連續的實體記憶體頁,是用於非連續實體記憶體分配。
當kmalloc分配不到記憶體且無實體記憶體連續的需求時,可以使用。(優先從高端記憶體中尋找)
e.ioremap()/iounmap()
ioremap()的作用是把device寄存器和記憶體的物理地址區域對應到核心虛擬地區,傳回值為核心的虛擬位址。使用的線性地址區間也在vmmlloc段
註:
vmalloc()與 alloc_pages(_GFP_HIGHMEM)+kmap();前者不連續,後者只能映射一個高端記憶體頁面
__get_free_pages與alloc_pages(NORMAL)+page_address(); 兩者完全等同
核心地址通過 __va/__pa進行中低記憶體的直接映射
高端記憶體採用kmap/kmap_atomic的方式來映射
個人總結如下:
a.在<128kB的一般記憶體配置時,使用kmalloc
允許睡眠:GFP_KERNEL
不允許睡眠:GFP_ATOMIC
b.在>128kB的記憶體配置時,使用get_free_pages,擷取成片頁面,直接返回虛擬位址(<4M)(或alloc_pages + page_address)
c.b失敗,
如果要求分配高端記憶體:alloc_pages(_GFP_HIGHMEM)+kmap(僅能映射一個頁面)
如果不要求記憶體連續: 則使用vmalloc進行分配邏輯連續的大塊頁面.(不建議)/分配速度較慢,訪問速率較慢。
d.頻繁建立和銷毀很多較大資料結構,使用slab.
e.高端記憶體映射:
允許睡眠:kmap (永久映射)
不允許睡眠:kmap_atomic (臨時映射)會覆蓋以前到映射(不建議)