日期:2014-05-16  浏览次数:20736 次

Linux内存寻址之二:逻辑地址到虚拟地址的转换

1.段选择符和分段寄存器

            一个逻辑地址包括两部分:段标识符 和 段内相对偏移地址。段标识符是一个被叫做段选择符(selector)的16比特的域,而偏移地址是一个32比特的域。

           为了方便快速检索段选择符,处理器提供了6个分段寄存器(segmentation register)来缓存段选择符,它们是:cs,ss,ds,es,fs和gs. 虽然只有这6个寄存器,但程序可以复用同一个寄存器来实现不同的目的,只需要把该寄存器的内容保存到内存中,在随后需要的时候可以恢复它的内容。需要注意的是,cs、ss、ds有专门的用途。请看下面介绍:

        cs-----内存段寄存器,指向含有代码指令的段;cs寄存器还有一个重要的功能:用于区分用户模式和内核模式,它包含一个指定当前优先级别(CPL, current priviledge level)的的2比特的域。如果该域的值为0,表明优先级最高;如果该值为3,表明最低的优先级。 Linux只使用了0和3,用以区分内核模式和用户模式。

        ss-----堆栈段寄存器,指向包含当前程序栈的段;

        ds-----数据段寄存器,指向包含静态和全局数据的段。

       其它三个,即es,fs和gs,都是通用分段寄存器,可以指向任意类型的段。

2.段描述符

          每一个段都由一个8字节的段描述符来表示,它描述了段的特征。段描述符要么存储在全局描述符表(GDT)里,要么存储在本地描述符表(LDT)里.

        通常只定义了一个GDT。然而,每个进程都允许有自己的LDT,如果进程需要额外创建除了GDT里描述的之外的段。主存里GDT的地址和大小都包含在gdtr控制寄存器中,而当前正在使用的LDT的地址和大小则包含在ldtr控制寄存器中。

          BASE:段的第一个字节的线性地址。

          G:如果为0,则段的大小用字节表示。

          Limit:保存了段中最后一个存储单元的偏移值,因此与segment的长度、大小是绑定在一起的。如果G为0,则LIMIT的大小范围为1字节~1MB;反之, LIMIT大小范围为4KB~4GB.

          S:如果为0,表示为一个系统段(system segment);反之,为一个普通的数据段或代码段。系统段里保存了关键的数据结构,如LDT.

         Type:描述segment的类型。

          DPL:描述符优先级别。主要用于对segment的访问进行限制。

          P:描述段是否在内存中存在的标记。

 

3.对段描述符的快速访问的实现

       我们知道,逻辑地址由一个16比特的段选择符和一个32比特的偏移地址组成。同时,我们也知道,分段寄存器里只存储了段选择符。

       我们先接着第一节继续对段选择符进行分析。它的格式如下所示:

     Index:标记了GDT或LDT中段描述符的入口。由于段寄存器有8个字节长,它在GDT或LDT中的相对地址是这样来计算的:13个bit之长(如上图,比特3-15位)的index域值乘以8. 假设GDT位于0x00020000 (该值存放在gdtr控制寄存器中) 并且 段选择符的index域值为2,那么相应的段描述符的地址是这么来计算的:

     0x00020000 +(2 x8),0x00020010.

   TI: table indicator。TI=0,表示段描述符位于GDT中;TI=1,表示段描述符位于LDT中。

   RPL:Requestor Previlige Level请求者优先级。