在IA32下,CPU有两种工作方式:实模式和保护模式。当我们按下开机按钮以后,CPU是工作在实模式下的,经过某种机制以后才进入保护模式。
Intel 8086是16为的CPU,有16为的寄存器、16位的数据总线以及20位的地址总线,因此它有1M的寻址空间,一个地址是又段和偏移两部分组成的,物理地址计算方法是:
Physical Address = (Segment) * 16 + Offset (Statement: Segment和Offset都是16位的)
从80386开始,Intel进入了32位的时代。80386有32位地址线,寻址空间达到4G。这个时候保护模式出现了,保护模式并没有抛弃实模式下Seg:Offset这样的寻址方式,只是对Seg的解释发生了变化。实模式下段值可以看作是地址的一部分,而保护模式下,段寄存器还是原来的16位,但它仅仅只是一个索引,它指向一个数据结构的一个表项,这个数据结构就是大名鼎鼎的GDT,GDT定义了段的起始地址、界限和属性。
简而言之,GDT的作用是用来提供段式存储机制,这种机制是通过寄存器和GDT中的描述符共同提供的。下面详细分析GDT的结构。
GDT中的一个描述符一共有8个字节组成:
; 说明:
;
; (1) P: 存在(Present)位。
;P=1 表示描述符对地址转换是有效的,或者说该描述符所描述的段存在,即在内存中;
;P=0 表示描述符对地址转换无效,即该段不存在。使用该描述符进行内存访问时会引起异常。
;
; (2) DPL: 表示描述符特权级(Descriptor Privilege level),共2位。它规定了所描述段的特权级,用于特权检查,以决定对该段能否访问。
;
; (3) S: 说明描述符的类型。
;对于存储段描述符而言,S=1,以区别与系统段描述符和门描述符(S=0)。
;
; (4) TYPE: 说明存储段描述符所描述的存储段的具体属性。
;
;
;数据段类型类型值说明
;----------------------------------
;0只读
;1只读、已访问
;2读/写
;3读/写、已访问
;4只读、向下扩展
;5只读、向下扩展、已访问
;6读/写、向下扩展
;7读/写、向下扩展、已访问
;
;
;类型值说明
;代码段类型----------------------------------
;8只执行
;9只执行、已访问
;A执行/读
;B执行/读、已访问
;C只执行、一致码段
;D只执行、一致码段、已访问
;E执行/读、一致码段
;F执行/读、一致码段、已访问
;
;
;系统段类型类型编码说明
;----------------------------------
;0
;1可用286TSS
;2LDT
;3忙的286TSS
;4286调用门
;5任务门
;6286中断门
;7286陷阱门
;8未定义
;9可用386TSS
;A
;B忙的386TSS
;C386调用门
;D
;E386中断门
;F386陷阱门
;
; (5) G: 段界限粒度(Granularity)位。
;G=0 表示界限粒度为字节;
;G=1 表示界限粒度为4K 字节。
; 注意,界限粒度只对段界限有效,对段基地址无效,段基地址总是以字节为单位。
;
; (6) D: D位是一个很特殊的位,在描述可执行段、向下扩展数据段或由SS寄存器寻址的段(通常是堆栈段)的三种描述符中的意义各不相同。
; ⑴ 在描述可执行段的描述符中,D位决定了指令使用的地址及操作数所默认的大小。
;① D=1表示默认情况下指令使用32位地址及32位或8位操作数,这样的代码段也称为32位代码段;
;② D=0 表示默认情况下,使用16位地址及16位或8位操作数,这样的代码段也称为16位代码段,它与80286兼容。可以使用地址大小前缀和操作数大小前缀分别改变默认的地址或操作数的大小。
; ⑵ 在向下扩展数据段的描述符中,D位决定段的上部边界。
;① D=1表示段的上部界限为4G;
;② D=0表示段的上部界限为64K,这是为了与80286兼容。
; ⑶ 在描述由SS寄存器寻址的段描述符中,D位决定隐式的堆栈访问指令(如PUSH和POP指令)使用何种堆栈指针寄存器。
;① D=1表示使用32位堆栈指针寄存器ESP;
;② D=0表示使用16位堆栈指针寄存器SP,这与80286兼容。
;
; (7) AVL: 软件可利用位。80386对该位的使用未左规定,Intel公司也保证今后开发生产的处理器只要与80386兼容,就不会对该位的使用做任何定义或规定。
前面说过在保护模式下,Seg相当于一个索引,而这个索引就是选择子(Selector),结构如下: