与直接映射的物理内存末端、高端内存的始端所对应的线性地址存放在high_memory变量中,在x86体系结构上,高于896MB的所有物理内存的范围大都是高端内存,它并不会永久地或自动地映射到内核地址空间,尽管x86处理器能够寻址物理RAM的范围达到4GB(启用PAE可以寻址到64GB)。一旦这些页被分配,就必须in射到内核的逻辑地址空间上。在x86上,高端内存中的页被映射到3GB-4GB。
内核可以采用三种不同的机制将页框映射到高端内存;分别叫做永久内核映射、临时内核映射以及非连续内存分配。在这里,只总结前两种技术,第三种技术将在后面总结。
建立永久内核映射可能阻塞当前进程;这发生在空闲页表项不存在时,也就是在高端内存上没有页表项可以用作页框的“窗口”时。因此,永久内核映射不能用于中断处理程序和可延迟函数。相反,建立临时内核映射绝不会要求阻塞当前进程;不过,他的缺点是只有很少的临时内核映射可以同时建立起来。
使用临时内核映射的内核控制路径必须保证当前没有其他的内核控制路径在使用同样地映射。这意味着内核控制路径永远不能被阻塞,后者其他内核控制路径有可能使用同一个窗口来映射其他的高端内存页。
永久内存映射
永久内核映射允许内核建立高端页框到内核地址空间的长期映射。他们使用住内核页表中一个专门的页表,其地址存放在变量pkmap_page_table中,这在前面的页表机制管理区初始化中已经介绍过了。页表中的表项数由LAST_PKMAP宏产生。因此,内核一次最多访问2MB或4MB的高端内存。
[cpp] view plain copy print ?
-
- #define PKMAP_BASE ((FIXADDR_BOOT_START - PAGE_SIZE * (LAST_PKMAP + 1)) \
- & PMD_MASK)
该页表映射的线性地址从PKMAP_BASE开始。pkmap_count数组包含LAST_PKMAP个计数器,pkmap_page_table页表中的每一项都有一个。
高端映射区逻辑页面的分配结构用分配表(pkmap_count)来描述,它有1024项,对应于映射区内不同的逻辑页面。当分配项的值等于0时为自由项,等于1时为缓冲项,大于1时为映射项。映射页面的分配基于分配表的扫描,当所有的自由项都用完时,系统将清除所有的缓冲项,如果连缓冲项都用完时,系统将进入等待状态。
[cpp] view plain copy print ?
-
-
-
-
-
-
-
- static int pkmap_count[LAST_PKMAP];
-
- static unsigned int last_pkmap_nr;
为了记录高端内存页框与永久内核映射包含的线性地址之间的联系,内核使用了page_address_htable散列表。该表包含一个page_address_map数据结构,用于为高端内存中的每一个页框进行当前映射。而该数据结构还包含一个指向页描述符的指针和分配给该页框的线性地址。
[cpp] view plain copy print ?
- * Hash table bucket
- */
- static struct page_address_slot {
- struct list_head lh;
- spinlock_t lock;
- } ____cacheline_aligned_in_smp page_address_htable[1<<PA_HASH_ORDER];
-
-
-
-
- struct page_address_map {
- struct page *page;
- void *virtual;
- struct list_head list;
- };
page_address()函数返回页框对应的线性地址
[cpp] view plain copy print ?
- * Returns the page's virtual address.
- */
-
- void *page_address(struct page *page)
- {
- unsigned long flags;
- void *ret;
- struct page_address_slot *pas;
-
- if (!PageHighMem(page))
-
-
-
- return lowmem_page_address(page);
-
- pas = page_slot(page);
- ret = NULL;
- spin_lock_irqsave(&pas->lock, flags);
- if (!list_empty(&pas->lh)) {
-
- struct page_address_map *pam;
-
- list_for_each_entry(pam, &pas->lh, list) {
- if (pam->page == page) {
- ret = pam->virtual;
- goto done;
- }
- }
- }
- done:
- spin_unlock_irqrestore(&pas->lock, flags);
- return ret;
- }
kmap()函数建立永久内核映射。
[cpp] view plain copy print ?
-
-
- void *kmap(struct page *page)
- {
- might_sleep();
- if (!PageHighMem(page))
- return page_address(page);
- return kmap_high(page);
- }
[cpp] view plain copy print ?
-
-
-
-
-
-
-
-
- void *kmap_high(struct page *page)
- {
- unsigned long vaddr;
-
-
-
-
-
- lock_kmap();
-
-
- vaddr = (unsigned long)page_address(page);
- if (!vaddr)
-
-
-
- vaddr = map_new_virtual(page);
- pkmap_count[PKMAP_NR(vaddr)]++;
- BUG_ON(pkmap_count[PKMAP_NR(vaddr)] < 2);
- unlock_kmap();
- return (void*) vaddr;
- }
[cpp] view plain copy print ?
- static inline unsigned long map_new_virtual(struct page *page)
- {
- unsigned long vaddr;
- int count;
-
- start:
- count = LAST_PKMAP;
-
- 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])
- break;
- if (--count)
- continue;
-
-
-
-
- {
- DECLARE_WAITQUEUE(wait, current);
-
- __set_current_state(TASK_UNINTERRUPTIBLE);
- add_wait_queue(&pkmap_map_wait, &wait);
- unlock_kmap();
- schedule();
- remove_wait_queue(&pkmap_map_wait, &wait);
- lock_kmap();
-
-
- if (page_address(page))
- return (unsigned long)page_address(page);
-
-
- goto start;
- }
- }
-
- vaddr = PKMAP_ADDR(last_pkmap_nr);
-
-
-
-
-
-
-
-
- set_pte_at(&init_mm, vaddr,
- &(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;
- }
kunmap()函数撤销先前由kmap()建立的永久内核映射
如果页确实在高端内存中,则调用kunmap_high()函数
[cpp] view plain copy print ?
- * kunmap_high - map a highmem page into memory
- * @page: &struct page to unmap
- *
- * If ARCH_NEEDS_KMAP_HIGH_GET is not defined then this may be called
- * only from user context.
- */
- void kunmap_high(struct page *page)
- {
- unsigned long vaddr;
- unsigned long nr;
- unsigned long flags;
- int need_wakeup;
-
- lock_kmap_any(flags);
- vaddr = (unsigned long)page_address(page);
- BUG_ON(!vaddr);
- nr = PKMAP_NR(vaddr);
-
-
-
-
-
- need_wakeup = 0;
- switch (--pkmap_count[nr]) {
- case 0:
- BUG();
- case 1:
-
-
-
-
-
-
-
-
-
-
- need_wakeup = waitqueue_active(&pkmap_map_wait);
- }
- unlock_kmap_any(flags);
-
-
- if (need_wakeup)
- wake_up(&pkmap_map_wait);
- }
http://blog.csdn.net/bullbat/article/details/7178348