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

原创 2007年09月17日 14:17:00

为什么地址空间的大小是4G

     使用32bit的cpu话,处理32bit的数据比较好。这就决定了在代码中将被非常频繁使用的地址是32bit的。而32bit的地址可以寻址的范围是从0x00000000 到 0xFFFFFFFF ,也就是4GB(对于以字节为单位的编址)大小的范围。也就决定了地址空间的大小是 4G。


为什么一页的大小是4K(只是我个人的看法)

     确定了虚拟地址空间的大小之后,又决定使用分页来进行内存管理。权衡把虚拟地址转换成物理地址的效率和保存分页信息所用的代价之后,决定了使用页目录和页表2层转换。而使用2层转换最公平的方式就是页目录中的项数和页表中的项数都一样。由于所要保存足够的信息,又由于是32bit cpu 所以,一个PTE或者一个PDE的大小是4个字节也是有理由的。当这些确定了之后,也就确定了一页的大小。

     设一页大小是 x ,单位是字节。那么这样一页中有 (x/4) 个PTE或者PDE。一个PTE可以对应一页的地址空间,也就是大小为x个字节的地址空间,一个 Page Table 页中有(x/4) 个PTE,所以一个 Page Table 页可以对应(x/4)*x个字节的地址空间。一个PDE可以找到一个 Page Table 页,也就是大小为(x/4)*x个字节的地址空间,一个 Page Directory 页中有 (x/4) 个PDE,所以一个 Page Directory 页可以对应 (x/4)(x/4)*x 个字节的地址空间。现在需要用一个PDE页对应整个地址空间,整个地址空间是4G字节,也就是4*1024*1024*1024 个字节。所以 (x/4)(x/4)*x=4*1024*1024*1024 可以算出 x = 4*1024 字节,也就是 4k 字节。

地址转换的性能

     一旦使用了分页(CPU的相关寄存器的分页标志位被设置),所有的内存操作都使用的是虚拟地址,CPU会自动转换成物理地址,可以想象将会有非常大量的地址转换,一次转换可能需要读页目录表,读页表,最后才在页中读写数据。所以有理由把一些页目录项和页表项放入高速缓存中来提高地址转换的速度。实际中也是这样做的,对于 x86 cpu ,如果启用了分页,cpu 会使用一个叫做 TLB(Translation Look-Aside Buffer) 的高速缓存来存储经常被用到的页目录和页表项。

页目录的地址为什么是C0300000,1个页目录加上1024个页表为什么只使用了1024*4K的地址空间

     对于要映射整个4G地址空间,是需要1024个页表和1个页目录的,每个都是4KB大小,也就是 1024*4KB+1*4KB=4MB+4KB。而实际中Win2k把每个进程的页目录和页表映射到了从 0xC0000000到0xC03FFFFF 这4MB的地址空间中(页目录在0xC0300000开始的4K)。注意是4MB地址空间而不是4MB+4KB。1024个页表和1个页目录,应该是需要(1024+1)*4KB的地址空间的。而现在Win2k只使用了1024*4KB的地址空间这是为什么?

     原因就是页表被映射到了进程的地址空间。
     如果页表和页目录没有被映射到进程的地址空间中,而一个进程的4GB地址空间又都映射了物理内存的话,那么就确实需要1024个物理页来存放页表,和另外1个物理页来存放页目录,也就是需要(1024+1)*4KB的物理内存。
但是页表被映射到了进程的地址空间中,这导致了一个页表的内容和页目录的内容是完全一样的,正是这种完全相同,使得将1024个页表加1个页目录映射到地址空间只需要1024*4KB的地址空间,其中的一个页表和页目录完全重合了。
     一个页表1024项,每项对应4KB地址空间,一个页表对应4MB的地址空间。1024个对应了整个4GB地址空间。1024个页表也被映射到了从 0xC0000000到0xC03FFFFF 的4MB地址空间中。这4MB地址空间也是由一个页表来对应的。我们来看对应于从 0xC0000000到0xC03FFFFF 这4MB的地址空间的页表。该页表有1024项,每项对应一页的地址空间,表明是否在物理内存中,如果在,物理地址是多少。而这个页表是对应页表所在的4MB地址空间的,所以它的每一项对应的一页,正是每个页表所在的页。也就是说这个页表的每一项指出了一个页表是否有物理内存映射,如果有的话,物理地址是多少。这正是页目录所做的工作。把1024个页表映射到了地址空间,导致了1024个页表中的一个的内容和页目录完全重合,它既是页目录又是页表。所以1个页目录加上1024个页表只使用了1024*4K的地址空间。

     页表被映射到地址空间的什么地方,是由操作系统的设计者决定的,他会综合考虑各种问题,作出最后的决定。不过一旦页表所在的地址空间的地址决定了,那么页目录的地址也就决定了,除非他打算多使用一页的地址空间保存和现在一个页表中完全相同的内容。

     Win2k中把页表映射到了从 0xC0000000到0xC03FFFFF 的4MB地址空间中,我们来计算一下负责这4M地址空间的那个页表的地址,那个页表就是和页目录重合的页表。4MB地址空间的首地址0xC0000000显然是由该页表的第一项负责的,我们用这个地址来计算。PTE_Address=(VirtualAddress>>12)*4+0xC0000000,
(0xC0000000>>12)*4+0xC0000000=0xC0000*4+0xC0000000=0x300000+0xC0000000=0xC0300000
正是页目录的虚拟地址。

     0xC0300000到0xC0300FFF这4KB的地址空间,作为普通一页的话由一个PTE指明所在物理页的物理地址,作为一个页表所在页的话由一个PDE指明所在物理页的物理地址,作为页目录的话由CR3指明所在物理页的物理地址。
     0xC0300000到0xC0300FFF这4KB作为普通的一页是由哪个PTE对应呢,PTE_Address=(VirtualAddress>>12)*4+0xC0000000,计算得到0xC0300C00。也就是说0xC0300C00处的4个字节作为PTE,指明了0xC0300000到0xC0300FFF这4KB所在的物理页的物理地址。
     0xC0300000到0xC0300FFF这4KB作为一个页表所在页是由哪个PDE对应呢,PDE_Address=(VirtualAddress>>22)*4+0xC0300000,计算得到0xC0300C00。也就是说0xC0300C00处的4个字节作为PDE,指明了0xC0300000到0xC0300FFF这4KB所在的物理页的物理地址。
     0xC0300000到0xC0300FFF这4KB作为页目录的话,由 CR3 指明所在物理页的物理地址。所以 0xC0300C00 处的4个字节的值,把标志位清0的话,就等于 CR3 中的值。

下面这个特殊地址的转换过程,你就会发现页目录和一个页表确实是重合了,而且它既是页目录又是页表。
虚拟地址 C0300C00 转换成物理地址,以下输出来自 kd。

kd> r cr3
r cr3
cr3=069ca000
// CPU第一步根据cr3里的物理地址找到页目录所在物理页
// 取C0300000的最高10bit, 1100 0000 00 = 11 0000 0000 = 300 ,作为页目录项索引,由于每项长4字节
// 所以页目录项的物理地址为 300*4+069ca000=069cac00
kd> !dd 069cac00
!dd 069cac00
# 69cac00 069ca063 01e2b063 00000000 01670163
# 69cac10 01671163 01672163 01673163 01674163
# 69cac20 01675163 01676163 01657163 01658163
# 69cac30 01659163 0165a163 0165b163 0165c163
# 69cac40 0165d163 0165e163 0165f163 016c0163
# 69cac50 01681163 01682163 01683163 01684163
# 69cac60 01685163 01686163 01687163 01688163
# 69cac70 01689163 0168a163 0168b163 0168c163
// 结果该页目录项中的物理地址(页目录项的高20bit是物理页帧号,低12bit是标志)是069ca000
// 注意页表的物理地址等于页目录的物理地址,说明了页目录和页表是重合的。

// CPU第二步根据该目录项中的物理地址找到页表所在的物理页
// 取C0300000的最高10bit之后的10bit, 11 0000 0000 = 300 ,作为页表项索引,由于每项长4字节
// 所以页表项的物理地址为 300*4+069ca000=069cac00
kd> !dd 069cac00
!dd 069cac00
# 69cac00 069ca063 01e2b063 00000000 01670163
# 69cac10 01671163 01672163 01673163 01674163
# 69cac20 01675163 01676163 01657163 01658163
# 69cac30 01659163 0165a163 0165b163 0165c163
# 69cac40 0165d163 0165e163 0165f163 016c0163
# 69cac50 01681163 01682163 01683163 01684163
# 69cac60 01685163 01686163 01687163 01688163
# 69cac70 01689163 0168a163 0168b163 0168c163
// 结果该页表项中的物理地址(页目录项的高20bit是物理页帧号,低12bit是标志)是069ca000

// CPU第三步根据页表项中的物理地址找到物理页,用C0300000低12bit作为页内偏移,
// 相加得到物理地址 ,低12bit, 1100 0000 0000 =c00,c00+069ca000=069cac00
// 这样就得到了虚拟地址 C0300C00 对应的物理地址 069cac00
kd> !dd 069cac00
!dd 069cac00
# 69cac00 069ca063 01e2b063 00000000 01670163
# 69cac10 01671163 01672163 01673163 01674163
# 69cac20 01675163 01676163 01657163 01658163
# 69cac30 01659163 0165a163 0165b163 0165c163
# 69cac40 0165d163 0165e163 0165f163 016c0163
# 69cac50 01681163 01682163 01683163 01684163
# 69cac60 01685163 01686163 01687163 01688163
# 69cac70 01689163 0168a163 0168b163 0168c163
// 该处的DWORD值为 069ca063
// 前面看到 CR3 的值为 069ca000

一个进程的页目录和页表本身所需要的物理内存

     一个进程需要物理页来放页表和页目录,如果有1024个页表,那么不是需要1024*4K=4M 的物理内存吗?一个进程只是页表信息就需要4M 的物理内存吗?不过实际上,当页目录中的一项有效标志位(第0位,最低位)为0,那么该项对应的页表就不存在,就不需要占用一个物理页。在前面的页目录的例子中我们已经看到了用户地址空间中有多少个无效项(系统地址空间中的东西,基本上大家都一样)。也就是说实际上为了保存一个进程的页表用不了多少物理内存。
     这也就是为什么要使用2级的地址转换。如果只使用一级,只用页表,那么每个进程1024个页表就必须都放在物理内存中,即每个进程需要4M物理内存来放页表,这个代价对目前来说是太大了。使用2级地址转换,多一个步骤,虽然转换速度会变慢一些,但是节省了物理内存,在速度与内存之间寻求了平衡。

页目录项的 Write 标志位
     从前面页目录的例子中,可以看到页目录的标志 bits1-1 Write ,总是为1。
表明页表所在的物理页总是可写的。这是因为页表和页目录是由系统维护的,系统需要向其中写入东西。

保护模式,Flat Model,线性地址,逻辑地址,ring0,ring3

以下内容只针对x86

     Win2k 运行在保护模式(Protected Mode )下,是否运行在保护模式下,取决于PE(Protection Enable)标志位 (CR0 的 bit 0 )。为0表示实模式(Real-Address Mode)(当机器加电启动和重启的时候,x86 CPU 处于实模式下),为1表示保护模式。观察Win2k的CR0的bit0,值为1,表明运行在保护模式下。

     对于x86体系结构来说,分段机制是必须被使用的(分页机制是可选的,分段和分页可以同时使用,Win2k使用了分页机制)。比如CPU执行代码总是参考代码段。在保护模式下,段寄存器(CS,DS,SS,ES,FS,GS)中放着的是段选择符,用于在某个描述符表中寻找段描述符。

     保护模式下的分段机制,有多种使用模型,Win2k使用的是一种叫做 Flat Model (平坦模型)。就是通过把段基址设为0,段界限设为4G,这样每个段都是整个地址空间,这样就隐藏了分段机制。

全局描述符表
全局描述符表(GDT,global descriptor table)用来存放描述符,整个系统只有一个。由GDTR(全局描述符表寄存器)指出所在位置和大小。由于GDTR中的界限部分为16位,所以全局描述符表最大可以为64KB,每个描述符占8个字节,所以全局描述符表中最多可以放8192个描述符。

段选择符(CS,DS,SS,ES,FS,GS中放的是段选择符)的格式定义如下(来自Intel手册):
段选择符,长2个字节,16bit。

Index: bit3-bit15 13位 在全局描述符表或者局部描述符表中的索引,可以索引8192个描述符
TI flag: bit2 1位 TI,table indicator,指定描述符表。为0在全局描述符表(GDT)中选择,为1在 当前的局部描述符表(LDT)中选择
RPL: bit0-bit1 2位 选择符的特权级

全局描述符表寄存器(GDTR,48bit)格式定义如下(来自Intel手册):

Table Limit: bit0-bit15 16位 指定描述符表的字节数。
Base Address: bit16-bit47 32位 指定描述符表的基地址。是线性地址。

段描述符格式定义如下:

typedef struct _SEG_DESCRIPTOR //size 8 bytes
{
unsigned short limit_0_15;
unsigned short base_0_15;
unsigned char base_16_23;

unsigned char accessed : 1;
unsigned char r_w_e : 1;
unsigned char e_c : 1;
unsigned char code_data : 1;
unsigned char app_system : 1;
unsigned char dpl : 2;
unsigned char present : 1;

unsigned char limit_16_19 : 4;
unsigned char unused : 1;
unsigned char always_0 : 1;
unsigned char seg_16_32 : 1;
unsigned char granularity : 1;

unsigned char base_24_31;
} SEG_DESCRIPTOR ,*PSEG_DESCRIPTOR;

limit_0_15,limit_16_19,granularity
段界限,两部分,共20bit。
如果granularity位为0,段界限的值以字节为单位。段最大为1MB。
如果granularity位为1,段界限的值以4KB为单位。段最大为4GB。

base_0_15,base_16_23,base_24_31
段基址,三部分,共32bit。

accessed,readable,conforming,code_data
accessed表示是否已经被访问过了。
code_data为0表示代码段,为1表示数据段。
对于数据段,r_w_e位表示是否可写,为0只读,为1可写。e_c位表示增长方向,为1向下增长。
对于代码段,r_w_e位表示是否可读,为0只执行,为1可读。e_c位表示conforming或者nonconforming。

app_system
0 = system, 1 = code or data。为1时,表明是代码段或者数据段。为0时表明是系统段,系统段一些位的含义和数据段或者代码段不同。有以下几种系统段:16-Bit TSS (Available),LDT,16-Bit TSS (Busy),16-Bit Call Gate,Task Gate,16-Bit Interrupt Gate,16-Bit Trap Gate,32-Bit TSS (Available),32-Bit TSS (Busy),32-Bit Call Gate,32-Bit Interrupt Gate,32-Bit Trap Gate。更多内容可以参考 Intel 手册。

dpl
指定段的特权级。

present
段是否在内存中

seg_16_32
段是16位还是32位。


下面看Win2k中实际的用法

一个ring3程序的段寄存器
(使用VC调试器获得,也可以自己写程序获得,也可以用SoftICE断到一个ring3程序中获得...)
CS = 001B DS = 0023 ES = 0023 SS = 0023 FS = 0038 GS = 0000

一个ring0程序的段寄存器
(使用KD获得,也可以写驱动程序获得,也可以用SoftICE断到一个ring0程序中获得...)
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000

GDTR的内容可以由指令sgdt得到(sgdt不是特权指令,ring3也可以执行)。
下面的内容来自SoftICE:
:gdt
Sel. Type Base Limit DPL Attributes
GDTbase=80036000 Limit=03FF
// GDTbase=80036000 Limit=03FF ,可以使用 sgdt指令做验证
// 16bit的界限值为03FF,表明Win2k中GDT只有1K,可以存放128个描述符

0008 Code32 00000000 FFFFFFFF 0 P RE
0010 Data32 00000000 FFFFFFFF 0 P RW
001B Code32 00000000 FFFFFFFF 3 P RE
0023 Data32 00000000 FFFFFFFF 3 P RW
0028 TSS32 801F4000 000020AB 0 P B
0030 Data32 FFDFF000 00001FFF 0 P RW
003B Data32 00000000 00000FFF 3 P RW
0043 Data16 00000400 0000FFFF 3 P RW
0048 Reserved 00000000 00000000 0 NP
0050 TSS32 80470040 00000068 0 P
0058 TSS32 804700A8 00000068 0 P
0060 Data16 00022AB0 0000FFFF 0 P RW
0068 Data16 000B8000 00003FFF 0 P RW
0070 Data16 FFFF7000 000003FF 0 P RW
0078 Code16 80400000 0000FFFF 0 P RE
0080 Data16 80400000 0000FFFF 0 P RW
0088 Data16 00000000 00000000 0 P RW
0090 Reserved 00000000 00000000 0 NP
0098 Reserved 00000000 00000000 0 NP
00A0 TSS32 8141A348 00000068 0 P
00A8 Reserved 00000000 00000000 0 NP
00B0 Reserved 00000000 00000000 0 NP
00B8 Reserved 00000000 00000000 0 NP
00C0 Reserved 00000000 00000000 0 NP
00C8 Reserved 00000000 00000000 0 NP
00D0 Reserved 00000000 00000000 0 NP
00D8 Reserved 00000000 00000000 0 NP
00E0 Code16 F0450000 0000FFFF 0 P RE ED
00E8 Data16 00000000 0000FFFF 0 P RW
00F0 Code16 8042DCE8 000003B7 0 P EO
00F8 Data16 00000000 0000FFFF 0 P RW
0100 Data32 F0460000 0000FFFF 0 P RW
0108 Data32 F0460000 0000FFFF 0 P RW
0110 Data32 F0460000 0000FFFF 0 P RW
0118 Reserved 00008003 00006120 0 NP
0120 Reserved 00008003 00006128 0 NP
0128 Reserved 00008003 00006130 0 NP
0130 Reserved 00008003 00006138 0 NP
0138 Reserved 00008003 00006140 0 NP
0140 Reserved 00008003 00006148 0 NP
0148 Reserved 00008003 00006150 0 NP
0150 Reserved 00008003 00006158 0 NP
0158 Reserved 00008003 00006160 0 NP
0160 Reserved 00008003 00006168 0 NP
0168 Reserved 00008003 00006170 0 NP
0170 Reserved 00008003 00006178 0 NP
0178 Reserved 00008003 00006180 0 NP
0180 Reserved 00008003 00006188 0 NP
0188 Reserved 00008003 00006190 0 NP
0190 Reserved 00008003 00006198 0 NP
0198 Reserved 00008003 000061A0 0 NP
01A0 Reserved 00008003 000061A8 0 NP
01A8 Reserved 00008003 000061B0 0 NP
01B0 Reserved 00008003 000061B8 0 NP
01B8 Reserved 00008003 000061C0 0 NP
01C0 Reserved 00008003 000061C8 0 NP
01C8 Reserved 00008003 000061D0 0 NP
01D0 Reserved 00008003 000061D8 0 NP
01D8 Reserved 00008003 000061E0 0 NP
01E0 Reserved 00008003 000061E8 0 NP
01E8 Reserved 00008003 000061F0 0 NP
01F0 Reserved 00008003 000061F8 0 NP
01F8 Reserved 00008003 00006200 0 NP
0200 Reserved 00008003 00006208 0 NP
0208 Reserved 00008003 00006210 0 NP
0210 Reserved 00008003 00006218 0 NP
0218 Reserved 00008003 00006220 0 NP
0220 Reserved 00008003 00006228 0 NP
0228 Reserved 00008003 00006230 0 NP
0230 Reserved 00008003 00006238 0 NP
0238 Reserved 00008003 00006240 0 NP
0240 Reserved 00008003 00006248 0 NP
0248 Reserved 00008003 00006250 0 NP
0250 Reserved 00008003 00006258 0 NP
0258 Reserved 00008003 00006260 0 NP
0260 Reserved 00008003 00006268 0 NP
0268 Reserved 00008003 00006270 0 NP
0270 Reserved 00008003 00006278 0 NP
0278 Reserved 00008003 00006280 0 NP
0280 Reserved 00008003 00006288 0 NP
0288 Reserved 00008003 00006290 0 NP
0290 Reserved 00008003 00006298 0 NP
0298 Reserved 00008003 000062A0 0 NP
02A0 Reserved 00008003 000062A8 0 NP
02A8 Reserved 00008003 000062B0 0 NP
02B0 Reserved 00008003 000062B8 0 NP
02B8 Reserved 00008003 000062C0 0 NP
02C0 Reserved 00008003 000062C8 0 NP
02C8 Reserved 00008003 000062D0 0 NP
02D0 Reserved 00008003 000062D8 0 NP
02D8 Reserved 00008003 000062E0 0 NP
02E0 Reserved 00008003 000062E8 0 NP
02E8 Reserved 00008003 000062F0 0 NP
02F0 Reserved 00008003 000062F8 0 NP
02F8 Reserved 00008003 00006300 0 NP
0300 Reserved 00008003 00006308 0 NP
0308 Reserved 00008003 00006310 0 NP
0310 Reserved 00008003 00006318 0 NP
0318 Reserved 00008003 00006320 0 NP
0320 Reserved 00008003 00006328 0 NP
0328 Reserved 00008003 00006330 0 NP
0330 Reserved 00008003 00006338 0 NP
0338 Reserved 00008003 00006340 0 NP
0340 Reserved 00008003 00006348 0 NP
0348 Reserved 00008003 00006350 0 NP
0350 Reserved 00008003 00006358 0 NP
0358 Reserved 00008003 00006360 0 NP
0360 Reserved 00008003 00006368 0 NP
0368 Reserved 00008003 00006370 0 NP
0370 Reserved 00008003 00006378 0 NP
0378 Reserved 00008003 00006380 0 NP
0380 Reserved 00008003 00006388 0 NP
0388 Reserved 00008003 00006390 0 NP
0390 Reserved 00008003 00006398 0 NP
0398 Reserved 00008003 000063A0 0 NP
03A0 Reserved 00008003 000063A8 0 NP
03A8 Reserved 00008003 000063B0 0 NP
03B0 Reserved 00008003 000063B8 0 NP
03B8 Reserved 00008003 000063C0 0 NP
03C0 Reserved 00008003 000063C8 0 NP
03C8 Reserved 00008003 000063D0 0 NP
03D0 Reserved 00008003 000063D8 0 NP
03D8 Reserved 00008003 000063E0 0 NP
03E0 Reserved 00008003 000063E8 0 NP
03E8 Reserved 00008003 000063F0 0 NP
03F0 Reserved 00008003 000063F8 0 NP
03F8 Reserved 00000000 00000000 0 NP

:dd 80036000 l 400
0010:80036000 00000000 00000000 0000FFFF 00CF9B00 ................
0010:80036010 0000FFFF 00CF9300 0000FFFF 00CFFB00 ................
0010:80036020 0000FFFF 00CFF300 400020AB 80008B1F ......... .@....
0010:80036030 F0000001 FFC093DF 00000FFF 0040F300 ..............@.
0010:80036040 0400FFFF 0000F200 00000000 00000000 ................
0010:80036050 00400068 80008947 00A80068 80008947 h.@.G...h...G...
0010:80036060 2AB0FFFF 00009302 80003FFF 0000920B ...*.....?......
0010:80036070 700003FF FF0092FF 0000FFFF 80009A40 ...p........@...
0010:80036080 0000FFFF 80009240 00000000 00009200 ....@...........
0010:80036090 00000000 00000000 00000000 00000000 ................
0010:800360A0 A3480068 81008941 00000000 00000000 h.H.A...........
0010:800360B0 00000000 00000000 00000000 00000000 ................
0010:800360C0 00000000 00000000 00000000 00000000 ................
0010:800360D0 00000000 00000000 00000000 00000000 ................
0010:800360E0 0000FFFF F0009F45 0000FFFF 00009200 ....E...........
0010:800360F0 DCE803B7 80009842 0000FFFF 00009200 ....B...........
0010:80036100 0000FFFF F0409346 0000FFFF F0409346 ....F.@.....F.@.
0010:80036110 0000FFFF F0409346 80036120 00000000 ....F.@. a......
0010:80036120 80036128 00000000 80036130 00000000 (a......0a......
0010:80036130 80036138 00000000 80036140 00000000 8a......@a......
0010:80036140 80036148 00000000 80036150 00000000 Ha......Pa......
0010:80036150 80036158 00000000 80036160 00000000 Xa......`a......
0010:80036160 80036168 00000000 80036170 00000000 ha......pa......
0010:80036170 80036178 00000000 80036180 00000000 xa.......a......
0010:80036180 80036188 00000000 80036190 00000000 .a.......a......
0010:80036190 80036198 00000000 800361A0 00000000 .a.......a......
0010:800361A0 800361A8 00000000 800361B0 00000000 .a.......a......
0010:800361B0 800361B8 00000000 800361C0 00000000 .a.......a......
0010:800361C0 800361C8 00000000 800361D0 00000000 .a.......a......
0010:800361D0 800361D8 00000000 800361E0 00000000 .a.......a......
0010:800361E0 800361E8 00000000 800361F0 00000000 .a.......a......
0010:800361F0 800361F8 00000000 80036200 00000000 .a.......b......
0010:80036200 80036208 00000000 80036210 00000000 .b.......b......
0010:80036210 80036218 00000000 80036220 00000000 .b...... b......
0010:80036220 80036228 00000000 80036230 00000000 (b......0b......
0010:80036230 80036238 00000000 80036240 00000000 8b......@b......
0010:80036240 80036248 00000000 80036250 00000000 Hb......Pb......
0010:80036250 80036258 00000000 80036260 00000000 Xb......`b......
0010:80036260 80036268 00000000 80036270 00000000 hb......pb......
0010:80036270 80036278 00000000 80036280 00000000 xb.......b......
0010:80036280 80036288 00000000 80036290 00000000 .b.......b......
0010:80036290 80036298 00000000 800362A0 00000000 .b.......b......
0010:800362A0 800362A8 00000000 800362B0 00000000 .b.......b......
0010:800362B0 800362B8 00000000 800362C0 00000000 .b.......b......
0010:800362C0 800362C8 00000000 800362D0 00000000 .b.......b......
0010:800362D0 800362D8 00000000 800362E0 00000000 .b.......b......
0010:800362E0 800362E8 00000000 800362F0 00000000 .b.......b......
0010:800362F0 800362F8 00000000 80036300 00000000 .b.......c......
0010:80036300 80036308 00000000 80036310 00000000 .c.......c......
0010:80036310 80036318 00000000 80036320 00000000 .c...... c......
0010:80036320 80036328 00000000 80036330 00000000 (c......0c......
0010:80036330 80036338 00000000 80036340 00000000 8c......@c......
0010:80036340 80036348 00000000 80036350 00000000 Hc......Pc......
0010:80036350 80036358 00000000 80036360 00000000 Xc......`c......
0010:80036360 80036368 00000000 80036370 00000000 hc......pc......
0010:80036370 80036378 00000000 80036380 00000000 xc.......c......
0010:80036380 80036388 00000000 80036390 00000000 .c.......c......
0010:80036390 80036398 00000000 800363A0 00000000 .c.......c......
0010:800363A0 800363A8 00000000 800363B0 00000000 .c.......c......
0010:800363B0 800363B8 00000000 800363C0 00000000 .c.......c......
0010:800363C0 800363C8 00000000 800363D0 00000000 .c.......c......
0010:800363D0 800363D8 00000000 800363E0 00000000 .c.......c......
0010:800363E0 800363E8 00000000 800363F0 00000000 .c.......c......
0010:800363F0 800363F8 00000000 00000000 00000000 .c..............

我们来分析一下:

对于ring3,CS=001B DS=0023 ES=0023 SS=0023 FS=0038 GS=0000,
DS,ES,SS,指向同一个段。
001B=0000000000011 0 11
0023=0000000000100 0 11
所以根据前面段选择符的定义,表示 CS,DS,ES,SS 都是在全局描述符表中进行选择。
选择符的特权级都是3(ring3)。对于CS,索引为3,由于每个描述符为8个字节,描述符相对于GDT开始处的地址为0x3*0x8=0x18。对于,DS,ES,SS,索引为4,描述符相对于GDT开始处的地址为0x4*0x8=0x20。

根据前面段描述符的定义,分析 80036018 开始的8个字节,和 80036020 开始的8个字节。
0010:80036010 0000FFFF 00CF9300 0000FFFF 00CFFB00 ................
0010:80036020 0000FFFF 00CFF300 400020AB 80008B1F ......... .@....
可以得出
Sel. Type Base Limit DPL Attributes
001B Code32 00000000 FFFFFFFF 3 P RE
0023 Data32 00000000 FFFFFFFF 3 P RW
可以看到,CS 对应的全局描述符表中的段描述符,是一个代码段,段基址为0,段界限为4G,DPL为3(ring3)。DS,ES,SS 对应的全局描述符表中的段描述符,是一个数据段,段基址为0,段界限为4G,DPL为3(ring3)。

对于ring0, cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000,
0008=0000000000001 0 00
0010=0000000000010 0 00
0023=0000000000100 0 11
Sel. Type Base Limit DPL Attributes
0008 Code32 00000000 FFFFFFFF 0 P RE
0010 Data32 00000000 FFFFFFFF 0 P RW
0023 Data32 00000000 FFFFFFFF 3 P RW
可以看到,代码段和堆栈段的DPL为0(ring0)。三个描述符的段基址都为0,段界限都为4G。

CS,DS,ES,SS,这些段都完全重叠了,而且都是当前的4G地址空间。和段的另外一种使用方法(不同的段在不同的一块地址空间)相比,现在的使用方法,整个地址空间是连续的,平坦的,所以叫做平坦模式(Flat Model)。

关于段寄存器(CS,DS,SS,ES,FS,GS),不可能每次参考一个段的时候CPU都去从内存中取出段描述符然后再做相应的处理,每个段寄存器还有看不到的部分,他们中存储着相应的段描述符中的信息。

全局描述表中的第一项不被使用,设为了"null descriptor" 。
0010:80036000 00000000 00000000
用来初始化没有被使用的段寄存器,以防止如果被不小心使用一个没有被使用的段寄存器,对系统造成不良影响。Win2k中,段寄存器GS就没有被使用,值都设为了0。

前面分析了 CS,DS,ES,SS 。对于 GS 来说,我们可以看到它的值总是为0,Win2k 并没有使用它。而对于 FS 来说,对于使用平坦模式的 Win2k 来说,它是一个例外,在其他地方会对 FS 做更多的介绍。

在 ntddk.h 中的以下内容,也说明了上面的一些分析。
#define KGDT_NULL 0
#define KGDT_R0_CODE 8
#define KGDT_R0_DATA 16
#define KGDT_R3_CODE 24
#define KGDT_R3_DATA 32
#define KGDT_TSS 40
#define KGDT_R0_PCR 48
#define KGDT_R3_TEB 56
#define KGDT_VDM_TILE 64
#define KGDT_LDT 72
#define KGDT_DF_TSS 80
#define KGDT_NMI_TSS 88

线性地址(linear address),把32bit可寻址的4G地址空间看做连续的线性地址空间,线性地址空间中的地址叫线性地址。逻辑地址(logical address),逻辑地址由一个段选择符(16位)和一个段内偏移(32位)组成。也经常被叫做远程指针(far pointer)。x86 CPU 总是使用分段机制的(即使是使用了最少的分段机制的平坦模型),因此 x86 CPU 指令中的地址也总是逻辑地址,是相对于某一个段而言的地址。在保护模式下,x86 CPU 首先把逻辑地址转换成线性地址,然后再把线性地址转换成物理地址。

对于Win2k来说,由于使用了平坦模型,虚拟地址XXXXXXXX 和线性地址XXXXXXXX 指的是同一个地方。而平坦模型下,CS,DS,SS 这几个段的段基址为0,段界限为4G,就是整个地址空间,所以逻辑地址中的段内偏移的值就等于该逻辑地址转换成线性地址以后的值。

保护模式下的 x86 CPU 有4个特权级,从0到3。被叫做ring0,ring1,ring2,ring3。ring0权限最大,ring3权限最小。对于Win2k来说,只使用了ring0,ring3两个特权级。用户应用程序运行在ring3,系统内核运行在ring0。
     Win2k中进程的地址空间,被分为用户地址空间和系统地址空间。用户模式下的程序将不能访问系统地址空间,这就是通过保护机制实现的。页目录项和页表项的 bit2 ,Owner标志位表明了访问所需的特权级,为0表示访问需要ring0的特权级,为1表示ring3的特权级就可以访问。在发生内存访问时,CPU会把虚拟地址转换成物理地址。转换过程中,在从页目录项和页表项中获得物理地址的同时,会检查页目录项和页表项中的Owner标志位和当前的特权级,如果没有足够的特权级将会产生一个 page-fault 异常。注意权限的检测和地址的转换是同时由硬件进行的,不会为此损失额外的性能。当前特权级(CPL,Current privilege level),当前执行程序的特权级,由CS,SS段寄存器中的最低2位指明。

保护模式下的 x86 CPU 提供了很多的保护机制,这也就是为什么叫保护模式,更多内容可以参考 Intel手册。

未来

     呃,从某种意义上来说,我认为未来是可以预知的。就像天气预报所作的,在今天就知道明天或者后天将要发生的事情。当然,实际上我认为对于人类来说未来是不可预知的,人类所能知道的事情太有限了。呃,如果要估计我们现在讨论的分页机制还能在内存中活几年,就得估计CPU的发展,可能的新的需求的出现,人们的创造力。挺费事,又没有预测以后一些人痛斥学校的愚昧的情景那么过瘾,所以我要说的未来不是关于分页的。我认为不久的将来人工智能和机器人将非常重要。当然,实际上我认为当有真正的人工智能出现的时候,人类很有可能也就完了。 

JIURL玩玩Win2k内存篇

  • 2013年08月17日 23:09
  • 231KB
  • 下载

JIURL文档-Linux的虚拟内存与分页机制(x86-64位)(二)

作者:JIURL 日期:2015年11月01日 还是分页机制 进程的页目录和页表在虚拟地址空间中的位置    上一篇中,我们把分页相关结构,分别叫做PML4,PDPT,PD,PT。...
  • jiurl
  • jiurl
  • 2015年11月02日 10:46
  • 169

JIURL玩玩Win2k进程线程篇 PEB

   PEB,Process Environment Block ,进程环境块。位于用户地址空间。在地址 0x7FFDF000 处。所以用户进程可以直接访问自己的 PEB 结构。Win2k Bu...

80386内存分页机制

  • 2010年06月24日 16:44
  • 316KB
  • 下载

关于内存的分段与分页机制

分段机制:   在8086时期,寄存器是16位,无法存放20位的物理地址,为了解决这问题,人们提出了分段机制,分段机制就是将内存分段,每段大小64kb(正好由16位表示),在段寄存器中放入段基址,然...

内存管理-分页机制

  • 2014年10月10日 21:17
  • 486KB
  • 下载

Linux内存分页机制原理

  • 2008年03月23日 17:53
  • 356KB
  • 下载

Linux内核源码分析--内存管理(一、分页机制)

最开始的地方是在head汇编中,如果看boot中那三个汇编的应该记得(那三个汇编还是比较重要的)。首先是分页机制,在CR0的第31位(PG位)置1表示开启分页机制,顺便也介绍下其他几个控制寄存器:CR...

详解Windows内存分页机制

原文链接: http://bbs.pediy.com/showthread.php?t=135274 标 题: 【原创】详解Windows内存分页机制 作 者: 莫灰灰 时 间: 2011-06-...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:JIURL玩玩Win2k内存篇 分页机制 (四)
举报原因:
原因补充:

(最多只允许输入30个字)