17 建立数据通路(上):指令+运算=CPU
前面几讲里,我从两个不同的部分为你讲解了CPU的功能。
在“指令”部分,我为你讲解了计算机的“指令”是怎么运行的,也就是我们撰写的代码,是怎么变成一条条的机器能够理解的指令的,以及是按照什么样的顺序运行的。
在“计算”部分,我为你讲解了计算机的“计算”部分是怎么执行的,数据的二进制表示是怎么样的,我们执行的加法和乘法又是通过什么样的电路来实现的。
然而,光知道这两部分还不能算是真正揭开了CPU的秘密,只有把“指令”和“计算”这两部分功能连通起来,我们才能构成一个真正完整的CPU。这一讲,我们就在前面知识的基础上,来看一个完整的CPU是怎么运转起来的。
指令周期(Instruction Cycle)
前面讲计算机机器码的时候,我向你介绍过PC寄存器、指令寄存器,还介绍过MIPS体系结构的计算机所用到的R、I、J类指令。如果我们仔细看一看,可以发现,计算机每执行一条指令的过程,可以分解成这样几个步骤。
1.Fetch(取得指令),也就是从PC寄存器里找到对应的指令地址,根据指令地址从内存里把具体的指令,加载到指令寄存器中,然后把PC寄存器自增,好在未来执行下一条指令。
2.Decode(指令译码),也就是根据指令寄存器里面的指令,解析成要进行什么样的操作,是R、I、J中的哪一种指令,具体要操作哪些寄存器、数据或者内存地址。
3.Execute(执行指令),也就是实际运行对应的R、I、J这些特定的指令,进行算术逻辑操作、数据传输或者直接的地址跳转。
4.重复进行1~3的步骤。
这样的步骤,其实就是一个永不停歇的“Fetch - Decode - Execute”的循环,我们把这个循环称之为指令周期(Instruction Cycle)。
指令周期(Instruction Cycle)
在这个循环过程中,不同部分其实是由计算机中的不同组件完成的。不知道你还记不记得,我们在专栏一开始讲的计算机组成的五大组件?
在取指令的阶段,我们的指令是放在存储器里的,实际上,通过PC寄存器和指令寄存器取出指令的过程,是由控制器(Control Unit)操作的。指令的解码过程,也是由控制器进行的。一旦到了执行指令阶段,无论是进行算术操作、逻辑操作的R型指令,还是进行数据传输、条件分支的I型指令,都是由算术逻辑单元(ALU)操作的,也就是由运算器处理的。不过,如果是一个简单的无条件地址跳转,那么我们可以直接在控制器里面完成,不需要用到运算器。
不同步骤在不同组件之内完成
除了Instruction Cycle这个指令周期,在CPU里面我们还会提到另外两个常见的Cycle。一个叫Machine Cycle,机器周期或者CPU周期。CPU内部的操作速度很快,但是访问内存的速度却要慢很多。每一条指令都需要从内存里面加载而来,所以我们一般把从内存里面读取一条指令的最短时间,称为CPU周期。
还有一个是我们之前提过的Clock Cycle,也就是时钟周期以及我们机器的主频。一个CPU周期,通常会由几个时钟周期累积起来。一个CPU周期的时间,就是这几个Clock Cycle的总和。
对于一个指令周期来说,我们取出一条指令,然后执行它,至少需要两个CPU周期。取出指令至少需要一个CPU周期,执行至少也需要一个CPU周期,复杂的指令则需要更多的CPU周期。
三个周期(Cycle)之间的关系
所以,我们说一个指令周期,包含多个CPU周期,而一个CPU周期包含多个时钟周期。
建立数据通路
在专栏一开始,不少同学留言问到,ALU就是运算器吗?在讨论计算机五大组件的运算器的时候,我们提到过好几个不同的相关名词,比如ALU、运算器、处理器单元、数据通路,它们之间到底是什么关系呢?
名字是什么其实并不重要,一般来说,我们可以认为,数据通路就是我们的处理器单元。它通常由两类原件组成。
第一类叫操作元件,也叫组合逻辑元件(Combinational Element),其实就是我们的ALU。在前面讲ALU的过程中可以看到,它们的功能就是在特定的输入下,根据下面的组合电路的逻辑,生成特定的输出。
第二类叫存储元件,也有叫状态元件(State Element)的。比如我们在计算过程中需要用到的寄存器,无论是通用寄存器还是状态寄存器,其实都是存储元件。
我们通过数据总线的方式,把它们连接起来,就可以完成数据的存储、处理和传输了,这就是所谓的建立数据通路了。
下面我们来说控制器。它的逻辑就没那么复杂了。我们可以把它看成只是机械地重复“Fetch - Decode - Execute“循环中的前两个步骤,然后把最后一个步骤,通过控制器产生的控制信号,交给ALU去处理。
听起来是不是很简单?实际上,控制器的电路特别复杂。下面我给你详细解析一下。
一方面,所有CPU支持的指令,都会在控制器里面,被解析成不同的输出信号。我们之前说过,现在的Intel CPU支持2000个以上的指令。这意味着,控制器输出的控制信号,至少有2000种不同的组合。
运算器里的ALU和各种组合逻辑电路,可以认为是一个固定功能的电路。控制器“翻译”出来的,就是不同的控制信号。这些控制信号,告诉ALU去做不同的计算。可以说正是控制器的存在,让我们可以“编程”来实现功能,能让我们的“存储程序型计算机”名副其实。
指令译码器将输入的机器码,解析成不同的操作码和操作数,然后传输给ALU进行计算
CPU所需要的硬件电路
那么,要想搭建出来整个CPU,我们需要在数字电路层面,实现这样一些功能。
首先,自然是我们之前已经讲解过的ALU了,它实际就是一个没有状态的,根据输入计算输出结果的第一个电路。
第二,我们需要有一个能够进行状态读写的电路元件,也就是我们的寄存器。我们需要有一个电路,能够存储到上一次的计算结果。这个计算结果并不一定要立刻拿到电路的下游去使用,但是可以在需要的时候拿出来用。常见的能够进行状态读写的电路,就有锁存器(Latch),以及我们后面要讲的D触发器(Data/Delay Flip-flop)的电路。
第三,我们需要有一个“自动”的电路,按照固定的周期,不停地实现PC寄存器自增,自动地去执行“Fetch - Decode - Execute“的步骤。我们的程序执行,并不是靠人去拨动开关来执行指令的。我们希望有一个“自动”的电路,不停地去一条条执行指令。
我们看似写了各种复杂的高级程序进行各种函数调用、条件跳转。其实只是修改PC寄存器里面的地址。PC寄存器里面的地址一修改,计算机就可以加载一条指令新指令,往下运行。实际上,PC寄存器还有一个名字,就叫作程序计数器。顾名思义,就是随着时间变化,不断去数数。数的数字变大了,就去执行一条新指令。所以,我们需要的就是一个自动数数的电路。
第四,我们需要有一个“译码”的电路。无论是对于指令进行decode,还是对于拿到的内存地址去获取对应的数据或者指令,我们都需要通过一个电路找到对应的数据。这个对应的自然就是“译码器”的电路了。
好了,现在我们把这四类电路,通过各种方式组合在一起,就能最终组成功能强大的CPU了。但是,要实现这四种电路中的中间两种,我们还需要时钟电路的配合。下一节,我们一起来看一看,这些基础的电路功能是怎么实现的,以及怎么把这些电路组合起来变成一个CPU。
总结延伸
好了,到这里,我们已经把CPU运转需要的数据通路和控制器介绍完了,也找出了需要完成这些功能,需要的4种基本电路。它们分别是,ALU这样的组合逻辑电路、用来存储数据的锁存器和D触发器电路、用来实现PC寄存器的计数器电路,以及用来解码和寻址的译码器电路。
虽然CPU已经是由几十亿个晶体管组成的及其复杂的电路,但是它仍然是由这样一个个基本功能的电路组成的。只要搞清楚这些电路的运作原理,你自然也就弄明白了CPU的工作原理。
推荐阅读
如果想要了解数据通路,可以参看《计算机组成与设计 硬件软件接口》的第5版的4.1到4.4节。专栏里的内容是从更高一层的抽象逻辑来解释这些问题,而教科书里包含了更多电路的技术细节。这两者结合起来学习,能够帮助你更深入地去理解数据通路。
课后思考
这一讲,我们说CPU好像一个永不停歇的机器,一直在不停地读取下一条指令去运行。那为什么CPU还会有满载运行和Idle闲置的状态呢?请你自己搜索研究一下这是为什么,并在留言区写下你的思考和答案。
欢迎你留言和我分享,你也可以把今天的文章分享给你的朋友,和他一起学习和进步。
- kdb_reboot 👍(11) 💬(1)
很喜欢这几章;大二时学数电,期末考了93分,但是仍然不知道它能做什么用,内心有困惑,但是也没更多的坚持这个问题;感谢你
2019-06-06 - LDxy 👍(165) 💬(6)
CPU在空闲状态就会停止执行,具体来说就是切断时钟信号,CPU的主频就会瞬间降低为0,功耗也会瞬间降低为0。由于这个空闲状态是十分短暂的,所以你在任务管理器里面也只会看到CPU频率下降,不会看到降低为0。当CPU从空闲状态中恢复时,就会接通时钟信号,这样CPU频率就会上升。所以你会在任务管理器里面看到CPU的频率起伏变化。这个知识也是我找工作面试时才学到的。
2019-06-04 - Akizuki 👍(61) 💬(0)
操作系统内核有 idle 进程,优先级最低,仅当其他进程都阻塞时被调度器选中。idle 进程循环执行 HLT 指令,关闭 CPU 大部分功能以降低功耗,收到中断信号时 CPU 恢复正常状态。
2019-06-05 - 黄马 👍(45) 💬(0)
uptime 命令查看平均负载 满载运行就是平均负载为1.0(一个一核心CPU) 定义为特定时间间隔内运行队列中的平均线程数。load average 表示机器一段时间内的平均load。 这个值越低越好。负载过高会导致机器无法处理其他请求及操作,甚至导致死机 当CUP执行完当前系统分配的任务,为了省电,系统将执行空闲任务(idle task),这个任务循环执行HLT指令,CPU就会停止指令的执行,并且让CPU处于HALT状态,CPU虽然停止指令执行,并且CPU的部分功能模块将会被关闭(达到降低功耗的目的),但是CPU的LAPIC(Local Advanced Programmable Interrupt Controller)并不会停止工作,即CPU将会继续接收外部中断、异常等外部事件(事实上,CPU HALT状态的退出将由外部事件触发).当CPU接收到这些外部事件的时候,将会从HALT状态恢复回来,执行中断服务函数,并且当中断服务函数执行完毕后,指令寄存器(CS:EIP)将会指向HLT指令的下一条指令,即CPU继续执行HLT指令之后的程序
2019-10-21 - 活的潇洒 👍(26) 💬(2)
要想成功三个动作很重要 1、做出来 2、写出来 3、讲出来 三个非常重要,缺一不可 day17 笔记:https://www.cnblogs.com/luoahong/p/11330406.html
2019-08-21 - roger 👍(19) 💬(0)
程序计数器一直在变化,就是满载吧,持续不变就是idle。CPU密集型任务需要CPU大量计算的任务,这个时候CPU负载就很高,IO密集型任务,CPU一直在等待IO,就会有idle。
2019-08-12 - 旺旺 👍(15) 💬(1)
cpu执行速度非常快,消耗性能资源也比较快。但实际上,电脑并不是时刻都需要进行大量运算。 所以,CPU需要一种“闲置”状态,来平衡这种矛盾(需要忙时,可以全速奔跑;暂无事务时,又可节能地随时待命。) “Idle 闲置”是一种低功耗的状态,cpu在执行最低功耗的循环指令。实际上并非啥都没干,而是一直在干最最轻松的事儿。
2019-06-04 - A君 👍(9) 💬(0)
所谓构建数据通路,就是把各种组建通过线路组合起来,让他们可以完成数据存储、处理和传输的操作。数据处理部分自然是交给ALU组合逻辑计算元件,它是由大量运算器组合而成;数据存储自然是交给各种寄存器,存储的数据除了有计算数据外,还有各种状态,以及指令的地址;负责读取指令,将指令转换成电信号的就是译码器;为了让fetch-decode-execute这个循环体运作下去,还需要一个自动设备。
2020-06-25 - 九云 👍(7) 💬(3)
指令周期、时钟周期2个概念就够了。引入CPU周期这个概念,要解释什么问题呢?
2019-11-15 - djfhchdh 👍(6) 💬(0)
cpu满载和空闲的分别主要是操作系统调度任务导致的,如果操作系统调度了一个高优先级的任务,那么cpu就优先执行这个任务即满载,如果操作系统调度了一个低优先级的idle任务,那么cpu就执行这个idle任务,显示为空闲状态,空闲即假装“没事做”,其实当有其他更高优先级的任务调度时,就可以抢占它,去执行更高优先级的任务
2019-06-18 - 北风一叶 👍(3) 💬(2)
指令周期由:取得指令,指令译码,执行指令三个步骤的不断循环组成,其中取指令和指令译码由控制器负责,执行指令由运算器负责。一个指令周期由多个CPU周期构成,而一个CPU周期又由多个时钟周期构成,这一部分感觉说的有点混乱。
2020-03-03 - 爱与自由 👍(2) 💬(0)
看了一下评论,都没讲到重点,很多还偏题了,讲到操作系统,线程撒的,注意了:这节课的重点是cpu 是这样几个组件组成的。ALU运算器,寄存器,PC计数器,译码器这几个物理组件(生产线上的4 个工人)共同完成一个工厂流水线产品(一条指令的完整执行,也就是拿到计算后的结果,且是按时间有序地源源不断的生产产品,因为指令是一条一条执行的,比如产品 A(指令A:包含4个步骤),产品 B(包含4个步骤),且4个步骤花的时间不等,ALU 执行命令的时间远小于取指令时间),比如A指令取指令的时候,ALU 空闲,A指令执行的时候,译码器空闲。。。那么这几个组件工作就不饱满,老板不会满意的,狠不得每个工人每时每刻都不能休息,榨干你才是老板愿意看到的事情。要达到这点,就需要A,B指令从同步串行改为并行,也就是A指令在执行的时候,译码器空闲,那么译码器可以读取B指令进行译嘛,把B指令的前工序提前准备好,一旦A指令执行完成,释放占有的资源ALU,那么B指令可以马上执行,不必等到取指令,译码的过程(因为提前完成了),几个工人工作饱和了,每时每刻都没闲着,节约了时间
2023-06-16 - thomas 👍(2) 💬(0)
每一条指令都需要从内存里面加载而来,所以我们一般把从内存里面读取一条指令的最短时间,称为 CPU 周期 ------------------------------------> 为什么要加上最短?
2021-07-19 - A君 👍(2) 💬(0)
因为cpu访问寄存器、cache、内存、硬盘的速度不同,为了不让宝贵的cpu资源浪费在数据传输过程中,因此引入中断,当cpu在访问硬盘等低速设备时可以将任务切换去,执行其他任务。如果不考虑进程调度策略、进程优先级等因素,如果几乎所有任务都是在做计算,那么cpu就会处在满载状态,如果当下没有任务在进行,操作系统就会切换ideal进程,该进程会主动挂起自己,所以说,虽然cpu当前在处理ideal任务,但并不会真正在读取处理传输数据。
2020-06-25 - Ronnyz 👍(2) 💬(0)
满载就是执行高优先级任务,不会被中断;空闲就是执行的任务优先级较低,可以被中断
2019-11-25