首先,在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. SpringMVC 实现文件的上传与下载

    一  配置SpringMVC ,并导入与文件上传下载有关的jar包(在此不再赘述) 二 新建 相应 jsp 和controller FileUpAndDown.jsp <%@ page lang ...

  2. fatal: The remote end hung up unexpectedly

    git push 的时候出错,提示: fatal: The remote end hung up unexpectedly 遇见几次了,原因是因为文件太大,把限制放宽就好了.命令: git confi ...

  3. 基于HTML5和WebGL的碰撞测试

    这是公司大神写的一个放官网上给用户学习的例子,我一开始真的不知道这是在干嘛,就只是将三个形状图元组合在一起,然后可以同时旋转.放大缩小这个三个图形,点击"Animate"就能让中间 ...

  4. RAID 构建

    RAID的创建 第一步:先查看我们系统的磁盘情况 [root@station40 ~]# lsblk NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT sda    ...

  5. 在Owin Self-Hosing下实现每个请求中共享上下文(数据)

    问题 这几天在做公司的外部WebApi网关,由于使用了OAuth2.0,所以不得不使用Owin来部署网关. 而涉及到请求上下文的问题,为了使业务层能获取到请求头的信息,又不与网关耦合,决定把请求信息写 ...

  6. asp.net 第三方UI控件 Telerik KendoUI 之 TreeVIew 的用法记录

    一.前台显示 备注:一次性取出所有节点 function loadTreeData() { $.ajax({ type: 'POST', url: '@(Html.UrlHref("Scri ...

  7. ES6 for-of循环和迭代器使用细节

    SE5之前我们可以用for循环来遍历数组,SE5为数组引进了新的方法forEach(),方便了很多,但是该方法不能够通过break或者return返回外层函数. arr.forEach(functio ...

  8. ASP.NET没有魔法——ASP.NET Identity的加密与解密

    前面文章介绍了如何使用Identity在ASP.NET MVC中实现用户的注册.登录以及身份验证.这些功能都是与用户信息安全相关的功能,数据安全的重要性永远放在第一位.那么对于注册和登录功能来说要把密 ...

  9. linux学习(五)系统目录结构,ls命令,文件类型,alias

    一.系统目录结构 在我们的根目录下,有这样一些文件夹 /bin /sbin /usr/bin /usr/sbin /sbin一般都是root用户用的 /boot 系统启动相关的,grup就放在这里,这 ...

  10. THINKPHP中几个缓存的问题

    1.字段缓存. THINKPHP是默认开启字段缓存的.如果关闭了APPDEBUG(即在index.php中设置了这样一句话:define("APP_DEBUG","FAL ...