Windows Debugging之七

地址转换

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

内存管理器创建了一种叫做页表的数据结构(page tables), CPU通过这个数据结构来完成从虚拟地址向物理地址的转换. 每一个虚拟地址都跟一个系统空间结构叫做PTE(page table entry)的相关. PTE结构包含了虚拟地址相对应的物理地址. 比如说, 下图就显示出了如何将连续的虚拟页面映射到三个不连续的物理页面上的(x86)

2009-11-10 9-30-46

默认的情况下, x86体系的Windows系统使用双层表结构来完成从虚拟地址向物理地址的转换. 一个32位的虚拟地址被翻译成三个独立的组件

1. 页目录索引

2. 页表索引

3. 字节索引

这三个索引和在一起用来描述也映射. 见下图. 一个页的大小是4KB, 正好2的12次方是4KB(4096), 所以字节索引是12个二进制位的.

2009-11-10 9-38-42

页目录索引是用来定位虚拟地址的PTE中的页表的. 下图展示了三个索引是如何协同工作, 寻找物理地址的.

2009-11-10 9-41-25

转换虚拟地址的步骤如下:

1. 内存管理硬件定位当前进程的页目录. 每一次进程上下文的切换中, 硬件都被告知新进程页目录的地址, 典型的情况是由操作系统设置某个特定的寄存器来记录该地址的.

2. 页目录索引被使用, 从而在页目录中找到了页目录入口(Page Directory Entry)(PDE). PDE描述了用来映射虚拟地址的页表的位置. PDE包含一页框架号(page frame number)(PFN).

3. 页表索引被用来在也表中寻找PTE. PTE描述了请求的虚拟页的物理地址.

4. 通过PTE寻找到物理页, 如果该页合法, 那么包含这个虚拟页的物理页的PFN一定是被该页包含的. 如果PTE说明该页不合法, 那么内存管理的falt handler就会定位到这个也, 并想法子使它合法. 如果该页不能被弄成合法的状态(比如说因为保护错误), 那么fault handler就会产生出一个access violation, 或者一个bug check.

5. 当PTE指向了合法的页, 字节索引被用来从物理页中读取请求的数据.

 

页目录

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

        每个进程都只有一个page directory, 内存管理器创建这样的一个页来影射所有的页表位置. 进程页目录的物理地址存储在Kernel process block(KPROCESS)中, 但是它仅仅是映射从0xC0300000开始的虚拟地址(X86). 所有在内核态运行的代码都引用虚拟地址, 而不是物理地址.

       CPU知道页目录的位置, 因为CPU内部有一个特殊的寄存器CR3, 该寄存器由操作系统加载, 内部存放页目录的物理地址. 每次上下文转换到另一个进程的线程的时候, 这个寄存器就被从目标进程的KPROCESS块中加载起来, 执行这个动作的是内核的切换上下文的函数(context switch routine). 同一进程中的线程上下文切换并不会导致重新加载页目录的物理地址, 因为在相同进程中的所有线程共享相同的进程地址空间.

        页目录由page directory entries(PDEs)组成, 每一个PDE都是4个字节长(x86), PDE描述了所有那个进程的可能的页表的位置.

        因为Windows给每一个进程都提供了私有的地址空间, 所以每一个进程都有自己的页表集合来映射进程的私有地址空间. 不管怎样, 描述系统地址空间的页表在所有的进程中是共享的(会话空间sesson space在一个会话中的进程中间是共享的). 为了避免许多页表描述相同的虚拟内存, 当一个进程创建的时候, 描述系统空间的页目录入口被初始化为指向已经存在的系统页表. 如果进程是会话的一部分, 会话空间的页表也会通过让会话空间的页目录入口指向已经存在的会话页表来共享.

 

页表和页表入口

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

进程页目录入口PDE指向一个个的页表. 页表由一组PTE组成(page table entry). 虚拟地址中的页表索引域指出了页表中的哪个PTE映射了请求的内存页. 在x86系统中, 页表索引是10个二进制位, 允许你引用1024个4字节大小的PTE.  因为32bit的Windows支持4-GB的私有地址空间, 如果要映射整个地址空间, 一个页表是无论如何也不够的.

计算寻址4GB的虚拟地址空间需要多少页表:

1. 将4GB的地址以页表的单位来拆分

2. 每个数据页的大小是4KB, 每个页表上有1024个页. 所以每个页表能够映射4MB的数据页.

3. 所以1024个页表才能够照顾到所有的4GB的虚拟地址空间.

 

合法的PTE有两个主要的数据域: PFN(page frame number)和一些标志位. PFN是包含数据的物理页的页框架地址, 还可能是内存中页的物理地址.

 2009-11-10 14-07-48

 

页内的字节

===========

一旦内存管理器找到了请求的物理页, 那剩下的就是要找到那个页里的请求的数据了. 这时就是使用byte index(字节索引)的时候了. 字节索引告诉CPU, 页里的哪一个字节的数据是你想要的, 字节索引的长度是12个二进制位, 这个长度可以寻址4096个字节, 正好是一个页的大小. 所以,从PTE(page table entry)拿回物理页的地址后,再加上字节索引,就可以完成有虚拟地址向物理地址的转换了.

 

页错误处理

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

错误原因

结果

访问一个不在内存但却在磁盘中的页面, 或者是影射了的文件.

分配一个物理页, 然后将需要的页读入物理页, 放入working set中

访问一个在standby或者modified list中的页.

转换这个页, 使之进入到系统或者进程的working set中

访问一个没有被committed的页(比如说保留的地址空间或者是还没被分配的地址空间)

非法访问(Access violation)

从用户态访问一个只能由内核态访问的页

Access violation

向一个只读的页中写

Access violation

访问一个demand-zero 页

添加一个用0初始化过的页到进程的working set中.

向guard page中写

Guard-page violation (如果引用到了用户态下的栈, 那么将自动执行栈展开)

向copy-on-write页中写入

Make process-private (or session-private) copy of page, and replace original in process, session, or system working set

Referencing a page in system space that is valid but not in the process page directory (for example, if paged pool expanded after the process page directory was created)

Copy page directory entry from master system page directory structure, and dismiss exception

On a multiprocessor system, writing to a page that is valid but hasn't yet been written to

Set dirty bit in PTE

Executing code in a page that is marked as no execute

Access violation (supported only on hardware platforms that support no execute protection running Windows XP Service Pack 2 or Windows Server 2003 Service Pack 1 and later)

 

PTE合法位被清空, 标志着所请求的页面由于某些原因(当前)对于进程不可访问.

对于一个非法页的引用, 叫做page fault(页错误). 内核的陷阱处理器(trap handler)把这种错误分配给memory manager fault handler(MmAccessFault)去处理. 这个函数运行在发生错误的线程的上下文中, 负责解析错误或者引发恰当的异常. 很多种原因可以引发这种错误的.

你可能感兴趣的:(windows)