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

📄 jiurl玩玩win2k内存篇 内存共享(一) protopte.htm

📁 关于win2000核心编程的文章
💻 HTM
📖 第 1 页 / 共 3 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- saved from url=(0071)http://jiurl.cosoft.org.cn/jiurl/document/JiurlPlayWin2k/MmProtoPTE.htm -->
<HTML><HEAD><TITLE>JIURL玩玩Win2k内存篇 内存共享(一) ProtoPTE</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内存篇 内存共享(一) 
    ProtoPTE</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=10000 
width="96%">
  <TBODY>
  <TR>
    <TD height=1041 vAlign=top width="131%">
      <P><FONT face=宋体><B>内存共享</B> </FONT>
      <P><FONT face=宋体>&nbsp;&nbsp;&nbsp; Windows 2000 中进程之间共享内存的几个主要应用是, 一个 
      dll(动态链接库)可能被多个进程使用,应该被共享。一个程序也应该可以被多个运行的实例共享。通过文件映射(Memory Mapped 
      File)实现的进程之间通过内存通信,传输数据。</FONT> 
      <P><FONT face=宋体>&nbsp;&nbsp;&nbsp; 
      每个进程有自己的4G地址空间,地址空间通过进程自己的页目录和页表,以页为单位映射物理内存。把两个不同进程的一页地址空间映射到同一个物理页上,两个进程的一页地址空间就实现了共享,两个进程对该页地址空间的读写,都是对同一个物理页的读写。两个进程自己的某个 
      PTE(页表项,对应一页地址空间)中的物理页帧号相同,就使两个进程的某页映射到了同一个物理页。比如进程1的0x10000-0x10FFF这一页映射物理内存的第100页,进程2的0x20000-0x20FFF这一页也映射物理内存的第100页,那么进程1的0x10000-0x10FFF 
      的就是 进程2的0x20000-0x20FFF ,就是物理内存的第100页。 </FONT>
      <P><FONT face=宋体>&nbsp;&nbsp;&nbsp; 我们下面观察一下Win2k中dll的共享。ntdll.dll 
      通常会被所有的进程使用。 我们打开一个记事本程序,一个计算器程序,使用 SoftICE 来看看这两个进程负责映射 ntdll.dll 的 
      页表项(PTE)是否相同。相同的话,就说明共享了物理内存,不相同就说明没有共享物理内存。<BR><BR>运行计算器和记事本,使用SoftICE观察。<BR><BR>首先来看计算器<BR><BR>// 
      使用 addr 命令转换到 calc进程 也就是计算器进程的地址空间<BR>:addr calc<BR>// 使用 map32 -u 
      命令,看看用户地址空间中映射的模组<BR>:map32 -u<BR>Owner Obj Name Obj# Address Size 
      Type<BR>calc .text 0001 001B:01001000 000124EE CODE RO<BR>calc .data 0002 
      0023:01014000 000010C0 IDATA RW<BR>calc .rsrc 0003 0023:01016000 00002B98 
      IDATA RO<BR>NVDESK32 .text 0001 001B:10001000 0000DB8F CODE 
      RO<BR>...<BR>gdi32 .reloc 0004 0023:77F7A000 0000151C IDATA RO<BR>ntdll 
      .text 0001 001B:77F81000 00042492 CODE RO<BR>ntdll ECODE 0002 
      001B:77FC4000 00004371 CODE RO<BR>ntdll PAGE 0003 001B:77FC9000 00003983 
      CODE RO<BR>ntdll .data 0004 0023:77FCD000 00002350 IDATA RW<BR>ntdll .rsrc 
      0005 0023:77FD0000 00026D08 IDATA RO<BR>ntdll .reloc 0006 0023:77FF7000 
      00001DA8 IDATA RO<BR>...<BR>MSVCRT .reloc 0005 0023:78043000 00002600 
      IDATA RO<BR>// 看到了 ntdll 的各种节 映射到地址空间中的地址<BR>// 代码放在 .text 节中, .text 
      节的开始地址为 77F81000&nbsp;<BR>// 我们看 .text 节中的 77F82000-77F82FFF 这一页<BR>// 
      这一页对应的 PTE 的虚拟地址为 (77F82000&gt;&gt;12)*4+0xC0000000= 0xc01dfe08<BR><BR>// 
      显示这部分页表项<BR>:dd c01dfe04 l 100<BR>0010:C01DFE04 00000000 04E63005 04E4B005 
      04E4C005 .....0..........<BR>0010:C01DFE14 04E4D025 04E2E005 04E2F005 
      04E30005 %...............<BR>0010:C01DFE24 04E11005 04E12005 04E1B025 
      04E1C025 ..... ..%...%...<BR>0010:C01DFE34 04E1D005 04DFE005 04DFF025 
      04E80025 ........%...%...<BR>0010:C01DFE44 04E61005 04E13005 04E14025 
      04E15005 .....0..%@...P..<BR>0010:C01DFE54 04E16005 04DF7025 04DF8025 
      00000000 .`..%p..%.......<BR>0010:C01DFE64 04E1A005 00000000 00000000 
      00000000 ................<BR>0010:C01DFE74 00000000 00000000 04E43025 
      04E44005 ........%0...@..<BR>0010:C01DFE84 00000000 04E46025 00000000 
      00000000 ....%`..........<BR>...<BR>// 0xc01dfe08 处的值为 04E63005 
      ,其中高20bit是物理页的页号,低12bit是标志<BR>// 所以 ntdll .text 节中的 77F82000-77F82FFF 这一页 
      映射到了物理页 04E63&nbsp;<BR>//(物理地址 04E63000-04E63FFF )<BR><BR>再来看记事本<BR><BR>// 
      转换到 notepad 的地址空间<BR>:addr notepad<BR>:map32 -u<BR>Owner Obj Name Obj# 
      Address Size Type<BR>NOTEPAD .text 0001 001B:01001000 000065CA CODE 
      RO<BR>NOTEPAD .data 0002 0023:01008000 00001944 IDATA RW<BR>NOTEPAD .rsrc 
      0003 0023:0100A000 00005238 IDATA RO<BR>NVDESK32 .text 0001 001B:10001000 
      0000DB8F CODE RO<BR>...<BR>gdi32 .reloc 0004 0023:77F7A000 0000151C IDATA 
      RO<BR>ntdll .text 0001 001B:77F81000 00042492 CODE RO<BR>ntdll ECODE 0002 
      001B:77FC4000 00004371 CODE RO<BR>ntdll PAGE 0003 001B:77FC9000 00003983 
      CODE RO<BR>ntdll .data 0004 0023:77FCD000 00002350 IDATA RW<BR>ntdll .rsrc 
      0005 0023:77FD0000 00026D08 IDATA RO<BR>ntdll .reloc 0006 0023:77FF7000 
      00001DA8 IDATA RO<BR>...<BR>MSVCRT .reloc 0005 0023:78043000 00002600 
      IDATA RO<BR>// 看到了 ntdll 的各种节 .text 节的开始地址为 77F81000&nbsp;<BR>// 同样看 .text 
      节中的 77F82000-77F82FFF 这一页<BR>// 这一页对应的 PTE 的虚拟地址为 
      (77F82000&gt;&gt;12)*4+0xC0000000= 0xc01dfe08<BR><BR>// 显示这部分页表项<BR>:dd 
      c01dfe04 l 100<BR>0010:C01DFE04 00000000 04E63005 04E4B025 04E4C005 
      .....0..%.......<BR>0010:C01DFE14 04E4D005 04E2E005 04E2F005 04E30005 
      ................<BR>0010:C01DFE24 04E11005 04E12025 04E1B005 04E1C005 
      ....% ..........<BR>0010:C01DFE34 04E1D005 04DFE005 04DFF025 04E80025 
      ........%...%...<BR>0010:C01DFE44 04E61025 04E13005 04E14005 04E15025 
      %....0...@..%P..<BR>0010:C01DFE54 04E16005 04DF7005 04DF8025 00000000 
      .`...p..%.......<BR>0010:C01DFE64 04E1A025 00F254C4 00000000 00000000 
      %....T..........<BR>0010:C01DFE74 00000000 00000000 04E43005 04E44005 
      .........0...@..<BR>0010:C01DFE84 00F254D2 00F254D4 00000000 00000000 
      .T...T..........<BR>...<BR>// 0xc01dfe08 处的值为 04E63005 
      ,其中高20bit是物理页的页号,低12bit是标志<BR>// 所以 ntdll .text 节中的 77F82000-77F82FFF 这一页 
      映射到了物理页 04E63&nbsp;<BR>//(物理地址 04E63000-04E63FFF )<BR><BR>可以看到,两个进程的地址空间 
      77F82000-77F82FFF 映射了相同的物理页。<BR><BR><B>Prototype PTE(原型PTE)</B></FONT> 
      <P><FONT face=宋体>&nbsp;&nbsp;&nbsp; 和共享的实现有着密切关系的一个数据结构是 页帧号数据库项 ( PFN 
      DataBase Entry ) ,对处于Active(Valid) 状态的物理页,该物理页的 PFN DataBase Entry 的 +08 
      处,4个字节,是 share 
      count,共享计数。<BR><BR>下面我们分析一下进程间共享物理页的几种情况。<BR><BR>进程1第一次将一些可以被共享的内容读入一个物理页。于是进程1相应的PTE有效,并指向这个物理页。被共享的物理页对应的 
      PFN DataBase Entry 状态为 Active(Valid) ,share count 
      值为1。<BR><BR>进程2需要共享这个物理页,于是进程2相应的PTE有效,并指向这个物理页。由于这个物理页现在被进程1和进程2共享,所以对应的 
      PFN DataBase Entry 的 share count 
      的值加1,变为2。<BR><BR>进程3需要共享这个物理页,于是进程3相应的PTE有效,并指向这个物理页。这个物理页对应的 PFN DataBase 
      Entry 的 share count 的值加1,变为3。<BR><BR>进程2修整 Working Set (工作集),决定把该页从自己的 
      Working Set 中移出,于是相应的PTE无效。这个物理页对应的 PFN DataBase Entry 的 share count 
      的值减1,变为2。<BR><BR>进程1结束,这个物理页对应的 PFN DataBase Entry 的 share count 
      的值又减1,变为1。<BR><BR>进程2又需要访问这个共享页。于是PTE重新有效,并指向这个物理页。物理页的 share count 
      加1,又变为2。<BR><BR>进程2,进程3,修整 Working Set (工作集),都把这页从 Working Set 
      中移出,于是两个进程相应的PTE都无效。物理页的 share count 从 2 变成了 0。当变成0时,该物理页的状态从 
      Active(Valid) 变成了 Standby,链入了 Standby 
      链,不过该物理页中的内容不会被改变。<BR><BR>进程2又需要访问这个共享页。由于该物理页在 Standby 
      链上,内容没有被改变。于是直接取回该物理页,PTE重新有效并指向这个物理页。物理页状态从 Standby 变为 Active(Valid) , 
      share count 变为1。<BR><BR>进程2又修整 Working Set (工作集),决定把该页从自己的 Working Set 
      中移出,于是相应的PTE无效。这个物理页的 share count 的值减1,变为0。当变成0时,该物理页的状态从 Active(Valid) 
      变成了 Standby,链入了 Standby 链,不过该物理页中的内容不会被改变。<BR><BR>系统需要内存,从 Standby 
      链上取走了该物理页。<BR><BR>进程2又需要访问这个共享页。没有物理页有原来的数据了,于是分配一个新的物理页,从文件中将数据读入。于是进程2相应的PTE有效,并指向这个新的物理页。这个物理页对应的 
      PFN DataBase Entry 状态为Active(Valid) ,share count 
      值为1。<BR><BR>&nbsp;&nbsp;&nbsp; 需要强调的一点是,只有被共享的物理页的 share count 减为 0 
      时,才能被移入 Standby 链,然后被用来做其他事。如果 share count 
      不为0,就说明还有进程在使用这个物理页,如果这时把这个物理页移入 Standby 
      链,可能会有非常严重的后果。比如放着被共享的dll的程序代码的一个物理页,被多个进程共享,而其中一个结束,share count 
      减1但不为0,这时如果把这个物理页移入 Standby 链,系统又把这个物理页给清零,放入 Zeroed 
      链中待用。而这时其他几个进程的相应的PTE仍然是有效的,并且指向这个本来该是程序代码的物理页。当这几个进程执行这里的代码的时候,将会彻底出错。如果 
      share count 为0 ,所有进程相应的 PTE 都已经无效了。</FONT> 
      <P><FONT face=宋体>&nbsp;&nbsp;&nbsp; 共享机制还有一个问题。一个被共享的物理页 share count 为0 
      ,并且已经被用来做其他事,所有进程相应的 PTE 都已经无效了。当一个进程访问相应 PTE 
      对应的地址空间,系统会分配一个新的物理页,从文件中将数据读入新的物理页,相应的PTE有效,并指向这个新的物理页,这样这个进程就可以访问这个共享页。但是其他的进程无法知道这个新的物理页,他们相应的PTE仍然无效。他们如果也重新访问这个共享页,他们将如何得知新的物理页来填入PTE?Win2k 
      使用 Prototype PTE 来解决这个问题。</FONT> 
      <P><FONT face=宋体>&nbsp;&nbsp;&nbsp; 
      对于一个载入内存的应用程序,载入内存的动态链接库,或者一个文件映射,他们都有可能被共享。比如一个可能被共享的东西,映射到地址空间,需要100页,那么每页都有一个相应的 
      Prototype PTE 
      (4个字节)。一个进程把这个100页的共享的东西映射到地址空间,需要用100个PTE。这100个PTE中,有效的PTE指向共享的物理页,无效的PTE将指向相应的 
      Prototype PTE ,Prototype PTE 
      指向物理页。这样一来,当遇到前面的问题,一个进程重新访问一个共享页,而这个共享页原来映射的物理页已经做其他事,系统分配新的物理页,把进程相应的PTE指向新的物理页,并把PTE变为有效之后,也把 
      Prototype PTE 指向新的物理页。其他的进程重新访问这个共享页时,他们的PTE是无效的,并且指向 Prototype PTE ,而 
      Prototype PTE 

⌨️ 快捷键说明

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