高端內存映射方式
高端內存映射分為三種:永久映射、臨時映射和非連續動態內存映射。高端內存一般是指896MB以上的頁框,這段區間內核一般不能直接訪問。
1.永久映射
永久內核映射允許內核建立高端頁框到內核地址空間的長期映射。它們使用主內核頁表中的一個專門的頁表,其地址存放在pkmap_page_table變量中,該頁表映射的線性地址從PKMAP_BASE開始,LAST_PKMAP(32位系統是512,64位系統是1024)決定其表的項數,pkmap_count數組包含LAST_PKMAP個計數器。計數器有三種情況:
- 計數器=0,表示對應的頁表項沒有映射到任何高端內存頁框,且是可用的
- 計數器=1,表示對應的頁表項沒有映射到任何高端內存頁框,但是是不用的,因為從它最后一次使用以來,其相應的TLB表項還為被刷新
- 計數器=n,表示相應的頁表項映射到一個高端內存頁框,這表明正好有n-1個內核成分在使用這個頁框
.../arch/x86/include/asm/pgtable_32_types.h
40 #define PKMAP_BASE ((FIXADDR_START - PAGE_SIZE * (LAST_PKMAP + 1)) \41 & PMD_MASK)
.../linux/mm/highmem.h
126 static int pkmap_count[LAST_PKMAP];
高端內存頁框與永久內核映射包含的線性地址的關系存放在page_address_htable散列表中,該表包含了一個page_address_map數據結構,用于每個頁框的映射
static struct page_address_map {
struct page *page;
void *virtual;
struct list_head list;
} page_address_maps[LAST_PKMAP];
如何建立永久內核映射呢?可以使用kmap()/kunmap()函數建立,不能用于中斷處理程序和可延遲函數:
void *kmap(struct page *page)
{
/* 判斷是不是高端內存 */
if (!PageHighMem(page))
return page_address(page);
might_sleep();
/* 建立映射 */
return kmap_high(page);
}
2.臨時映射
臨時映射可以用在中斷處理程序和可延遲函數,留給臨時映射的頁框是很少的。一般調用kmap_atomic()函數進行臨時映射,一般是FIXADDR_START-FIXADDR_TOP的區間。
3.非連續動態內核映射
非連續內核映射,一般是從PAGE_OFFSET開始的,使用VMALLOC()/VMAP()進行映射。
小記:內核頁表是從0xc0000000開始的1G大小的范圍,但是內核頁表也必須把0xc0000000開始的線性地址轉換成從0開始的物理地址,內核的1G線性地址可以訪問4G的物理地址,
而用戶則不能直接訪問物理地址,那么內核如何訪問4G的物理地址呢?必須設定一個規則,物理內存也有分類:zone_dma、zone_normal、zone_highmem。zone_dma是訪問速度最
快的,所以一般希望映射到這塊內存,zone_normal也可以直接訪問,所以就有了0-896MB的一一映射,而高于896MB的物理地址則不能直接訪問。