1. 程式人生 > >3.5.8 核心對映------《深入Linux核心架構》筆記

3.5.8 核心對映------《深入Linux核心架構》筆記

vmalloc能完成高階記憶體到核心虛擬空間的對映,但是核心更期望一種能專門正對高階記憶體的對映關係,持久對映 便是將高階記憶體長久對映到記憶體虛擬地址空間。

持久對映原理

類同vmalloc需要建立物理頁到虛擬地址的對映關係,持久對映顯然要簡單的多,所涉及資料結構如下:

struct page_address_map {
   struct page *page; //物理頁
   void *virtual; //虛擬地址,該地址必須在持久對映區(PKMAP_BASE→ FIXMAP_START)
   struct list_head list;//系統標準連結串列
};

為了便於組織,系統使用了散列表來儲存

page_address_map例項。對映關係是示意圖如下:


圖中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是分開的。如下圖: