JIURL玩玩Win2k内存篇 分页机制 (三)

来源:互联网 发布:linux命令学习手册 编辑:程序博客网 时间:2024/06/05 20:41
8种转换由于页表被映射到了0xc0000000 开始的4MB地址空间。所以我们也可以象CPU那样完成虚拟地址到物理地址的转换。系统按照对应虚拟空间的先后顺序,把一个进程的页表映射在0xc0000000 开始的4MB地址空间中,把页目录映射在0xc0300000 开始的4KB地址空间中。于是我们可以做如下几种地址的相互转换。1 虚拟地址->虚拟地址对应的PDE地址PDE_Address=(VirtualAddress>>22)*4+0xC0300000 2 虚拟地址->虚拟地址对应的PTE地址PTE_Address=(VirtualAddress>>12)*4+0xC0000000 3 虚拟地址->物理地址如果 虚拟地址大于等于0x80000000 并且小于0xa0000000(在 Large Page 部分),直接用虚拟地址减去0x80000000就得到了物理地址。其他情况取得该虚拟地址的PDE,判断是否有效。有效的话,取得该虚拟地址的PTE,判断是否有效。有效的话,将PTE的低12位清0加上虚拟地址的低12位就得到了物理地址。由于页表和页目录在系统地址空间中,访问需要程序运行在ring0,所以要测试的话,需要写驱动程序。unsigned int PDE;unsigned int PTE;if(VirtualAddress>=0x80000000 && VirtualAddress<0xa0000000){PhysicalAddress=VirtualAddress-0x80000000;}else{PDE=*(unsigned int*)((VirtualAddress>>22)*4+0xC0300000);if(PDE&0x00000001){PTE=*(unsigned int*)((VirtualAddress>>12)*4+0xC0000000);if(PTE&0x00000001){PhysicalAddress=((PTE&0xFFFFF000)+(VirtualAddress&0x00000FFF));}}}4 一个PDE的地址->相应的虚拟地址范围VirtualAddressStart=((PDE_Address-0xC0300000)/4)<<22VirtualAddressEnd=VirtualAddressStart+0x003FFFFF5 一个PTE的地址->相应的虚拟地址范围VirtualAddressStart=((PTE_Address-0xC0000000)/4)<<12VirtualAddressEnd=VirtualAddressStart+0x00000FFF6 物理地址->虚拟地址一个物理地址常常会对应多个虚拟地址。转换方法就是遍历所有页表和页目录,如果有效就比较该项的高20bit是否等于我们提供的物理地址的高20bit,如果相等,就找到了一个。比如该项是第i个PDE的,第j个PTE,那么虚拟地址等于,i*4M+j*4K+物理地址的低12bit。遍历所有页表和页目录找到每一个。7 一个PTE的地址->相应PDE的地址一个PTE的地址可以找到相应的虚拟地址范围,就可以找到虚拟地址对应的PDE地址PDE_Address=(VirtualAddress>>22)*4+0xC0300000 PDE_Address=((((PTE_Address-0xC0000000)/4)<<12)>>22)*4+0xC0300000 8 一个PDE的地址->相应PTE的地址范围一个PDE的地址可以找到相应的虚拟地址范围的开始地址,就可以找到该虚拟地址对应的PTE地址,一个PDE对应1024个PTE,4K大小。PTE_AddressStart=(VirtualAddress>>12)*4+0xC0000000PTE_AddressStart=((((PDE_Address-0xC0300000)/4)<<22)>>12)*4+0xC0000000 PTE_AddressEnd=PTE_AddressStart+0x00000FFF无效页与 Page Fault访问的虚拟地址所在页在物理内存中时,该虚拟地址所在页相应的 PDE,PTE 都有效,CPU 自动根据相应的PDE,PTE把虚拟地址转换成物理地址,完成访问。一个虚拟地址所在页不在物理内存中时,比如在硬盘上的交换文件中,该虚拟地址所在页相应的 PDE,PTE 都无效,访问该虚拟地址将引起 Page-Fault 异常(Exception)。从而使 CPU 转去执行异常处理程序,异常处理程序会做相应的处理。对于发现访问的虚拟地址所在的页在硬盘上的交换文件中,就从交换文件中读入该页到物理内存,重新使PTE有效,并指向正确的物理页。最后 CPU 重新执行引起异常的指令,这时该指令所访问的虚拟地址已经在物理内存中了,并且该虚拟地址的PTE也有效了,于是就可以顺利执行。下面我们针对 x86 CPU 做更详细的说明。当某条指令访问无效页时,比如指令 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 已经在堆栈中压入了下面的内容|-------------| |    EFLAGS   ||-------------||      CS     ||-------------||     EIP     ||-------------||  Error Code ||-------------|<---- [ ESP ]page-fault 异常 (#PF) 的 Error Code 定义如下( CPU 定义 )                                   |  3 | 2 | 1 | 0 |+---------------------------------------------------+ |          Reserved                |RSVD|U/S|R/W| P |+---------------------------------------------------+ P   0 错误由无效页引起    1 错误由违反页保护引起W/R 0 引起错误的内存访问是读    1 引起错误的内存访问是写U/S 0 访问错误时处理器处在管理模式    1 访问错误时处理器处在用户模式需要说明的是堆栈中压入的 EIP 就是引发异常的指令地址,将来将根据这个地址重新执行该指令。而寄存器 cr2 中是引发异常时访问的地址。#PF异常处理程序 KiTrap0E(由Win2k提供)将会调用 ntoskrnl!MmAccessFault ,MmAccessFault 通过 CR2 中的访问地址,计算出相应的 PDE,PTE地址,通过分析PTE中的内容,可以知道是哪种情况引起的异常,并根据情况作出相应的处理。当发生异常时,CPU会把一些寄存器压入堆栈,转到相应的异常处理程序(由Win2k提供),Win2k 在异常处理程序中又会把一些寄存器压入堆栈,最后会在堆栈中形成一个 KTRAP_FRAME 结构。这个 KTRAP_FRAME 是 ntoskrnl!MmAccessFault 的参数之一。!strct KTRAP_FRAMEstruct _KTRAP_FRAME (sizeof=140)+00 uint32 DbgEbp+04 uint32 DbgEip+08 uint32 DbgArgMark+0c uint32 DbgArgPointer+10 uint32 TempSegCs+14 uint32 TempEsp+18 uint32 Dr0+1c uint32 Dr1+20 uint32 Dr2+24 uint32 Dr3+28 uint32 Dr6+2c uint32 Dr7+30 uint32 SegGs+34 uint32 SegEs+38 uint32 SegDs+3c uint32 Edx+40 uint32 Ecx+44 uint32 Eax+48 uint32 PreviousPreviousMode+4c struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList+50 uint32 SegFs+54 uint32 Edi+58 uint32 Esi+5c uint32 Ebx+60 uint32 Ebp+64 uint32 ErrCode+68 uint32 Eip+6c uint32 SegCs+70 uint32 EFlags+74 uint32 HardwareEsp+78 uint32 HardwareSegSs+7c uint32 V86Es+80 uint32 V86Ds+84 uint32 V86Fs+88 uint32 V86Gs系统,CPU 与 页目录项,页表项的关系一个进程的PDE(页目录项),PTE(页表项)是由系统维护的。地址转换由CPU自动完成。访问无效地址CPU 将产生异常,执行异常处理程序。异常处理程序是系统提供的。对于有效的 PDE,PTE,他们的格式大部分由CPU定义,CPU 将按照这个格式,根据每一位的值,决定相应的处理方式。CPU 利用这个格式中自己定义的物理页物理地址的部分来进行地址转换,自己定义的一些标志位来实现页的保护。系统必须按照这个格式定义,来维护 PDE 和 PTE。x86 CPU 的有效页表项,CPU 定义如下struct _HARDWARE_PTE_X86 (sizeof=4)bits0-0 Validbits1-1 Writebits2-2 Ownerbits3-3 WriteThroughbits4-4 CacheDisablebits5-5 Accessedbits6-6 Dirtybits7-7 LargePagebits8-8 Globalbits9-11 reservedbits12-31 PageFrameNumber其中要注意的是 bits9-11 reserved 这3位,CPU 没有定义,留给操作系统使用。对于无效的 PDE,PTE,他们的格式大部分由系统定义。当 CPU 发现访问的 PDE,PTE 无效时,就会转去执行异常处理程序。异常处理程序是由系统提供的。系统将按照自己定义的格式,根据每一位的值,决定相应的处理方式。x86 CPU 的无效页表项,CPU 定义如下struct _HARDWARE_PTE_X86 (sizeof=4)bits0-0 Validbits1-31 reserved对于无效页来说,CPU只定义了bits0-0,用来判断是否有效。无效页的 bits0-0 值为0。其他位都留给操作系统使用。