kmap的实现分析
kmap/unkmap系统调用是用来映射高端物理内存页到内核地址空间的api函数,他们分配的内核虚拟地址范围属于[PKMAP_BASE,PAGE_OFFSET]即[0xbfe00000,0xc0000000]范围,大小是2M的虚拟空间,为了映射该块虚拟地址,所使用的二级页表的大小刚好是一个物理page的总计是两个pte table(4KB)
kmap的调用流程分析:
arch/arm/mm/highmem.c
void *kmap(struct page *page)
{
might_sleep();
if (!PageHighMem(page)){//如果是低端内存,则直接返内存页对应的直接映射虚拟地址
//printk("low mem page\n");
return page_address(page);//所有的低端内存,在内核初始化时就已经映射好了,并且是不变得,且物理到虚拟相差0xc0000000
}else{
//printk("high mem page\n");
}
return kmap_high(page);//高端内存页
}
进入/trunk/mm/highmem.c的kmap_high
/**
* kmap_high - map a highmem page into memory
* @page: &struct page to map
*
* Returns the page's virtual memory address.
*
* We cannot call this from interrupts, as it may block.
*/
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);
if (!vaddr)//如果该页的映射还未建立
vaddr = map_new_virtual(page);//开始建立新的映射
pkmap_count[PKMAP_NR(vaddr)]++;//该数组的值为1,说明映射已经建立,为2表明该应声存在着引用
BUG_ON(pkmap_count[PKMAP_NR(vaddr)] < 2);
unlock_kmap();
return (void*) vaddr;
}
static inline unsigned long map_new_virtual(struct page *page)
{
unsigned long vaddr;
int count;
start:
count = LAST_PKMAP; // 2MB/4096KB=512 entries = LAST_PKMAP
/* Find an empty entry */
for (;;) {
last_pkmap_nr = (last_pkmap_nr + 1) & LAST_PKMAP_MASK;
if (!last_pkmap_nr) {
flush_all_zero_pkmaps();
count = LAST_PKMAP;
}
if (!pkmap_count[last_pkmap_nr])//为0,说明该虚拟地址不存在映射,没人使用
break; /* Found a usable entry */
if (--count)//如果遍历了整个kmap虚拟空间,都不能找到空闲的虚拟地址,则休眠等待unkmap释放虚拟地址
continue;
/*
* Sleep for somebody else to unmap their entries
*/
{
DECLARE_WAITQUEUE(wait, current);
__set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&pkmap_ma