分段机制
x86处理器提供了分段机制和分页机制两种内存管理模式。如果要进入保护模式,就必须要启动分段机制(分页机制不是必须的)。
分段机制将内存划分为若干个段,每一个段都由段基址、段界限和段属性构成。由一个段描述符表(可以理解为一个数组)描述所有段的信息。段描述符表可以是全局的也可以是局部的。
简化来说,程序首先将对应的段选择子(数组索引)加载到段寄存器中。然后在执行程序时,根据指令的内容确定应该使用的段寄存器,然后根据段寄存器的段选择子再确定最终要使用的段描述符。结合段描述符中包含的信息加上指令自身的地址构造出实际的线性地址(什么是线性地址呢?这个在lab2中会介绍)。最终将物理地址送到地址总线上,在物理内存中进行寻址,取回相应的数据。
全局描述符表寄存器
x86处理器提供了专门的全局描述符表寄存器GDTR(Global Description Table Register)用于保存全局描述符表的表基址和表限长。GDTR由两个字节的表限长(limit)和4个字节的表基址(base)构成。表基址制定了全局描述符表的起始地址,表限长确定了全局描述符表的大小,结构体描述如下:
struct gdtr {
u16 limite;
u32 base;
} __attribute__ ((packed));
机器刚刚加电,或者处理器复位后,表基址被默认设置为0,而表限长默认设置为0xffff。在保护模式初始化的过程中,必须给GDTR加载新的值。可以使用lgdt指令为GDTR加载新的值。
段选择子
段选择子(2Bytes)用于选择特定的描述符表以及表中特定的描述符。段选择子一般都是被放置于段寄存器中,段选择子由13位的索引、1位的表指示位以及2位的请求特权级三部分组成。其中索引指定了描述符;表指示位选择应该访问的描述符表-0代表全局描述符表,1代表局部描述符表;请求特权级用于段级的保护机制,自0到4分别代表ring 0 到ring 3。结构体如下:
struct selector {
u16 index:13;
u16 ti:1;
u16 rpl:2;
}
段描述符
段描述符(8Bytes)是段描述符表这个"数组"的"元素"。结构体描述如下:
struct gdtdesc {
u16 lim0_15;
u16 base0_15;
u8 base16_23;
u8 acces;
u8 lim16_19:4;
u8 other:4;
u8 base24_31;
} __attribute__ ((packed));
总共包含32位的段基址、20位的段界限、12位的类型。
段基址确定了段的起始地址。段界限规定了段的大小。类型用于区别不同类型的描述符(包括描述符特权级、段存在位、已访问位等)