分类: LINUX
2008-10-15 09:54:38
从80286 模型开始,Intel 微处理器以两种不同的方式执行地址转换,这两种方式分别称为实模式(real mode)和保护模式(protected mode)。我们将从下一节开始描述保护模式下的地址转换。实模式存在的主要原因是要维持处理器与早期模型兼容,并让操作系统自举(参阅附录一中针对实模式的简短描述)。
*段选择符和段寄存器
一个逻辑地址由两部分组成:一个段标识符和一个指定段内相对地址的偏移量。段标识符是一个16 位长的字段,称为段选择符(Segment Selector)如图2-2 所示,而偏移量是一个32 位长的字段。我们将在本章“快速访问段描述符”一节描述段选择符字段。
为了快速方便地找到段选择符,处理器提供段寄存器,段寄存器的唯一目的是存放段选择符。这些段寄存器称为cs,ss,ds,es,fs 和gs。尽管只有6 个段寄存器,但程序可以把同一个段寄存器用于不同的目的,方法是先将其值保存在内存中,用完后再恢复。
6 个寄存器中3 个有专门的用途:
cs 代码段寄存器,指向包含程序指令的段。
ss 栈段寄存器,指向包含当前程序栈的段。
ds 数据段寄存器,指向包含静态数据或者全局数据段。
其他3 个段寄存器作一般用途,可以指向任意的数据段。
cs 寄存器还有一个很重要的功能:它含有一个两位的字段,用以指明CPU 的当前特权级(Current Privilege Level,CPL)。值为0 代表最高优先级,而值为3 代表最低优先级。Linux 只用0 级和3 级,分别称之为内核态和用户态。
*段描述符
每个段由一个8 字节的段描述符(Segment Descriptor)表示,它描述了段的特征。段描述符放在全局描述符表(Global Descriptor Table ,GDT)或局部描述符表(Local Descriptor Table ,LDT)中。
通常只定义一个GDT,而每个进程除了存放在GDT 中的段之外如果还需要创建附加的段,就可以有自己的LDT。GDT在主存中的地址和大小存放在gdtr 控制寄存器中,当前正被使用的LDT 地址和大小放在ldtr 控制寄存器中。
图2-3 阐明了段描述符的格式;表2-1 解释了图中各个字段的含义
表2-1:段描述符字段
字段名 |
描述 |
Base |
包含段的首字节的线性地址 |
G |
粒度标志:如果该位清0,则段大小以字节为单位,否则以4096 字节的倍 |
Limit |
存放段中最后一个内存单元的偏移量,从而决定段的长度。如果G 被置为 |
S |
系统标志:如果它被清0,则这是一个系统段,存储诸如LDT 这种关键的 |
Type |
描述了段的类型特征和它的存取权限(请看表下面的描述) |
DPL |
描述符特权级(Descriptor Privilege Level)字段:用于限制对这个段的存 |
P |
Segment-Present 标志:等于0 表示段当前不在主存中。Linux 总是把这个 |
D 或B |
称为D 或B 的标志,取决于是代码段还是数据段。D 或B 的含义在两种情 |
AVL |
标志可以由操作系统使用,但是被Linux 忽略 |
有几种不同类型的段以及和它们对应的的段描述符。下面列出了Linux 中被广泛采用的类型:
代码段描述符:表示这个段描述符代表一个代码段,它可以放在GDT或LDT 中。该描述符置S 标志为1(非系统段)。
数据段描述符
表示这个段描述符代表一个数据段,它可以放在GDT或LDT 中。该描述符置S 标志为1。栈段是通过一般的数据段实现的。
任务状态段描述符(TSSD)
表示这个段描述符代表一个任务状态段(Task State Segment,TSS),也就是说这个段用于保存处理器寄存器的内容(参见第三章中的“任务状态段”一节)。它只能出现在GDT 中。根据相应的进程是否正在CPU 上运行,其Type 字段的值分别为11 或9。这个描述符的S 标志置为0。
*快速访问段描述符
我们回忆一下:逻辑地址由16 位段选择符和32 位偏移量组成,段寄存器仅仅存放段选择符。
为了加速逻辑地址到线性地址的转换,80x86 处理器提供一种附加的非编程的寄存器(一个不能被程序员所设置的寄存器), 供6 个可编程的段寄存器使用。每一个非编程的寄存器含有8 个字节的段描述符(在前一节已讲述),由相应的段寄存器中的段选择符来指定。每当一个段选择符被装入段寄存器时,相应的段描述符就由内存装入到对应的非编程CPU 寄存器。从那时起,针对那个段的逻辑地址转换就可以不访问主存中的GDT或LDT,处理器只需直接引用存放段描述符的CPU 寄存器即可。仅当段寄存器的内容改变时,才有必要访问GDT 或LDT(参见图2-4)。
表2-2 描述了任意段选择符所包含的3 个字段。
表2-2:段选择符字段
字段名 |
描述 |
index |
指定了放在GDT或LDT中的相应段描述符的入口(在下面将作进一步的讲述) |
TI |
TI((Table
Indicator)标志:指明段描述符是在GDT 中(TI=0)或在LDT |
RPL |
请求者特权级:当相应的段选择符装入到cs 寄存器中时指示出CPU
当前 |
由于一个段描述符是8 字节长,因此它在GDT 或LDT 内的相对地址是由段选择符的最高13 位的值乘以8 得到的。例如:如果GDT在0x00020000(这个值保存在gdtr寄存器中),且由段选择符所指定的索引号为2,那么相应的段描述符地址是0x00020000 + (2 × 8),或 0x00020010。
GDT的第一项总是设为0。这就确保空段选择符的逻辑地址会被认为是无效的,因此引起一个处理器异常。能够保存在GDT 中的段描述符的最大数目是8191,即213-1。
*分段单元
图2-5详细显示了一个逻辑地址是怎样转换成相应的线性地址的。分段单元(segmentationunit)执行以下操作:
. 先检查段选择符的TI字段,以决定段描述符保存在哪一个描述符表中。TI字段指明描述符是在GDT 中(在这种情况下,分段单元从gdtr 寄存器中得到GDT的线性基地址)还是在激活的LDT中(在这种情况下,分段单元从ldtr寄存器中得到LDT 的线性基地址)。
. 从段选择符的index 字段计算段描述符的地址,index 字段的值乘以8(一个段描述符的大小),这个结果与gdtr 或ldtr 寄存器中的内容相加。
. 把逻辑地址的偏移量与段描述符Base 字段的值相加就得到了线性地址。
请注意,有了与段寄存器相关的不可编程寄存器,只有当段寄存器的内容被改变时才需要执行前两个操作。