Linux页表解析

页表

intel的5级页表为:

简称全称
PML5Page Map Level 5
PML4Page Map Level 4
PDPTPage Directory Point Table
PDPage Directory
PTPage Table

对应kernel的5级页表为:

简称全称
PGDPage Global Directory
P4DPage Level 4 Directory
PUDPage Upper Directory
PMDPage Middle Directory
PTEPage Table Entry

大部分情况下4级页表已经够了,这里以4级页表为例说明

4KB页

offset为低12位

2MB页

offset为低12位 + 9位PT

1GB页

offset为低12位 + 9位PT + 9位PD

PGD初始化

pgd在mm_init中初始化,有3种场景会调用mm_init:

start_kernel -> mm_init

SYSCALL_DEFINE0(fork) -> ... -> dup_mm -> mm_init

SYSCALL_DEFINE3(execve, ...) -> ... -> mm_alloc -> mm_init

mm_init

mm_init
	mm_alloc_pgd
		pgd_alloc
			pgd = _pgd_alloc();
				__get_free_pages
			mm->pgd = pgd;
			pgd_ctor(mm, pgd);
				clone_pgd_range(pgd + KERNEL_PGD_BOUNDARY,
								swapper_pg_dir + KERNEL_PGD_BOUNDARY,
								KERNEL_PGD_PTRS); // 复制内核页表

__get_free_pages

/*
 * Common helper functions. Never use with __GFP_HIGHMEM because the returned
 * address cannot represent highmem pages. Use alloc_pages and then kmap if
 * you need to access high mem.
 */
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
{
	struct page *page;

	page = alloc_pages(gfp_mask & ~__GFP_HIGHMEM, order); // 分配物理页
	if (!page)
		return 0;
	return (unsigned long) page_address(page); // 获取物理页的虚拟地址
}

#define page_address(page) lowmem_page_address(page)

static __always_inline void *lowmem_page_address(const struct page *page)
{
	return page_to_virt(page);
}

#define page_to_virt(page)	pfn_to_virt(page_to_pfn(page))

切换CR3

切换进程上下文时切换CR3

context_switch

context_switch
	switch_mm_irqs_off
		load_new_mm_cr3(next->pgd, new_asid, true);
			new_mm_cr3 = build_cr3(pgdir, new_asid);
				__sme_pa
					__pa
						__phys_addr
							__phys_addr_nodebug // 虚拟地址转物理地址
			write_cr3(new_mm_cr3);

__phys_addr_nodebug

当x >= __START_KERNEL_map(kernel映射区),return x - __START_KERNEL_map + phys_base
当x < __START_KERNEL_map(direct mapping映射区),return x - PAGE_OFFSET

static inline unsigned long __phys_addr_nodebug(unsigned long x)
{
	unsigned long y = x - __START_KERNEL_map;

	/* use the carry flag to determine if x was < __START_KERNEL_map */
	x = y + ((x > y) ? phys_base : (__START_KERNEL_map - PAGE_OFFSET));

	return x;
}

运行模式

IA-32架构包含3个运行模式和1个准运行模式:

  • 实模式
  • 保护模式
  • 系统管理模式
  • 虚拟8086模式

Intel 64架构支持IA-32架构的所有模式,同时引入了一个新的运行模式:

  • IA-32e模式,包含2个子模式:
    • 兼容模式(CS.L为0)
    • 64位模式(CS.L为1)
  1. 实模式、保护模式、虚拟8086模式、IA-32e模式都可以通过SMI#进入系统管理模式,通过RSM回到原模式
  2. 保护模式、系统管理模式、虚拟8086模式、IA-32e模式都可以通过Reset直接回到实模式
  3. 实模式通过PE=1进入保护模式,反之亦然
  4. 保护模式通过VM=1进入虚拟8086模式,反之亦然
  5. 保护模式通过LME=1 && PG=1进入IA-32e模式,反之亦然

分页模式

4种分页模式:

  • 32-bit
  • PAE
  • 4-level
  • 5-level

PG=0 && PAE=0 && LME=0是起点

  1. 先设置PG=1,进入32-bit,不能设置LME,再设置PAE=1,进入PAE,不能设置LME
  2. 先设置PAE=1,再设置PG=1,进入PAE,不能设置LME
  3. 先设置PAE=1,再设置LME=1,PG=0 && PAE=1 && LME=1,最后设置PG=1,进入4-level
  4. 先设置LME=1,不能设置PG=1,再设置PAE=1,PG=0 && PAE=1 && LME=1,最后设置PG=1,进入4-level

通过虚拟地址访问页表本身

对于CPU来说,MMU需要经过4次转换找到物理页(4-level分页模式),PML4/PDPT/PD/PT(占用一个物理页)和普通物理页没有区别,因此都可以通过页表寻址

设置PML4的最后一个PML4E指向PML4本身
当PLM4 index = 511时,PLM4产生1次递归转换,在CPU完成4次转换时,找到的物理页其实是PT
当PLM4/PDPT index = 511时,PLM4产生2次递归转换,在CPU完成4次转换时,找到的物理页其实是PD
当PLM4/PDPT/PD index = 511时,PLM4产生3次递归转换,在CPU完成4次转换时,找到的物理页其实是PDPT
当PLM4/PDPT/PD/PT index = 511时,PLM4产生4次递归转换,在CPU完成4次转换时,找到的物理页其实是PLM4

通过这种"欺骗"CPU的方式,可以实现通过虚拟地址访问页表本身,代价是高9位为511的虚拟地址被页表占用

访问普通页

访问PT

访问PD

访问PDPT

访问PLM4

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值