临时内核映射:
临时内核映射和永久内核映射相比,不会阻塞请求映射页框的进程,可知临时内核映射请求可以发生在中断和可延迟函数中。也称原子映射;在高端内存的任一页框可以通过一个窗口映射到内核地址空间,根据不同的需求,选择不同的窗口来创建映射,这些窗口都以枚举类型定义在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
- #define __fix_to_virt(x) (FIXADDR_TOP - ((x) << PAGE_SHIFT))
分析:
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>
kunmap_atomic(),这个函数减少当前进程的preempt_count;因此,如果在请求临时内核映像之前能抢占内核控制路径,那么在同一个映像被撤销后可以再次抢占。??????
linux内存布局;
来源于http://blog.youkuaiyun.com/bullbat/article/details/7179282