# 前言
本系列记录学习计组时的知识点梳理,我更喜欢采用讲故事的方式来表达我对知识点的理解。若文章所写之处过于繁杂,可忽略本篇😊 😊
# 一、了不起的冯诺依曼
如果说图灵机是计算机的种子,那么我愿称冯诺依曼为促使这颗种子发芽的养分,他的思想真是一绝,太了不起了。
个人认为,冯诺依曼最大的贡献不是提出采用二进制存储数据,而是存储程序工作原理。何为存储程序工作原理?说白了就是解放双手,我给计算机下达一系列命令,存在计算机的某一个位置,计算机自己去那个位置读取命令,挨个儿执行。
或许你会说,就这?那是你没有了解其真正的精髓😏。他的点睛之笔在于,他将人脑思考问题的方式引入计算机当中,让它的核心部件如同人的大脑一般思考问题,这是所谓的精华。当你站在大脑的角度看待计算机的问题时,你会发现一切豁然开朗。
OK,我们来盘一下思路。用人的行为来模拟一下计算机的五大部件,当两人见面打招呼的时候,一个人的行为恰恰完整的体现了计算机内部的工作过程。
场景如下:张三路上碰见李四,张三和李四打了一声招呼。张三看见李四的时候,他的眼睛就是输入设备,李四的模样倒映在他的脑海里,勾起了他尘封已久的回忆,而这动人的回忆所在之处起个名字就是:存储器。张三老远儿就看见李四了,他在想一会儿走过去要不要打个招呼?大概走到什么位置再和他打招呼呢?这番操作由运算器完成。这一系列训练有素的动作,肯定是被一个“高级军官”控制的,那控制器就当仁不让地出场啦。那么最后,在他嘴巴里说出“你好”二字时,输出设备也顺利露面,五大设备齐活了。
# 知识点一
- 计算机五大组成部分:运算器、控制器、存储器、输入设备、输出设备
再来反观存储程序工作原理:给计算机下达命令,存在计算机某个地方,专业点讲是不是就可以说:将指令
存入计算机的存储器
中啦。
# 知识点二
- 存储程序是指:将指令以二进制代码的形式事先输入计算机的主存储器,然后按其在存储器中的首地址执行程序的第一条指令,以后就按该程序的规定顺序执行其他指令,直至程序执行结束。
# 二、早期冯诺依曼机VS现代计算机
1、早期冯诺依曼计算机
早期的计算机主要是用来科学计算的,所以当初的冯诺依曼计算机它便是以:运算器为核心的。
甭管你输入的是什么东西,上来先交由运算器给我一顿计算,计算完了数据后,是存在存储器里呢,还是给输出设备进行输出呢,都是后话了。但这里有一个隐藏的高级指挥官,就是控制器。这些输入、输出设备的运转,以及运算器、存储器这些核心部件的工作,都是由控制器进行控制的,这就像一个霸道总裁一样指挥着你的一举一动。
我们其实可以把控制器当成上帝视角,计算机的一切行为是由控制器控制的,但它的核心主线还是由输入设备--->运算器--->输出设备。这两条交织在一起的线不要搞混了你的思维。或者更通俗的理解就是:我们在做数学题的时候,你所能感受到的整体流程就是,大脑在计算数学题,算出答案后再写出答案,这一步骤我们可称之为主线任务,是我们切切实实感受到的。然你通常会忽略的是,是什么在操控我的双手、眼睛,是什么在操控我的大脑进行计算?这一步自然便是对应那个在计算机中拥有着上帝视角的:控制器。
反思如此设计是否合理!!!
其实讨论它是否合理言语有点过激了,从我个人理解的角度,我认为这样设计有些别扭。我们不妨再站到人类大脑的立场上看待问题,当我们的眼睛看到某个熟悉的人也好,遇到某个不会的难题也罢,我个人认为这样的场景首先会在我的记忆深渊里荡起阵阵波澜的😄😄。倘若我看到的是一个熟悉的人,我可能会先回忆一下他曾经的样貌;若见到一道较难的题,脑海里估计会瞬间浮现出被其支配的恐惧画面。**言外之意便是,我的输入设备接收到信息后,会先经过我的存储器,再做别的事情。**所以,再对比以运算器为核心的冯诺依曼计算机,就不禁感到些许别扭。
再来分析性能上的损耗!!!
从“输入设备--->运算器--->输出设备”这条主线可以看出,运算器不光承担运算的功能,还兼职I/O设备之间的数据传输,早期数据计算还没有现在复杂,这样做影响也不是特别大,但现在咱们的数据可是越来越复杂了,这么玩就容易造成性能上的浪费。我们就想,要是运算器他能安心运算就好了,与I/O设备间的数据传输交给别人就好了。那这个艰巨的任务推给谁好呢🤔?计算机五大组件部件,除掉I/O设备,不就剩下存储器和控制器了嘛。霸道总裁岂是你说动就动的?上帝视角不是盖的。得,组织上就把这个艰巨的任务给了存储器了。
至此,引出了我们现代计算机的结构。
2、现代计算机
有了上面的推理,再来看这张图是不是就很容易接受了呢。全程以存储器为核心,输入设备传来的数据先存入存储器中,需要运算时再交由运算器,运算的结果存入存储器中,最后交给输出设备。整个操作的上帝视角,依旧由我们的霸道总裁控制器把控全局,这样一来,就能够更高效地进行运算啦。同时我个人认为,这张图也更贴合我们人脑的运转方式了,这便是现代计算机结构图。
说到这里了,我们可以采用一下封装的思想归归类了,计算机五大部件分开来讲就是上图所画的设备,实则可以再抽象一把。
我们不妨把输入设备和输出设备归为一类,输入输出嘛,就称其为:I/O设备(Input和Output);存储器是核心,就自为一类吧;🤔还剩下运算器和控制器,那就把他俩搞到一起吧,正好我们现在的技术也成熟了,大的小的集成电路咱都有,把他俩焊一块儿也不是不可以,自此出现个新词:CPU。即CPU=运算器 + 控制器。
所以有时候我们会看到这张图。
# 知识点三
- CPU = 运算器 + 控制器
- 存储器 = 主存(内存条) + 辅存(硬盘)
- 主机=CPU + 主存
- I/O设备 = 输入设备 + 输出设备 + 辅存
# 三、各个硬件组成
正所谓一花一世界,一叶一菩提,要想理解计算机内部更细微的运行细节,就要深入其每一个组成部分的世界,探索其结构。
# 1、主存储器
主存储器,也就是我们所熟知的内存条(下文以内存称呼)。它和那些硬盘最大的区别就是,断电不会保存。当然,他的读写速度也是远远高于硬盘的,因此将其放在CPU这个层面进行数据的交互。
主存比较有意思的一点就是它的内部存储结构,我们不妨思考一下,如果将某个数据存到了内存中,过了一会儿我要找这个数据,该怎么找呢🤔?
有人说好办,我把它存的数据都拿出来,挨个儿找一遍,找到为止。办法倒是可行,就是有点儿慢呀,这在最底层的硬件层面就已经开始从头到尾找一遍,那等上层拿到数据黄花菜都凉了。emm,有人出了个主意,不如效仿我们所住的楼房,每个人家不都有一个门牌号嘛,如果我要找xxx,直接找他们家门牌号不就OK了。
内存也是一样,给每一个存储数据的空间都整一个编号,一个编号对应一个数据,到时候要找某个数据的话,记住他的编号,直接根据编号找到对应位置的数据,不就好了嘛。这个编号我们称之为:内存地址。那么问题来了,加一个编号是个好主意,但是假如我要找的编号在一大串编号中间,那岂不是还要从头到尾挨个儿找一遍编号,这不又绕回去了?此言差矣,当你去敲门牌号为:1005的门时,进楼发现第一个房间号是:1001,难道你会挨个儿从头到尾找一遍嘛?当然不会,直接往前走四个房间,第五个不就是:1005嘛。
内存在存储时也是遵循这种规则的,你只需要知道第一个编号是什么,也就是:首地址,之后需要跳过几个位置,根据你需要找的内存地址进行加减运算一下即可。而你所加或减的数字,我们称之为:偏移量。而这种很明显具有顺序的结构,我们称之为顺序存储。
发散一下思维你就会发现,这样的结构在编程中还是比较常见的,比如说:数组
内存存储模型图(附)
# 2、寄存器
根据现代计算机的结构,我们知道,内存是要和CPU进行数据传输的,人们用了一段时间后发现,这样传输的效率还是有那么一丢丢慢。底层嘛,务必要追求极致的效率,才能满足上层的高性能需求。
怎么办呢?究其原因不就是读写速度的问题嘛!我们嫌弃内存的读写速度慢,那能不能换一个读写速度比内存快的家伙呢?这里涉及到一个成本的问题,内存是RAM(只读存储器),它的材料是电容,如果换成读写速度较高的硬件,那成本便是成几何的速度往上增加,用指数爆炸形容都不为过。所以不能用高性能的硬件存储设备代替内存,成本上只允许更高性能的存储设备存储内存较小的数据。
🤔又要高性能,还不能存太多的东西,这可是个难题。不知道哪个哥们儿这么聪明,想到了这样一个办法。你不是不让换内存嘛,行,我不给你换,我拆!!!。内存存储的时候,内存地址表示门牌号,存储单元表示具体要存储的数据。寻址的时候我们需要内存地址,取数据的时候我们需要存储单元,把他俩单独拎出来,是不是正好符合小容量的需求呢!
体会一下这样做的好处,我用一个高性能存储设备专门存储内存地址,再用一个高性能存储设备专门存储与之对应的存储单元。当CPU需要内存中的某一个数据时,原来的操作是:把手直接伸到内存中去找(根据内存地址直接从内存中寻找对应的数据)。现在变了,我先将要找的内存地址存到这个高性能存储设备里,然后内存根据该存储设备的地址寻找数据,找到数据后,把找到的数据返还给另一个高性能存储设备里,CPU取数据时,直接从存储最终数据的高性能设备中拿即可。
这就好比取快递的时候,你告诉前台人员快递号,前台人员去库存里找到快递后送到柜台里,你再从柜台取走即可。
这个高性能存储设备我们给了它一个名字:寄存器,顾名思义,暂时存储的硬件设备。你会发现它其实就是一个中间设备,暂时存放内存地址和存储数据。当一次寻址结束后,即CPU拿到他想要的数据后,寄存器中立马就会换成新的数据进行下一步工作。如同前台取快递人员心里不会一直记着你的取件号,取完你的号码后就会为下一个人服务😄。
注意,我们给专门存放内存地址的寄存器起名:MAR寄存器(Memory Address Register 存储地址寄存器);给专门存放存储数据的寄存器起名:MDR寄存器(Memory Data Register 存储数据寄存器)。采用寄存器的好处就在于,无需CPU直接访问内存,直接访问寄存器即可,提高了数据传输的效率。
接下来就有趣了,既然MAR寄存器要存放内存地址,那是不是也就意味着:MAR寄存器最大存储的地址就表示最多有几户人家呢?故:MAR的位数反应的是存储单元的个数。那MDR寄存器存放的是每一个存储单元里存储的数据信息,那是不是同样可推出,MDR寄存器最大存储的位数表示一个屋子里能容纳多少东西呢?
需要注意的是,主观上寄存器属于存储的设备,所以我们很自然地认为寄存器是在存储器中的,但实际上人们把寄存器焊到了CPU中,把它们集成在了一起。逻辑上寄存器在存储器里,实际却在CPU中,这个无论以后见了那种图,心里明白就好。
# 知识点四
- 存储单元:每个存储单元存放一串二进制代码
- 存储字(word):存储单元中二进制代码的组合
- 存储字长:存储单元中二进制代码的位数
- 存储元:即存储二进制的电子元件,每个存储元可存1bit
- 1个字节(Byte)=8bit
- 1B=1个字节,1b=1个bit
# 3、运算器
聊起这个部件,就又要认识一些寄存器啦,别急,理解的去记忆就不会感到害怕了。
运算器这个家伙主要就是做运算的,什么加减乘除、左移右移就是它的家常便饭。我们来归归类哈,像这种加减乘除的运算,给它起个名儿:算术运算;而这种左移右移,与或非这类型的运算呢,我们再给它起名儿:逻辑运算。
总结起来就是:运算器用于实现算术运算和逻辑运算。
我们知道,运算器计算的数从哪来?内存里来的呗!谁给它搞过来的,还不是控制器抢过来的。我们以“1+1”来举例,人脑计算两个加法的时候,我们是不是先要想第一个加数是多少,诶是1,第二个加数还是1,然后把脑海里刚刚记忆的俩数加起来,算出结果。描述这个场面是想表达:我们在进行计算时,总是要先记忆一下进行运算的数字,再进行运算。
运算器也是一样的,控制器从内存中拿来要计算的数字,在运算器中总要找个地方先存一下,所以我们引出了第一个寄存器:ACC(累加器)。累加器主要是存放操作的数字或运算结果的,至于为什么叫累加,也很好理解。还是拿人类计算举例,若我们要计算 1 + 1 + 3 的时候,我们会这样去做,先算出1 + 1 = 2后,将计算的结果2记忆一下,下次再计算 2 + 3 = 5,最终我们只需要记住结果5即可。也就是说,我们只需记忆每一步的计算结果即可,不断覆盖前面的计算结果,这样是不是就相当于累加呢😏。ACC寄存器也是只存放最终的结果,前面计算的结果会对它进行覆盖。
这么一看好像运算器里只需要ACC累加器即可,但实际上我们希望更高的运算效率,就要拆分每一个步骤,增加更多的寄存器来完成操作。比如说我们可以这样做,再加一个寄存器叫:MQ(乘商寄存器)。让它在做乘除运算时,存放操作的数字或者运算结果。再加一个ALU(算数逻辑单元),让它完成算数运算和逻辑运算;最后再加一个X(通用寄存器),让它可存放任意通用的操作数。
如此一来,运算的时候就可以这么玩了,计算 9 + 8的时候,控制器先把9放到ACC中,把加数 8 放入通用寄存器X中,然后它俩算出来的结果17再放入ACC中进行覆盖。若计算 9 * 8时可以这样,把9放入通用寄存器X中,8放入MQ寄存器中,它俩计算的结果72再放入ACC中。至于ALU的操作就不赘述了😄。
# 知识点五
- 运算器:用于实现算术运算(如:加减乘除)、逻辑运算(如:与或非)
- ACC: 累加器,用于存放操作数,或运算结果
- MQ: 乘商寄存器,在乘、除运算时,用于存放操作数或运算结果
- X: 通用的操作数寄存器,用于存放操作数
- ALU: 算术逻辑单元,通过内部复杂的电路实现算数运算、逻辑运算
# 4、控制器
上个知识点我们解决了CPU两大核心部件中的运算器,自然也不能放过控制器。
先思考一个问题,控制器是用来干啥的?高级指挥官、霸道总裁... 这些词浮现在你的脑海中时,是不是有把控全场,挥斥方遒的感觉。说简单点就是控制计算机部件的,也就是说:它可以控制 I/O设备、存储器和运算器。OK,它在操控这些设备时我们称其:给出控制信号。
再深入思考一下,控制器控制这些I/O设备是为了干啥?把输入进来的数进行运算,再输出出去,就是数据传输嘛。那它操控运算器呢?很显然,是为了计算啊。再细分一下,运算器怎么计算?是乘法还是除法、加法还是减法?这一步从本质上来讲是我们给计算机下达的命令吧,也就是指令。由此是不是可以推出,控制器还有个艰巨的任务就是执行指令呢😄。
但是执行完一条指令可大有学问咯😏。都知道把大象装冰箱分三步,执行一条指令同样分三步。首先,指令肯定存在一个地方,存在哪里?当然是存储器(内存里)。要执行指令,你总得把它拿出来吧,OK,第一步取指令。拿出来你就能执行了?天真,别忘了指令可是二进制代码组成的,你拿到一份数学卷子还得读读题分析一下题目,更何况是控制器这个硬件呢😃,所以紧接着第二步就是:分析指令。分析完成后,才是最后一步:执行指令。
控制器在完成整个指令执行时,过程还是蛮有趣的。我们知道,指令既然存放在内存里,那必然会对应一个内存地址,前面我们说为了加快CPU的效率,整了个MAR寄存器来存放要从内存中取数据的地址;这边控制器看的眼红,虽然说你MAR寄存器现在已经在我CPU内部了,但我还是觉得效率能再高一点,怎么办?套娃呗,我控制器内部也整一个和你类似的,专门用来存放:内存中存放指令的内存地址,而这个部件就是:PC(程序计数器)。而由于我们的控制器还有个任务是分析指令,那你分析指令总得拿到指令吧,PC只是记录了存放指令的门牌号,我拿到指令后还需要一个东西来暂存一下指令,诶,IR(指令寄存器)出来了。IR的作用就是取出来指令后先给我存到这个里面,然后由控制器里分析指令的那个部件从IR里面取出来进行分析即可。至此,分析指令的兄弟也登场了:CU(控制单元)。
值得注意的是:由于我们完成一系列功能,本质上是指令的集合,也就是指令集(前面我们提到过)。那么一套指令集存储在内存中必然是顺序存储的,也就是门牌号是挨着的。所以说,当控制器取完一条指令,放到IR指令寄存器里以后,PC计数器,这个存放每条指令的门牌号就应该加1,方便下次指令的拿取(也就是编程中的指针后移)。
这里可以用一个简单粗暴的生活小例子看一下,包租公一家三口来收租,结果发现整栋楼都是他们家的。那就分工明确咯,女儿充当PC计数器的角色,先找到第一家门牌号,包租公相当于IR寄存器,进去收租,收到的租金便是取走的指令啦。此时包租公收到租后,女儿会走到下一家的门牌号前,这里完成的就是PC计数器加1的操作。钱嘛,自然是交给家里管事儿的包租婆咯,包租婆拿到租金后开始点钱,发零花钱,这里便是由包租婆充当CU控制单元的角色,点钱就是分析指令,发零花钱自然是传递控制信号啦!
# 知识点六
- CU: 控制单元,分析指令,给出控制信号
- IR: 指令寄存器,存放当前执行的指令
- PC: 程序计数器,存放下一条指令地址,有自动加1功能
- 完成一条指令分三步:①取指令 ② 分析指令 ③执行指令
- 其中,取指的操作由PC和IR完成,执行的操作由CU完成