3.5.8 核心對映------《深入Linux核心架構》筆記
vmalloc能完成高階記憶體到核心虛擬空間的對映,但是核心更期望一種能專門正對高階記憶體的對映關係,持久對映 便是將高階記憶體長久對映到記憶體虛擬地址空間。
持久對映原理
類同vmalloc需要建立物理頁到虛擬地址的對映關係,持久對映顯然要簡單的多,所涉及資料結構如下:
struct page_address_map {
struct page *page; //物理頁
void *virtual; //虛擬地址,該地址必須在持久對映區(PKMAP_BASE→ FIXMAP_START)
struct list_head list;//系統標準連結串列
};
為了便於組織,系統使用了散列表來儲存
圖中vitual address space裡面的每一個格子的空間大小為4kB,及一個頁的大小,該空間及虛擬空間。pkmap_count所指代的陣列的每個單位大小是4B,及int型別,該陣列主要是為了對vitual address space 中的虛擬地址被對映多少次的計數
持久對映功能函式
// 該函式根據page指標找到與之對映的虛擬地址 void *kmap_high(struct page *page) { unsigned long vaddr; /* * For highmem pages, we can't trust "virtual" until * after we have the lock. */ lock_kmap(); vaddr = (unsigned long)page_address(page); // page_address的實現上節已經提及 if (!vaddr) vaddr = map_new_virtual(page); // 如果虛擬地址不存在,需要建立新的對映 pkmap_count[PKMAP_NR(vaddr)]++; // 引用計數加1 BUG_ON(pkmap_count[PKMAP_NR(vaddr)] < 2); unlock_kmap(); return (void*) vaddr; } void kunmap_high(struct page *page); // 解除對映關係 該函式的主要任務對pkmap_count陣列中的對應位置減1
臨時核心對映原理
上文描述的持久對映因為不能用於中斷處理程式,所以系統還需要一種原子執行的函式來完成任務,邏輯上稱為kmap_atomic,他的主要優點是執行速度快,但是不能用於進入睡眠的程式碼。
但在描述臨時對映之前,闡明固定對映是必要的,畢竟它是基於固定對映的。固定對映在3.4節中有所提及。
在核心虛擬記憶體的劃分中,最後一段FIXMAPS即為固定對映所處的虛擬地址段。
首先建立一個列舉型別,這裡我略去了與本次主題無關的變數
enum fixed_addresses { 。。。。。。 VSYSCALL_LAST_PAGE, VSYSCALL_FIRST_PAGE = VSYSCALL_LAST_PAGE + ((VSYSCALL_END-VSYSCALL_START) >> PAGE_SHIFT) - 1, VSYSCALL_HPET, FIX_DBGP_BASE, FIX_EARLYCON_MEM_BASE, #ifdef CONFIG_X86_32 FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */ /* 儲存到頁表中,用於建立核心臨時對映*/ FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1, #ifdef CONFIG_PCI_MMCONFIG FIX_PCIE_MCFG, #endif #endif 。。。。。。 __end_of_fixed_addresses };
unsigned long __FIXADDR_TOP = 0xfffff000; // 注意這裡的並不是oxffffffff
#define FIXADDR_TOP ((unsigned long)__FIXADDR_TOP)
//通過列舉變數計算虛擬地址
#define__fix_to_virt(x) (FIXADDR_TOP - ((x) << PAGE_SHIFT))
#define__virt_to_fix(x) ((FIXADDR_TOP - ((x)&PAGE_MASK)) >>PAGE_SHIFT)
其次因為__fix_to_virt(x)已經能將列舉變數轉換為虛擬地址了,那麼下一步,只需要將列舉變數於物理頁關聯便可以了。系統通過函式set_fixmap(idx,phys)實現。該函式是通過頁表來建立關聯關係。
最後來討論我們的臨時映射了,臨時對映也會建立一個列舉型別
enum km_type {
KM_BOUNCE_READ,
KM_SKB_SUNRPC_DATA,
KM_SKB_DATA_SOFTIRQ,
KM_USER0,
KM_USER1,
KM_BIO_SRC_IRQ,
KM_BIO_DST_IRQ,
KM_PTE0,
KM_PTE1,
KM_PTE2,
KM_IRQ0,
KM_IRQ1,
KM_SOFTIRQ0,
KM_SOFTIRQ1,
KM_TYPE_NR
};
虛擬地址的計算過程:
idx= type + KM_TYPE * smp_processor_id();
vaddr= __fix_to_virt(FIX_KMAP_BEGIN + idx);
FIX_KMAP_BEGIN來自固定對映的列舉型別
從上面的計算公式我們知道每一個臨時核心對映對於不同的cpu是分開的。如下圖: