高端内存映射方式
高端内存映射分为三种:永久映射、临时映射和非连续动态内存映射。高端内存一般是指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的物理地址则不能直接访问。