Linux vmalloc缺页异常处理_vmalloc_fault

129 阅读4分钟

以下讨论不考虑页表隔离的情况下。

一、do_page_fault()

内核初始阶段结束后,任何进程或内核线程都不能直接使用内核页表,当内核态进程对vmalloc分配非连续内存区的第一次访问时,当把内核虚拟地址转换为物理地址时,CPU的内存管理单元遇到空的页表项并产生一个缺页。vmalloc也会产生do_page_fault()。

We fault-in kernel-space virtual memory on-demand. The’reference’ page table is init_mm.pgd.

do\_page\_fault()
	-->\_\_do\_page\_fault()
		-->vmalloc\_fault()

// linux-3.10.1/arch/x86/mm/fault.c

/\*
 \* This routine handles page faults. It determines the address,
 \* and the problem, and then passes it off to one of the appropriate
 \* routines.
 \*/
static void __kprobes
\_\_do\_page\_fault(struct pt\_regs \*regs, unsigned long error_code)
{
	/\* Get the faulting address: \*/
	address = read\_cr2();

		/\*
 \* We fault-in kernel-space virtual memory on-demand. The
 \* 'reference' page table is init\_mm.pgd.
 \*
 \* NOTE! We MUST NOT take any locks for this case. We may
 \* be in an interrupt or a critical region, and should
 \* only copy the information from the master page table,
 \* nothing more.
 \*
 \* This verifies that the fault happens in kernel space
 \* (error\_code & 4) == 0, and that the fault was not a
 \* protection error (error\_code & 9) == 0.
 \*/
	fault\_in\_kernel\_space(address);
	vmalloc\_fault(address);
	......
}

vmalloc_fault缺页异常只会发生在内核态且缺页的虚拟地址空间大于TASK_SIZE_MAX。因此vmalloc_fault检查相应的内核页表init_mm.pgd。

// linux-3.10.1/arch/x86/include/asm/processor.h
/\*
 \* User space process size. 47bits minus one guard page.
 \*/
#define TASK\_SIZE\_MAX ((1UL << 47) - PAGE\_SIZE)

// linux-3.10.1/arch/x86/mm/fault.c
static int fault\_in\_kernel\_space(unsigned long address)
{
	return address >= TASK_SIZE_MAX;
}


二、vmalloc_fault

vmalloc_fault主要是发生vmalloc缺页异常时用于关联内核页表项,将内核页表同步到用户进程的页表中。

// linux-3.10.1/arch/x86/include/asm/pgtable\_64\_types.h

#define VMALLOC_START    \_AC(0xffffc90000000000, UL)
#define VMALLOC_END      \_AC(0xffffe8ffffffffff, UL)

// linux-3.10.1/arch/x86/mm/fault.c

/\*
 \* 64-bit:
 \*
 \* Handle a fault on the vmalloc area
 \*
 \* This assumes no large pages in there.
 \*/
static noinline __kprobes int vmalloc\_fault(unsigned long address)
{
	//分别定义用户进程页表的pgd、pud、pmd、pte和内核页表的pgd、pud、pmd、pte
	//然后准备将内核页表同步到用户进程的页表中
	pgd_t \*pgd, \*pgd_ref;
	pud_t \*pud, \*pud_ref;
	pmd_t \*pmd, \*pmd_ref;
	pte_t \*pte, \*pte_ref;

	/\* Make sure we are in vmalloc area: \*/
	if (!(address >= VMALLOC_START && address < VMALLOC_END))
		return -1;

	WARN\_ON\_ONCE(in\_nmi());

	/\*
 \* Copy kernel mappings over when needed. This can also
 \* happen within a race in page table update. In the later
 \* case just flush:
 \*/
	 //将内核页表同步到用户进程的页表中
	 
	 /\*读取当前进程PGD \*/
	pgd = pgd\_offset(current->active_mm, address);
	/\*pgd\_ref 为vmalloc建立的内核页表 \*/
	pgd_ref = pgd\_offset\_k(address);
	if (pgd\_none(\*pgd_ref))
		return -1;

	if (pgd\_none(\*pgd)) {
		set\_pgd(pgd, \*pgd_ref);
		arch\_flush\_lazy\_mmu\_mode();
	} else {
		BUG\_ON(pgd\_page\_vaddr(\*pgd) != pgd\_page\_vaddr(\*pgd_ref));
	}

	/\*
 \* Below here mismatches are bugs because these lower tables
 \* are shared:
 \*/

	 /\*依次复制内核页表的PUD->PMD->PTE \*/
	 
	pud = pud\_offset(pgd, address);
	pud_ref = pud\_offset(pgd_ref, address);
	if (pud\_none(\*pud_ref))
		return -1;

	if (pud\_none(\*pud) || pud\_page\_vaddr(\*pud) != pud\_page\_vaddr(\*pud_ref))
		BUG();

	pmd = pmd\_offset(pud, address);
	pmd_ref = pmd\_offset(pud_ref, address);
	if (pmd\_none(\*pmd_ref))
		return -1;

	if (pmd\_none(\*pmd) || pmd\_page(\*pmd) != pmd\_page(\*pmd_ref))
		BUG();

	pte_ref = pte\_offset\_kernel(pmd_ref, address);
	if (!pte\_present(\*pte_ref))
		return -1;

	pte = pte\_offset\_kernel(pmd, address);

	/\*
 \* Don't use pte\_page here, because the mappings can point
 \* outside mem\_map, and the NUMA hash lookup cannot handle
 \* that:
 \*/
	if (!pte\_present(\*pte) || pte\_pfn(\*pte) != pte\_pfn(\*pte_ref))
		BUG();

	return 0;
}

(1)pgd_offset

最全的Linux教程,Linux从入门到精通

======================

  1. linux从入门到精通(第2版)

  2. Linux系统移植

  3. Linux驱动开发入门与实战

  4. LINUX 系统移植 第2版

  5. Linux开源网络全栈详解 从DPDK到OpenFlow

华为18级工程师呕心沥血撰写3000页Linux学习笔记教程

第一份《Linux从入门到精通》466页

====================

内容简介

====

本书是获得了很多读者好评的Linux经典畅销书**《Linux从入门到精通》的第2版**。本书第1版出版后曾经多次印刷,并被51CTO读书频道评为“最受读者喜爱的原创IT技术图书奖”。本书第﹖版以最新的Ubuntu 12.04为版本,循序渐进地向读者介绍了Linux 的基础应用、系统管理、网络应用、娱乐和办公、程序开发、服务器配置、系统安全等。本书附带1张光盘,内容为本书配套多媒体教学视频。另外,本书还为读者提供了大量的Linux学习资料和Ubuntu安装镜像文件,供读者免费下载。

华为18级工程师呕心沥血撰写3000页Linux学习笔记教程

本书适合广大Linux初中级用户、开源软件爱好者和大专院校的学生阅读,同时也非常适合准备从事Linux平台开发的各类人员。

需要《Linux入门到精通》、《linux系统移植》、《Linux驱动开发入门实战》、《Linux开源网络全栈》电子书籍及教程的工程师朋友们劳烦您转发+评论

详情docs.qq.com/doc/DSmdCdUNwcEJDTXFK