计算机的本质

  这是我一直以来就有的疑问,我很想知道计算机最最基本的工作原理。也找了好多的书籍,但这些只是从各个层次来告诉你计算机是由哪些部件构成,分别起了什么作用,但仍然解决不了最最基本的疑惑,你甚至不知道这疑惑究竟是什么。直到最近看了《编码·隐匿在计算机软硬件背后的语言》这本大师之作,终于对这个疑问有了较为清晰的认识。这本书好在它没有试图去解释计算机各个组成部分,而是从两个试图用手电筒交流的孩子这样一个需求出发,按照人最合理的欲望和思考过程,非常自然地搭建了一台最基本的计算机。甚至作者如果不说这就是计算机,也不说逐步构建的东西就叫cpu、内存,你会颇为惊讶地发现原来这就是在说计算机呀。

  这本书虽然不是专业书籍,但对你第一次构建起对计算机整体的深刻理解却是至关重要的,作者生动的讲述使这本书的代入感极强,会让你忍不住一口气读完,你将会认识到从几块电池、几根电线,到最终构建成一台完整的计算机,其过程是如此的自然。我今天的目的就是把部永不褪色的计算机科学经典著作写成一篇博客分享给大家,我的标题并不是书的目录,图片也是自己重新画的,内容也并非书中的原文,而是按照我自己的理解,按照博文的篇幅重新构思的结果。哈哈有点难度,我们开始吧。

 

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

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

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

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

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

 

  灯泡就相当于之前的手电筒,只不过用手去拨动手电筒开关这个过程,转化为了电路的开关。再次恭喜你,你已经知道了形成一台计算机最基本的两个方面,编码和电路。并且,你知道了他们之间的转换,即语言信号与电信号的转换PS:书中还用了大量篇幅去讲述为什么灯泡会亮,从原子核的构造讲起。这部分我就假装你是知道的,如果你绕不过去这个坎,这部分可以当做专门的课题去研究下初高中物理知识。

 

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

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

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

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

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

 

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

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

 

 

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

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

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

 

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

 

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

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

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

加数A 加数B 加和输出 进位输出
0 0 0 0
0 1 1 0
1 0 1 0
1 1 0 1

  不论你用什么方法,这张表的对应关系总能转换成某种门电路。如果你刚刚熟记了各种门电路的效果,不难发现它们的对应关系非常简单粗暴。就是异或门进位就是与门。画出来就是下面这个样子,由于我们只考虑了向后进位,而没有考虑前一个数的进位,因此我们称这种装置为半加器

 

  如果将前一个进位考虑进来,只需再多一个半加器就可以了,至于为什么进位输出的加和没有用半加器而是用了一个或门,请暂停一分钟思考一下。这回我们已经建立好了一个完美的一位计算器,我们便可以自豪地称之为全加器

  一位计算器做出来之后,8位计算器就只需将全加器逐个拼起来即可,并且再次抽象整体,我们称之为8位加法器

   OK,大功告成。我不知道你此时的感受如何,不知不觉我们连续进行了3次抽象(即把之前的器件封装起来),有没有深刻体会到之前所说的你必须开始习惯这种抽象。每当一个复杂的构造被装进一个黑盒子里时,你便再也不要考虑里面的构造了,只要你做到了这一点,这三步的抽象便会是so easy。有了加法计算器,减法也就不是问题了,在本博客中将略去这部分内容,如果有兴趣,可以去了解一下计算机是如何用补码表示减法的,之后你会发现,减法就是加法。

 

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

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

  左图:当闭合开关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。因此,我们有了如下天才般的设计。

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

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

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

 

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

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

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

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

 

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

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

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

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

  • 将手动输入的时钟信号等改为上一层信号的传递
  • 需要有一个自动变化的计数器

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

  • 设计各种机器码和其对应的电路使其完成:加载、存储、相加、停止、地址跳转等基本操作
  • 利用这些机器码顺序地编写并存入内存,等待依次被读出,我们称之为程序

  计算机就是这样,没啥的。本书第17章自动操作可以说是经典中的经典,篇幅限制不好表现,但都是对已有知识的排列组合了,但你会发现脑洞大开,也会有了计算机的蓝图。虽然略过,但还是要放一张书中的原图装个逼。

 

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

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

 

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

posted @ 2017-12-10 19:13  闪客sun  阅读(8839)  评论(16编辑  收藏  举报