JIURL文档-Linux的虚拟内存与分页机制(x86-64位)(二)

本文详细探讨了Linux在64位环境下如何实现虚拟内存管理和分页机制,包括内存分段、页表结构、页级转换等关键概念。
摘要由CSDN通过智能技术生成
作者:JIURL
日期:2015年11月01日

还是分页机制

进程的页目录和页表在虚拟地址空间中的位置


   上一篇中,我们把分页相关结构,分别叫做PML4,PDPT,PD,PT。这是Intel对x86-64中这些结构的称呼。Linux需要支持多种不同类型的CPU架构,所以对分页相关结构,做了一层抽象。对于需要4级地址转换的分页相关结构,分别叫做PGD,PUD,PMD,PTE。对于x86-64CPU来说,PGD=PML4,PUD=PDPT,PMD=PD,PTE=PT(注意PTE是PTE表的意思)。两者指的是同样的东西,仅仅是叫法不同(番茄和西红柿,番茄炒西红柿算不算黑暗料理?)。

   当执行一条访问内存的指令时,如果寄存器中的分页标志位已经被设置,CPU就自动完成虚拟地址到物理地址的转换,来完成该指令。CPU在转换地址过程中,并不需要PGD,PUD,PMD,PTE的虚拟地址。但是分页标志位被设置之后,CPU会把所有指令中的地址当做虚拟地址转换成物理地址。所以所有程序(包括操作系统)中使用的都是虚拟地址。一个进程的一套PGD,PUD,PMD,PTE结构是由系统维护的,比如一个进程申请或者释放内存,系统就需要设置对应的一些页表项。系统需要访问PGD,PUD,PMD,PTE结构,但是系统只能使用虚拟地址,所以系统必须把一个进程的PGD,PUD,PMD,PTE结构映射到地址空间中(32位的Linux情况有不同。目前64位用于映射物理内存的地址空间足够大,估计未来也不需要做进一步处理)。

    Linux会把物理内存从物理地址0x0000000000000000开始,一一映射到 0xffff880000000000 -0xffffc7ffffffffff这块地址空间中。这块地址空间的大小为64TB。物理内存小于64TB的话,将全部被映射到这块地址空间中。也就是说,通过这个区域的虚拟地址,将可以访问整个物理内存(相信将来也不会有使用大于64TB物理内存的PC,那得交多少电费?吓死本宝宝了。)。

    Linux中,可以通过每个进程的进程结构(task_struct)找到该进程的PGD表的物理地址,顺着PGD表就可以找到该进程的所有分页相关结构的物理地址。task_struct.mm->pgd中的值,就是该进程PGD表的物理地址。切换地址空间时,将会把要切换过来的进程的task_struct.mm->pgd值,放入CR3中(具体细节参考Linux源码中的switch_mm函数)。
task_struct 结构是linux的进程结构,在/include/linux/sched.h中定义。
task_struct 结构中有成员变量 struct mm_struct *mm, *active_mm;
mm_struct 结构在 /include/linux/mm_types.h中定义。
mm_struct 结构中有成员变量 pgd_t * pgd;

   我们来访问一个进程的所有分页相关结构。整个物理内存被全部一一映射到了0xffff880000000000 -0xffffc7ffffffffff 这块地址空间中,所以 0xffff880000000000 +物理地址,就是该物理地址的一个虚拟地址(一个物理页可能被映射到多个进程地址空间的多个虚拟地址)。通过这个虚拟地址,就可以访问该物理页中的内容。通过每个进程的mm->pgd,可以得到该进程PGD表的物理地址,该物理地址+0xffff880000000000,就得到了该进程PGD表的虚拟地址,就可以访问每一个PGD表项。找到该进程每一个PUD表的物理地址,同样方法可以得到该进程每一个PUD表的虚拟地址。通过所有PUD表的内容,使用同样方法可以得到该进程所有PMD表的虚拟地址。通过所有PMD表的内容,使用同样方法可以得到该进程所有PTE表的虚拟地址。通过所有PTE表的内容,使用同样方法可以得到该进程所有物理页的虚拟地址(意味着一个内核模块可以方便的访问任何进程的地址空间中的内容)。0xffff880000000000- 0xffffc7ffffffffff的64TB地址空间位于高128TB的系统地址空间内,需要系统级的权限才能访问。

    Linux系统也是通过上面的方法来访问一个进程的分页结构的。kmap_atomic函数的源码实现,可以部分验证这一点。(调试环境为,eclipse+qemu源码级内核调试)

static inline void *kmap_atomic(struct page *page)
{
   pagefault_disable();
    returnpage_address(page);
}

#define page_address(page) lowmem_page_address(page)

static __always_inline void *lowmem_page_address(const struct page*page)
{
    return__va(PFN_PHYS(page_to_pfn(page)));
}

#define__va(x)          ((void*)((unsigned long)(x)+PAGE_OFFSET))

#definePAGE_OFFSET       ((unsignedlong)__PAGE_OFFSET)
#define__PAGE_OFFSET          _AC(0xffff880000000000, UL)

有效页目录项和有效页表项

   PGD表项,PUD表项,PMD表项,PTE表现的最低位,第0位,指明了一页是否映射了物理内存。最低位为1,表示该页映射了物理内存。PGD表项,PUD表项,PMD表项,PTE表现,8个字节,64位,有着差不多的结构。结构细节可以参考Intel软件开发手册。

PGD,PUD,PMD,PTE 实例解析

   我写了一个简单的内核模块,可以得到指定进程的pgd值,可以输出指定地址处4KB的内容。

下面的内容,是通过我写的这个内核模块,在我的计算机上运行得到的结果。

实例进程1
init进程(pid为1)的 task_struct.mm->pgd 值为 0x369de000。
即 init进程的PGD表的虚拟地址为0xffff880000000000+0x369de000=0xffff8800369de000。

1. PGD表
0xffff8800369de000 处,4KB内容:
0000000000000000 0000000000000000 0000000000000000 00000000000000000000000000000000 0000000000000000 00000000000000000000000000000000
0000000000000000 0000000000000000 0000000000000000 00000000000000000000000000000000 0000000000000000 00000000000000000000000000000000
0000000000000000 0000000000000000 0000000000000000 00000000000000000000000000000000 0000000000000000 00000000000000000000000000000000
0000000000000000 0000000000000000 0000000000000000 00000000000000000000000000000000 0000000000000000 00000000000000000000000000000000
0000000000000000 0000000000000000 0000000000000000 00000000000000000000000000000000 0000000000000000 00000000000000000000000000000000
0000000000000000 0000000000000000 0000000000000000 00000000000000000000000000000000 0000000000000000 00000000000000000000000000000000
0000000000000000 0000000000000000 0000000000000000 00000000000000000000000000000000 0000000000000000 00000000000000000000000000000000
0000000000000000 0000000000000000 0000000000000000 00000000000000000000000000000000 0000000000000000 00000000000000000000000000000000
0000000000000000 0000000000000000 0000000000000000 00000000000000000000000000000000 0000000000000000 00000000000000000000000000000000
0000000000000000 0000000000000000 0000000000000000 00000000000000000000000000000000 0000000000000000 00000000000000000000000000000000
0000000000000000 0000000000000000 0000000000000000 00000000000000000000000000000000 0000000000000000 00000000000000000000000000000000
0000000000000000 0000000000000000 0000000000000000 00000000000000000000000000000000 0000000000000000 00000000000000000000000000000000
0000000000000000 0000000000000000 0000000000000000 00000000000000000000000000000000 0000000000000000 00000000000000000000000000000000
0000000000000000 0000000000000000 0000
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值