天天看点

x86保护模式

处理器架构实际上是不断扩展的,新处理器必须延续旧的设计思路,并保持兼容性和一致性;同时还会有所扩充和增强。

尽管8086是16位的处理器,但它也是32位架构内的一部分。原因在于,32为处理器架构是从8086那里发展来的,是基于8086的,具有延续性和兼容性。

寄存器的扩展

32位处理器在16位处理器的从基础上,扩展了这8个通用寄存器(AX,BX,CX,DX,SI,DI,BP,SP)的长度,使之达到32位。

32位通用寄存器的高16位是不可独立使用的,但低16位保持同16位处理器兼容性。因此,在任何时候他们都可以照往常一样使用。

但是32位处理器并不是16位处理器简单的增强。事实上32位处理器有自己的32位工作模式,而我学习的32模式是32位保护模式。在这种模式下,处理器可以使用它全部的32跟地址线,能够访问4GB内存。

在32位模式下,为了生成32位物理地址,处理器需要使用32位的指令指针寄存器。为此,32位处理器扩展了IP,使之达到32位,工作在32位模式下时使用EIP,但是当工作在16位模式下仍使用IP。

对于32位模式下因为IA-32架构的处理器是基于分段模型,可是32位模式下,对内存的访问从理论上不需要分段,因为它可以自由访问任何一个内存位置。所以引入了平坦模型,即只分一个段,段的基址是0x00000000,段的长度是4GB,在这种情况下视为不分段。

在32位模式下,处理器要求在加在程序时,先定义该程序所拥有的段,然后允许使用这些段。定义段时,除了基地址外,还附加了段界限,特权级别,类型等属性。当程序访问一个段时,处理器将用固件实施各种检查工作,以防止对内存的违规访问。

在32位模式下,传统的段寄存器,如CS,SS,DS,ES,保存的不再是16位段基地址,而是段的选择子(实模式下是段寄存器,在保护模式下就是段选择子,因为在保护模式下他们存储的不再是段地址而是段描述符在描述符表中的索引号),即用于选择要访问的段,除了段选择子外,每个段寄存器还包括一个不可见部分,称为描述符高速缓冲器,里面有段的基地址和各种访问属性。

在保护模式下访问一个段的时候,传送到段选择器的是段选择子。他由三个部分组成,第一部分是索引号,用来在描述符表种选择一个段描述符。T1是描述符表指示器,T1=0时,描述符在GDT中,T1=1时,描述符在LDT中。RPL是请求特权级,表示给出当前选择子的那个程序的特权级

x86保护模式

线性地址

我们传统上讲的,段地址个偏移地址称为逻辑地址,偏移地址叫做有效地址,在指令中给出有效地址的方式叫做寻址方式。

然而段的管理是由处理器的段部件负责进行的,段部件将段地址和偏移地址相加,得到访问内存的地址。一般来说,段部件产生的地址就是物理地址。

但是为了解决内存空间碎片化(内存每次分配大小不定,时间长后会产生过多过小的内存空间块),IA-32处理器支持分页功能,分页功能将物理内存空间划分成逻辑上的页。通过使用页,可以简化内存管理。

当页功能开启时,段部件产生的地址就不再是物理地址了,而是线性地址,线性地址还要经过页部件转换后,才是物理地址。

线性地址的概念用来描述任务的地址空间。IA-32处理器上的每个任务拥有4GB的虚拟内存空间,这是一段长4GB的平坦空间,就像一段平直的线段。相应的,由段部件产生的地址,就对应着线性地址空间上的每一个点,这就是线性地址。

全剧描述符表

在保护模式下,对内存的访问仍能使用段地址和偏移地址,但是,在每个段能够访问之前,必须先进行登记。当你访问的偏移量超出段的界限时,处理器就会阻止这种访问,并产生一个叫做内部异常的中断。

断描述符,用八个字节来描述一个段有关的信息。为了存放这些描述符,需要在内存中开辟一段空间,在这段空间里,所有的描述符都是挨在一起,集中存放的,这就形成了一个描述符表。

最主要的描述符表是全局描述符表(GDT),在进入保护模式前,必须要定义全局描述符表。

x86保护模式

图品出自

为了跟踪全局描述符表,处理器内部有一个48位的寄存器,称为全局描述符表寄存器(GDTR)。该寄存器高32为存放全局描述符表线性基地址,低16位存放全局描述符表边界。

因为GDT的界限是16位所以表的大小最多是2^16字节(64KB),又因为全局描述符大小为8字节,所以最多可以定义8192个描述符。

虽说基地址有32位,但是由于在进入保护模式之后,处理器立即要按新的内存访问模式工作,所以,必须在进入保护模式之前定义GDT。但是,由于在实模式下只能访问1MB的内存,故GDT通常都定义在1MB以下的范围内。也允许在进入保护模式之后换位置重新定义GDT。

段描述符中的段属性也被安排在两个域中。下面对其定义及意义作说明。

(1)G为就是段界限粒度(Granularity)位

G=0表示界限粒度为字节;G=1表示界限粒度为4K 字节。注意,界限粒度只对段界限有效,对段基地址无效,段基地址总是以字节为单位。

(2)D/B位是一个很特殊的位在描述可执行段、向下扩展数据段或由SS寄存器寻址的段(通常是堆栈段)的三种描述符中的意义各不相同

在描述可执行段的描述符中,D位决定了指令使用的地址及操作数所默认的大小。

D=1表示默认情况下指令使用32位地址及32位或8位操作数,这样的代码段也称为32位代码段;

D=0 表示默认情况下,使用16位地址及16位或8位操作数,这样的代码段也称为16位代码段,它与80286兼容。可以使用地址大小前缀和操作数大小前缀分别改变默认的地址或操作数的大小。

在向下扩展数据段的描述符中,D位决定段的上部边界。

D=1表示段的上部界限为4G;

D=0表示段的上部界限为64K。

在描述由SS寄存器寻址的段描述符中,D位决定隐式的堆栈访问指令(如PUSH和POP指令)使用何种堆栈指针寄存器。

D=1表示使用32位堆栈指针寄存器ESP;

D=0表示使用16位堆栈指针寄存器SP。

(3)AVL位是软件可利用位

80386对该位的使用未做规定,此位被linux和windows操作系统忽略。

(4)P位称为存在(Present)位

P=1表示描述符对地址转换是有效的,或者说该描述符所描述的段存在,即在内存中;P=0表示描述符对地址转换无效,即该段不存在。使用该描述符进行内存访问时会引起异常。

(5)DPL表示描述符特权级(Descriptor Privilege level)

共2位。它规定了所描述段的特权级,用于特权检查,以决定对该段能否访问。

(6)DT位说明描述符的类型

对于存储段描述符而言,

DT=1,以区别与系统段描述符和门描述符(DT=0)。

(7)TYPE说明存储段描述符所描述的存储段的具体属性

x86保护模式

其中的位0指示描述符是否被访问过(Accessed),用符号A标记。

A=0表示尚未被访问,

A=1 表示段已被访问,

当把描述符的相应选择子装入到段寄存器时,80386把该位置为1,表明描述符已被访问,操作系统可测试访问位,已确定描述符是否被访问过。

此为位1,对应的type为奇数,因此type对应的数值为奇数时标识该描述符被访问过,否则为偶数标识未被访问过(可从前面的表中看出)

综上我们可以看到在类型不同时,段描述符的区别之处,如下所示

代码段描述符

表示这个段描述符代表一个代码段,它可以放在GDT中。该描述符置S标志为1。

数据段描述符

表示这个段描述符代表一个数据段,它可以放在GDT中,该描述符置S标志为1。

任务状态段描述符

表示这个段描述符代表一个任务状态段,也就是说这个段用于保存处理器寄存器的内容。它只能出现在GDT中,根据相应的进程是否正CPU上运行,其Type字段的值分别为11或9。这个描述符的S标志置为0。