硬件中的分段

1.1 段选择符和段寄存器

分段是一种朴素的内存管理机制,它将内存划分成以起始地址(Base)和长度(Limit)描述的块,这些内存块就被称为“段”。一个逻辑地址由两部分组成:一个段选择符和段内偏移量。段选择符是一个16位长的字段,格式如下图所示:

为了快速方便地找到段选择符,处理器提供段寄存器,段寄存器的唯一目的就是存放段选择符。这些寄存器称为cs,ss,ds,es,fs和gs。

1.2 段描述符

段描述符描述某个段的基地址、长度以及各种属性。段描述符放在全局描述符表(Global Descriptor Table,GDT)或局部描述符表(Local Descriptor Table,LDT)中。

系统中至少有一个GDT可以被所有进程共享。相对的,系统中可以有一个或多个LDT,可以被某个进程私有,也可以被多个进程共享。GDT仅仅是内存中的一个数据结构,可以把它看成一个数组,每个元素由基地址(Base)和长度(Limit)描述。LDT则是一个段,它需要一个段描述符来描述它。LDT的的段描述符存放在GDT中,当系统中有多个LDT时,GDT中就必须有对应数量的段描述符。

GDT在主存中的地址和大小存放在gdtr寄存器中,当前正在被使用的LDT的地址和大小放在ldtr寄存器中。

通过段选择符索引GDT/LDT的过程如下图:

1.3 快速访问段描述符

段寄存器仅仅存放段选择符。为了加速逻辑地址到线性地址的转换,80x86处理器提供一种非编程的寄存器,供6个可编程的段寄存器使用。每一个非编程的寄存器含有8个字节的段描述符,由相应的段寄存器中的段选择符来指定。每当一个段选择符被装入段寄存器时,相应的段描述符就由内存装入到对应的非编程CPU寄存器。从那时起,针对那个段的逻辑地址转换就可以不访问主存中的GDT或LDT,处理器只需要直接引用存放段描述符的CPU寄存器即可。仅当段寄存器的内容改变时,才有必要访问GDT或LDT,过程如下图:

1.4 分段单元

Linux中的分段

四个主要的Linux段的段描述符字段的值如下:

2.1 Linux GDT

在单处理器系统中只有一个GDT,而在多处理器系统中每个CPU对应一个GDT。

下图是GDT的布局示意图:

2.2 The Linux LDTs

大多数用户态下的Linux程序不使用局部描述符表,这样内核就定义了一个缺省的LDT供大多数进程共享。


参考资料:

  1. 《深入理解linux内核》
  2. 《系统虚拟化:原理与实现》