临时内核映射:
临时内核映射和永久内核映射相比,不会阻塞请求映射页框的进程,可知临时内核映射请求可以发生在中断和可延迟函数中。也称原子映射;在高端内存的任一页框可以通过一个窗口映射到内核地址空间,根据不同的需求,选择不同的窗口来创建映射,这些窗口都以枚举类型定义在km_type中;
内核必须确保同一个窗口不能被两个不同的控制路径同时使用;
enum km_type {
KMAP_D(0) KM_BOUNCE_READ,
KMAP_D(1) KM_SKB_SUNRPC_DATA,
KMAP_D(2) KM_SKB_DATA_SOFTIRQ,
KMAP_D(3) KM_USER0,
KMAP_D(4) KM_USER1,
KMAP_D(5) KM_BIO_SRC_IRQ,
KMAP_D(6) KM_BIO_DST_IRQ,
KMAP_D(7) KM_PTE0,
KMAP_D(8) KM_PTE1,
KMAP_D(9) KM_IRQ0,
KMAP_D(10) KM_IRQ1,
KMAP_D(11) KM_SOFTIRQ0,
KMAP_D(12) KM_SOFTIRQ1,
KMAP_D(13) KM_SYNC_ICACHE,
KMAP_D(14) KM_SYNC_DCACHE,
/* UML specific, for copy_*_user - used in do_op_one_page */
KMAP_D(15) KM_UML_USERCOPY,
KMAP_D(16) KM_IRQ_PTE,
KMAP_D(17) KM_NMI,
KMAP_D(18) KM_NMI_PTE,
KMAP_D(19) KM_TYPE_NR
};
在km_type中的每个符号(除了最后一个)都是固定映射的线性地址的一个下标。enum fixed_addressed数据结构包含符号FIX_KMAP_BEGIN和FIX_KMAP_END;把后者的值赋成下标FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1。在这种方式下,系统中的每个CPU都有KM_TYPE_NR个固定映射的线性地址。
/* * kmap_atomic/kunmap_atomic is significantly faster than kmap/kunmap because * no global lock is needed and because the kmap code must perform a global TLB * invalidation when the kmap pool wraps. * * However when holding an atomic kmap it is not legal to sleep, so atomic * kmaps are appropriate for short, tight code paths only. */ void *kmap_atomic_prot(struct page *page, pgprot_t prot) { unsigned long vaddr; int idx, type; /* even !CONFIG_PREEMPT needs this, for in_atomic in do_page_fault */ pagefault_disable(); /*为了保证函数的原子性,禁止page fault handler*/ if (!PageHighMem(page)) return page_address(page); type = kmap_atomic_idx_push(); <span style="white-space:pre"> /*smp_processor_id()得到CPU的标识号,用KM_TYPE_NR乘以该标识号就得到了该CPU可用窗口的区段,</span> idx = type + KM_TYPE_NR*smp_processor_id(); vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);/*得到线性地址对应页表项的虚拟地址*/ BUG_ON(!pte_none(*(kmap_pte-idx))); set_pte(kmap_pte-idx, mk_pte(page, prot));/*将页表项与page进行关联,用kmap_pte-idx而不是用kmap_pte+idx,因为固定映射区是逆向生长的,即枚举项越靠前的部分的虚拟地址越靠后*/ arch_flush_lazy_mmu_mode(); return (void *)vaddr; } EXPORT
分析:
void __kunmap_atomic(void *kvaddr) { unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK; if (vaddr >= __fix_to_virt(FIX_KMAP_END) && vaddr <= __fix_to_virt(FIX_KMAP_BEGIN)) { //记住固定地址是逆向生长的 int idx, type; type = kmap_atomic_idx(); idx = type + KM_TYPE_NR * smp_processor_id(); #ifdef CONFIG_DEBUG_HIGHMEM WARN_ON_ONCE(vaddr != __fix_to_virt(FIX_KMAP_BEGIN + idx)); #endif /* * Force other mappings to Oops if they'll try to access this * pte without first remap it. Keeping stale mappings around * is a bad idea also, in case the page changes cacheability * attributes or becomes a protected page in a hypervisor. */ kpte_clear_flush(kmap_pte-idx, vaddr); /*如果建立了映射则清除页表项的内容,并且淸刷相应的TLB项*/ kmap_atomic_idx_pop(); arch_flush_lazy_mmu_mode(); } #ifdef CONFIG_DEBUG_HIGHMEM else { BUG_ON(vaddr < PAGE_OFFSET); BUG_ON(vaddr >= (unsigned long)high_memory); } #endif pagefault_enable();<span class="comment" style="margin: 0px; padding: 0px; border: none; color: rgb(0, 130, 0); font-family: Consolas, 'Courier New', Courier, mono, serif; font-size: 13.3333px; line-height: 20px; background-color: rgb(248, 248, 248);"></span>/*重新使能page fault handler*/ <span style="margin: 0px; padding: 0px; border: none; font-family: Consolas, 'Courier New', Courier, mono, serif; font-size: 13.3333px; line-height: 20px; background-color: rgb(248, 248, 248);"></span>
linux内存布局;
来源于http://blog.csdn.net/bullbat/article/details/7179282