首先,在x86架构的处理器上,一个正常页面大小为4KB,非PAE模式下,CR3持有页目录页面的物理地址,PDE和PTE格式相同大小为4字节。此时每个页表页面包含1024个PTE,可以映射1024个页面。而x86的4GB地址空间共包含1M个4KB的页面,映射所有这些页面共需要1024个页表页面,映射这1024个页表页面又需要1个页目录页面。乍看起来我们一共需要1025个页面来管理1M个页面的映射,其实不然。

我们不妨逆向理顺一下思路。首先,我们的地址空间中有1M=1024x1024个页面,而一个页表页面可以映射1024个页面。所以,我们把4GB地址空间的1M个页面按每1024个页面分为1024组。映射这1024组页面正好需要1024个页表页面,所以我们从刚才的1024组页面中拿掉一组用作页表。刚才的页面还剩下1023组,会消耗掉我们这组页表页面中的1023个用来映射。所以我们这组页表页面中还剩下一个,我们用它来映射刚刚的1023个页表页面,从而消耗掉了1023个Entry,那剩下的1个Entry呢?用来映射最后这个页面本身。

这样分配听起来似乎清晰多了,能实现吗?按上面的思路,最后一个页表页面无疑是要用作页目录。x86处理器MMU访问线性地址是要经过两次查找加一个偏移(非PAE模式)来确定物理地址的。以此为基础,通过页目录、页表、再加上偏移访问那1023组页面很好理解,但是如何在线性地址中访问1023个页表页面及页目录页面呢?也就是如何通过页目录页面把1023个页表页面和页目录页面自身映射到线性地址中。其实这就是32位Windows的页表自映射机制。

在32位的Windows中,页表的起始线性地址恒为0xC0000000,整个页表区域占用了连续的4MB线性地址空间。最关键的一个设计,就是页目录的第0x300项Entry指向页目录本身,进而也就决定了页目录页面的线性地址为0xC0300000。这一设计使得MMU在查找0xC0000000~0xC03FFFFF这一线性地址空间内的任一线性地址的物理地址时,会把页目录页面当做页表页面来使用,也就是说会访问页目录页面两次。如下图,是微软2004年在东京大学举办Windows Kernel Internals讲座解释GetPteAddress原理时所给出的一张图示:

其实GetPteAddress功能就是给定一个线性地址,得出用以映射这个线性地址所属页面的PTE的地址。原理很明了,4GB的地址空间中包含1M个页面,而4MB的页表区域包含1MB个PTE,这些PTE正是按顺序一一对应了所有这些页面。所以把整个地址空间看作是页面的数组,可以很轻松计算出某个线性地址所属页面的索引,例如0xE4321000>>12=0xE4321,而PTE数组中对应PTE的线性地址即为0xC0000000+(0xE4321<<2)=0xC0390C84。

由上图可以看出,给定的线性地址的高10位为1100000000=0x300,即MMU转译线性地址的第一步:在页目录页面中查找页表指针时使用的索引为0x300,从而使MMU得到页目录页面本身的物理地址。因此MMU在第二个步骤:在页表页面中查找最终的页面指针时,会使用页目录页面作为页表页面以供查找。而页目录页面的所有1024个Entry存储的是其本身和其他1023个页表页面的物理地址,所以MMU经过两次查找最终得到的是某个页表页面的物理地址。当线性地址的高10位固定为1100000000时,剩余低22位仅能表示4MB的范围,因此其所能表示的线性地址限定在0xC0000000~0xC03FFFFF这一区间内,也正是所有页表页面所占用的线性地址空间范围。因为微软所给出的图示适是用于讲解PTE的,PTE大小为4字节并且其地址也是4字节对齐,所以最后的12位页面内偏移也可以理解为利用前10位用作PTE数组索引,最低2位恒为0。

不难想到当MMU第二步查找时如果仍然使用0x300索引,也就是线性地址高20位为11000000001100000000,那么MMU经过两次查找最终得到的物理地址就是页目录页面的物理地址,如下图:

再加上最低12位的页面内偏移, MMU最终转译得到的地址落在页目录页面内,也就是说线性地址0xC0300000~0xC0300FFF表示的区间范围即为页目录页面在线性地址中的地址区间范围。如果在12位的页面偏移中,继续使高10位为1100000000用作PTE索引,那么得到的就是指向页目录自身的PTE,也是PDE,其线性地址为0xC0300C00。

总结,使用一个页面用作页目录,使它的第0x300个Entry指向其自身,剩余的1023个Entry指向其他的1023个页表页面。从而巧妙的将页表区间映射到0xC0000000~0xC03FFFFF这一线性地址区间。

x86平台上的Windows页表映射机制的更多相关文章

  1. 运行在TQ2440开发板上以及X86平台上的linux内核编译

    一.运行在TQ2440开发板上的linux内核编译 1.获取源码并解压 直接使用天嵌移植好的“linux-2.6.30.4_20100531.tar.bz2”源码包. 解压(天嵌默认解压到/opt/E ...

  2. OpenStack平台上,windows云主机可以ping通百度但是无法打开网页,部分其它网页可以打开

    问题描述: 在OpenStack平台上的64位Windows7虚拟机,可以ping通百度,但是却无法打开百度网页. 于是,笔者又对其它网址进行的测试,发现淘宝.京东.携程部分网页可以打开,而新浪等等网 ...

  3. windows的页自映射机制

    windows下由于启用了页机制,所有软件层面的地址操作都是VA,通过descriptor(base address(32bit))+offset得到的线性地址并不直接对应物理地址,而是经过页转换机构 ...

  4. 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件

    本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...

  5. Windows平台上Caffe的训练与学习方法(以数据库CIFAR-10为例)

    Windows平台上Caffe的训练与学习方法(以数据库CIFAR-10为例) 在完成winodws平台上的caffe环境的搭建之后,亟待掌握的就是如何在caffe中进行训练与学习,下面将进行简单的介 ...

  6. Java平台上的AOP实现机制

    Java平台上的AOP实现机制 动态代理(Dynamic Proxy)机制,在运行期间动态的为相应接口生成对应的代理对象.SpringAop默认情况下采用这种机制来实现AOP机能.缺点:相对于编译后的 ...

  7. KVM基于X86硬件辅助的虚拟化技术实现机制【转】

    内存虚拟化 Shadow Paging 作者 Shawn 在其中文博客中很详尽地介绍了 KVM 在只支持一级分页的 x86 平台上用 “Shadow Paging”进行 MMU 虚拟化的实现,由于目前 ...

  8. Linux平台上DPDK入门指南

    1. 简介 本文档包含DPDK软件安装和配置的相关说明.旨在帮助用户快速启动和运行软件.文档主要描述了在Linux环境下编译和 运行DPDK应用程序,但是文档并不深入DPDK的具体实现细节. 1.1. ...

  9. linux 逆向映射机制浅析

    2017-05-20 聚会回来一如既往的看了会羽毛球比赛,然后想到前几天和朋友讨论的逆向映射的问题,还是简要总结下,免得以后再忘记了!可是当我添加时间……这就有点尴尬了……520还在写技术博客…… 闲 ...

随机推荐

  1. springcloud之服务注册与发现(Eureka)

    springcloud服务注册与发现 使用Eureka实现服务治理 作用:实现服务治理(服务注册与发现) 简介: Spring Cloud Eureka是Spring Cloud Netflix项目下 ...

  2. java学习笔记(一) 服务器的认识

    RPC与RMI.SOAP的联系及区别 http://www.jb51.net/article/68971.htm 几大服务器的区别nginx/tomcat/ http://www.cnblogs.co ...

  3. JS中replace()用法举例

    语法: string.replace(regexp,replacement) 参数: regexp:声明了要替换的模式的RegExp对象.如果该参数是一个字符串,则将它作为要检索的直接量文本模式,而不 ...

  4. 迭代法与开根号求值(letcode 69)

    p { margin-bottom: 0.25cm; line-height: 120% } 一.理论证明 p { margin-bottom: 0.25cm; line-height: 120% } ...

  5. Python练习----多级菜单

    多级菜单要求:      1.三级菜单          2.可依次选择进入各子菜单      3.可以返回上一层      4.输入'q'可以退出   脚本: zone = { '北京' : { ' ...

  6. Mybatis基本用法--下

    Mybatis基本用法--下 第七部分 mybatis-spring-boot-starter 官网:http://www.mybatis.org/spring-boot-starter/mybati ...

  7. label联动checkbox

    label联动checkbox时,若label包含在checkbox外层时label不需for属性,设置label的display属性为block时可以使整个div联动.

  8. Python中如何防止sql注入

    sql注入中最常见的就是字符串拼接,研发人员对字符串拼接应该引起重视,不应忽略. 错误用法1: sql = "select id, name from test where id=%d an ...

  9. Warning: connect.static is not a function

    grunt-contrib-connect从0.11.x版本开始不支持connect.static和connect.directory 你应该安装serve-static(加载静态文件)和serve- ...

  10. Linux中main是如何执行的

    Linux中main是如何执行的 这是一个看似简单的问题,但是要从Linux底层一点点研究问题比较多.找到了一遍研究这个问题的文章,但可能比较老了,还是在x86机器上进行的测试. 原文链接 开始 问题 ...