3.5.8 内核映射------《深入Linux内核架构》笔记

vmalloc能完成高端内存到内核虚拟空间的映射,但是内核更期望一种能专门正对高端内存的映射关系,持久映射 便是将高端内存长久映射到内存虚拟地址空间。


持久映射原理

类同vmalloc需要建立物理页到虚拟地址的映射关系,持久映射显然要简单的多,所涉及数据结构如下:

struct page_address_map {
   struct page *page; //物理页
   void *virtual; //虚拟地址,该地址必须在持久映射区(PKMAP_BASE→ FIXMAP_START)
   struct list_head list;//系统标准链表
};

为了便于组织,系统使用了散列表来保存page_address_map实例。映射关系是示意图如下:



图中vitual address space里面的每一个格子的空间大小为4kB,及一个页的大小,该空间及虚拟空间。pkmap_count所指代的数组的每个单位大小是4B,及int类型,该数组主要是为了对vitual address space 中的虚拟地址被映射多少次的计数


持久映射功能函数

// 该函数根据page指针找到与之映射的虚拟地址
void *kmap_high(struct page *page)
{
	unsigned long vaddr;

	/*
	 * For highmem pages, we can't trust "virtual" until
	 * after we have the lock.
	 */
	lock_kmap();
	vaddr = (unsigned long)page_address(page); // page_address的实现上节已经提及
	if (!vaddr)
		vaddr = map_new_virtual(page); // 如果虚拟地址不存在,需要建立新的映射
	pkmap_count[PKMAP_NR(vaddr)]++; // 引用计数加1
	BUG_ON(pkmap_count[PKMAP_NR(vaddr)] < 2);
	unlock_kmap();
	return (void*) vaddr;
}

void kunmap_high(struct page *page); // 解除映射关系
该函数的主要任务对pkmap_count数组中的对应位置减1

临时内核映射原理

上文描述的持久映射因为不能用于中断处理程序,所以系统还需要一种原子执行的函数来完成任务,逻辑上称为kmap_atomic,他的主要优点是执行速度快,但是不能用于进入睡眠的代码。

但在描述临时映射之前,阐明固定映射是必要的,毕竟它是基于固定映射的。固定映射在3.4节中有所提及。

在内核虚拟内存的划分中,最后一段FIXMAPS即为固定映射所处的虚拟地址段。


首先创建一个枚举类型,这里我略去了与本次主题无关的变量


enum fixed_addresses {
	 。。。。。。

	VSYSCALL_LAST_PAGE,
	VSYSCALL_FIRST_PAGE = VSYSCALL_LAST_PAGE
			    + ((VSYSCALL_END-VSYSCALL_START) >> PAGE_SHIFT) - 1,
	VSYSCALL_HPET,
	FIX_DBGP_BASE,
	FIX_EARLYCON_MEM_BASE,
#ifdef CONFIG_X86_32
	FIX_KMAP_BEGIN,	/* reserved pte's for temporary kernel mappings */
						/* 保存到页表中,用于建立内核临时映射*/
	FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,
#ifdef CONFIG_PCI_MMCONFIG
	FIX_PCIE_MCFG,
#endif
#endif
	。。。。。。
	__end_of_fixed_addresses
};

unsigned long __FIXADDR_TOP = 0xfffff000; // 注意这里的并不是oxffffffff
#define FIXADDR_TOP ((unsigned long)__FIXADDR_TOP)

//通过枚举变量计算虚拟地址
#define__fix_to_virt(x) (FIXADDR_TOP - ((x) << PAGE_SHIFT))
#define__virt_to_fix(x) ((FIXADDR_TOP - ((x)&PAGE_MASK)) >>PAGE_SHIFT)

其次因为__fix_to_virt(x)已经能将枚举变量转换为虚拟地址了,那么下一步,只需要将枚举变量于物理页关联便可以了。系统通过函数set_fixmap(idx,phys)实现。该函数是通过页表来建立关联关系。


最后来讨论我们的临时映射了,临时映射也会建立一个枚举类型


enum km_type {
	KM_BOUNCE_READ,
	KM_SKB_SUNRPC_DATA,
	KM_SKB_DATA_SOFTIRQ,
	KM_USER0,
	KM_USER1,
	KM_BIO_SRC_IRQ,
	KM_BIO_DST_IRQ,
	KM_PTE0,
	KM_PTE1,
	KM_PTE2,
	KM_IRQ0,
	KM_IRQ1,
	KM_SOFTIRQ0,
	KM_SOFTIRQ1,
	KM_TYPE_NR
};

虚拟地址的计算过程:

idx= type + KM_TYPE * smp_processor_id();

vaddr= __fix_to_virt(FIX_KMAP_BEGIN + idx);

FIX_KMAP_BEGIN来自固定映射的枚举类型

从上面的计算公式我们知道每一个临时内核映射对于不同的cpu是分开的。如下图:



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值