计算机究竟是怎么跑起来的

不论你是计算机相关从业者,还是门外汉,你一定曾经或者正在疑惑,计算机究竟是怎么跑起来的。即使你看过计算机组成原理,知道冯诺依曼体系结构,了解操作系统的发展史,但你仍然有可能因为没有直观感受而总是在思考这个问题。

本篇 Chat 通过讲故事的方式,深入浅出带你一点点了解计算机的本质。你会发现从始至终都不会突然蹦出一个专业名词,但最后我们确实一步步构建出了一个完整的计算机!文章内容涉及:

  1. 编码与电路——信号的转换
  2. 继电器——信号的传递
  3. 门电路——信号的关联
  4. 加法器设计——信号可以表示计算
  5. 触发器——信号的保存
  6. 存储器组织——信号可以表示地址
  7. 自动操作——信号可以表示代码
  8. 操作系统——已有知识的一种应用而已

  这是我曾经一直以来就有的疑问,我很想知道计算机最最基本的工作原理。也找了好多的书籍,但这些只是从各个层次来告诉你计算机是由哪些部件构成,分别起了什么作用,但仍然解决不了最最基本的疑惑,你甚至不知道这疑惑究竟是什么。直到某一天,看了《编码·隐匿在计算机软硬件背后的语言》这本大师之作,终于对这个疑问有了较为清晰的认识。于是继续深入研究,通过自制 CPU,自制操作系统等方式对计算机建立了一个从头到脚的通路。

  如果你还没有建立这样一个通路,那么现在请跟我一起,从一个故事开始吧。(本篇 Chat 适合计算机初学者或计算机小白)

1、编码与电路——信号的转换

  你今年10岁,你最好的朋友住在街对过,你们的窗子彼此相对。每当夜幕降临,你都要隔着窗子和好友“对话”,且不能让你的父母发现。而你们各自只有一把手电筒,这时你们会怎么交流呢?我想一开始你们一定会觉得用手电筒交流是不可能的,但你们会想出第一个办法,就是用手电筒比划出你们要说的每一个字。恭喜你,你已经开始探索如何用手电筒去“编码”你们要说的话了,但显然你们没有抓住编码的本质。

  为什么我们要管猫叫猫,管狗叫狗,管我看到你时心跳加速这种感觉叫喜欢呢?这是因为它便于我们说和写,我们正是用语言来为世界的种种事物进行了编码,而这种编码正是选择了对于嘴和手都最为方便的策略。但当我们去使用手电筒时,我们所选择的编码策略并没有考虑它的方便性。认识到了编码的本质,我们便会认识到,用手电筒比划文字的方式交流是多么愚蠢了。

  那怎么编码才对手电筒是方便呢?我们知道,如果编码后的所有操作都可以只用手电筒的亮灭来表示,无论对于使用者还是观察者,都是最为清晰明了的。这种策略的编码可以有很多种,其中一种我们所熟知的便是莫尔斯电码。不难理解,将手电筒亮1秒对应摩尔斯电码的“点”,亮 3 秒对应莫尔斯电码的“划”,我们便可以很轻松地用手电筒进行“交流”了。

  不知道你有没有发现,刚刚的这种尝试,已经将语言转换成为了手电筒的闪烁,而作为观察者又可以很轻松地将这种闪烁转换成为语言。因为光在两栋楼之间可以瞬间且悄无声息地传递,解决了夜间传递信息的难题,所以这种转换是有意义的。我们完成了编码与手电筒的转换,也就能很自然地联想到,编码与电路也存在着类似的转换,而且你会发现这种转换具有更深远的意义,电信号的传递具有更加灵活的优势。试想一下如果你的朋友不是住在街对过,而是住在隔壁呢?你们可以用两块电池,几根导线,两个电灯泡来进行这种交流。如图(只画了一方)

enter image description here

  灯泡就相当于之前的手电筒,只不过用手去拨动手电筒开关这个过程,转化为了电路的开关。再次恭喜你,你已经知道了形成一台计算机最基本的两个方面,编码和电路。并且,你知道了他们之间的转换,即语言信号与电信号的转换。但这里要注意一点,编码是一定要有的,但电路却不一定是电,你完全可以用机械的方式传递信号,姑且可以管它叫做机械路吧。

2、继电器——信号的传递

  我们已经完成了伟大的第一步,即信号的转换。但有一点小遗憾,通过对开关的打开和闭合的控制,直接反映在了灯泡的亮和灭,整个信号过程就此终结。尽管它已经能够满足我们“对话”这样一个简单的需求,但我们的目标显然不止如此,如何让信号得以延续呢?试想一个最简单的场景,仍然以上面为例,假如你的朋友不是住在你的隔壁,而是在距离你一站地的另一个小区呢,也即是说当导线不能将电信号传递那么长的距离时我们该怎么办呢?很显然,我们想到的解决方案是,让电信号先传递一段小距离,在传递一段小距离,最后传递到远处的灯泡。再次明确一下,我们的新目标就是,让电信号传递。

  试想一下,我们已经做到的是,将电信号从开关处传递到了灯泡处,但这显然不是我们想表达的“传递”。我们如何让灯泡亮和灭这个信号,变成下一个开关的打开和关闭呢?如果完成了这一步转换,我们只需构建 n 多个上面的结构,就能将信号传递下去了。如果你还对当初的物理知识有印象的话,你一定不会对下面的图感到陌生。 

enter image description here

  没错,它叫通电螺线管,当通电时带有磁性,可以将那个带磁性的铁棒棒吸下来,于是他做到了关闭第二个开关这样一件伟大的事,我们的确通过这样一个装置完成了信号的传递。而刚刚发明的这套装置叫做继电器,它原本的作用是将之前已经微弱的电信号放大,作为一个中转站,使电信号传递更远的距离。但显然它还有更为深远的意义,就是完成了信号的传递。

  至此,你已经掌握了信号的转换与传递。虽然你可能不太相信,仅凭借这两种设计,你已经可以做出一台计算机了。可是事实就是如此,之后的所有设计,都是基于这两个最最基本源。只要到目前为止你可以接受并且理解,那之后的所有令人叹为观止的精巧设计,也都不在话下。如果你正在从事用 Java 等高级语言编写代码,你可能觉得自己只是在应用前人的理论去创造,但其实你错了。从这一步开始之后,所有的一切都是在应用这两个基本的知识,我们都是在这两个理论基础之上搞应用的俗人罢了。

3、门电路——信号的关联

  上一部分我们做到了信号的传递,但这种传递就像是传话一样,你说一我也说一,你说二我就说二,是一种完全照搬式的转换,很无脑。我们是否可以让这种信号的传递稍稍带一点“智慧呢”?回答这个问题前,我们先来看两张你更加熟悉的图。

enter image description here在这里插入图片描述

  没错,就是串联和并联!这两张图与之前最大的区别在于,多了一个开关。第一张图告诉我们,当两个开关同时闭合时,灯泡才会亮;而第二张图则是当两个开关有一个闭合时,灯泡就会亮。这便让两个信号之间有了某种逻辑的关系。如果你足够敏感的话,你会发现意义不仅仅如此,通过对编码的认识,我们知道此处开关的打开与闭合可以代表更加丰富的含义。

  试想一下,如果你去宠物店买一只猫,猫有黑白两种颜色,公母两种性别,你的要求是买一只黑色的公猫,你便可以用第一张图的电路来帮你判断是否符合要求。我们让第一个开关的开代表白猫,关代表黑猫,第二个开关的开代表母猫,关代表公猫。这样每当把一只猫拿来时,我们将开关分别置于相应的状态,只要灯泡亮则代表符合你的要求。有没有感觉,信号已经不再是孤立的,而是有了一点点“智慧”,它可以帮你解决选猫的问题,自然也能解决逻辑复杂的难题。那么如果你用第二张图做同样的判断,那么你的要求一定是想选一只黑猫或者公猫。

  这两张图仍然用了电灯泡,而上一部分我们知道,用继电器将电灯泡替换,我们就可以将这种逻辑信息传递下去,也就打破了单一电路无法描述的复杂逻辑。一旦信号可以传递,再配以其他各种各样的逻辑电路的设计(这是电气工程师干的事),理论上我们能满足任何复杂的逻辑要求。既然电灯泡可以用继电器替换,那么开关也可以理解为上一个电路通过继电器传过来的电信号,此时,我们将一切都归为一种信号,而不用依赖灯泡或是开关这种具体的实物了。同时,开关所代表的的猫的颜色和公母,也可以抽象成开表示数字0,关闭表示数字1。因此,我们把这两种电路抽象成两种门电路,分别是与门和或门。我们必须理解下面的图其实和上面是一样的,如果不去做这样的抽象,一台计算机的电路图将会是天书。

enter image description hereenter image description here  不错,A 和 B 分别代表上图的两个开关,也可以说是输入信号,而 F 则代表灯泡,更准确说是输出信号。你必须开始习惯这种抽象。而门电路不只有这两种,其内部结构也不都是如此简单,你可以自行百度将常用的门电路示意图找到并将其记住,之后的部分我将直接画出所需要的门电路而不做这样详细的解释。如果你心里过不去这道坎,你可以亲自尝试去实现各种门电路的电路图,这将是有意义的。但从此处往后,你必须不再纠结门电路里面具体是怎么实现的,而应该开始花心思去想利用这些门电路的组合与设计。因为抽象的目的就是为了封装实现细节以便开展更深入的研究,甚至这两个门电路内部不一定非要是用开关和电线实现的,有可能是真空管、晶体管,也有可能根本不是用电实现的,这便是抽象的魅力,好在我们已经掌握其中一种最笨的实现方式了。还是那句话,你必须开始习惯这种抽象。

4、加法器的设计——信号可以表示计算

  刚刚我们所设计的门电路,已经使得信息似乎具有了某种“智慧”,但这还远远不够,其实我们更需要一个能帮我们进行数学计算的电路设计。一旦打通了这一关,可以说你已经了解了计算机的全部,因为计算机所做的一切,就只有计算,更绝对一点说,计算的一切,也就只有加法。在这里我做一个大胆的假设,正在阅读这篇文章的你已经知道了二进制的含义。那你是否能够用刚刚的知识,给自己做一个 8 位二进制数的加法计算器呢?它大概应该是这个样子。

enter image description here  其实这不是一个新知识,我相信给你足够的时间你一定会设计出来,你不妨在此暂停一段时间拿出一张纸试试。我们先从最简单的一位数相加开始,如何设计出一个一位数相加的计算器呢?我们首先应该整理出这样一张清晰的表,即  0 + 0 = 00;  0 + 1 = 01;  1 + 0 = 01;  1 + 1 = 10;  这里我们直接将进位也考虑进来。

加数A加数B加和输出进位输出
0000
0110
1010
1101

  不论你用什么方法,这张表的对应关系总能转换成某种门电路。如果你刚刚熟记了各种门电路的效果,不难发现它们的对应关系非常简单粗暴。和就是异或门,进位就是与门。画出来就是下面这个样子,由于我们只考虑了向后进位,而没有考虑前一个数的进位,因此我们称这种装置为半加器。enter image description here  如果将前一个进位考虑进来,只需再多一个半加器就可以了,至于为什么进位输出的加和没有用半加器而是用了一个或门,请暂停一分钟思考一下。这回我们已经建立好了一个完美的 1 位计算器,我们便可以自豪地称之为全加器。enter image description here  1 位计算器做出来之后,8 位计算器就只需将全加器逐个拼起来即可,并且再次抽象整体,我们称之为8位加法器。enter image description here   OK,大功告成。我不知道你此时的感受如何,不知不觉我们连续进行了 3 次抽象(即把之前的器件封装起来),有没有深刻体会到之前所说的你必须开始习惯这种抽象。每当一个复杂的构造被装进一个黑盒子里时,你便再也不要考虑里面的构造了,只要你做到了这一点,这三步的抽象便会是 so easy 。有了加法计算器,减法也就不是问题了,在本博客中将略去这部分内容,如果有兴趣,可以去了解一下计算机是如何用补码表示减法的,之后你会发现,减法就是加法。

5、触发器——信号的保存

  到目前为止,我敢说如果你第一次了解这些,你会感觉有些许的兴奋,但我更敢说,这些东西依旧没能解决你对于计算机最好奇最困惑的部分,因为我读到这里也是这种感受。没关系,从这部分开始才是我认为最为精彩的部分,回过头再看之前的知识简直就是铺垫。让我们先从两个能毁你三观的一个设计开始吧。

enter image description here

  左图:当闭合开关A时,整个电路联通,开关B将会被吸下来,整个电路断开,电磁铁失去磁性,开关B又会弹上去,此时电路又联通,开关B又被吸下来。就这样,开关B不断地快速地在开和闭之间循环进行,而我们始终没有去干预这个电路,因此该电路有了自反馈的特性。由于开关B的来回震荡,我们将这种电路称为振荡器,振荡器又通常被称为时钟。

  右图:这个需要稍稍绕个弯。

  • 一开始输入 A 和 B 均为 0,此时灯泡不亮,或者说输出为 0

  • 现在让输入 A 变为 1,灯泡亮。但当A再次变为 0 时,灯泡仍然是亮的。也就是说输入A除了一开始对灯泡有影响外,之后再怎么变都是对灯泡毫无影响的。

  • 保持A输入为 0,我们再来看 B,当B的输入为 1 时,灯泡灭。此时 B 无论是 1 还是 0,灯泡永远处于灭的状态,跟 A 的情况刚好相反。

  • 我们注意到,该电路好像记住了之前的信号,如果灯泡亮,则说明上一个变为1的信号一定是输入 A 。我们将这种电路称为触发器。

      天呐,我们已经可以让机器有了记忆,相信通过之前的引导,你已经知道这意义非凡。因为一旦实现了记住 1 位,那就意味着我们可以让机器记住一切。但这个电路还需要小小地改善一下。我们是否能让输入仅仅为一位,再增加一个保持位。当保持位为 1 时,输入便会记录在输出上,当保持位为 0 时,无论输入怎么改变,输出仍然是上一次记住的状态。很简单,其实我们要做的就是将右图中的输入A和B之前加入一个装置,当输入为 1 时,令输入 A 为 1 ,B 为 0 ;当输入为 0 时,输入 A 为 0,B 为 1 。因此,我们有了如下天才般的设计。

enter image description here

  时钟使得我们可以控制该存储装置是否存储来自输入的信号。8个这样的装置组合起来,我们就创造了可以存储8位的存储器,我们称之为8位锁存器。

  通过振荡器,触发器,以及之前提到的加法器的组合,可以做出很多有意义的部件,由于篇幅限制不做展开,但要简单列一下我们可以完成的工作。

  • 计数器:通过振荡器和多个触发器相连,可以做出计数器,使得信号从 0000 0000 一直增加到 1111 1111。
  • 自动加法器:通过加法器和锁存器的连接,可以做出能够进行多次连续相加并保存结果的机器。

6、存储器组织——信号可以表示地址

  上面我们实现的8位锁存器有一点缺陷,那就是它只能8位一起存入,并且是一一对应的。我们可不可以实现这样一种装置,使得我可以随意选取一位输入信号,并且让他在指定的输出位输出呢?为了达成这个目标,我们首先要回答根据什么选取。我们可以分别给 8 个输入位一个编号,这里我们就可以形象地称之为地址。显然,我们可以用3位的信号来表示 8 个不同的地址,因此我们所要实现的装置应该是长这个样子。

enter image description here

  为了连贯性,将具体实现和最终的简图画在了一起。我在这里只画了上面部分的具体实现,下面的没有画出,读者可以自行思考。其实到这里你应该明白,你不必去知道每一个实现细节,当你知道只要给你一定的时间也一定能自己画出时,便可以省下这部分精力,适当的放弃也是抽象思维的一个表现。

  我们管上面的部分叫 3-8 译码器,下面的部分叫 8-1 选择器,整个的部分我们可以骄傲地成为存储器( RAM 的既视感)。这套装置实现了将一个1位的信号存储 8 个位置上指定的一个位置中,并且通过输出可以观测到。不难发现,我们已经实现了 1 位信号的存和取,那么事实上,我们已经可以存取一切了。事实上,本书讲解了多位存取的装置 RAM 的具体实现,仅仅是多个上述的装置拼起来,将一位的输入变成多位,并且有多个多位的输入,这真的可以称之为内存了。

7、自动操作——信号可以表示代码

  现在我们来回顾一下,我们依次实现了以下“技术”

  信号的转换(编码)——信号的传递(继电器)——信号的关联(门电路)——信号的计算(加法器设计)——信号的保存(触发器)——信号的地址(存储器组织)

  似乎一台计算机就快要浮出水面了,但不知道你有没有发现一个缺陷。回想之前的加法器、锁存器以及改造后的存储器,始终是我们人为去控制后(例如打开开关)机器才能运转起来,我们希望存储时必须调整时钟信号为1才表示可以存储,是否可以让一切过程自动进行呢?答案是肯定的。要做到这一点,我们需要做两件事。

  • 将手动输入的时钟信号等改为上一层信号的传递

  • 需要有一个自动变化的计数器

      如果将某一串信号和时钟信号连起来,使得时钟信号一碰到它就变成了 1 ,存储器开始起作用,那么我们可以把这种信号形象地理解为代码,当它用二进制表示时,就是机器码。那刚刚这段代码的含义就是,将数字存储到存储器中。当然我们之前所用到的信号,现在可以把它称作数据。我们将代码和数据这两种形式的信号,依次存入存储器中,利用一个用振荡器实现的计数器依次将存储器中的信息,按照地址顺序读出,便可以自动进行操作了。我们要做的只是:

  • 设计各种机器码和其对应的电路使其完成:加载、存储、相加、停止、地址跳转等基本操作

  • 利用这些机器码顺序地编写并存入内存,等待依次被读出,我们称之为程序

      计算机就是这样,限于篇幅和本篇 Chat 的科普性不好表现,这部分如果要真正理解还是得去啃细节,不可能再通过科普来描述了,这里放一张《编码》原著里的原理图。

enter image description here以及我最喜欢的 ben eater 大师自制的 8-bit CPU 的原理图在这里插入图片描述

8、操作系统——已有知识的一种应用而已

  好了,到这里我就可以停止了,再往后的所谓操作系统、汇编语言、高级语言,甚至什么大数据、人工智能这些,统统是上述内容的扩展,其本质永远无法逃脱那单调而神器的 0 和 1 。


  PS:最后再次感谢这本大神之作《编码 隐匿在计算机软硬件背后的语言》,如果我这篇文章写的不够味,最起码能激起你阅读这本书的兴趣,我的目的也就达到了。总会有很多人告诉你大数据很火,人工智能很火,你用该学学 Java ,应该去搞一搞 python 。但少有人会告诉你,你应该先学习最最基础的计算机原理,尤其是对于计算机初学者而言,对这些基础的最初印象是很重要的。虽然这在短时间内不能使你得到明显的收益,但它会让你在技术的道路上走得更远。

阅读全文: http://gitbook.cn/gitchat/activity/5d380521a4d90b6ed9f8fbac

您还可以下载 CSDN 旗下精品原创内容社区 GitChat App ,阅读更多 GitChat 专享技术内容哦。

FtooAtPSkEJwnW-9xkCLqSTRpBKX

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页