返回信息流......................................................................................................................
author : flat.noise
......................................................................................................................
一 前言
本文分析IA-32,以及基于IA-32的win2k的内存管理,内容都不是原创,只是我自己在学习过程中的一些理解。说得也很不仔细,只是希望高手看了不要见笑,其他只是希望能让大家有点兴趣,再去查看相关资料。
二 IA-32保护模式下的内存管理简介
2.1什么是IA-32
8086应该是大家比较熟悉的处理器,一般微机原理的课程都是8086来讲的,8086具有16位寄存器,16位数据总线和20位的地址总线可寻址1M的地址空间,8086采用分段的寻址方式,例如CS:IP,虽然20位的地址线可以寻址1M空间,但是由于8086只具有4个段寄存器,所以实际上在不切换段寄存器的情况下,他只能寻址256KB(一个寄存器16位,寻址64KB,4个寄存器256KB)。具体大家可以查阅微机原理。
Intel386处理器是IA-32家族的第一款处理器,之所以称为32大概是因为它具有32位地址线支持4GB的物理内存,386支持分段内存模式(A segmented-memory model)和平坦内存模式(flat memory model),并且支持一页为4KB的分页机制(paging),386之后IA-32家族继续发展,但是寻址方式没有产生太大变化,其中Pentium处理器加入了一页为4MB的分页机制。
2.2 IA-32保护模式下的内存管理
操作系统是构建在硬件之上的最底层软件,所以它与硬件息息相关,不了解硬件特别是CPU的知识,很难理解操作系统是如何实现某些技术,为什么能实现某些技术,为什么不能实现某些技术。IA-32的内存管理可以通过下图表示:
2.3 分段机制
上图看出逻辑地址首先通过分段机制转化变成线性地址,线性地址再通过分页机制变成物理地址,如果CPU没有开启分页机制(CR0寄存器的第31位被置为1)则线性地址直接为物理地址。
讨论分段机制必须弄清GDT与LDT(Global and Local Descriptor Tables),这是386以后才加入的东西,保护模式下所有的内存访问不是经过GDT就是经过LDT,GDT与LDT的表项都称作段描述符(segment descriptor),而此时段寄存器中存储的不再是此段在内存中的基地址(8086中,段寄存器存储基地址,物理地址=段寄存器《4 +一个通用寄存器),而是此段在全局或者局部描述表(GDT,LDT)中的描述符的索引。
而线形地址的基地址与长度以及其他很多信息都保存在段描述符(segment descriptor)中。
2.4 分页机制
要使用分页机制必须将CR0寄存器的第31位置为1,此时线性地址和物理地址不再是同一个概念(注意分页机制只是对于线性地址而言,此时逻辑地址已经被转换成线性地址)。
从图中看出开启分页机制以后,线性地址由三部分组成,高10位被称作目录,中间十位被称作表,低十位是偏移。以下对线性地址转变成物理地址的过程作一个描述:
1首先CPU从CR3寄存器中的高20位,作为页目录基址的物理地址(注意是物理地址),因为只有20位,所以很明显,页目录必然是4KB对齐的(2^12=4KB)。
2然后取出线性地址的高十位作为在页目录中的索引(2^10=1024),所以页目录中有1024项,每一项叫做页目录项(PDE,32位),每一个页目录项的高20位作为页表的基址的物理地址(在IA-32的手册中没有找到描述,但是通过分析windows和linux可以确定)。
3取出线性地址的中间十位作为在页表中的索引,找到页表项(PTE,32位),页表项的高20位作为一页的基址的物理地址。
4线性地址的低12位用作在此物理业中偏移。至此,一个32位的线性地址就被转换成了物理地址。当然PDE和PTE的低12位都作为标志位使用,具体的作用这里就不再介绍了。从此IA-32的虚拟内存机制从此实现了。
2.5
一页4M的分页机制的实现与上类似,只是不再需要页表项,具体不再介绍。IA-32处理器还支持4KB一页与4MB一页的混用(设置CR4寄存器PSE位),windows的实现中就用到了这种机制。
三 windows的内存管理
下面将讲述基于IA-32的windows的内存管理的设计,因为是基于IA-32的windows,所以设计必须完全遵照IA-32,在IA-32的基础上,windows有选择的并且巧妙的使用IA-32的各种机制实现了一个安全可靠的系统。
3.1
大家知道Windows下每一个进程认为自己独占4GB的内存空间并且不能访问其他进程的地址空间,这在IA-32下如何实现呢?线性地址到物理地址的转换都是从CR3寄存器开始的,其内容叫做页目录,windows为每一个进程维护一个自己的页目录,而不是整个系统维护同一个页目录。这样每一个进程都只能访问自己的物理内存空间而不必担起其他进程可以进入自己的内存空间。下图显示了win2k的几个进程的页目录的地址(高20位)。
从上图看出每一个进程的页目录都不同。
3.2 Windows的使用分页机制
这是一条镜像帖。来源:北邮人论坛 / security / #3318同步于 2006/9/12
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Security机器人发帖
[原创]IA32的内存管理以及WINDOWS的实现
zzm7000
2006/9/12镜像同步7 回复
订阅后,新回复会通过你的通知中心匿名送达。
7 条回复
3.2 Windows使用的分段分页机制
逻辑地址由段选子和偏移地址组成,段选子为16位放在段寄存器中,偏移地址为32位,下图所示的代码段的段选子放在CS中,偏移放在EIP中,所以逻辑地址为0008:80069B02,但是这与我们平时看到的地址并不相同,平时我们在windows写程序看到的地址都只有后32位,没有前面的16位,这是为什么呢?为了解释这个问题,dump出win2k下的GDT:
从上图看出CS段选子描述的段描述符的基地址为0,长度为4GB,其实完全寻址整个线性地址空间,所以我们平时在windows下看到的地址只需要用逻辑地址的偏移量表示,他等同于线性地址。
四 参考书籍
《IA-32 Intel Architecture Software Developer's Manual VIII》
《Undocumented win NT》《Understanding linux kernel 3rd》
《JIURL玩玩Win2k》
在winxp+sp2下面,有新的变化,
高11个bit做为pde的索引,中间9个bit做为pte的索引,最后12个bit还是偏移地址。
虽然还是4K一页,不过现在有2048张页表,第张页表有512个PTE,每个PTE占用8个字节,2个双字,我想这和物理内存扩展有关,我似乎在哪看到过类似的名词,一时想不起来了,这样的话,如果PC的物理内存大于4个G,64位的PTE就可寻址到更高的物理地址。
我是通过反汇编分析MmIsAddressValid得到的这样的结论,下面是我分析的过程,写得比较晦涩,也不打算仔细整理了,想搞清楚的话下面就当是一个参考吧:
------------------------------------------------------------------------------------------------------------------
80510c82 8bff mov edi,edi
80510c84 55 push ebp
80510c85 8bec mov ebp,esp
80510c87 51 push ecx
80510c88 51 push ecx
80510c89 8b4d08 mov ecx,dword ptr [ebp+8];va
80510c8c 56 push esi
80510c8d 8bc1 mov eax,ecx;eax,va
80510c8f c1e812 shr eax,12h;右移18位? >>21 <<3
80510c92 bef83f0000 mov esi,3FF8h
80510c97 23c6 and eax,esi;11 1111 1111 1 000
80510c99 2d0000a03f sub eax,3FA00000h;we got pde adress,0-3fa00000=c0600000
80510c9e 8b10 mov edx,dword ptr [eax];pde high
80510ca0 8b4004 mov eax,dword ptr [eax+4];pde low
80510ca3 8945fc mov dword ptr [ebp-4],eax;形参1 pde low
80510ca6 8bc2 mov eax,edx;pde high
80510ca8 57 push edi
80510ca9 83e001 and eax,1;pde valid?
80510cac 33ff xor edi,edi
80510cae 0bc7 or eax,edi;pde valid?
80510cb0 7461 je nt!MmIsAddressValid+0x91 (80510d13); not valid
;now pde is valid
80510cb2 bf80000000 mov edi,80h
80510cb7 23d7 and edx,edi;1000 0000,get bit7 of pde,LargePage
80510cb9 6a00 push 0
80510cbb 8955f8 mov dword ptr [ebp-8],edx;形参2 pde high
80510cbe 58 pop eax
80510cbf 7404 je nt!MmIsAddressValid+0x43 (80510cc5);not LargePage
80510cc1 85c0 test eax,eax
80510cc3 7452 je nt!MmIsAddressValid+0x95 (80510d17);go to LargePage
80510cc5 c1e909 shr ecx,9;ecx=va,右移9位,相当于>>12 <<3
80510cc8 81e1f8ff7f00 and ecx,7FFFF8h;0111 1111 1111 1111 1111 1 000,20位,第31到第12位,即前面20位
80510cce 8b81040000c0 mov eax,dword ptr [ecx-3FFFFFFCh]; ((VA>>12)<<3)&0x7FFFF8 +0xc0000004
80510cd4 81e900000040 sub ecx,40000000h;get pte, pte=(VA>>12)<<3+0xc0000000 (-0x40000000 means +0xc0000000)
80510cda 8b11 mov edx,dword ptr [ecx];pte
80510cdc 8945fc mov dword ptr [ebp-4],eax;形参1
80510cdf 53 push ebx
80510ce0 8bc2 mov eax,edx;pte
80510ce2 33db xor ebx,ebx
80510ce4 83e001 and eax,1;pte valid?
80510ce7 0bc3 or eax,ebx;pte valid?
80510ce9 5b pop ebx
80510cea 7427 je nt!MmIsAddressValid+0x91 (80510d13);not valid
;now pte is valid
80510cec 23d7 and edx,edi;edx=pte,edi=80h,is LargePage?
80510cee 6a00 push 0
80510cf0 8955f8 mov dword ptr [ebp-8],edx
80510cf3 58 pop eax
80510cf4 7421 je nt!MmIsAddressValid+0x95 (80510d17);not LargePage,valid
80510cf6 85c0 test eax,eax;LargePage
80510cf8 751d jne nt!MmIsAddressValid+0x95 (80510d17)
80510cfa 23ce and ecx,esi;ecx=pte,esi=3FF8h
80510cfc 8b89000060c0 mov ecx,dword ptr [ecx-3FA00000h];
80510d02 b881000000 mov eax,81h
80510d07 23c8 and ecx,eax
80510d09 33d2 xor edx,edx
80510d0b 3bc8 cmp ecx,eax;ecx= ,eax=81h if Large and valid,ok
80510d0d 7508 jne nt!MmIsAddressValid+0x95 (80510d17)
80510d0f 85d2 test edx,edx;not valid
80510d11 7504 jne nt!MmIsAddressValid+0x95 (80510d17)
80510d13 32c0 xor al,al
80510d15 eb02 jmp nt!MmIsAddressValid+0x97 (80510d19)
80510d17 b001 mov al,1
80510d19 5f pop edi
80510d1a 5e pop esi
80510d1b c9 leave
80510d1c c20400 ret 4
--------------------------------------------------------------------------------------------------------------
现在,PDE,PTE地址的计算公式是:
PDE=(VA>>22)<<3+0xc0600000
PTE=PT+((VA>>12)&0x3ff])*8
PT=(VA>>21)*4K+c0000000
以上VA表示virtual address
另外,通过这次仔细的反汇编跟踪,我觉得编译xp系统的编译器和编译2K相比肯定变了,
但我觉得一些代码编译出的代码指令很不好,像一个条件判断,2条指令就可以搞定了,
test一下,再jx就可以,但在xp下面需要做出条件判断跳转的这些地方,多了不少其实根本没有用的指令,并且这还是内核代码,所以,xp性能不如2K快在此又得到了证明。
我以前自己写过一个根据虚拟地址计算PDE,PTE,再转成实际物理地址的内核函数,其中没有API调用,只有汇编指令,
看来以后这种有点无聊的事还是少干比较好,哈哈。