linux kernel learn1

来源:互联网 发布:淘宝买家信誉在哪里看 编辑:程序博客网 时间:2024/05/22 17:01

Intel X86 CPU 系列的寻址方式

我们说一个CPU是16 位或者32位时,指的是处理器中“逻辑算术单元”(ALU)的宽度。系统总线中的数据线部分,称为“数据总线”,通常与ALU 具有相同的宽度(但有例外)。最自然的地址总线宽度是与数据总线一致。因为从程序设计角度来说,一个地址,也就是一个指针,最好与一个整数的长度一致。

在16位CPU 8086中采用1M字节的内存地址空间,地址总线的宽度也就相应的确定了,即20位。如何将地址总线的20位映射成可以直接被CPU加以运算的16位地址空间,就成为一个问题:现在看来,当时设计人员采用了一种不失巧妙的方法, 即分段的方法。

Intel在8086CPU中设置了四个“段寄存器”:CS、DS、SS和ES,分别用于可执行代码即指令、数据、堆栈和其他。每个段寄存器都是16位的,对应于地址总线中的高16位。每条“访内”中的“内部地址”都是16位的,但是在送上地址总线之前都在CPU内部自动地与某个段寄存器中的内容相加,形成一个20位的实际地址。这样,就实现了从16位内部地址到20位实际地址的转换,或者“映射”。注意:段寄存器中的内容对应于20位地址总线中的高16位,所以在相加时实际上是拿内部地址中的高12位与段寄存器中的16位相加,而内部地址中的低4位保留不变。这个方法与操作系统理论中的“段式内存管理”相似,但并不完全一样,主要是没有地址空间的保护机制。由于8086的这种内存寻址方式缺乏对内存空间的保护,所以为了区别后来出现的“保护模式”,就称为“实地址模式”。

Intel选择了在段寄存器的基础之上构筑保护模式的构思,并且保留了段寄存器为16位(这样才可以利用原有的四个段寄存器),但是却又增添了两个段寄存器FS和GS。为了实现保护模式,光是用段寄存器来确定一个基地址是不够的,至少还得要有一个地址段的长度,并且还需要一些其他信息,如访问权限之类。所以这里需要的是一个数据结构,使其从一个单纯的基地址(变相的基地址)变成指向这样一个数据结构的指针。这样,当一条访问内存指令发出一个内存地址时,CPU就可以这样来归纳出实际上应该放上数据总线的地址:

1.根据指令的性质来确定该使用哪一个段寄存器,例如转移指令中的地址在代码段,而取数指令中的地址在数据段。这一点与实地址模式相同。

2.根据段寄存器的内容,找到相应的“段地址描述结构”

3.从段地址描述结构中得到基地址。

4.将指令中发出的地址作为位移,与段描述结构中规定的段长度相比,看看是否越界。

5.根据指令性质和段描述结构中的访问权限来确定是否越权。

6.将指令中发出的地址作为位移,与基地址相加而得出实际的“物理地址”。

80386 的段式内存管理机制:

首先,在80386CPU 中增设两个寄存器:一个是全局性的段描述表寄存器GDTR(global descriptor table register),另一个是局部性的段描述表寄存器LDTR(local descriptor table register)。分别可以用来指向存储在内存中的一个段描述结构数组,或者称为段描述表。访问这两个寄存器的指令被设计成“特权指令”。

在此基础上,段寄存器的高13位(低3位另做他用)用作访问段描述表中具体描述结构的index。如下图所示:


GDTR或者LDTR中的段描述表指针和段寄存器中给出的下标结合在一起,才决定了具体的段描述表项在内存中的什么地方,换句话说,将段寄存器内容的低3位屏蔽掉以后与GDTR或LDTR中的基地址相加得到描述表项的起始地址。每个段描述表项的大小是8个字节,每个描述表项含有段的基地址和段的大小,再加上一些其他信息,其结构如图所示:


结构中的B31~B24和B23~B16分别是基地址的bit16~bit23和bit24~bit31。而L19~L16和L15~L0则为段长度的bit0~bit15和bit16~bit19。其中DPL是两个位的为段,而type是一个4位的位段。

0 0