2018年12月23日 16:32:23 rikeyone 阅读数:110更多
所属专栏: 深入浅出内存管理
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.youkuaiyun.com/rikeyone/article/details/85223796
我们知道系统中的高端内存是不能作为直接映射区映射到内核空间的,那么我们想要使用它怎么办呢?前面的文章我们已经有过相关的介绍,可以使用三种方法,分别是pkmap(永久映射)/fixmap(临时固定映射)/vmlloc,本文主要介绍fixmap,也就是固定映射又叫临时映射。
关键点
通过前面的介绍我们知道,fixmap和pkmap的最大区别是fixmap不会被阻塞,因此可以在中断上下文中使用。那会不会遇到一个问题呢?pkmap在申请映射时发现映射区已经被申请完了所以才会阻塞等待,那么fixmap如何处理这种场景呢?实际上每次fixmap都是可以成功执行映射的,原因是fixmap不会动态申请映射,它是以固定映射的方式进行的,如果我们传入了一个映射type,它就肯定会执行对应的映射,而把之前的映射冲刷掉,因此fixmap的映射关系是靠内核代码来进行维护的,所以内核需要保证同一个映射区不被重复使用,从而出现错误。
入口函数
kmap_atomic:
void *kmap_atomic(struct page *page)
{
unsigned int idx;
unsigned long vaddr;
void *kmap;
int type;
pagefault_disable();
if (!PageHighMem(page))
return page_address(page);
#ifdef CONFIG_DEBUG_HIGHMEM
/*
* There is no cache coherency issue when non VIVT, so force the
* dedicated kmap usage for better debugging purposes in that case.
*/
if (!cache_is_vivt())
kmap = NULL;
else
#endif
kmap = kmap_high_get(page);
if (kmap)
return kmap;
type = kmap_atomic_idx_push();
idx = type + KM_TYPE_NR * smp_processor_id();
vaddr = __fix_to_virt(idx);
#ifdef CONFIG_DEBUG_HIGHMEM
/*
* With debugging enabled, kunmap_atomic forces that entry to 0.
* Make sure it was indeed properly unmapped.
*/
BUG_ON(!pte_none(get_fixmap_pte(vaddr)));
#endif
/*
* When debugging is off, kunmap_atomic leaves the previous mapping
* in place, so the contained TLB flush ensures the TLB is updated
* with the new mapping.
*/
set_fixmap_pte(idx, mk_pte(page, kmap_prot));
return (void *)vaddr;
}
内核中为每个CPU定义了KM_TYPE_NR个fixmap页面,每个页面地址用一个type来表示,那么我们寻找一个对应type的地址时可以使用如下方式:
#define KM_TYPE_NR 16
idx = type + KM_TYPE_NR * smp_processor_id();
vaddr = __fix_to_virt(idx);
内核中对type在linux-4.0版本中没有明确的定义,只是通过如下方式进行了获取,实际上时按照调用顺序来进行分配的。
DECLARE_PER_CPU(int, __kmap_atomic_idx);
static inline int kmap_atomic_idx_push(void)
{
int idx = __this_cpu_inc_return(__kmap_atomic_idx) - 1;
#ifdef CONFIG_DEBUG_HIGHMEM
WARN_ON_ONCE(in_irq() && !irqs_disabled());
BUG_ON(idx >= KM_TYPE_NR);
#endif
return idx;
}