其他语言

本类阅读TOP10

·基于Solaris 开发环境的整体构思
·使用AutoMake轻松生成Makefile
·BCB数据库图像保存技术
·GNU中的Makefile
·射频芯片nRF401天线设计的分析
·iframe 的自适应高度
·BCB之Socket通信
·软件企业如何实施CMM
·入门系列--OpenGL最简单的入门
·WIN95中日志钩子(JournalRecord Hook)的使用

分类导航
VC语言Delphi
VB语言ASP
PerlJava
Script数据库
其他语言游戏开发
文件格式网站制作
软件工程.NET开发
《Undocumented Windows 2000 Secrets》翻译 --- 第四章(2)

作者:未知 来源:月光软件站 加入时间:2005-2-28 月光软件站

第四章  探索Windows 2000的内存管理机制

翻译:Kendiv( fcczj@263.net )

更新:Sunday, February 14, 2005

 

声明:转载请注明出处,并保证文章的完整性,本人保留译文的所有权利。


数据结构

本章随后的示例代码的某些部分将涉及底层的内存管理机制,在前面我们已快速浏览了该机制内部的大致轮廓。为了方便,我用C语言定义了几个数据结构。这是因为i386 CPU内部的很多数据项需要使用一个二进制位或一组二进制位,而C的位域(bit-fields)唾手可得。位域可以很有效的访问一个大的数据中的一个位或从中提取一组连续的位。微软的Visual C/C++可以产生非常棒的代码来完成位域的操作。列表4-2是一系列CPU数据类型定义的一部分,该列表包含如下的内容:

l         X86_REGISTER 这是一个基本的无符号32位整数类型,该类型可描述多个CPU寄存器。这包括:通用的、索引、指针、控制、调试和测试寄存器。

 

l         X86_SELECTOR 代表一个16位的段选择器,如CSDSESFSGSSS。在4-14-2中,选择器可描述48位逻辑地址的高8位,或作为描述符表的索引。为了计算的方便,16位选择器的值被扩展到32位,不过高16位被标识为“保留”。注意,X86_SELECTOR结构实际是两个结构的联合(union)。第一个指定了选择器的值,该值占用一个16位的WORD,其名字为wValue,第二个采用了位域。RPL域指定了请求的特权级,在Windows 2000上其值或者为0(内核模式)或者为3(用户模式)。TI位用来选择GDTLDT

 

l         X86_DESCRIPTOR 定义了由选择器指向的页表项的格式。这是一个64位的数值,由于历史演化,该结构比较让人费解。线性基地址定义了与其相关的段的起始位置,它们分散在三个位域中:Base1Base2Base3Base1是作用最小的部分。段的界限指定了段的大小,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组成,以允许按不同的方式解释它的值。如果你必须复制描述符(在忽略其内部情况下)那么dValueLowdValueHigh成员将会很有帮助。

 

l         X86_GATE 该结构看起来有些像X86_DESCRIPTOR。事实上,这两个结构是相关的:X86_DESCRIPTRO是一个GDT项,并描述了一个段的内存属性,X86_GATE代表中断描述符表(IDT)中的一项,并描述了中断例程的内存属性。IDT可以包含任务、中断和陷阱门(不! Bill Gates并没有存储在IDT中! 哈哈)。X86_GATE结构可匹配上述三种类型,并通过Type位域来进行区分。Type 5表示这是一个任务门;Type 614为中断门;Type 715为陷阱门。Type中最重要的位是用来描述门的位数的位:该位若为0则表示是16位门;其余情况表示32位门。

 

l         X86_TABLE 是一个巧妙的结构,该结构用来读取GDTRIDTR的当前值,分别通过汇编指令SGDT(存储GDT寄存器)和SIDT(存储IDT寄存器)来实现(cf. Intel 1999b, pp.3-636)。这两个指令需要一个48位的内存操作数,在该操作数中存放限制值和基地址值。通过在结构体中增加一个DWORD来对齐32位的基地址,X86_TABLE以一个16位的哑元成员wReserved开始。根据是否使用了SGDTSIDT指令,其基地址将被解释为一个描述符指针或一个门指针,就像PX86_DESCRIPTORPX86_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-34-4给出的几个成员。

 

l         X86_PDBR 该结构对应CPUCR3寄存器,即众所周知的页目录基地址寄存器(PDBR)。其高20位为PFN,即4KB物理页数组的索引。PFN=0对应物理地址0x00000000PFN=10x00001000,依此类推。20个位足够转换整个4GB地址空间。PDBR中的PFN是物理页的索引,用来控制整个页目录。PFN中剩余的位大多数都被保留,但3号位例外,它用来控制页一级的write-throughpage-level write-through, PWT),4号位如果为1,则禁止页一级的高速缓冲。

 

l         X86_PDE_4M X86_PDE_4K 是页目录项(PDE)的两个可选方案,用来选择4MB页或者4KB的页。一个页目录中最多包含1024PDEPFN是页帧号,它指向下一级的页。对于一个4MBPDE,其PFN位域仅有10个位的宽度,可寻址一个4MB的数据页。4KBPDE拥有20位的PFN,可指向一个页表,由页表最终选择一个数据页。剩余的位用来定义多种属性。这些属性中最有趣的是“页大小”位PS,用于控制页的大小(0=4KB1=4MB)和“存在”位P,标识下属的数据页(4MB模式)或页表(4KB模式)是否存在于物理内存中。

 

X86_PTE_4K  定义了页表项(属于一个页表)的内部结构。和页目录类似,一个页表可拥有1024个项。X86_PTE_4KX86_PDE_4K的不同之处为:前者没有PS位,这根本不需要,因为页的大小肯定是4KB。需要注意的是,没有所谓的4MBPTE,因为采用4MB页的内存模式不需要页表这一中间层。

 

X86_PNPE 代表一个“不存在的页”项(page-not-present entry, PNPE),也就是说,一个PDEPTE中的P位为0。如Intel的手册所说的,保留的第31位是“对操作系统或执行体(executive)均可用”(Intel 1999c,pp. 3-28)。如果一个线性地址映射到了一个PNPE,这意味着这个地址或者还未使用或者它所指向的页已经被置换到了页面文件中。Windows 2000使用PNPE保留的第31位来存储页的信息。有关页信息的结构没有文档记载,不过它类似于名为PageFile的第10位,如列表4-3所示,如果设置了该位,则表示页已被置换出物理内存。在这种情况下,Reserved1Reserved2位域将包含系统在页面文件中定位该页的信息,因此,当需要访问该页时,可很快的将其换回物理内存。

 

X86_PE  该结构是为了方便使用而加入的。它仅包含一个union,该union包括页项所有可能的状态,此处的页项是指:PDBR的内容、所有4MB4KBPDEPTE,以及所有的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

            unsigned US        :  1; // user/supervisor

            unsigned PWT       :  1; // page-level write-through

            unsigned PCD       :  1; // page-level cache disabled

            unsigned A         :  1; // accessed

            unsigned Reserved  :  1; // dirty

            unsigned PS        :  1; // page size (0 = 4-KB page)

            unsigned G         :  1; // global page

            unsigned Available :  3; // available to programmer

            unsigned PFN       : 20; // page-frame number

            };

        };

    }

    X86_PDE_4K, *PX86_PDE_4K, **PPX86_PDE_4K;

 

#define X86_PDE_4K_ sizeof (X86_PDE_4K)

 

// -----------------------------------------------------------------

 

typedef struct _X86_PTE_4K // page-table entry (4-KB 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 Reserved  :  1;

            unsigned G         :  1; // global page

            unsigned Available :  3; // available to programmer

            unsigned PFN       : 20; // page-frame number

            };

        };

    }

    X86_PTE_4K, *PX86_PTE_4K, **PPX86_PTE_4K;

 

#define X86_PTE_4K_ sizeof (X86_PTE_4K)

 

// -----------------------------------------------------------------

 

typedef struct _X86_PNPE // page not present entry

    {

    union

        {

        struct

            {

            DWORD dValue;            // packed value

            };

        struct

            {

            unsigned P         :  1; // present (0 = not present)

            unsigned Reserved1 :  9;

            unsigned PageFile  :  1; // page swapped to pagefile

            unsigned Reserved2 : 21;

            };

        };

    }

    X86_PNPE, *PX86_PNPE, **PPX86_PNPE;

 

#define X86_PNPE_ sizeof (X86_PNPE)

 

// -----------------------------------------------------------------

 

typedef struct _X86_PE // general page entry

    {

    union

        {

        DWORD      dValue; // packed value

        X86_PDBR   pdbr;   // page-directory Base Register

        X86_PDE_4M pde4M;  // page-directory entry (4-MB page)

        X86_PDE_4K pde4K;  // page-directory entry (4-KB page)

        X86_PTE_4K pte4K;  // page-table entry (4-KB page)

        X86_PNPE   pnpe;   // page not present entry

        };

    }

    X86_PE, *PX86_PE, **PPX86_PE;

 

#define X86_PE_ sizeof (X86_PE)

列表4-3.   i386 PDBRPDEPTEPNPE

 

列表4-4中,我增加了线性地址的结构化表示。这些结构是4-34-4中的“线性地址”的正式形式。

 

l         X86_LINEAR_4M  该结构是指向4MB数据页的线性地址的正式形式,如4-4所示。页目录索引(PDI)是一个页目录的索引,页目录地址由PDBR给出,使用PDI可选择页目录中的一个PDE22位的Offset成员指向一个目标地址,此目标地址对应4MB的物理页。

 

l         X86_LINEAR_4K  是一个4KB线性地址类型的变量,如4-3所示。该结构由三个位域组成:和4MB地址类似,高10位为PDI,用来选择一个PDE;页表索引PTI的任务与PDI相似,指向由PDE(该PDE由前面的PDI指定)确定的页表中的一个PTE;剩余的12个位是在4KB物理页中的偏移量。

 

l         X86_LINEAR  是另一个为使用方便而加入的结构。该结构只是简单的将X86_LINEAR_4K X86_LINEAR_4M联合为一个数据类型。详见列表4-4

 

typedef struct _X86_LINEAR_4M // linear address (4-MB page)

    {

    union

        {

        struct

            {

            PVOID pAddress;       // packed address

            };

        struct

            {

            unsigned Offset : 22; // offset into page

            unsigned PDI    : 10; // page-directory index

            };

        };

    }

    X86_LINEAR_4M, *PX86_LINEAR_4M, **PPX86_LINEAR_4M;

 

#define X86_LINEAR_4M_ sizeof (X86_LINEAR_4M)

 

// -----------------------------------------------------------------

 

typedef struct _X86_LINEAR_4K // linear address (4-KB page)

    {

    union

        {

        struct

            {

            PVOID pAddress;       // packed address

            };

        struct

            {

            unsigned Offset : 12; // offset into page

            unsigned PTI    : 10; // page-table index

            unsigned PDI    : 10; // page-directory index

            };

        };

    }

    X86_LINEAR_4K, *PX86_LINEAR_4K, **PPX86_LINEAR_4K;

 

#define X86_LINEAR_4K_ sizeof (X86_LINEAR_4K)

 

// -----------------------------------------------------------------

 

typedef struct _X86_LINEAR // general linear address

    {

    union

        {

        PVOID         pAddress; // packed address

        X86_LINEAR_4M linear4M; // linear address (4-MB page)

        X86_LINEAR_4K linear4K; // linear address (4-KB page)

        };

    }

    X86_LINEAR, *PX86_LINEAR, **PPX86_LINEAR;

 

#define X86_LINEAR_ sizeof (X86_LINEAR)

列表4-4.  i386的线性地址

 

宏和常量

列表4-5给出的定义是对列表4-2列表4-4所示结构的补充,让我们可以更容易的和i386内存管理一起工作。列表4-5的定义可以分为三大组。第一组用于控制线性地址:

 

1.         X86_PAGE_MASKX86_PDI_MASKX86_PTI_MASK 都是位掩码(bit mask),用来选择线性地址中的某一部分。它们都基于常量:PAGE_SHIFT (12)PDI-SHIFT (22)PTI-SHIFT (12),这些常量定义于Windows 2000 DDK的头文件ntddk.h中。X86_PAGE_MASK等价于0xFFFFF000,可有效的屏蔽4KB线性地址(X86_LINEAR_4K)中的偏移量部分。X86_PDI_MASK等价于0xFFC00000,显然这可从线性地址中提取高10位的PDIX86_PTI_MASK等价于0x003FF0000,用于屏蔽线性地址中除PTI外的所有位。

 

2.         X86_PAGE()X86_PDI()X86_PTI() 使用上面的常量来计算给定线性地址的页索引、PDIPTIX86_PAGE()一般用来从Windows 2000PTE数组(该数组首地址为:0xC0000000)中读取一个PTEX86_PDI()X86_PTI()只是针对给定的指针,简单的使用X86_PDI_MASKX86_PTI_MASK,并将得到的索引移动到最右边。

 

3.         X86_OFFSET_4M()X86_OFFSET_4K() 分别从4MB4KB线性地址中提取偏移量部分。

 

4.         X86_PAGE_4MX86_PAGE_4K 根据DDK中的常量PDI_SHIFTPTI_SHIFT来计算4MB4KB页的大小。X86_PAGE_4M=4,194,304X86_PAGE_4K=4,096。注意,X86_PAGE_4K等价于DDK常量PAGE_SIZE,该常量也定义于ntddk.h中。

 

5.         X86_PAGES_4MX86_PAGES_4K 分别表示4GB地址空间中可容纳的4MB4KB页的总数。X86_PAGES_4M等价于1,024X86_PAGES_4K等价于1,048,576

 

#define X86_PAGE_MASK (0 - (1 << PAGE_SHIFT))

#define X86_PAGE(_p)  (((DWORD) (_p) & X86_PAGE_MASK) >> PAGE_SHIFT)

 

#define X86_PDI_MASK  (0 - (1 << PDI_SHIFT))

#define X86_PDI(_p)   (((DWORD) (_p) & X86_PDI_MASK) >> PDI_SHIFT)

 

#define X86_PTI_MASK  ((0 - (1 << PTI_SHIFT)) & ~X86_PDI_MASK)

#define X86_PTI(_p)   (((DWORD) (_p) & X86_PTI_MASK) >> PTI_SHIFT)

 

#define X86_OFFSET(_p,_m) ((DWORD_PTR) (_p) & ~(_m))

#define X86_OFFSET_4M(_p) X86_OFFSET (_p, X86_PDI_MASK)

#define X86_OFFSET_4K(_p) X86_OFFSET (_p, X86_PDI_MASK|X86_PTI_MASK)

 

#define X86_PAGE_4M   (1 << PDI_SHIFT)

#define X86_PAGE_4K   (1 << PTI_SHIFT)

 

#define X86_PAGES_4M  (1 << (32 - PDI_SHIFT))

#define X86_PAGES_4K  (1 << (32 - PTI_SHIFT))

 

// -----------------------------------------------------------------

 

#define X86_PAGES         0xC0000000

#define X86_PTE_ARRAY     ((PX86_PE) X86_PAGES)

#define X86_PDE_ARRAY     (X86_PTE_ARRAY + (X86_PAGES >> PTI_SHIFT))

 

// -----------------------------------------------------------------

 

#define X86_SEGMENT_OTHER           0

#define X86_SEGMENT_CS              1

#define X86_SEGMENT_DS              2

#define X86_SEGMENT_ES              3

#define X86_SEGMENT_FS              4

#define X86_SEGMENT_GS              5

#define X86_SEGMENT_SS              6

#define X86_SEGMENT_TSS             7

 

// -----------------------------------------------------------------

 

#define X86_SELECTOR_RPL            0x0003

#define X86_SELECTOR_TI             0x0004

#define X86_SELECTOR_INDEX          0xFFF8

#define X86_SELECTOR_SHIFT          3

 

#define X86_SELECTOR_LIMIT          (X86_SELECTOR_INDEX >> \

                                     X86_SELECTOR_SHIFT)

 

// -----------------------------------------------------------------

 

#define X86_DESCRIPTOR_SYS_TSS16A       0x1

#define X86_DESCRIPTOR_SYS_LDT          0x2

#define X86_DESCRIPTOR_SYS_TSS16B       0x3

#define X86_DESCRIPTOR_SYS_CALL16       0x4

#define X86_DESCRIPTOR_SYS_TASK         0x5

#define X86_DESCRIPTOR_SYS_INT16        0x6

#define X86_DESCRIPTOR_SYS_TRAP16       0x7

#define X86_DESCRIPTOR_SYS_TSS32A       0x9

#define X86_DESCRIPTOR_SYS_TSS32B       0xB

#define X86_DESCRIPTOR_SYS_CALL32       0xC

#define X86_DESCRIPTOR_SYS_INT32        0xE

#define X86_DESCRIPTOR_SYS_TRAP32       0xF

 

// -----------------------------------------------------------------

 

#define X86_DESCRIPTOR_APP_ACCESSED     0x1

#define X86_DESCRIPTOR_APP_READ_WRITE   0x2

#define X86_DESCRIPTOR_APP_EXECUTE_READ 0x2

#define X86_DESCRIPTOR_APP_EXPAND_DOWN  0x4

#define X86_DESCRIPTOR_APP_CONFORMING   0x4

#define X86_DESCRIPTOR_APP_CODE         0x8

列表4-5.  附加的i386内存管理相关定义

 

第二组宏和常量与Windows 2000PDEPTE数组有关。和其他几个系统地址不同,这些数组的基地址并没有在系统启动时作为一个全局变量出现,而是被定义成了一个常量。可以通过反编译内存管理API函数:MmGetPhysicalAddress()MmIsAddressValid()来证明,在这些函数里,这些地址都以“魔术数字”的形式出现。这些常量并没有包括在DDK头文件中,不过列表4-5展示了如何定义它们。

 

l         X86_PAGES  是一个硬编码的地址和指针(指向0xC0000000),0xC0000000Windows 2000PTE数组开始的地方。

 

X86_PTE_ARRAY  等价于X86_PAGES,但是被转型为PX86_PE,也就是说,指向一个X86_PE类型的数组,X86_PE定义于列表4-2

 

X86_PDE_ARRAY  是一个巧妙的定义,它通过PTE数组的位置来计算PDE数组的基地址,这需要用到PTI_SHIFT常量。将线性地址映射为PTE地址的通用格式为:((LinearAdress >> 12*4+0xC0000000,线性地址0xC0000000转换后的地址为页目录的基地址。

列表4-5的最后两部分包括选择器和特殊类型的描述符,以及对列表4-2的补充。

 

l         X86_SELECTOR_RPLX86_SELECTOR_TIX86_SELECTOR_INDEX 都是位掩码,分别对应X86_SELECTOR结构中的RPLTIIndex成员。

 

l         X86_SELECTOR_SHIFT  是一个右移因子,用来使选择器的Index的数值向右对齐。

 

l         X86_SELECTOR_LIMIT  定义了选择器可使用的最大索引值,该限制为8,191。这个值确定了描述符表的最大尺寸。每个选择器索引均指向一个描述符,每个描述符包含64个位(即8个字节)。所以,描述符表的最大尺寸为:8,192*8=64KB

 

l         X86_DESCRIPTOR_SYS_*  是一组常量,用于定义系统描述符类型。如果描述符的S位被设为0,那么描述的Type成员将采用这一组类型中的某一个。请参考列表4-2中的X86_DESCRIPTOR的定义。系统描述符类型在Intel手册中有详细介绍(Intel 1999c, pp. 3-15f),4-1给出了所有可用的系统描述符类型。

 

列表4-5中的X86_DESCRIPTOR_APP_*常量也可用于定义描述符的Type成员,前提是描述符的S位不为0。此时,该应用程序描述符可能需要引用一个代码或数据段。因为应用程序描述符类型的属性受Type域的第四个位影响,所以X86_DESCRIPTOR_APP_*常量被定义为单位掩码(single-bit mask),这样一些位就可针对数据和代码段有不同的解释。

 

l         X86_DESCRIPTOR_APP_ACCESSED  如果一个段可以被访问,则采用

 

l         X86_DESCRIPTOR_APP_READ_WRITE  决定一个数据段是否允许只读或读/写访问。

 

l         X86_DESCRIPTOR_APP_CONFORMATING  说明一个代码段是否相匹配。也就是说,它是否可以被以被弱特权代码(less privileged code)调用(参考 Intel 1999c,pp. 4-13ff)。

 

l         X86_DESCRIPTOR_APP_CODE  用来区别代码段和数据段。注意,堆栈属于数据段的范畴,而且必须总是可写的。

 

稍后,当下一章中的Memory Spy程序开始运行时,我们将重温系统描述符。4-1算是i386内存管理的一个简短总结。有关本话题的更多内容,请参考Intel Pentium手册(Intel 1999a1999b1999c)。

 

4-1.    系统描述符类型

      

    

X86_DESCRIPTOR_SYS_TSS16A

0x1

16位任务状态段(可用)

X86_DESCRIPTOR_SYS_LDT

0x2

本地描述符表(LDT

X86_DESCRIPTOR_SYS_TSS16B

0x3

16位任务状态段(繁忙)

X86_DESCRIPTOR_SYS_CALL16

0x4

16位调用门

X86_DESCRIPTOR_SYS_TASK

0x5

任务门

X86_DESCRIPTOR_SYS_INT16

0x6

16位中断门

X86_DESCRIPTOR_SYS_TRAP16

0x7

16位陷阱门

X86_DESCRIPTOR_SYS_TSS32A

0x9

32位任务状态段(可用)

X86_DESCRIPTOR_SYS_TSS32B

0xB

32位任务状态段(繁忙)

X86_DESCRIPTOR_SYS_CALL32

0xC

32位调用门

X86_DESCRIPTOR_SYS_INT32

0xE

32位中断门

X86_DESCRIPTOR_SYS_TRAP32

0XF

32位陷阱门

 

 

………………..待续…………………..

 




相关文章

相关软件