首先,实模式下存在一些问题:
- 实模式的用户程序可以破坏存储代码的内存区域,所以要添加个内存段类型属性来阻止这种行为
- 实模式下的用户程序和操作系统是同一级别的,所以要添加个特权级属性来区分用户程序和操作系统的地位
其次, 是一些访问内存段的必要属性条件
- 内存段是一片内存区域,访问内存就要提供段基址,所以要有段基址属性
- 为了限制程序访问内存地范围,还要对大小进行约束,所以要有段界限属性
一个段描述符只是用来定义(描述)一个内存段. 代码段要占用一个段描述符,数据段和栈段等, 多个内存段也要各自占用一个段描述符, 这些描述符放在哪里呢?答案是放在全局描述符表(Global Descriptor Table)里. 全局描述符表GDT相当于段描述符的数组, 数组的每个元素都是8字节的描述符.
段描述符的低 32 位分为两部分,前 16 位用来存储段的段界限的前 15 位,后 16 位用来存储段基址的 15 位。
主要的属性都在段描述符的高 32 位, 眼望去可不少呢,现在我开始逐 和大家介绍。
0~7位是段基址的 16 ~23,24 ~ 31 位是段基址的24 ~ 31 位,加上在段描述符低 32 位中的段基址0 ~ 15 位,这下 32 位基地址才算齐全了。
8 ~ 11 位是 type 字段 位,用来指定本描述符的类型。这里要提前说下段描述符的S字段了
一个段描述符,在 CPU 眼里分为两大类,要么描述的是系统段,要么描述的是数据段,这是由段描述符中的S位决定的,用它指示是否是系统段。在 CPU 眼里,凡是硬件运行需要用到的东西都可称之为系统,凡是软件(操作系统也属于软件, CPU 眼中,它与用户程序无区别〕需要的东西都称为数据,
无论是代码,还是数据,甚至包括栈,它们都作为硬件的输入,都是给硬件的数据而己,所以代码段在段描述符中也属于数据段(非系统段)。S为0时表示系统段, 为1时表示数据段。 type 字段是要和S段配合在 起才能确定段描述符的确切类型,只有 S字段的值确定后, type 宇段的值才有具体意义。
什么是系统段?各种称为“门”的结构便是系统段,也就是硬件系统需要的结构,非软件使用的,如
调用门、任务门。简而言之,门的意思就是入口,它通往一段程序。关于系统段这里咱们不再多说,目前
主要是关注 S为1时,非系统段的type子类型。
赶紧回来继续说 type 字段,该字段共4位,用于表示内存段或门的子类型。
那怎么找到全局描述符表?
CPU有一个专门的寄存器GDTR, 专门用来存储GDT的内存地址及大小. GDTR是个48位的寄存器.
0~15位表示界限值,因为只有16位,所以全局描述表只能有64KB大小, 每个描述符8字节, 所以GDT中最多可容纳2^16 / 8 = 2^13(8192) 个描述符表
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200921180212108.png#pic_center)
保护模式中的段描述符看起来有怪异又复杂, 但它本质上只是一段内存区域的"身份证"而已,尽管它不像实模式那样直接,即段的大小统一为64KB, 段基址代表内存的起始, 偏移地址代表段内偏移量, 但它同样也描述一段内存
段描述符有了, 描述符表有了,我们该如何使用它?段选择子
在保护模式下, 段寄存器中存入的是一种叫选择子的东西—selector
0~1位,用来存储RPL,即请求特权级
2位, TI(Table Indicator)位,用来指示选择子是在GDT中, 还是在LDT中
选择子的高13位3~15, 用来索引段描述符表中的段描述符的偏移地址
选择子的作用主要是用来确定段描述符表,确定描述符的目的,一是为了特权级,权限等安全考虑,最主要的还是要确定段的基地址.
CPU厂商建议每个任务的私有内存段都应该放在自己的段描述符表当中,该表就是局部描述符表(LDT, Local Descriptor Table). 即每个任务都有自己的LDT,随着任务切换, 也要有切换相应任务的LDT. LDT也位于内存中, 其地址需要被先加载到某个寄存器后, CPU才能使用LDT, 该寄存器是LDTR.
准备工作: LDT虽然是个表,但其实也是一片内存区域, 所以也需要用个描述符在GDT中先注册. 段描述符是需要用选择子去访问的. 故, lldt的指令格式 lldt 16位寄存器/16位内存
, 无论是内存还是寄存器.其内容一定是个选择子,该选择自用来在GDT中索引LDT的段描述符
LDTR包含32位的线性基地址字段和16位段界限字段, 在LDT被加载到LDTR寄存器后, 之后再去访问某个段时, 选择子的T1位若为1, 就会用该选择子的高13位在LDTR所指向的LDT中去索引相应的段描述符.