保护模式的基础就是段描述符,分为全局描述符、局部描述符。
在16位实模式下,寻址方式为:段*16+偏移,而在32位保护模式下,段寄存器仍然是16位,但它是一个指向值,指向一个8字节的描述符,在描述符中确定了所要寻址的开始地址,以及这段内存的长度、权限、属性等。
全局描述符在使用时没有什么明显的区别,只不过全局描述符表一般只有一个,(嗯~!也许能定义多个,然后重新加载,在多个全局描述符集之间切换,有什么用呢?我没试过,有时间可以想一想!)
而局部描述符是全局描述中的一分子,只是一个对应关系,联系并不是很紧密!
全局描述符简称GDT,有专门的寄存器保存GDT开始的地址与GDT表的长度,所以要加载一个GDT表需要一个指向6个字节的内存,前两个字节为长度,后4个字节为基址。
lgdt [GDT_TABLE] ;<< 加载GDT
GDT_TABLE:
dw x ;<< GDT表长度
dd y ;<< GDT表开始的基址
描述符由8个字节组成。
高地址
byte7 段基址2,第31-24位
byte6 *
byte5 **
byte4 /
byte3 > 段基址1,第23-0位
byte2 /
byte1 /
> 段界限1,第15-0位
byte0 /
低地址
byte6 byte5
7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
G D/B 0 AVL 段界限2,第19-16位 P DPL S TYPE
G:颗粒度,0-段界限单位为字节,1-段界限单位为4K;
D/B:不同类型的段所表示的含不一样,可以理解为尺寸标志;=0为16位尺寸,=1为32位尺寸;
代码段D=0,默认为16位地址及16位或8位操作数;
代码段D=1,默认为32位地址及32位或8位操作数;
数据段B=0,表示段的上部界限为64KB;
数据段B=1,表示段的上部界限为4GB;
堆栈段B=0,使用16位堆栈指针SP;
堆栈段B=1,使用32位堆栈指针ESP;
AVL:自定义使用。操作系统或软件使用。
P:存在位,0-本段在内存中不存在;1-本段在内存中存在;
DPL:本段的特权级别,0~3
S:本段描述符的类型,0-系统段或门描述符,1-数据段或代码段描述符;
TYPE:段描述符类型
当S=0时,0-未定义,1-可用286TSS, 2-LDT, 3-忙的286TSS,4-286调用门,5-任务门,6-286中断门,7-286陷阱门,
8-未定义,9-可用的386TSS,A-未定义,B-忙的386TSS,C-386调用门,D-未定义,E-386中断门,F-386陷阱门;
当S=1时:
位数 =0时含义 =1时含义
0 未访问 已访问
1 只读/仅执行 可写/可执行可读取
2 - 向下扩展/一致代码段
3 数据段 代码段
Param Count:传递参数的个数。
堆栈必须是一个可读写的数据段。
所有数据段都是非一致性的。
要注意的是GDT描述符表的第一个描述符是全0的,作为开始。
在调用时是用一个16位字的序号来指示,这个字称为选择子,意思是选择哪个描述符,其中低3位为属性位。
0-1位是RPL,为调用请求权限(有关特权级另外讨论);
2位为局部描述符标志位,=0时为全局描述符,=1时为局部描述符。
所以选择子是除了低3位外的高13位确定描述符的偏移,从这里可以理解为什么描述符表第一个描述符一定要是空的了,即使写了也没有用,因为如果选择子是0,则这个选择子被判定无效了。
而LDT局部描述符则不需要将第一个描述符设为0,因为它有一个标志位。
局部描述符的选择子是在局部描述符寄存器中指定的局部描述表中去查找,原理同GDT。