首先要看几个重要的数据结构和概念:
#ifdef CONFIG_X86_PAE
#define LAST_PKMAP 512
#else
#define LAST_PKMAP 1024
#define LAST_PKMAP_MASK (LAST_PKMAP - 1)
#endif
定义了永久地址映射的,页表的数量,从这里我们也可以知道,永久地址映射一共映射的大小是4M,即:1024个页表
#define PKMAP_BASE (0xff800000UL)
#define PKMAP_NR(virt) ((virt - PKMAP_BASE) >> PAGE_SHIFT)
#define PKMAP_ADDR(nr) (PKMAP_BASE + ((nr) << PAGE_SHIFT))
PKMAP_BASE定义了永久地址映射线性地址的基地址,然后是线性地址和所映射的第几个PKMAP之间的转换
static int pkmap_count[LAST_PKMAP];
static unsigned int last_pkmap_nr;
pte_t * pkmap_page_table;
pkmap_count记录了pkmap nr的count,pkmap_page_table 则就是映射页表的指针
pakmap_page_table的初始化在permanent_kmaps_init()里面初始化
从里面我们可以得到pdg = swapper_pg_dir + pgd_index(PKMAP_BASE);用一个页目录就应该够了,因为一共映射的大小是4M
pkmap_page_table也在这里初始化
下面我们看一下具体是怎么分配的:
void *kmap(struct page *page)
{
if(!PageHighMem(page))
return page_address(page);
return kmap_high(page);
}
如果page不在高端页框中,则线性地址总是存在的,所以就不需要高端映射了,因为高端内存的存在的目的就是用留出的
线性地址来映射无法再ZONE_NORMAL中映射的页框。
void fastcall *kmap_high(struct page *page)
{
unsigned long vaddr;
spin_lock(&kmap_lock);
vaddr = (unsigned long)page_address(page);
if(!vaddr)
vaddr = map_new_virtual(page);
这里我有些小疑问,vaddr如果不为0,那分配的vaddr不会属于当前要映射的kmap_high,下面应该重新分配呐
pkmap_count[PKMAP_NR(vaddr)]++;
spin_unlock(&kmap_lock); /*这里释放的有可能是map_new_virtual里面加的锁*/
return (void *)vaddr;
}
static inline unsigned long map_new_virtual(struct page *page)
{
unsigned long vaddr;
int count;
start:
count = LAST_PKMAP;
下面要找到一个空的nr
for(;;){
last_pkmap_nr = (last_pkmap_nr + 1) & LAST_PKMAP_MASK;
if(!last_pkmap_nr){
flush_all_zero_pkmaps();
count = LAST_PKMAP;
}
last_pkmap_nr 已经全部搜索一遍,然而在每次重新搜索之前都有调用flush_all_zero_pkmaps把count计数为0,
if(!pkmap_count[last_pimap_nr])
break; /*找到一个可以使用的nr*/
if(--count)
continue;
{
DECLARE_WAITQUEUE(wait, current);
__set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&pkmap_map_wait, &wait);
spin_unlock(&kmap_lock); /*在这里释放的是前面我加的锁,因为要进行调度,切换到别的进程执行*/
schedule();
remove_wait_queue(&pkmap_map_wait, &wait);
spin_lock(&kmap_lock);
上面是把当前进程加入到等待队列里
if(page_address(page))
return (unsigned long)page_address(page);
goto start;
}
}
vaddr = PKMAP_ADDR(last_pkmap_nr);
set_pte(&(pkmap_page_table[last_pkmap_nr]), mk_pte(page, kmap_prot));
转换为线性地址后,设置页表
pkmap_count[last_pkmap_nr] = 1;
set_page_address(page, (void *)vaddr);
在高端内存中,建立了专门的散列数据结构来存放页框和线性地址之间的映射
return vaddr;
}