⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 jiurl玩玩win2k内存篇 分页机制 (三).htm

📁 关于win2000核心编程的文章
💻 HTM
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- saved from url=(0070)http://jiurl.cosoft.org.cn/jiurl/document/JiurlPlayWin2k/MmPaging3.htm -->
<HTML><HEAD><TITLE>JIURL玩玩Win2k内存篇 分页机制 (三)</TITLE>
<META content="text/html; charset=gb2312" http-equiv=Content-Type>
<STYLE type=text/css>.title {
	FONT-FAMILY: "黑体", Arial, sans-serif; FONT-SIZE: 21px; FONT-WEIGHT: bold; LINE-HEIGHT: 48px; TEXT-DECORATION: none
}
.author {
	FONT-FAMILY: "宋体"; FONT-SIZE: 12px; LINE-HEIGHT: 16px
}
.content {
	FONT-SIZE: 14px; LINE-HEIGHT: 20px
}
</STYLE>

<META content="MSHTML 5.00.2614.3500" name=GENERATOR></HEAD>
<BODY bgColor=#f7f7f7 topMargin=5>
<DIV align=center>
<CENTER>
<TABLE border=0 cellPadding=0 cellSpacing=0 height=29 width="96%">
  <TBODY>
  <TR>
    <TD class=title height=41 width="100%">
      <P align=center><FONT face=宋体>JIURL玩玩Win2k内存篇 分页机制 
  (三)</FONT></P></TD></TR></CENTER>
  <TR>
    <TD class=author height=9 width="100%">
      <P align=center><FONT face=宋体>作者: <A 
      href="mailto:jiurl@mail.china.com">JIURL</A> </FONT></P></TD></TR>
  <TR>
    <TD class=author height=6 width="100%">
      <P align=center><FONT 
      face=宋体>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      主页: <A href="http://jiurl.yeah.net/">http://jiurl.yeah.net/</A> 
    </FONT></P></TD></TR>
  <TR>
    <TD class=author height=2 width="100%">
      <P align=center><FONT face=宋体>&nbsp;&nbsp;&nbsp; 日期: 2003-7-30</FONT> 
    </P></TD></TR></TBODY></TABLE></DIV>
<DIV align=center>
<CENTER>
<TABLE border=0 cellPadding=0 cellSpacing=0 height=1 width="96%">
  <TBODY>
  <TR>
    <TD height=1 width="100%">
      <HR color=#396da5 SIZE=3>
    </TD></TR></TBODY></TABLE></CENTER></DIV>
<DIV align=center>
<TABLE border=0 cellPadding=0 cellSpacing=0 class=content height=4300 
width="96%">
  <TBODY>
  <TR>
    <TD height=1066 vAlign=top width="131%"><FONT 
      face=宋体><B>8种转换</B><BR><BR>由于页表被映射到了0xc0000000 
      开始的4MB地址空间。<BR>所以我们也可以象CPU那样完成虚拟地址到物理地址的转换。<BR><BR>系统按照对应虚拟空间的先后顺序,把一个进程的页表映射在0xc0000000 
      开始的4MB地址空间中,把页目录映射在0xc0300000 开始的4KB地址空间中。于是我们可以做如下几种地址的相互转换。<BR><BR>1 
      虚拟地址-&gt;虚拟地址对应的PDE地址<BR><BR>PDE_Address=(VirtualAddress&gt;&gt;22)*4+0xC0300000&nbsp;<BR><BR>2 
      虚拟地址-&gt;虚拟地址对应的PTE地址<BR><BR>PTE_Address=(VirtualAddress&gt;&gt;12)*4+0xC0000000&nbsp;<BR><BR>3 
      虚拟地址-&gt;物理地址<BR><BR>如果 虚拟地址大于等于0x80000000 并且小于0xa0000000(在 Large Page 
      部分),<BR>直接用虚拟地址减去0x80000000就得到了物理地址。<BR>其他情况<BR>取得该虚拟地址的PDE,判断是否有效。<BR>有效的话,取得该虚拟地址的PTE,判断是否有效。<BR>有效的话,将PTE的低12位清0加上虚拟地址的低12位就得到了物理地址。<BR>由于页表和页目录在系统地址空间中,访问需要程序运行在ring0,所以要测试的话,需要写驱动程序。<BR><BR>unsigned 
      int PDE;<BR>unsigned int PTE;<BR><BR>if(VirtualAddress&gt;=0x80000000 
      &amp;&amp; 
      VirtualAddress&lt;0xa0000000)<BR>{<BR>PhysicalAddress=VirtualAddress-0x80000000;<BR>}<BR>else<BR>{<BR>PDE=*(unsigned 
      int*)((VirtualAddress&gt;&gt;22)*4+0xC0300000);<BR>if(PDE&amp;0x00000001)<BR>{<BR>PTE=*(unsigned 
      int*)((VirtualAddress&gt;&gt;12)*4+0xC0000000);<BR>if(PTE&amp;0x00000001)<BR>{<BR>PhysicalAddress=((PTE&amp;0xFFFFF000)+(VirtualAddress&amp;0x00000FFF));<BR>}<BR>}<BR>}<BR><BR>4 
      一个PDE的地址-&gt;相应的虚拟地址范围<BR><BR>VirtualAddressStart=((PDE_Address-0xC0300000)/4)&lt;&lt;22<BR>VirtualAddressEnd=VirtualAddressStart+0x003FFFFF<BR><BR>5 
      一个PTE的地址-&gt;相应的虚拟地址范围<BR><BR>VirtualAddressStart=((PTE_Address-0xC0000000)/4)&lt;&lt;12<BR>VirtualAddressEnd=VirtualAddressStart+0x00000FFF<BR><BR>6 
      物理地址-&gt;虚拟地址<BR><BR>一个物理地址常常会对应多个虚拟地址。转换方法就是遍历所有页表和页目录,如果有效就比较该项的高20bit是否等于我们提供的物理地址的高20bit,如果相等,就找到了一个。比如该项是第i个PDE的,第j个PTE,那么虚拟地址等于,i*4M+j*4K+物理地址的低12bit。遍历所有页表和页目录找到每一个。<BR><BR>7 
      一个PTE的地址-&gt;相应PDE的地址<BR><BR>一个PTE的地址可以找到相应的虚拟地址范围,就可以找到虚拟地址对应的PDE地址<BR>PDE_Address=(VirtualAddress&gt;&gt;22)*4+0xC0300000&nbsp;<BR>PDE_Address=((((PTE_Address-0xC0000000)/4)&lt;&lt;12)&gt;&gt;22)*4+0xC0300000&nbsp;<BR><BR>8 
      一个PDE的地址-&gt;相应PTE的地址范围<BR><BR>一个PDE的地址可以找到相应的虚拟地址范围的开始地址,就可以找到该虚拟地址对应的PTE地址,<BR>一个PDE对应1024个PTE,4K大小。<BR><BR>PTE_AddressStart=(VirtualAddress&gt;&gt;12)*4+0xC0000000<BR>PTE_AddressStart=((((PDE_Address-0xC0300000)/4)&lt;&lt;22)&gt;&gt;12)*4+0xC0000000&nbsp;<BR>PTE_AddressEnd=PTE_AddressStart+0x00000FFF<BR><BR><B>无效页与 
      Page Fault</B><BR><BR>访问的虚拟地址所在页在物理内存中时,该虚拟地址所在页相应的 PDE,PTE 都有效,CPU 
      自动根据相应的PDE,PTE把虚拟地址转换成物理地址,完成访问。一个虚拟地址所在页不在物理内存中时,比如在硬盘上的交换文件中,该虚拟地址所在页相应的 
      PDE,PTE 都无效,访问该虚拟地址将引起 Page-Fault 异常(Exception)。从而使 CPU 
      转去执行异常处理程序,异常处理程序会做相应的处理。对于发现访问的虚拟地址所在的页在硬盘上的交换文件中,就从交换文件中读入该页到物理内存,重新使PTE有效,并指向正确的物理页。最后 
      CPU 
      重新执行引起异常的指令,这时该指令所访问的虚拟地址已经在物理内存中了,并且该虚拟地址的PTE也有效了,于是就可以顺利执行。<BR><BR>下面我们针对 
      x86 CPU 做更详细的说明。<BR><BR>当某条指令访问无效页时,比如指令 MOV EAX,InValidAddress 
      ,执行这条指令时,CPU 会自动通过页目录和页表把虚拟地址 InValidAddress 转换成物理地址,在地址转换过程中,CPU 
      在从页表项得到物理页地址的同时,会进行页保护检查,比如看该页表项是否有效,是否是只读等等。当CPU发现指令中地址的页表项无效,就会引发异常(Exception)。异常也是由 
      CPU 实现的。这里引起的是一个 Page Fault 异常,它的中断号是 0xe (十进制14),需要注意的是 Page Fault 的中断号是 
      0xe 这是由 CPU 定义的( x86 CPU 的 从 0 - 31 这32个中断是由 CPU 定义的,CPU 
      将根据这个定义做相应工作)。在发生异常时,CPU 
      自动把一些寄存器压入堆栈,然后根据中断号,(通过IDTR寄存器找到中断描述符表)在中断描述符表中找到相应的中断描述符,根据中断描述符中的地址,转到异常处理程序。中断描述符是由Win2k设置,异常处理程序也是由Win2k决定。对于 
      Win2k Build 2195 来说,中断 0xe 的处理程序是 ntoskrnl!KiTrap0E 地址在 804648a4 
      。当转到KiTrap0E 时,CPU 
      已经在堆栈中压入了下面的内容<BR><BR>|-------------|&nbsp;<BR>|&nbsp;&nbsp;&nbsp; 
      EFLAGS&nbsp;&nbsp; |<BR>|-------------|<BR>|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      CS&nbsp;&nbsp;&nbsp; 
      &nbsp;|<BR>|-------------|<BR>|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EIP&nbsp;&nbsp;&nbsp;&nbsp; 
      |<BR>|-------------|<BR>|&nbsp; Error Code |<BR>|-------------|&lt;---- [ 
      ESP ]<BR><BR><BR>page-fault 异常 (#PF) 的 Error Code 定义如下( CPU 定义 
      )<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp; 
      3 | 2 | 1 | 0 
      |<BR>+---------------------------------------------------+&nbsp;<BR>|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      Reserved&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|RSVD|U/S|R/W| 
      P 
      |<BR>+---------------------------------------------------+&nbsp;<BR><BR>P&nbsp;&nbsp; 
      0 错误由无效页引起<BR>&nbsp;&nbsp;&nbsp; 1 错误由违反页保护引起<BR>W/R 0 
      引起错误的内存访问是读<BR>&nbsp;&nbsp;&nbsp; 1 引起错误的内存访问是写<BR>U/S 0 
      访问错误时处理器处在管理模式<BR>&nbsp;&nbsp;&nbsp; 1 访问错误时处理器处在用户模式<BR><BR>需要说明的是堆栈中压入的 
      EIP 就是引发异常的指令地址,将来将根据这个地址重新执行该指令。而寄存器 cr2 中是引发异常时访问的地址。<BR><BR>#PF异常处理程序 
      KiTrap0E(由Win2k提供)将会调用 ntoskrnl!MmAccessFault ,MmAccessFault 通过 CR2 
      中的访问地址,计算出相应的 
      PDE,PTE地址,通过分析PTE中的内容,可以知道是哪种情况引起的异常,并根据情况作出相应的处理。<BR><BR>当发生异常时,CPU会把一些寄存器压入堆栈,转到相应的异常处理程序(由Win2k提供),Win2k 
      在异常处理程序中又会把一些寄存器压入堆栈,最后会在堆栈中形成一个 KTRAP_FRAME 结构。这个 KTRAP_FRAME 是 
      ntoskrnl!MmAccessFault 的参数之一。<BR><BR>!strct KTRAP_FRAME<BR>struct 
      _KTRAP_FRAME (sizeof=140)<BR>+00 uint32 DbgEbp<BR>+04 uint32 DbgEip<BR>+08 
      uint32 DbgArgMark<BR>+0c uint32 DbgArgPointer<BR>+10 uint32 
      TempSegCs<BR>+14 uint32 TempEsp<BR>+18 uint32 Dr0<BR>+1c uint32 Dr1<BR>+20 
      uint32 Dr2<BR>+24 uint32 Dr3<BR>+28 uint32 Dr6<BR>+2c uint32 Dr7<BR>+30 
      uint32 SegGs<BR>+34 uint32 SegEs<BR>+38 uint32 SegDs<BR>+3c uint32 
      Edx<BR>+40 uint32 Ecx<BR>+44 uint32 Eax<BR>+48 uint32 
      PreviousPreviousMode<BR>+4c struct _EXCEPTION_REGISTRATION_RECORD 
      *ExceptionList<BR>+50 uint32 SegFs<BR>+54 uint32 Edi<BR>+58 uint32 
      Esi<BR>+5c uint32 Ebx<BR>+60 uint32 Ebp<BR>+64 uint32 ErrCode<BR>+68 
      uint32 Eip<BR>+6c uint32 SegCs<BR>+70 uint32 EFlags<BR>+74 uint32 
      HardwareEsp<BR>+78 uint32 HardwareSegSs<BR>+7c uint32 V86Es<BR>+80 uint32 
      V86Ds<BR>+84 uint32 V86Fs<BR>+88 uint32 V86Gs<BR><BR><B>系统,CPU 与 
      页目录项,页表项的关系</B><BR><BR>一个进程的PDE(页目录项),PTE(页表项)是由系统维护的。地址转换由CPU自动完成。访问无效地址CPU 
      将产生异常,执行异常处理程序。异常处理程序是系统提供的。<BR><BR>对于有效的 PDE,PTE,他们的格式大部分由CPU定义,CPU 
      将按照这个格式,根据每一位的值,决定相应的处理方式。CPU 
      利用这个格式中自己定义的物理页物理地址的部分来进行地址转换,自己定义的一些标志位来实现页的保护。系统必须按照这个格式定义,来维护 PDE 和 
      PTE。<BR><BR>x86 CPU 的有效页表项,CPU 定义如下<BR><BR>struct _HARDWARE_PTE_X86 
      (sizeof=4)<BR>bits0-0 Valid<BR>bits1-1 Write<BR>bits2-2 Owner<BR>bits3-3 
      WriteThrough<BR>bits4-4 CacheDisable<BR>bits5-5 Accessed<BR>bits6-6 
      Dirty<BR>bits7-7 LargePage<BR>bits8-8 Global<BR>bits9-11 
      reserved<BR>bits12-31 PageFrameNumber<BR><BR>其中要注意的是 bits9-11 reserved 
      这3位,CPU 没有定义,留给操作系统使用。<BR><BR>对于无效的 PDE,PTE,他们的格式大部分由系统定义。当 CPU 发现访问的 
      PDE,PTE 
      无效时,就会转去执行异常处理程序。异常处理程序是由系统提供的。系统将按照自己定义的格式,根据每一位的值,决定相应的处理方式。<BR><BR>x86 
      CPU 的无效页表项,CPU 定义如下<BR><BR>struct _HARDWARE_PTE_X86 (sizeof=4)<BR>bits0-0 
      Valid<BR>bits1-31 reserved<BR><BR>对于无效页来说,CPU只定义了bits0-0,用来判断是否有效。无效页的 
      bits0-0 值为0。其他位都留给操作系统使用。</FONT> 
      <P>欢迎交流,欢迎交朋友,<BR>欢迎访问 <A 
      href="http://jiurl.yeah.net/">http://jiurl.yeah.net/</A> <A 
      href="http://jiurl.cosoft.org.cn/forum">http://jiurl.cosoft.org.cn/forum</A></P>
      <P> </P>
      <P> </P></TD></TR></TBODY></TABLE></DIV></BODY></HTML>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -