处理器架构
处理器架构或者处理器编程架构,是指一整套的硬件架构以及与之相适应的工作状态
回顾8086处理器
8086处理器有20根地址线,可以寻址1MB内存,但处理器内部的寄存器只有16位,也就是数据线是16根,只能处理16位的数据
我们没法用16位的寄存器去访问1MB的内存,简单来说就是无法用16位表示任意20位的物理地址
为此诞生了内存分段模型,也就是通过段地址:偏移地址的方式访问内存,其中段地址与偏移地址都是16位即可表示的,只需将段地址左移4位再加上偏移地址就能表示20位物理地址了
但其中也是有限制的,当我们将段地址左移4位,这意味着段地址的低4位都是0,也就是说段地址必须是16位对齐的,且因为偏移地址也是16位,这意味着每个段最大是64KB。还有一个问题,段地址左移这种方式使得段地址的有效位仅有16位,而每个段最大又只有64KB,这使得我们只能访问低端1MB的内存,即使增加地址线也无济于事
这种工作模式称为实模式,在该模式下我们能够通过段地址:偏移地址的方式随意的访问内存,即使是未打算分配给我们的,我们仍能访问。而这也会造成安全问题
介绍80286处理器和80386处理器
80286处理器
80286处理器也是一款16位的处理器,大部分的寄存器和8086处理器一样
80286处理器有24根地址线,可以寻址16MB的内存;由16根数据线,能够处理16位的数据,在实模式下仍是需要分段访问内存
80286和8086不同的地方在于,80286处理器有保护模式,在保护模式下,段寄存器中保存的不在是段地址,而是段选择子,真正的段地址位于段寄存器的描述符高速缓存中,是24位的
简单来说,80286处理器访问内存时,不在需要将段地址左移,这使得段能够位于16MB内存空间的任意位置,不在限于低端1MB范围内
但80286因为寄存器只有16位,只能提供16位偏移地址,这也使得段的长度还是不超过64KB,即使是在保护模式下也一样,大限制了80286处理器的应用
80386处理器
80386处理器是一款32位处理器,地址线和数据线都是32根,这意味这能够寻址4GB的内存和处理32位的数据
换个角度想,寄存器是32位,也就说明能提供偏移地址,也就是每个段最大不超过4GB,这和寻址范围一样大了。也就是说只需一个段就能访问任意位置内存
而且,32位处理器兼容实模式,可以运行实模式下的8086程序。在刚加电时,处理器自动处于实模式下,相当于一个快速的8086处理器,经过一番设置后才会进入保护模式
实模式与保护模式
实模式下的内存访问
-
地址空间:
实模式使用20位地址总线,最大支持1MB的内存空间(从0x00000到0xFFFFF) -
段和偏移量:
实模式下的内存访问使用段寄存器和偏移量的组合来计算实际的物理地址。物理地址的计算公式为:物理地址 = (段寄存器值 × 16) + 偏移量- 例如,如果段寄存器(如CS寄存器)设置为0x1000,偏移量为0x1234,则实际物理地址为 (0x1000 × 16) + 0x1234 = 0x11234。
-
内存保护:
实模式没有内存保护机制,所有程序都可以访问整个1MB的内存空间。这意味着程序可以直接读写任何内存地址,可能导致程序间的冲突和系统不稳定 -
寻址限制:
实模式只能寻址到1MB的内存,如果需要访问更大的内存空间,必须切换到保护模式或使用特定的扩展技术
保护模式下的内存访问
-
地址空间:
保护模式通常使用32位地址总线(在64位操作系统中,支持更大的地址空间),可以支持更大的内存空间(例如4GB内存或更多)。 -
分段和分页:
- 分段:在保护模式下,内存被划分为多个段,每个段由段描述符定义。段描述符包含了段的基址、大小、访问权限等信息。通过段寄存器指定当前段,内存访问会根据段描述符的设置进行保护和验证
- 分页:分页机制将内存划分为4KB或2MB的页,通过页表来进行虚拟地址到物理地址的映射。分页提供了更高效的内存管理和虚拟内存支持
-
地址计算:
在保护模式下,虚拟地址通过段选择子和偏移量来进行计算。段选择子用于从全局描述符表(GDT)或本地描述符表(LDT)中获取段描述符 -
内存保护:
保护模式提供了内存保护机制。每个段和页都有访问权限(读、写、执行等),硬件会在程序试图访问不被允许的内存区域时触发异常。这可以防止程序之间互相干扰,提高系统的稳定性和安全性 -
虚拟内存:
保护模式支持虚拟内存技术,允许程序使用比物理内存更大的地址空间。虚拟地址由操作系统和硬件进行转换,程序运行在一个虚拟地址空间中,系统可以管理物理内存的使用和调度
总结
- 实模式:地址计算简单,但没有内存保护和扩展性,地址计算依赖于段寄存器和偏移量的组合。
- 保护模式:地址计算复杂,使用段描述符和页表来管理内存,提供内存保护、虚拟内存等先进特性,支持更大的地址空间和多任务处理
IA-32架构的基本执行环境
Intel 32位处理器架构简称 IA-32
寄存器的扩展
32位处理器在16位处理器的基础上扩展了这8个通用寄存器的长度是之达到32位。这里的 E 表示 extend 扩展的意思
需要注意的是,32位通用寄存器的高16位不可独立使用,低16位保持同16位处理器的兼容性
在32位模式下,对内存的访问从理论上来说不在需要分段,但IA-32架构的处理器是基于分段模型的,所以32位处理器还是以段为单位访问内存
而且传统的段寄存器保存的不再是16为段基地址而是段的选择子,即用于选择要访问的段,段寄存器还有一部分不可见部分叫做描述符高速缓冲器,存有段的基地址和各种访问属性,如下图所示
平坦模型
只分一个段,段的基地址是0x00000000,短的长度是4GB,在这种情况下,可以视为不分段,即平坦模型
逻辑地址、线性地址和物理地址
访问内存时,需要在程序中给出段地址和偏移量,因为分段是IA-32架构的基本特征之一
-
物理地址:实际的硬件地址,是内存芯片上的真实地址
-
逻辑地址:传统上段地址和偏移地址称为逻辑地址
- 有效地址:偏移地址叫做有效地址
- 寻址方式:在指令中给出有效地址的方式叫做寻址方式
-
线性地址:用来描述任务的地址空间,每个任务都拥有4GB的虚拟内存空间(平坦空间),叫做线性地址空间,该空间上的地址就叫做线性地址
- 多任务环境下,任务的创建需要分配内存空间,任务终止后要回收空间,分段模型下内存分配不定长,导致碎片化
- 分页功能将物理内存空间划分成逻辑上的页,页大小固定,不会使内存空间碎片化
页功能未开启,段部件产生的地址就是物理地址
页功能开启后,段部件产生的地址是线性地址,经过页部件转换成物理地址
现代处理器的结构和特点
流水线
处理器执行一条指令大致可分为取指令、译码和执行三个步骤
- 指令预取:早在8086处理器时代就有指令预取队列,当指令执行时,如果总线空闲(无访问内存的操作),就可以在指令执行的同时预取指令并提前译码
- 流水线技术:将一条指令的执行分解成若干个细小的步骤,并分配给相应的单元完成。各个单元的执行是对的、并行的。如下所示,时间上重叠
高速缓存
从处理器内部向外看,存储器依次是寄存器、内存和硬盘
-
寄存器:访问速度最快,因为使用了触发器,工作速度纳秒级别
-
内存:
- 静态存储器(SRAM):用触发器实现,速度快但成本高
- 动态存储器(DRAM):用电容和单个晶体管实现,由于电容需要定时刷新(动态的原因)导致速度慢(几十个纳秒)但成本低
-
硬盘:机电设备,是机械和电子的混合体,访问速度最慢,毫秒级别
-
高速缓存:处理器和内存之间的一个SRAM,容量小但速度快,缓解了处理器等待内存和硬盘这样的慢速设备的矛盾
- 基于局部性原理实现,简单来说就是将你最近使用的相邻的数据都加载进缓存,就像是一种预判
- 如果你访问的内容在高速缓存中,预判成功,速度就会快很多,这叫命中
- 不在缓存中,需要将其加载进缓存,这叫不中,而这等待加载进缓存的时间损失叫不中惩罚
- 基于局部性原理实现,简单来说就是将你最近使用的相邻的数据都加载进缓存,就像是一种预判
乱序执行
流水线技术的实现就是将指令拆分成更小的可独立执行的部分,这叫做拆分成微操作
比如说指令A要访问两个内存的内容,然后将他们相加,那就分成3步,从内存X中读数据,从内存Y中读数据,然后执行相加的动作;指令B是将内存X中的内容写入寄存器,分成2步,从内存X中读数据,将其写入寄存器中
现在假设指令B跟在指令A后,按照上面我们分成的小步骤,我们发现当我们执行指令A的从内存X中读数据这个小步骤后,其实指令B就已经可以执行了,但指令A还需访问数据。因为指令B在指令A后面,这样的执行没有按照顺序,所以叫乱序执行
寄存器重命名
假设我们要做两件互不相干的事情A和B,但汇编代码的实现都是用了eax寄存器
那么通过上面的乱序执行我们知道,逻辑上是按顺序的,但物理上处理器并不会老实的按顺序执行,也就是说寄存器的使用是先到先得的,不会等你用完了在交给后面的指令用
但是事情A和事情B是互不相干的,都将数据写入同一个寄存器,其中一个事件就不得不等待另一个事件结束在使用这个寄存器,否则就会产生意想不到的事
为了减少这部分时间损耗,处理器中其实还有一些临时寄存器,处理器在使用时可以将他们重命名,对于上述的情况就是变成有2个eax,一人一个,互不干扰,所有操作都完成后,临时寄存器中存储的最终结果会被写入真实的eax,这个过程叫引退
分支目标预测
事实上流水线技术仍有很多问题,比如条件转移指令,在程序的执行过程中,会根据执行结果进行跳转,也就是代码存在分支
流水线就是并行的执行指令,若是并行的指令错了,我们就得先清空流水线重新取指令,流水线越长,清空的代价越大。分支的预测也只能凭借概率,无法做到完全正确
处理器内部存在一个小容量的高速缓存器,叫做分支目标缓存器(Branch Target Buffer, BTB),就是用来预测分支
32位模式的指令系统
32位处理器的内存寻址方式
-
16位处理器的寻址方式,任意组合
-
32位处理器的内存寻址方式,任意组合
特别注意的是16位下不允许使用sp作为内存寻址方式的操作数,32位下可以用esp
操作数大小的指令前缀
- 指令前缀0x66:这个前缀(体现在机器码中)的作用是反转当前默认操作数大小;简单来说,32位模式下,加了该前缀的指令表示是16位的,16位模式下,加了该前缀的指令表示是32位的
- 伪指令bits:用于指明其后的指令应该被编译成16位或者32位
bits 16
或[bits 16]
bits 32
或[bits 32]