Intel X86系列CPU寻址方式

来源:互联网 发布:excel更改坐标轴数据 编辑:程序博客网 时间:2024/05/21 10:12

Intel X86系列CPU寻址方式

1 历史上第一个微处理器芯片4004就是Intel制造的。

所谓X86系列,是指Inte从16位微处理器8086开始的整个CPU芯片系列。在X86系列中8086和8088是16位处理器,而从80386开始为32位处理器。
2 当我们说一个CPU是16位或者32位时,指的是处理器中“算术逻辑单元”ALU的宽度。
系统总线中的数据线部分,称为数据总线,通常与ALU具有相同的宽度。Intel在其16位的CPU8086中采用
1M字节地址空间,地址总线的宽度也就相应地确定了,那就是20位。这样,一个问题摆在了Intel的设计人员面前:虽然地址总线的宽度是20位,但CPU中ALU的宽度却只有16位,也就是说可直接加以运算的指针长度是16位的,也就是说可直接加以运算的指针的长度是16位的。如何来填补这个空隙呢?
Intel采用分段的方法。
Intel在8086CPU中设置了四个“段寄存器”:CS、DS、SS和ES,分别用于可执行代码(指令)、数据、堆栈和其他。每个段寄存器都是16位的,对应地址总线中的高16位。每条“访问内存”指令中的“内部地址”都是16位的,但是在送上地址总线之前都在CPU内部自动地与某个段寄存器中的内容相加,形成一个20位的实际地址。这样,就实现了从16位到20位实际地址的转换,或者“映射”。由于这种内存寻址方式缺乏对内存空间的包括,所以为了区别于后来出现的“保护模式”,就称为“实地址模式”。
针对8086的这种缺陷,Intel从80286开始实现其“保护模式”。
从8088/8086到80386完成了一次从比较原始的16位CPU到现代的32位CPU的飞跃,而80286则变成这次飞跃的一个中间步骤。从80386以后,Intel的CPU历经80486、Penitium、PentiumII等等型号,虽然在速度上提高了好几个量级,功能上也有不小的改进,但基本上属于同一种系统结构中的改进和加强,而并无重大的质的改变,所以统称为i386结构。
3 当一条访问内存指令发出一个内存地址时,CPU就可以这样来归纳出实际上应该放上数据总线的地址:
(1)根据指令的性质来确定应该使用哪一个段寄存器,例如转移指令中的地址在代码段,而取数指令中的地址在数据段。这一点与实地址模式相同。
(2)根据段寄存器的内容,找到相应的“地址段描述结构”。
(3)从地址段描述结构中得到基地址。
(4)将指令中发出的地址作为位移,与段描述结构中规定的段长度相比,看看是否越界。
(5)根据指令的性质和段描述符中的访问权限来确定是否越权。
(6)将指令中发出的地址作为位移,与基地址相加得出实际的物理地址。
4  80386的段式内存管理机制的实际实现:
首先,在80386CPU中增设了两个寄存器:一个是全局性的段描述表寄存器GDTR,另一个是局部性的段描述表寄存器LDTR,分别可以用来指向存储在内存中的一个段描述结构数组,或者称为段描述表。由于这两个寄存器是新增设的,不存在与原有的指令是否兼容的问题,访问这两个寄存器的专用指令便设计成“特权指令”。

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



GDTR或LDTR中的段描述表指针和段寄存器中给出的下标结合在一起,才决定了具体的段描述表项在内存中的什么地方,也可以理解成,将段寄存器内容的低3位屏蔽掉以后与GDTR或LDTR中的基地址相加得到描述表项的起始地址。(在整个系统中,全局描述符表GDT只有一张(一个处理器对应一个GDT),GDT可以被放在内存的任何位置,但CPU必须知道GDT的入口,也就是基地址放在哪里,Intel的设计者门提供了一个寄存器GDTR用来存放GDT的入口地址,程序员将GDT设定在内存中某个位置之后,可以通过LGDT指令将GDT的入口地址装入此寄存器,从此以后,CPU就根据此寄存器中的内容作为GDT的入口来访问GDT了。GDTR中存放的是GDT在内存中的基地址和其表长界限。)

5 段描述符表项

每个段描述符表项的大小是8字节,每个描述表项含有段的基地址和段的大小,再加上其他一些信息,其结构如下: 

                                  

其中B31~B24和B23~B16分别为基地址的bit16~bit23和bit24~bit31.而L19~L16和L15~L0则为段长度(limit)的bit16~bit19和bit0~bit15.其中DPL是个2位的位段,而type是一个4位的位段。它们所在的整个字节分解如下图:

                                             

为什么基地址的高8位和低24位不连在一起?

最自然也最合理的解释就是:开始时Intel的意图是24位地址空间,后来又改成32位地址空间。这也可以从段长度字段也是拆成两节得到印证:当g标志位为1时,长度的单位为4KB,而段长度字段的低16位的容量是64K,所以一个段的最大可能长度为64K X 4K =256M,而这正是24位地址空间的大小。所以,可以看出,Intel起先想使用24位地址空间,不久又认识到应该用32位,但是80286已经发售了,于是就只好修修补补。

 6 每当一个段寄存器的内容改变时(通过MOV、POP等指令或发生中断等事件),CPU就把这段寄存器的新内容所决定的段描述项装入CPU内部的一个“影子”描述项。这样,CPU中有几个段寄存器就有几个影子描述项,所以也可以看作是对段寄存器的扩充。扩充后的段寄存器分成两部分,一部分是可见的(对程序而言),还与原来的段寄存器一样,另一部分是不可见的,就是用来存放影子描述项的空间,这一部分是专供CPU内部使用的。

7 在80386的段式内存管理的基础上,如果把每个段寄存器都指向同一个描述项,而在该描述项中则将基地址设成0,并将段长度设成最大,这样便形成一个从0开始覆盖整个32位地址空间的一个整段。由于基地址为0,此时的物理地址与逻辑地址相同,CPU放到总线上去的地址就是在指令中给出的地址。这样的地址有别于由“段寄存器/位移量”构成的层次式地址,所以Intel成其为平面(Flat)地址。Linux内核的源代码采用平面地址。

8 80386段式虚存管理

当一个段寄存器的内容改变时,CPU要根据新的段寄存器内容以及GDTR或LDTR的内容找到相应的段描述符项并将其装入CPU中。在此过程中,CPU会检查该描述项中的P标志位,如果P位0,表示该描述项所指向的那一段内容不在内存中(也就是说,在磁盘的某个地方),此时CPU会产生一次异常,而相应的服务程序便可以从磁盘交换区将这一段的内容读入内存中的某个地方,并据此设置描述项中的基地址,再将P标志设置成1。相应的,内存中暂时不用的存储段则可以写入磁盘,并将其描述项中P标志位改为0。

对段式内存管理的支持只是i386保护模式的一个组成部分。如果没有系统状态和用户状态的分离,以及特权指令(只允许在系统状态下使用)的设立,那么尽管有了前述的段式内存管理,也还不能起到保护的效果。例如用来装入和存储GDTR和LDTR的指令LGDT/LLDT和SGDT/SLDT等都是特权指令。正是由于这些指令都只能在系统状态使用(也就是在操作系统内核中使用),才使得用户程序不但不能改变GDTR和LDTR的内容,还因为既无法确知其段描述表在内存中的位置,又无法访问其段描述符表所在的空间,从而无法通过修改段描述项来打破系统的保护机制。

80386并不只是像一般CPU通常所做的那样,划分出系统状态和用户状态,而是划分成四个特权级别,其中0级最高,3级最低。每一条指令也有其适用级别,如前述的LGDT,只有在0级的状态下才能使用。通常,用户的应用程序都是3级。一般程序的当前运行级别由其代码段的局部描述项dpl字段决定。当然,每个描述项中的dpl字段都是在0级状态下由内核设定的。而全局段描述的dpl字段,则又有所不同,它是表示所需的级别。


0 0
原创粉丝点击