引言:借助于简单的例子,全面讲了CPU如何工作的,需要慢慢消化的一篇讲解。
CPU是Center Processing Unit(中央处理器)的缩写,是计算机的大脑,一旦了解了它的运作,也就理解了计算机是如何工作的。
6502CPU内部平面示意图 (6502CPU曾用于Apple2个人电脑及Commodore64)
CPU内部有很多连线,这些连线时刻都在传递信号,每个CPU都有一根电压以固定频率变化的信号线,它为了保证CPU各个部件的同步工作,这根线叫做时钟信号线,现代CPU的频率都是以GHz来计算的,G表示十亿,Hz表示每秒的次数,所以现代CPU的时钟频率都在几十亿Hz。这样高的速度使得CPU能够快速的完成非常复杂的工作。然而,在每个时钟周期内,CPU所做的事情却是非常简单的。
我们现在的CPU可能是由Intel或者AMD这样的公司生产的,接下来我们将以Scott CPU为例讲述CUP的大概工作过程。这款Scott CPU并不是真实存在的,而是John Scott在他的《But How Do It Know ?》这本书上设计的一款理论上的CPU,在不涉及非常多技术细节的情况下详细地讲解了CPU的每个部件。有兴趣可以买这本书来仔细读读。
如下图,可以看到在CPU的底部有许多突出的管脚,CPU通过这些管脚和外界交换信息。CPU安装在我们所熟知的主板上,主板可以让计算机的所有部件连接在一起。
主板和翻开的CPU示意图
主板右侧区域是RAM(也就是内存)。RAM是Random Access Memory(随机访问存储器)的简称,它用于存储计算机运行时所需要处理的所有数据。让我们通过观察CPU和内存如何相互配合,来了解一下内存。
内存包含一系列的地址,在每个地址处都存储着一块数据,CPU通常是一个一个按顺序地从内存里取数据,然而,如果程序需要CPU打断执行顺序,它也可以做到(随机存取),这也是为什么内存RAM也叫做随机访问存储器的原因,因为数据可以被随机访问,即使正常情况下是按顺序访问的。
当计算机运行一个程序时,它会首先发送一个内存地址给内存,然后开始载入这段程序。内存地址是一个只包含0和1的二进制数值,0和1代表对应地址线上电平的高低。然而,只输入地址内存是并不会工作的,只有等CPU打开设置(set)信号线或使能(enable)信号线时才会。如果使能信号线打开,内存会自动把对应内存地址上的任何数据都传送给CPU。这样CPU就可以处理该数据。一旦CPU结束处理该数据,它就会发送另一个地址给内存,然后打开使能信号线,从内存获取另外一个数据。这个过程会在计算机中一遍又一遍地重复。
CPU从内存中取数据的示意图
如果CPU需要保存数据,那它会首先输出一个地址信号,然后输出数据内容,然后打开设置信号线,内存就会用新输入的值重写该地址处的数据。即上图的下面蓝色箭头改成反方向。
内存内部的数据又是些什么呢?它们看起来好像也是一堆0和1。内存中存储的数据是由很多部分组成的,其中最重要的一部分叫做指令。内存还可以存储数据,这些数据可能用于做加法,或者做比较,或者其它某种处理数据的方法。另外,内存中也可以存储地址,也就是在内存某个地址处的存储内容还是一个地址,这些地址可以用来完成很多事情。比如,你想把一个数据输出到外部设备上,那么你必须要知道该设备的地址,例如你是想把它输出到打印机上呢,还是输出到显示器上。内存上同样还可以保存字符。如果你想在屏幕上显示一些字符,你实际上是在内存中存储了一堆的0和1,每个字符都按照一张特定的字符编码表,以01组合的形式存储在内存中,字符的编码值可以是任意的,比如某人可以决定,这是一个小写的a,还是一个大写的G。这些就是内存中实际存储的内容。我们把内存看成一系列地址和数据的组合。
前面讲过,内存可以存储指令,而每个CPU都有一套只有自己能够执行的指令集。所以CPU有一个叫做LOAD(装载)的指令,用于将数据从内存装载到CPU。装载指令后可能有一个ADD(相加)指令,用于将数据相加。相加指令后可能会有一个STORE(存储)指令,用于将相加的结果保存到内存中。在多个装载指令后可能还会有一个COMPARE(比较)指令,用于比较两个数的大小。比较指令的一个非常重要的应用场合是条件跳转指令(JUMP IF)。之前讲过,CPU通常是一个一个按照顺序从内存中取数据的,但有时候CPU可能要跳转到一个并非连续的地址上去执行一些内存中的指令。条件跳转指令会在跳转之前检查条件是否成立(JUMP IF Condition),它使用比较指令的结果来判断条件是否成立。还有一类跳转指令称为无条件跳转指令(JUMP) 用于无条件跳转。最后还有输入与输出指令,它们可以用于将数据输入到外部设备,比如显示器,或者将数据从设备上输入,如键盘。这两个指令通常都会与某个地址一起使用,像我们之前描述的那样。
Instruction Set
至此我们已经知道,内存中包括指令,数据,地址,以及字符。那么我们通过这些指令,来完成一个猜数字的游戏程序。
猜数字游戏的CPU指令集
上面介绍了一个猜数字游戏的CPU指令集,现在让我们简单看看CPU内部是如何处理指令的。
CPU内部的第一个部件是控制器(Control Unit),它的地位就好比军队中的指挥官。控制器从内存中以指令的形式接收命令,然后把该指令进行分解,分解成各个部件的子命令。控制器的一个最重要的子部件是算术逻辑单元,或者简称ALU。ALU用于完成CPU内部的所有算术运算,比如加法运算,减法运算或者之前提到的比较运算。
· 算术逻辑有两个输入,我们以输入A和输入B来表示。假设现在有两个来自装载指令的数字,我们要把它们加起来,那么首先控制器要从内存中加载指令,然后根据指令确定ALU要进行何种操作,接着ALU执行该操作然后输入结果。有时候根据指令的类型,算术结果可以被忽略,比如要执行的是比较指令,那么ALU就不需要输出值,只需要告诉控制器两个数比较的结果。
ALU组件处理A+B的示意图
为此ALU使用两种叫标志位的东西,来帮助控制器在收到下一条指令,比如跳转指令时,CPU该如何进行操作。现在我们只假设在执行一个需要输出结果的操作。输出结果这样传输,从ALU单元出来的8根线将最终连接到一个寄存器。寄存器是一个非常简单的部件,它的作用就是用来暂存数据,和内存一样,只不过寄存器位于CPU内部。这使得它的工作速度非常快,并且在指令运行期间存储临时数据时非常有用。当ALU向寄存器输入数据时,数据并不会立即存入寄存器,而是要等控制器打开设置信号线时才会,此处的设置信号线和之前内存的是一样的。当设置信号线打开时,寄存器就会保存任何在它的输入信号线上的数据。
控制器打开set信号线,使ALU的结果保存到寄存器上
一旦我们在寄存器中存储了运算结果,要怎样才可以把数据取出来呢?要把数据从寄存器取出时,还需要从控制器上再连接一根使能信号线。只要打开使能信号线,寄存器就会把它存储的任何东西都输出。寄存器的输出信号线被连接到称之为CPU总线的地方。CPU总线就是主板上,一组用于连接计算机各个组件的信号线。
控制器打开使能信号线,使得ALU结果可以保存到总线上
总线上还有连着其它的寄存器,这些寄存器也有自己的使能设置信号线,里面可能存储着之前某个指令所保存的数据。如果控制器要在某个寄存器里保存数据,它会打开这个寄存器的设置信号线。然后数据就被保存到寄存器中,接下来CPU会关闭这个寄存器的使能信号线并清空总线。下图中顶部的四个寄存器都是用来在CPU运行过程中保存数据的,所以它们的输出信号线都直接连接在总线上。
总线上连接的其他寄存器示意图
由此,我们就已经可以通过打开和关闭某些信号线来在寄存器之间转移数据,这就是总线的好处,它可以非常轻易地在组件之间传输数据。总线的缺点就是它在同一时间只能传输一个数据。由于它的这个缺点,ALU使用暂存寄存器来保存输入B。
当控制器要处理一个和ALU相关的指令时,它会先把输入B保存在暂存寄存器中。暂存寄存器可以不需要使能信号线。因为它只把数据输出给ALU,不会与其它寄存器产生冲突。ALU的另一个输入来自于总线,控制器会打开另一个寄存器的使能信号线,于是这个寄存器的数据就变成了ALU的输入A。这个数据会在ALU完成运算之前一直存在总线上。现在ALU的两个输入都已经具备,ALU就可以执行运算操作了。
ALU准备好两个输入A和B的示意图
之前我们看到,通过从内存中装载指令,控制器可以知道让ALU执行何种操作。指令本身保存在另外一个叫做指令寄存器的寄存器中。来自总线的输入不会影响到指令寄存器,因为我们已经按照之前的步骤,将指令保存到寄存器中了。这个寄存器和暂存寄存器一样不需要使能信号线,因为它只把数据输出到控制器。基于指令寄存器中的内容,控制器可以指示ALU进行何种类型的操作。
底部加入指令寄存器的位示意图
假设我们要执行一个比较指令,我们并不关心比较运算输出的结果,只想知道两个数比较之后的大小关系。因此我们要使用到之前提到的标志位(Flags)。每个标志位都是一根信号线,它的打开或者关闭都是根据某些条件是否满足来决定的。在Scott-CPU内部有四根这样的信号线,我们来看看其中两根是如何工作的。
“A更大”这个标志位会在输入A大于B时打开,如果A等于B,那么“相等”这个标志位会打开。如果这两个标志位都是关闭状态,则说明B大于A。
标志位的信号线示意图1
当比较指令结束时,我们仍需要在下一个指令中使用标志位,所以我们把标志位保存到一个只有四位的寄存器中,每个位保存一个标志位信息。一旦这些标志被设置(保存)到寄存器中,比较指令也就结束了,CPU可以从内存中去下一条指令。
标志位寄存器工作示意图1
通常比较指令之后都会跟随一个条件跳转指令(JUMP IF),这种“比较-条件跳转”指令组合在计算机编程中是非常常见的。任何时候,当计算机程序包含不止一条执行路线时,程序都会包含比较-条件跳转指令来决定走哪条路线。现在我们搞定了比较指令,并且已经把比较结果保存到了标志位寄存器中,我们需要告诉内存我们已经准备好接收下一个数据了,这个例子中,下一个数据是一条指令。
在CPU中,另外一个非常重要的寄存器是指令地址寄存器。CPU通过这个寄存器来获取下一条要执行指令的内存地址。当CPU准备好接收下一条指令时,它会把指令地址寄存器使能并输出到总线上,最终指令地址会传递到内存,但并不直接传递过去的,在这中间还有一个寄存器,叫做内存地址寄存器(前面的叫做指令地址寄存器),它的唯一作用就是告诉内存CPU取数据的地址是多少。因为CPU不光是只从内存里面取指令,还有可能是数据。一旦指令地址被设置到内存地址寄存器中,该地址就会立刻传递到内存,因为内存地址寄存器并没有使能信号,然后控制器打开内存的使能信号线,内存就会自动把该地址的内容输出到总线。这个例子中该地址内从是一条指令。接下来这条指令会保存到指令寄存器中,然后控制器会处理它。
指令地址寄存器示意图
该例子是一条条件相等跳转指令,用于检测相等标志位是否被设置。它通过把自己的某位,以及相等标志位,一起输入到一个与门进行逻辑与操作。如果与门的两个输入都是打开的,则与门的输出也是打开的。这个输出最终会触发跳转动作。这个跳转动作会最终从内存中取下一个数据,这个数据刚好就是要跳转的地址,并且会把这个地址保存到指令寄存器中。
跳转指令工作示意图
当条件相等跳转指令结束时,CPU就会从这个地址开始执行新的指令。从这个地址开始的指令可能是向屏幕输出“你猜对啦”之类的文字信息。因为我们通过比较指令已经知道用户猜对了。
最后CPU还有四根信号线用来控制外围设备,例如显示器,键盘之类的。
现在关于Scott-CPU的结构图已经基本完整了。数据在CPU内部通过总线进行传输,根据数据的不同用途保存在不同的寄存器中。前面介绍过的每个指令在Scott-CPU 内部处理时间大概是6个时钟周期。现代CPU可以在每个时钟周期处理多条指令。现代CPU比Scott-CPU 要复杂的多,但做的基本工作是差不多的。
下图是信号线和芯片管脚的连接。最右侧是内存的设置/使能信号线。顶部是内存地址线,底部是数据线,数据线既连内存,也连外部设备。左侧是输入/输出控制线,
-》
CPU内部(左),和缩小的CPU (右)
CPU和其它计算机部件的连接示意图
通过使用左侧的接口,我们可以插入显示器的键盘,每个端口都有一个地址,这个地址用于在CPU输入输出指令中指定设备地址,这台计算机中,端口地址是通过数据总线来传输的,因为地址总线只留给内存使用。
主机中最后一个我们需要关注的组件:硬盘。一旦关闭电源,所有在计算机内存中保存的数据都会丢失。这时就需要一个可以永久保存数据的方法,为此我们可以使用硬盘。
计算机硬盘示意图
硬盘内部有一个旋转的表面覆盖有磁性存储材料的金属碟片和一个悬浮的金属臂,金属臂可以移动到盘片的不同部位,这样就可以读写相应位置的数据。碟片和金属臂通常移动速度是极快的,但是再快也赶不上CPU处理数据的速度。因为这个原因,CPU在处理磁盘数据时,通常都要把它们先读取到内存。
把硬盘放回主机箱并缩小显示如下,现在我们可以看到刚才运行的程序,并且程序提示用户猜测结果正确。这就是计算机大概是如何工作的了。
来自视频: