x86 平台:开放、统一、兼容。
CPU(Central Processing Unit,中央处理器) 是计算机中最核心的单元,负责数据的计算。CPU 通过 总线(Bus) 与其他设备相连,在这些设备中,最重要的是 内存(Memory)。因为单靠 CPU 是没办法完成计算任务的,很多复杂的计算任务都需要将中间结果保存下来,然后基于中间结果进行进一步的计算。CPU 本身没办法保存这么多中间结果,这就要依赖内存了。

CPU 和内存是完成计算任务的核心组件,那他们是如何配合完成工作的呢?
CPU 实际上是由三个部分组成:
进程一旦运行,比如图中两个进程 A 和 B,会有独立的内存空间,互相隔离,程序会分别加载到进程 A 和进程 B 的内存空间里面,形成各自的代码段(实际上更加复杂)。

CPU 的控制单元里面,有一个 指令指针寄存器,它里面存放的是下一条指令在内存中的地址。控制单元会不停地将代码段的指令拿进来,先放入指令寄存器。
当前的指令分两部分:
要执行这条指令,就要把第一部分交给运算单元,第二部分交给数据单元。数据单元 根据数据的地址,从数据段里读到数据寄存器里,就可以参与运算了。运算单元 做完运算,产生的结果会暂存在数据单元的数据寄存器里。最终,会有指令将数据写回内存中的数据段。
CPU 和内存来来回回传数据,靠的都是总线。其实总线上主要有两类数据,一个是地址数据,也就是我想拿内存中哪个位置的数据,这类总线叫 地址总线(Address Bus);另一类是真正的数据,这类总线叫 数据总线(Data Bus)。
地址总线的位数,决定了能访问的地址范围到底有多广。 例如只有两位,那 CPU 就只能认 00,01,10,11 四个位置,超过四个位置,就区分不出来了。位数越多,能够访问的位置就越多,能管理的内存的范围也就越广。
数据总线的位数,决定了一次能拿多少个数据进来。 例如只有两位,那 CPU 一次只能从内存拿两位数。要想拿八位,就要拿四次。位数越多,一次拿的数据就越多,访问速度也就越快。
我们来看 x86 中最经典的一款处理器,8086 处理器。虽然它已经很老了,但是咱们现在操作系统中的很多特性都和它有关,并且一直保持兼容。

为了暂存数据,8086 处理器内部有 8 个 16 位的通用寄存器,也就是刚才说的 CPU 内部的数据单元,分别是 AX、BX、CX、DX、SP、BP、SI、DI。这些寄存器主要用于在计算过程中暂存数据。
这些寄存器比较灵活,其中 AX、BX、CX、DX 可以分成两个 8 位的寄存器来使用,分别是 AH、AL、BH、BL、CH、CL、DH、DL,其中 H 就是 High(高位),L 就是 Low(低位)的意思。
IP 寄存器 就是 指令指针寄存器(Instruction Pointer Register),指向代码段中下一条指令的位置。CPU 会根据它来不断地将指令从内存的代码段中,加载到 CPU 的指令队列中,然后交给运算单元去执行。
每个进程都分代码段和数据段,为了指向不同进程的地址空间,有 4 个 16 位的段寄存器,分别是 CS、DS、SS、ES。其中,CS 就是 代码段寄存器(Code Segment Register),通过它可以找到代码在内存中的位置;DS 是 数据段的寄存器(Data Segment Register),通过它可以找到数据在内存中的位置。SS 是 栈寄存器(Stack Register)。
对于一个段,有一个起始的地址,而段内的具体位置,称为 偏移量(Offset)。在 CS 和 DS 中都存放着一个段的起始地址。代码段的偏移量在 IP 寄存器中,数据段的偏移量会放在通用寄存器中。
这时候问题来了,CS 和 DS 都是 16 位的,也就是说,起始地址都是 16 位的,IP 寄存器和通用寄存器都是 16 位的,偏移量也是 16 位的,但是 8086 的地址总线地址是 20 位。怎么凑够这 20 位呢?方法就是“起始地址 *16+ 偏移量”,也就是把 CS 和 DS 中的值左移 4 位,变成 20 位的,加上 16 位的偏移量,这样就可以得到最终 20 位的数据地址。
不展开,可看csapp,后续再填坑。
首先,通用寄存器有扩展,可以将 8 个 16 位的扩展到 8 个 32 位的,但是依然可以保留 16 位的和 8 位的使用方式。其中,指向下一条指令的指令指针寄存器 IP,就会扩展成 32 位的,同样也兼容 16 位的。

而改动比较大,有点不兼容的就是 段寄存器(Segment Register)。CS、SS、DS、ES 仍然是 16 位的,但是不再是段的起始地址。段的起始地址放在内存的某个地方。这个地方是一个表格,表格中的一项一项是 段描述符(Segment Descriptor)。这里面才是真正的段的起始地址。而段寄存器里面保存的是在这个表格中的哪一项,称为 选择子(Selector)。
这样,将一个从段寄存器直接拿到的段起始地址,就变成了 先间接地从段寄存器找到表格中的一项,再从表格中的一项中拿到段起始地址。
因而到了 32 位的系统架构下,我们将前一种模式称为 实模式(Real Pattern),后一种模式称为 保护模式(Protected Pattern)。当系统刚刚启动的时候,CPU 是处于实模式的,这个时候和原来的模式是兼容的。