主要介绍段描述符,段选择子
在保护模式下,段是实现虚拟地址到线性地址转换的基础。在保护方下,每个段有三个参数:段基址,段界限,段属性。段基址规定了线性地址空间中段的开始地址,段基址长度为32位,所以任何一个段都可以从32位线性地址空间中的任何一个字节开始,这一点和实式方式不同,实式方式下要求段的边界必须被16整除。段界限规定段的大小,段界限用20位表示,而且段界限可以是字节或4K为单位,这个称为段的粒度。当段界限以字节为单位时,那么段的范围是1字节至1M字节;当段界限是以4K字节为单位时,那么段的范围是4K至4G。段的界限同时也是用来校验偏移地址的合法性,比如说段A的基址为00123456H,段界限为1000H,如果段界限以字节为单位,那么段的范围是00123456H-00124456H;如果段界限以4K字节为单位,那么段的范围是00123456H-00223456H。事实上,段的界限也可以用来校验偏移地址的合法性,上面的例子中界限为1000H,那么偏移地址的范围就是0-1000H,如果偏移地址不在这个范围内那就会引起异常。需要说明的是,数据段有点特殊,因为数据段的偏移范围不仅仅是由段界限来决定,还要由段的扩展方向(Extension Direction)来决定,因为要照顾到堆栈段(堆栈段是一种特殊的数据段,它是向低端地址扩展的),如果段界限为Limit,段的扩展方向为向高端地址扩展的话,那么我们可以断定它是一普通的数据段,0-Limit是有效的偏移范围,而Limit以上属于无效的偏移范围;如果段界限为Limit,段的扩展方向为向低端地址扩展的话,那么可以断定它是一堆栈段,此时0-Limit是无效的偏移范围,Limit以上则属于有效的偏移范围,正好和向高端地址扩展的普通数据段相反。除了堆栈段以外,其它的段均是自然向高端扩展。
段基址,段界限及段属性这三个参数在保护模式下用描述符来描述,每个描述符的长度为8个字节,每个段都有一个对应的描述符。在保护模式下有三种描述符:存储段描述符,系统段描述符,门描述符。
A.存储段描述符:存储段是指程序直接执行的代码段和数据段,存储段描述符是用来描述存储段的,也可以说是用来描述代码和数据段的,它的长度为8个字节,该描述符结构示意图:
第7字节 第6字节 第5字节 第4字节 第3字节 第2字节 第1字节 第0字节
|--------|------------------|-----------------------------|-----------------|
|段基址的| | | |
|高8位 |Segment Attributes| 段基址的低24位 | 段界限的低16位 |
| 24~31 | 段属性,占用两 | 0~23 | 0~15 |
| | 个字节 | | |
|--------|------------------|-----------------------------|-----------------|
| |
| |
_________| |_____________________________
| 15 14 13 12 11 8 7 6 5 3 0|
|---|---|---|---|-------------|---|--- -|---|------------|
| G | D |0 |AVL|段界限的高4位| P | DPL |DT | TYPE |
|---|---|---|---|--- ---------|---|-----|---|------------|
段基址和段界限都被安排在描述符的两个域中,主要是来看段的属性:
a.G(第15位),这是段界限粒度,即是说段界限到底是以字节为单还是以4K字节为单位。G=0表示段界限是字节,G=1表示段界限为4K字节。
b.D(第14位),D是一个很特殊的位,在描述可执行段,向低扩展数据段或者由SS寄存器寻址的段。在描述可执行段的描述符中,D位决定了指令使用的地址及操作数据默认的大小,D=1表示默认情况下使用32位地址及32位或8位操作数,这样的代码段称为32位代码段;D=0表示默认情况下使用16位地址及16位操作数或8位操作数,这样的代码段称为16位代码段;在向低扩展的数据段中,D=1表示段的上部界限为4G,D=0表示段的上部界限为64K;在描述由SS寄存器寻址的段中,该位决定使用隐式的堆栈访问指令使用何种堆栈指针寄存器。D=1表示使用32位堆栈指针寄存器ESP,D=0表示使用16位堆栈指针寄存器SP,隐式的堆栈访问指令指的是那些指令中没有明显对SP或ESP进行操作的指令,比如说PUSH,POP,PUSHA,POPA,PUSHAD,POPAD都属于隐式的堆栈访问指令。
c.0(第13位),这一位恒为0,为80386以后的处理器保留的。
d.AVL(第12位),软件可利用位,主要是为了保持和以后的处理兼容。
e.第11位到第8位是段界限的高4位。
f.P(第7位),存在位,P=1表示描述符对转换地址有效。P=0表示描述符对转换地址无效,如果使用该描述符将会引起异常。
g.DPL(Descriptor Privelege Level)描述符特权级,共2位,它规定了所述段的特权级别,用于特权检查,以决定是否能对该段进行访问。
h.DT(Descriptor Type)描述符的类型,DT=0表示存储段描述符,DT=0表示系统段描述符和门描述符。
i.TYPE,共4位,说明存储段的具体属性:
TYPE0:指示描述符是否被访问,用A标记,A=0表示描述符未被访问,A=1表示描述符已被访问。
TYPE1:根据TYPE3来确定。
TYPE2:根据TYPE3来确定。
TYPE3:指示描述符所描述的段是数据段还是代码段,用E标记。E=0表示是不可执行段,是数据段,对应的描述符也就是数据段描述符。E=1表示是可执行段,也就是代码段,对就的描述符也就是代码段描述符。
如果TYPE3=0,也就是说描述符是数据段描述符,那么TYPE1指示该数据段是否可写,用W标记。W=0表示对应的数据段不可写,只读。W=1表示对应的数据段可写。TYPE2则指示数据段的扩展方向,用ED标记。ED=0表示向高端扩展,ED=1表示向低端扩展。
如果TYPE3=1,也就是说描述符是代码段描述符,那么TYPE1指示该代码段是否可读,用符号R标记。R=0表示对应的代码段不可读,只能执行,R=1表示对应的代码可读可执行。TYPE2则指示所描述的代码段是否是一致代码段,用C表示。C=0表示代码段不是一致代码段,C=1表示是一致代码段。
TYPE3-TYPE0这四位可以列成一个表:
___________________________________________________________________________________
|0000 |只读 |
|_____|____________________________________________________________________________|
|0001 |只读,已访问 |
|_____|____________________________________________________________________________|
|0010 |可读,可写 |
|_____|____________________________________________________________________________|
|0011 |读写,已访问 |
|_____|____________________________________________________________________________|
|0100 |只读,向低扩展 |
|_____|____________________________________________________________________________|
|0101 |只读,向低扩展 |
|_____|____________________________________________________________________________|
|0110 |读/写,向低扩展 |
|_____|____________________________________________________________________________|
|0111 |读/写,向低扩展,已访问 |
|_____|____________________________________________________________________________|
|1000 |只执行 |
|_____|____________________________________________________________________________|
|1001 |只执行,已访问 |
|_____|____________________________________________________________________________|
|1010 |可执行,可读 |
|_____|____________________________________________________________________________|
|1011 |可执行,可读,已访问 |
|_____|____________________________________________________________________________|
|1100 |只执行,一致代码段 |
|_____|____________________________________________________________________________|
|1101 |只执行,一致代码段,已访问 |
|_____|____________________________________________________________________________|
|1110 |可执行,可读,一致代码段 |
|_____|____________________________________________________________________________|
|1111 |可执行,可读,一致代码段,已访问 |
|_____|____________________________________________________________________________|
存储段描述符的结构可以这样定义:
DESCRIPTOR STRUCT
Segment_LimitL16 DW 0;段界限的低16位
Segment_BaseL16 DW 0;段基址的低16位
Segment_BaseM8 DB 0;段基址的中间8位
Segment_BaseH8 DB 0;段基址的高8位
Segment_Attributes DW 0;段属性
DESCRIPTOR ENDS
一个任务有多个段,每个段都有一个描述符。因此在80386下,为了方便管理这些段描述符,将描述符组成一个线性表,称之为描述符表。在80386下有三种描述符表:GDT(Global Descriptor Table),LDT(Local Descriptor Table),IDT(Interrupt Descriptor Table)。在整个系统中全局描述符表GDT和中断描述符表只有一张,局部描述符表可以由若干张。每个描述符表都形成一个特殊的16位数据段,这样的特殊数据段最多可以有8192个描述符,具体使用哪一个段描述符,由段的选择子来确定。每个任务都有自已的局部描述符表LDT,它包含自已的代码段,数据段,堆栈段,也包含该任务使用的一些门描述符。随着任务的切换,LDT也跟着切换。GDT包含每一个任务都可能或可以访问的段的描述符,通常包含描述操作系统所用的代码段,数据段以及堆栈段的描述符,也包含描述任务LDT的描述符。在任务切换时,并不切换GDT。一个任务的整个虚拟地址空间可以分为相等的两半,一半空间的描述符在全局描述符表GDT中,一半空的描述符在局部描述符表LDT中。由于全局描述符表和局部描述符表都可以包含最多为8192个描述符,而每个描述符所描述的段的最大长度为4G,因此最大的虚拟地址空间为:8192*4G*2=64TB。
段选择子用来确定使用描述符表中的哪一个描述符。实式模式下逻辑地址由段地址*16再加上段内偏移地址;保护模式下虚拟地址空间由段选择子和段内偏移来确定,和实式模式比较,段选择子代替了段值,实际上通过段选择子就可以确定了段基址。选择子的高13位是描述符表中的索引号,用来确定描述符,因为是13位,所以说最多可以有2的13次方8192个描述符,索引号:0-8191。标记TI指示是从全局描述符中读取描述符还是从局部描述符表中读取描述符。TI=0指示是从全局描述符表中读取描述符,TI=1指示从局部描述符表读取描述符。RPL表示请求特权级,用于特权检查。假设段选择子为88H,则表示请求的特权级别是0,从全局描述表中读取描述表,描述符的索引号为11H。有一个特殊的选择子称为空选择子,它的Index=0(即高13位为0),TI=0,RPL则可以为任意值。当用空选择子对存储器进行访问,会出现异常。空选择子对应于全局描述表中的第0个描述符,因此全局描述符表中的第0个描述符总是不会被访问。如果TI=1,那么就不是空选择子,它指定的是当前局部描述符表中的第0个描述符。为了更快地从段选择子中获得段的基本信息(段基址,段界限,段属性),从80386开始为每个段寄存器在硬件上配备了段描述符高速缓冲存储器,对我们写程序的人来讲,它是不可编程的。有了这种高速缓冲寄存器后,每当将选择子装入段寄存器后,处理器将自动装入描述符表中相应的描述符,并将描述表的信息装入到高速缓冲寄存器,这样可以加快访问速度,以下是段选择子的结构示意图:
15________________________________________________________________3__2__1_____0
| |TI | RPL |
|________________________________________________________________|___|________|