是这样的,我想问下Windows 在Intel Protection Mode下工作的问题。在指令手册第三卷system programming 可以看到,通常GDT只有一个,而LDT可以有很多的。本来我很天真地想:那么我有多少个进程在running 就应该有多少个LDT才对。于是我打开softice, 输入GDT结果只有一个被标为LDT,参考了using softice,只看到如下一段:Under Winnt , LDT are per process data structures and are only used for virtual dos machines.The 16 bit WOW box is executed within a NTVDM process and has an LDT.Like Windows 3.1 the LDT for a WOW contains the selectors for every 16 bit proteced mode code and data segment for each 16 bit application or DLL that is loaded. It also contains the selectors for each task database, module database, local heaps, global allocation, and all USER and GDI objects that require the creation of a selector. Under a WOW, because the number of selectors needed can be quite large, a full LDT is created with a majority of the entries initially reserved. These reserved selectors are allocated as needed. Under a non-WOW VDM, the size of the LDT is significant smaller.
老实说我不知道它在说什么,最后的结果就是我只能看到一个LDT.这实在太奇怪了,请问难道windows不用LDT?
第二个奇怪的是GDTbase=80036000 Limit=03FF 0008 Code32 Base=00000000 Lim=FFFFFFFF DPL=0 P R 0010 Data32 Base=00000000 Lim=FFFFFFFF DPL=0 P R 001B Code32 Base=00000000 Lim=FFFFFFFF DPL=3 P R 0023 Data32 Base=00000000 Lim=FFFFFFFF DPL=3 P R 0028 TSS32 Base=8000B000 Lim=000020AB DPL=0 P B 0030 Data32 Base=FFDFF000 Lim=00001FFF DPL=0 P R 003B Data32 Base=7FFDE000 Lim=00000FFF DPL=3 P R 0043 Data16 Base=00000400 Lim=0000FFFF DPL=3 P R 0048 LDT Base=E156C000 Lim=0000FFEF DPL=0 P
显然code32 data32也就是从0008-001b这段是固定的,所谓的flat mode,可是真的不明白,明明规定4g地址只有低2g空间是用户可以访问的。高2G操作系统用,也就是ring0才能访问。那么这里有两个Code32,一个是ring3(0008),一个是ring0(001B)那么同一句汇编指令,访问32位偏移0xYYYYYYYY地址,操作系统怎么知道是访问上面那个还是下面那个,有两个段,这两个段根本都覆盖了4g空间,却具有不同的权限,dpl 不同,同一个地址到底要怎么解释呢?
晕眩。
是的是的,我已经非常非常清楚,您所说的:首先会根据段寄存器中的段选择子在GDT或LDT中找到一个描述符,取其中的基地值,加上这个偏移,等到所谓的线性地址
其实Intel 指令手册第三卷中关于保护模式已经详细地说明了这个问题,我觉得我自己是比较清楚了。关于PTE,页属性我也都看了,我觉得我非常清楚了,
让我很混乱的是:所谓Windows Flat内存模式,Undocument Winnt上指示说那代表其实windows是不分段的,只使用段偏移,段选择子只加载一次,如下0008 Code32 Base=00000000 Lim=FFFFFFFF DPL=0 P R0010 Data32 Base=00000000 Lim=FFFFFFFF DPL=0 P R 001B Code32 Base=00000000 Lim=FFFFFFFF DPL=3 P R0023 Data32 Base=00000000 Lim=FFFFFFFF DPL=3 P R这前面四个段,就是不分段的证明。
原文是这么写的:首先我们来看 Windows NT 是如何为进程提供平面地址空间的。 通过前面一节我们知道,Intel 的 80386 既提供了分段又提供了分页。那么 Windows NT 又是如何只提供平面内存而不提供分段的呢?关闭了分段吗?80386 上的分段可是关不掉的。 但是操作系统可以只加载一次段寄存器然后再后面的指令里只指定偏移,这在 80386 处理器上是可以做到的。Windows NT 就是这么干的。Windows NT 初始化所有的寄存器使之指向 0 到 4GB 的内存单元。页就是说基址设为 0,界限值设为 4GB。 CS、SS、DS 和 ES 都用指向 0 至 4GB 内存单元的独立的描述符初始化。现在,应用程序就可以只用32位偏移了。 最后,我们看到的就是32位平面地址空间。Windows NT 下的32位应用程序不用改变任何的段寄存器。
让我疑惑的是同一个4g地址为什么要有两个同样的段Code32和Data32,是不是当CPL = 0 指代的就是DPL=0的那个Code32?更奇怪的是,windows是不是基本上都不用LDT?否则,不应该只有一个LDT显示才对的。
其实我就是想对照386 protection mode和windows的工作方式应该能够看到一一对应才对。真是可惜首先LDT就对不上号,TSS32也对不上,386当中说每个任务都有tss,104个字节,这确实不错,可惜Softice里面的第一个tss,不是104个字节的。而且似乎只有5个TSS段在GDT内,这也不符合386保护模式,也只有一个描述LDT的段描述符在GDT内,
如下摘录win32汇编:
GDTR指向的描述符表为全局描述符表GDT(Global Descriptor Table)。它包含系统中所有任务都可用的段描述符,通常包含描述操作系统所使用的代码段、数据段和堆栈段的描述符及各任务的LDT段等;全局描述符表只有一个。
LDTR则指向局部描述符表LDT(Local Descriptor Table)。80386处理器设计成每个任务都有一个独立的LDT。它包含有每个任务私有的代码段、数据段和堆栈段的描述符,也包含该任务所使用的一些门描述符,如任务门和调用门描述符等。
不同任务的局部描述符表分别组成不同的内存段,描述这些内存段的描述符当做系统描述符放在全局描述符表中。和GDTR直接指向内存地址不同,LDTR和CS,DS等段选择器一样只存放索引值,指向局部描述符表内存段对应的描述符在全局描述符表中的位置。随着任务的切换,只要改变LDTR的值,系统当前的局部描述符表LDT也随之切换,这样便于各任务之间数据的隔离。但GDT并不随着任务的切换而切换。
看到这里,读者可能会提出一个问题,既然有全局描述符表和局部描述符表两个表,那么段选择器中的索引值对应哪个表中的描述符呢。实际上,16位的段选择器中只有高13位表示索引值。剩下的3个数据位中,第0,1位表示程序的当前优先级RPL;第2位TI位用来表示在段描述符的位置;TI=0表示在GDT中,TI=1表示在LDT中。
我猜测如果windows基本不用LDT,那么TI永远等于0也就不存在到LDT去找线性地址的可能了。
WINDOWS下平坦模式难道就是直接直接从偏移取线性地址的,因为段选择子limit= 0 ~ 4G.
0023 Data32 Base=00000000 Lim=FFFFFFFF DPL=3 P R 0028 TSS32 Base=8000B000 Lim=000020AB DPL=0 P B
而这个更是违反规则的,段描述符是64位的,
0023-0028只有5个字节,怎么可能?