Intel微处理器以两种不同的方式执行地址转换,这两种方式成为实模式(为了兼容早期模型)和保护模式。介绍保护模式下的地址转换:
逻辑地址由两个16位的地址分量构成,一个为段基值,另一个为偏移量。两个分量均为无符号数编码。
分段单元:
逻辑地址是怎样转换成相应的线性地址? ①先检查段选择符的TI字段,决定段描述符保存在哪一个描述表中(GDT或LDT)。
②从段选择符的index字段计算段描述符的地址,index字段的值*8(描述符大小),这个结果与gdtr或ldtr寄存器的内容相加。
③把逻辑地址的偏移量与段描述符base字段的值相加就得到了线性地址。
(各类专用名词在下做解释)
段选择符和段寄存器
段标识符:16位长的字段,称为段选择符(segment selector) ,作用是为了找到段描述符
索引号(index):指定了放在GDT或LDT中的相应段描述符入口,由于一个段描述符是8字节长,所以他在GDT或LDT内的相对地址是index*8
TI: 决定段描述符保存在哪一个描述表中,TI=0-->GDT,TI=1-->LDT。
RPL:请求者特权级,当相应的段描述符装入到cs寄存器中时指示出CPU当前的特权级;还可以用在访问数据段时有选择地削弱处理器的特权级。
***************************************************
例子:如果GDT在0x00020000,且由段选择符所指定的索引号为2,那么相应的段描述符地址是:0x00020000+(2*8)。
************************************************************************************
注:GDT的第一项总是设为0,确保空段选择符的逻辑地址会被认为是无效的,因此引起一个处理器异常。
为了方便快速找到段选择符,处理器提供段寄存器,段寄存器目的是为了存放段选择符。这些寄存器为cs,ss,ds,es,fs,gs,可以把同一个段寄存器用于不同的目的,先将值保存在内存,用完后恢复。
有3个有专门用途的寄存器:
cs:代码段寄存器,指向包含程序指令的段。还有一个两位的字段,指明CPU当前特权级,0代表最高,3代表最低,linux只用0级和3级(内核态和用户态)
ss:栈段寄存器,指向包含当前程序栈的段。
ds:数据段寄存器,指向包含静态数据或全局数据段。
段描述符
8字节长,描述段特征,段描述符放在全局描述表(GDT)或局部描述表(LDT)中。
(GDT和LDT加链接)
通常只定义一个GDT,而进程除了存放GDT的段之外如果还需要附加的段就可以有自己的LDT,GDT在主存中的地址和大小存放在gdtr控制寄存器中,LDT地址和大小放在ldtr控制寄存器中。
base:包含段的首字节的线性地址
G:粒度,清0,段大小以字节为单位,否则以4096字节的倍数计。
Limit:存放段中最后一个内存单元的偏移量,从而决定段的长度,如果G被置为0,则段的大小在1个字节~1M,否则4kb~4GB
S:系统标志,如果被清0,则是一个系统段,存储LDT这种关键的数据结构,否则它是一个普通的代码段或数据段。
Type:描述了段的类型特征和它的存取权限
DPL:描述特权级字段,限制对这个段的存取,dpl设置成0的段只有cpl为0才可以访问,dpl设为3任何cpl的值都可以访问。
P:等于0表示段当前不在主存中,linux总是把这个标志置1,因为它从来不把整个段交换到磁盘上。
D或B:代码是数据段还是代码段,段偏移量32位--》置1,段偏移量16位--》清0。
AVL标志:可以由操作系统使用,但是Linux忽略。
代码描述符,数据段描述符,任务状态段描述符,局部描述符表描述符。
快速访问段描述符
为了加速逻辑地址到线性地址转换,8086提供一种附加的非编程的寄存器,供6个可编程的段寄存器使用。每一个非编程的寄存器含有8个字节的段描述符,由相应的段寄存器中的段选择符来指定,每当一个段选择符被装入段寄存器,相应的段描述符由内存装入到对应的非编程CPU寄存器。针对那个段的逻辑地址转换就可以不访问主存中的GDT或LDT,直接引用存放段描述符的CPU寄存器。