5. 计算模型与机器语言
最后更新于
最后更新于
在前面章节中我们深入底层,详细讨论了计算机的计算单元与存储单元的构造方式,这一节我们将视野提升一个抽象层次,来看一下通过这些组件构建起来的最基础的计算机模型,并简单讨论一下为该模型设计指令集所需要考虑的因素。
到目前为止,我们讨论的内容局限于两个芯片:
CPU: 计算芯片,前文提到的ALU、寄存器都是CPU的组成部分,除此之外CPU还至少需要一个控制组件(Control Unit),用来“指挥”CPU的工作
内存: 存储芯片,CPU 所执行的指令以及操作的数据都存储在内存中
CPU 与内存通过总线(Bus)进行通信,总线上包含三种信号:地址信号、数据信号、控制信号。如下图所示:
虽然该图很简单,但已经呈现出了一个最基本的计算模型:在 CU(Control Unit) 的控制下,CPU 反复地执行存放在内存中的指令,指令的执行效果就是将内存或寄存器中的数据扔进 ALU 做运算,再将结果写回内存或寄存器。至于CPU来回倒腾这些数据到底是为了什么,这是宏观的层面上所具备的意义,可能是在完成一个交易、可能是在模拟一个电子游戏、也可能是在播放一部电影。但不管是什么事情,在微观层面的体现都是这样。
如果读者熟悉图灵机模型,会发现上文所说的计算模型就是图灵机的一种映射。事实上如果我们再加上输入与输出设备,就是一个冯诺伊曼架构的计算机模型。
CPU 执行指令通常划分成多个阶段:取指、解码、执行、存储,一个完整的循环被称为 CPU 的一个执行周期。整个流程由 CU 主导,CPU 的各个组件会在一起密切配合,例如寄存器 PC(Program Counter) 总是保存着下一条指令的内存地址,PC 的内容由当前执行的指令决定;如果需要 ALU 参与运算,CU 在解码后需要将各种输入信号传给 ALU。这些逻辑需要非常复杂的硬件电路来支撑,但这里我们不再深入讨论硬件实现,而是将其看着是一个功能完善的整体。
在该模型下,我们需要如下几类指令:
算术、逻辑运算。该工作通过 ALU 来完成,输入、输出来自寄存器或者内存
数据传输。将数据在寄存器与内存之间传输
流程控制。如何获取并执行下一条指令,程序默认是顺序执行的,我们还要考虑如何完成条件判断以及完成程序跳转等
设计指令集时需要考虑如下因素:
内存模型 既然 CPU 的毕生精力都在倒腾内存中的数据,那么如何对内存进行寻址是指令集的重点。内存模型决定了寻址方式,以及能够寻址的内存空间大小。例如上文中提到的 Intel 8086 芯片,其为了在16位寄存器中支持20位的地址空间,采用的是分段寻址(Segmented Addressing)模式。
指令格式
寄存器种类 哪些寄存器是专用寄存器,哪些是通用寄存器。例如 Intel 8086 芯片中,为了支持分段寻址,专门设计了段寄存器(Segment Registers)与索引寄存器(Index Registers)。
其中最重要的是内存模型,内存模型会显著影响上层程序的设计,例如操作系统的实现。事实上内存地址的用途是多样化的,不仅仅局限在对内存芯片的寻址上,如果我们重新审视 CPU 与内存的关系,会发现 CPU 根本不知道存储芯片的存在,CPU 面对的只是一个个具备存储能力的地址而已,如果使用高级程序设计的词汇来描述,内存地址就是一个接口,而内存芯片是其中的一种实现;其它的实现例如 Memory-mapped IO 将内存地址映射成 I/O 设备,而 Memory-mapped File 将一段内存地址空间映射成磁盘上的文件的某一段内容,这样 CPU 就能够像操作内存一样操作 I/O 设备以及文件了。
指令集是 CPU 全部功能的描述,是面向 CPU 进行编程的唯一媒介,所以也叫着机器语言,机器语言是抽象层次最低的编程语言,用二进制表示,其中每个0与1都可以想象成某个精灵在拨动逻辑门中的电路开关,以便让电子信号以预设的轨迹流过整个电路。机器语言是硬件到软件的过渡,从更深刻的意义上看,机器语言刻画的是一个计算模型,而如何合理、高效地使用该计算模型来解决现实之中的问题,我们还需要专门的系统来进行管理与协调,并提供更高层次的抽象模型,这个系统就是操作系统。