《Undocumented Windows 2000 Secrets》翻译 --- 第四章(2)
第四章 探索Windows 2000的内存管理机制
翻译:Kendiv( fcczj@263.net )
更新:
数据结构
本章随后的示例代码的某些部分将涉及底层的内存管理机制,在前面我们已快速浏览了该机制内部的大致轮廓。为了方便,我用C语言定义了几个数据结构。这是因为i386 CPU内部的很多数据项需要使用一个二进制位或一组二进制位,而C的位域(bit-fields)唾手可得。位域可以很有效的访问一个大的数据中的一个位或从中提取一组连续的位。微软的Visual C/C++可以产生非常棒的代码来完成位域的操作。列表4-2是一系列CPU数据类型定义的一部分,该列表包含如下的内容:
l X86_REGISTER 这是一个基本的无符号32位整数类型,该类型可描述多个CPU寄存器。这包括:通用的、索引、指针、控制、调试和测试寄存器。
l X86_SELECTOR 代表一个16位的段选择器,如CS、DS、ES、FS、GS和SS。在图4-1和图4-2中,选择器可描述48位逻辑地址的高8位,或作为描述符表的索引。为了计算的方便,16位选择器的值被扩展到32位,不过高16位被标识为“保留”。注意,X86_SELECTOR结构实际是两个结构的联合(union)。第一个指定了选择器的值,该值占用一个16位的WORD,其名字为wValue,第二个采用了位域。RPL域指定了请求的特权级,在Windows 2000上其值或者为0(内核模式)或者为3(用户模式)。TI位用来选择GDT或LDT。
l X86_DEscriptOR 定义了由选择器指向的页表项的格式。这是一个64位的数值,由于历史演化,该结构比较让人费解。线性基地址定义了与其相关的段的起始位置,它们分散在三个位域中:Base1、Base2和Base3,Base1是作用最小的部分。段的界限指定了段的大小,The segment limit specifying the segment size minus one is divided into the pair Limitl and Limit2, with the former representing the least significant half.剩余的位域存放不同的段属性(cf. Intel 1999c, pp.3-11)。例如,G位域定义了段的粒度。如果为零,段的限制按字节指定;否则,限制值为4KB的倍数。像X86_SELECTOR一样,X86_DEscriptOR结构由一个union组成,以允许按不同的方式解释它的值。如果你必须复制描述符(在忽略其内部情况下)那么dValueLow和dValueHigh成员将会很有帮助。
l X86_GATE 该结构看起来有些像X86_DEscriptOR。事实上,这两个结构是相关的:X86_DEscriptRO是一个GDT项,并描述了一个段的内存属性,X86_GATE代表中断描述符表(IDT)中的一项,并描述了中断例程的内存属性。IDT可以包含任务、中断和陷阱门(不! Bill Gates并没有存储在IDT中! 哈哈)。X86_GATE结构可匹配上述三种类型,并通过Type位域来进行区分。Type 5表示这是一个任务门;Type 6和14为中断门;Type 7和15为陷阱门。Type中最重要的位是用来描述门的位数的位:该位若为0则表示是16位门;其余情况表示32位门。
l X86_TABLE 是一个巧妙的结构,该结构用来读取GDTR或IDTR的当前值,分别通过汇编指令SGDT(存储GDT寄存器)和SIDT(存储IDT寄存器)来实现(cf. Intel 1999b, pp.3-636)。这两个指令需要一个48位的内存操作数,在该操作数中存放限制值和基地址值。通过在结构体中增加一个DWORD来对齐32位的基地址,X86_TABLE以一个16位的哑元成员wReserved开始。根据是否使用了SGDT或SIDT指令,其基地址将被解释为一个描述符指针或一个门指针,就像PX86_DEscriptOR和PX86_GATE中的union所暗示的那样。最后的wLimit成员在这两种类型的表中的意义均相同。
译注:
列表4-2中的这些结构定义可以在随书光盘的\src\common\include\w2k_spy.h中找到。
typedef DWORD X86_REGISTER, *PX86_REGISTER, **PPX86_REGISTER;
// -----------------------------------------------------------------
typedef struct _X86_SELECTOR
{
union
{
struct
{
WORD wValue; // packed value
WORD wReserved;
};
struct
{
unsigned RPL : 2; // requested privilege level
unsigned TI : 1; // table indicator: 0=gdt, 1=ldt
unsigned Index : 13; // index into descriptor table
unsigned Reserved : 16;
};
};
}
X86_SELECTOR, *PX86_SELECTOR, **PPX86_SELECTOR;
#define X86_SELECTOR_ sizeof (X86_SELECTOR)
// -----------------------------------------------------------------
typedef struct _X86_DEscriptOR
{
union
{
struct
{
DWORD dValueLow; // packed value
DWORD dValueHigh;
};
struct
{
unsigned Limit1 : 16; // bits 15..00
unsigned Base1 : 16; // bits 15..00
unsigned Base2 : 8; // bits 23..16
unsigned Type : 4; // segment type
unsigned S : 1; // type (0=system, 1=code/data)
unsigned DPL : 2; // descriptor privilege level
unsigned P : 1; // segment present
unsigned Limit2 : 4; // bits 19..16
unsigned AVL : 1; // available to programmer
unsigned Reserved : 1;
unsigned DB : 1; // 0=16-bit, 1=32-bit
unsigned G : 1; // granularity (1=4KB)
unsigned Base3 : 8; // bits 31..24
};
};
}
X86_DEscriptOR, *PX86_DEscriptOR, **PPX86_DEscriptOR;
#define X86_DEscriptOR_ sizeof (X86_DEscriptOR)
// -----------------------------------------------------------------
typedef struct _X86_GATE
{
union
{
struct
{
DWORD dValueLow; // packed value
DWORD dValueHigh;
};
struct
{
unsigned Offset1 : 16; // bits 15..00
unsigned Selector : 16; // segment selector
unsigned Parameters : 5; // parameters
unsigned Reserved : 3;
unsigned Type : 4; // gate type and size
unsigned S : 1; // always 0
unsigned DPL : 2; // descriptor privilege level
unsigned P : 1; // segment present
unsigned Offset2 : 16; // bits 31..16
};
};
}
X86_GATE, *PX86_GATE, **PPX86_GATE;
#define X86_GATE_ sizeof (X86_GATE)
// -----------------------------------------------------------------
typedef struct _X86_TABLE
{
WORD wReserved; // force 32-bit alignment
WORD wLimit; // table limit
union
{
PX86_DEscriptOR pDescriptors; // used by sgdt instruction
PX86_GATE pGates; // used by sidt instruction
};
}
X86_TABLE, *PX86_TABLE, **PPX86_TABLE;
#define X86_TABLE_ sizeof (X86_TABLE)
列表4-2. i386的寄存器、选择器、描述符、门和表
接下来的一组与i386内存管理相关的结构,它们收录在列表4-3中,这些结构包括:与请求式分页相关的结构和图4-3和图4-4给出的几个成员。
l X86_PDBR 该结构对应CPU的CR3寄存器,即众所周知的页目录基地址寄存器(PDBR)。其高20位为PFN,即4KB物理页数组的索引。PFN=0对应物理地址0x00000000,PFN=1为0x00001000,依此类推。20个位足够转换整个4GB地址空间。PDBR中的PFN是物理页的索引,用来控制整个页目录。PFN中剩余的位大多数都被保留,但3号位例外,它用来控制页一级的write-through(page-level write-through, PWT),4号位如果为1,则禁止页一级的高速缓冲。
l X86_PDE_4M 和 X86_PDE_4K 是页目录项(PDE)的两个可选方案,用来选择4MB页或者4KB的页。一个页目录中最多包含1024个PDE。PFN是页帧号,它指向下一级的页。对于一个4MB的PDE,其PFN位域仅有10个位的宽度,可寻址一个4MB的数据页。4KB的PDE拥有20位的PFN,可指向一个页表,由页表最终选择一个数据页。剩余的位用来定义多种属性。这些属性中最有趣的是“页大小”位PS,用于控制页的大小(0=4KB,1=4MB)和“存在”位P,标识下属的数据页(4MB模式)或页表(4KB模式)是否存在于物理内存中。
X86_PTE_4K 定义了页表项(属于一个页表)的内部结构。和页目录类似,一个页表可拥有1024个项。X86_PTE_4K和X86_PDE_4K的不同之处为:前者没有PS位,这根本不需要,因为页的大小肯定是4KB。需要注意的是,没有所谓的4MB的PTE,因为采用4MB页的内存模式不需要页表这一中间层。
X86_PNPE 代表一个“不存在的页”项(page-not-present entry, PNPE),也就是说,一个PDE或PTE中的P位为0。如Intel的手册所说的,保留的第31位是“对操作系统或执行体(executive)均可用”(Intel 1999c,pp. 3-28)。如果一个线性地址映射到了一个PNPE,这意味着这个地址或者还未使用或者它所指向的页已经被置换到了页面文件中。Windows 2000使用PNPE保留的第31位来存储页的信息。有关页信息的结构没有文档记载,不过它类似于名为PageFile的第10位,如列表4-3所示,如果设置了该位,则表示页已被置换出物理内存。在这种情况下,Reserved1和Reserved2位域将包含系统在页面文件中定位该页的信息,因此,当需要访问该页时,可很快的将其换回物理内存。
X86_PE 该结构是为了方便使用而加入的。它仅包含一个union,该union包括页项所有可能的状态,此处的页项是指:PDBR的内容、所有4MB和4KB的PDE、PTE,以及所有的PNPE。
typedef struct _X86_PDBR // page-directory base register (cr3)
{
union
{
struct
{
DWORD dValue; // packed value
};
struct
{
unsigned Reserved1 : 3;
unsigned PWT : 1; // page-level write-through
unsigned PCD : 1; // page-level cache disabled
unsigned Reserved2 : 7;
unsigned PFN : 20; // page-frame number
};
};
}
X86_PDBR, *PX86_PDBR, **PPX86_PDBR;
#define X86_PDBR_ sizeof (X86_PDBR)
// -----------------------------------------------------------------
typedef struct _X86_PDE_4M // page-directory entry (4-MB page)
{
union
{
struct
{
DWORD dValue; // packed value
};
struct
{
unsigned P : 1; // present (1 = present)
unsigned RW : 1; // read/write
unsigned US : 1; // user/supervisor
unsigned PWT : 1; // page-level write-through
unsigned PCD : 1; // page-level cache disabled
unsigned A : 1; // accessed
unsigned D : 1; // dirty
unsigned PS : 1; // page size (1 = 4-MB page)
unsigned G : 1; // global page
unsigned Available : 3; // available to programmer
unsigned Reserved : 10;
unsigned PFN : 10; // page-frame number
};
};
}
X86_PDE_4M, *PX86_PDE_4M, **PPX86_PDE_4M;
#define X86_PDE_4M_ sizeof (X86_PDE_4M)
// -----------------------------------------------------------------
typedef struct _X86_PDE_4K // page-directory entry (4-KB page)
{
union
{
struct
{
DWORD dValue; // packed value
};
struct
{
unsigned P : 1; // present (1 = present)
unsigned RW : 1; // read/write
