每一个时代,都不会亏待会学习的人。java
你们好,我是 yes。程序员
对于咱们程序员来讲计算机的重要性不言而喻,相信你们对计算机内部也有必定的了解。服务器
可是你们有没想过为何一堆逻辑门组合起来就能运算了?它是如何运做来实现加减法的?微信
为何 cpu 会不停地取指执行?是什么在驱动着它?异步
今天我就和你们一块儿来探索一下底层的奥秘,可是术业有专攻,咱们大体的了解一下便可,不少细节不清晰也不影响。工具
不过相信经过这篇文章你会对底层有不同的认识,包括运算单元、内存、时钟、地址、溢出、补码等等。学习
先打个预防针吧,这篇文章有不少电路图,你可能感受这啥啊,和咱们开发有关系吗?测试
看下去你会懂的,虽然说平日里咱们都是 CRUD Boy,可是咱们也得时刻保持着好奇心,要有求知欲和探索精神。编码
正文
这个故事得从「电」开始提及。spa
生活中电无处不在,而它却时刻保持着神秘感,为什么插上电咱们的屏幕就会亮?咱们的服务器就能跑?
电是如何来的?
电起源于电子的运动,咱们知道一切物质都是由原子组成的,而原子又是由中子、质子和电子构成。
在某种状况下电子从原子中电离出来,这样电就产生了。
质子和电子都具备带电荷的特性,质子带正电荷、电子带负电荷。
而异电相吸,同电相斥,当质子数和电子数相等的时候是最稳定的,若是数量不平衡也会往趋于平衡的方向发展。
像雷雨天气,云层下层积累电子而云层顶层失去电子,而闪电就是大量的电子迅速从一端转移到另外一端产生的结果,为了趋于平衡。
题外话 :细心的朋友可能看到这原子核质子不都合在一块儿了啊,不是说同电相斥嘛?这是由于有个叫强内力的玩意汇集了它们,释放核能的原子核裂变就是由强内力致使的。
相信你们都作过电池点亮灯泡的物理实验。
这其实就是电池发生化学反应,在负极产生多余的电子,而后经过回路中的原子相似接力的形式,一个原子获得电子以后会传递给相邻的另外一个原子,如此循环传递电路就造成了,最终经过灯泡到达电池的正极。
改装下再套上个外壳,手电筒就这样被造出来了。
而手电筒不只仅能够用来照明,还能用来通讯。相信你们都看过相似的电影场景,我这手电筒的光闪三下我们就上!
而说到这样简易的通讯就不得不提摩尔斯电码,相信你们也从各渠道对摩尔斯电码有必定的了解,好比「星际穿越」这部贼好看的电影。
在 19 世纪初期,那时候的远距离通讯还得利用马车等工具长时间运输传递,人们一直在摸索即时远距离通讯的方法。那时的摩尔斯就开始埋头实验,最终发明了电报。
电报的思想和上述说的手电筒思想同样,手电筒通讯的思想是经过开关来控制灯的亮暗,而电报利用的是电磁现象。
将导线缠绕在铁棒上,而后通电以后铁棒就变成了磁铁,断电了磁性又会消失,而后再搞个发声器,经过磁性来吸引可动棒敲击发声。
通电后可动棒被拉下,敲击下方就会发出 “滴” 的声音,断电则可动棒复位,敲击上方发出 “嗒” 的声音。将快速的滴答做为点,慢速的滴答做为划。
经过导线的长距离链接就能实现远距离通讯,经过判别点和划的组合查阅摩尔斯电码表,转成最终的信息。
若是要双向通讯,就再搞一个反过来部署就行了,这就是电报机了。
不过导线是有电阻的,导线越长电阻越大,因此是有距离限制的,不过这难不倒咱们,最简单的方法就是转发一下。
在中间距离也建个电报站,而后雇一我的,获得发送方的电报信息以后,从新敲一遍发送给真正的接收方,可是这须要多余的人力,因此能够以下图所示,搞个棒子连起来带动下一个开关的输出。
这其实就是继电器原理,咱们来看看继电器是如何的设计的。
下方通电产生磁力,吸引上方的金属杆挂下,而后上方造成回路所以也通电了,这样远距离传输的微弱电流就被又一次放大输出了,因此最终的远距离电报应该是这样的。
能够看到继电器这个发明是真的巧妙。
理解了上面所述的电的生成、电报以及继电器以后咱们再来看看二进制。
基于二进制的数字系统是最简单的,只有 0 和 1,不能再进一步简化了,而简单就表明着清晰,就像开关要么开要么关。
而二进制的组合又能够表明多种可能,好比第一个 0 表示男,1 表示女 ,第二个 0 表示胖,1 表示瘦。
让咱们再回到以前的电池电灯图中,此次搞两个开关。
能够得知两个开关都闭合电灯才会亮,若是转化成二进制表示,0 表示开关断开,1 表示开关闭合,0 表示灯泡不亮,1 表示灯泡亮,总结成一张表格的话就是:
左开关 | 右开关 | 灯泡 |
---|---|---|
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
这其实就是咱们熟知的 AND 操做,若是把电路稍微改一下就是 OR 操做了。
若是把不少开关组合起来就能执行简单的逻辑任务,可是开关须要手动的去控制。
记得以前提到的继电器吗?它也能串联或者并联电路,并且能够被其余继电器联动控制,不须要一个一个拨动,所以用继电器来组合更加合适,而继电器的组合称之为逻辑门。
简单点的就像下图所示,开关闭合灯泡就会亮。
有些人以为这不是屡次一举吗,这实际上是个缓冲器,能够延迟信号,也能够放大信号,并且这个电路比较简单,实际上有不少组合,好比下图的这个反向操做,开关闭合的灯反而不会亮。
还有像这样的串联组合,只有两个开关都闭合灯泡才会亮。
固然这里的输入不必定得是开关,输出也不必定得是灯泡,只是为了更加直观的表现出来,不过这样画电路太麻烦了,因而电气工程师们就搞了个符号来表示这些电路,好比上面的串联其实就是 AND 操做,是与门。
简化一下上面的图就变成下面的样子。
若是电路图以下所示,就是并联,随便一个开关开了灯泡都会亮,这就是或门。
简化符号是这样的:
前面还提到个反向操做的,开关闭上灯泡反而不亮的叫反向器,符号以下图所示。
咱们再来看看这样的电路。
只有当两个开关都断开的状况下灯泡才会亮,任何一个开关闭合灯泡都会熄灭,这个操做和 OR 操做相反,称之为 NOR 即或非门,简化后的符号比或门多了个小圆圈,表明反向。
或者这样,组合着画也同样。
而后咱们再来看看这种电路,只有两个开关都断开才会熄灭,这和与门正好相反,称之为 NAND 与非门。
简化符号是这样的,也是多了个圆圈:
我再总结一下这几个简化图,加深一下印象。
二进制加法机
有了上面这几样东西,咱们就能够造个二进制加法机,不要小看加法,由于能够用加法来实现减法、乘法、除法等操做。
加法咱们知道会获得当前的和、进位这两个信息,例如二进制中 1 + 1,当前和是 0 ,进位 1。
进位 | 0 | 1 |
---|---|---|
0 | 0 | 0 |
1 | 0 | 1 |
能够看到只有 1 +1 进位 1 ,再仔细看看是否是和 AND 操做很像?只有 1 AND 1 结果才为1 。
AND | 0 | 1 |
---|---|---|
0 | 0 | 0 |
1 | 0 | 1 |
咱们再来看看当前和的计算
和 | 0 | 1 |
---|---|---|
0 | 0 | 1 |
1 | 1 | 0 |
你们能够在脑子里面想象下,若是拿 OR 操做来套用的话右下角结果不对,若是是 NAND 操做的话左上角结果不对,因此得两个结合一下,电路图以下。
分别经过或门和与非门以后再作与门,出来的结果就是当前和的结果,这个其实就是 XOR 异或门,简化表示就是:
因此加法须要经过两个逻辑门,分别是异或门来操做当前和,与门来操做进位,结合起来以下图所示:
这其实就是个半加器,简化的图以下所示:
那为何叫半加器?由于只能一位一位的加,而前一位的进位参与不到下一位的计算,若是要加入进位那下一位的运行就是 A 的当前位 + B 的当前位 + A 和 B 以前的进位。
所以须要改装一下,两个半加器合起来再加一个或门。
假设 A 输入 1 , B 输入 1, 进位输入 1,从最左边开始第一个半加器 S 输出 0 , CO 输出 1,第二个半加器 S输出 1,CO 输出 0,最终和输出 1,进位输出 1,结果没毛病可行,这叫全加器,简化一下图:
全加器有了,我们得组合起来,而且须要有输入和输出,咱们经过开关来输入数字,由灯泡的亮暗显示结果。
这就是一个 8 位的计算器,有 9 个灯是由于两个 8 位相加结果多是 9 位。
而后从最右边开始以下图所示接上全加器,进位接地表示 0 输入
中间的都以下接法,前一个的进位输出是下一位的进位输入。
最后一个就是把进位输出直接接到第九个灯上就好了。
此时你摆动控制面板的开关,就能够经过机器获得相加的结果。简化的画法以下图所示:
如今咱们已经造出了八位加法器了,若是要 16 位呢?简单合一下就行了。
固然真实的计算机原理差很少是这样的,不过会更复杂,好比不会像咱们的加法器,一个一个的进位加,而是会先行进位,并且也不会用继电器,而是晶体管等等。
减法怎么弄?
加法器咱们搞出来了,那减法怎么作?减法须要有借位操做。
咱们先拿熟悉的十进制来讲。假设你的帐户上限是499,你的透支额度是500,也就是说你的帐户金额范围是 -500~499 这 1000 个数字,要求不能用负号来表示。
能够看到这是个三位数,而最大值就到 499 过,说明 500~999 之间的数没用,那拿来表示负数不就刚恰好吗?
因此让 500 表示 - 500 ,501 表示 -499,以此类推。
500,501.......998,999,000,001......498,499
,让五、六、七、八、9开头的数都表明负数,并且是否是看起来还造成了个环形, 499 + 1 就变成 500 了,而后 999 + 1 变成 1000 ,可是只能三位数表示,因此溢出了变成 000。
这种处理叫 10 的补数,若是要把三位负数转为 10 的补数,就是让 999 减去它再加一,也就是说 10 的补数等于 9 的补数加一。
补数的概念:拿 9 的补数来讲,将一个数从一串 9 中减去获得的结果就叫这个数 9 的补数,好比 123 ,它是三位数 ,999-123 = 876 因此 123 的 9 的补数就是 876,若是把结果 + 1那就是 10 的补数了。
就拿 -499 来讲,咱们要转化成补数,就是 999 - 499 + 1
等于 501 ,看上面的排列确实用 501 来表明 - 499。
那减去一个数不就是加上一个数的负数吗?因此经过补数咱们就不须要作减法,只须要转成补数再相加就好了!
如今咱们再换成二进制,二进制相比于十进制就更简单了。
拿八位二进制数来讲,范围是 00000000~11111111, 对应的十进制是 0~255,但如今咱们想让它能表示负数,前面十进制的时候咱们将 五、六、七、八、9开头的正数来表示负数,对应于二进制咱们能够将第一位以1开头的做为负数。
那此时的范围就是:
若是你理解了上面的十进制转化,这个二进制确定是没问题的,这其实就是算出 2 的补数,而 2 的补数又是 1 的补数 +1。
咱们拿 125 来举个例子,125 二进制表示是 01111101,求 1 的补数就是 11111111 - 01111101
,这个减法在二进制中不须要,由于这其实就是求反,还记得上文提到的反向器吗?
取反了以后再加一,就获得 2 的补码。
因此 -125 就是 10000011。
固然这一切的前提都是数字的位数须要固定,因此计算机中的位数就是固定的,超出了就会溢出,到这里你应该能够理解计算机中的补码是怎么来的,并且理解了为何最大值 +1会变成最小值?
因此减法咱们只须要改造一下上面的加法器,给个开关表示要这个数是负数,若是是负数则进行一波反向器操做而后再 +1,以后再进行加法操做便可获得最终的结果。
乘法和除法我就不分析了,同样也能经过加减法来实现。
振荡器(时钟)、锁存器(触发器)和计数器
固然这个和咱们所认识的计算机还差不少,如今只能进行一些很是简陋的加减操做,别急咱们先来看看这个电路。
这个电路颇有意思,当你闭合开关的时候电路通了,此时因为电磁效应可动棒被吸了下来,电路就断了,断了以后磁性消失了可动棒又移了上去,这样电路又通了,如此往复。
这种电路叫振荡器,这是一个很关键的东西,记住它。
它的来回振荡其实就是在输出 0 和 1 的交替序列,画成图以下所示:
随着时间的变化在 0 和 1之间交替变化,所以也称之为时钟。
一个变化循环所须要的时间称之为周期,频率是周期的倒数,若是周期是 0.05 秒,那么频率就是 20,每秒 20 个循环,用赫兹来做其单位,因此就是 20 Hz。
咱们再来看下这个电路。
此时灯泡是不亮的。当上面的开关闭合后,左边的或非门输出 0 ,右边的或非门输出是 1,所以灯泡亮了。神奇的地方来了,此时你断开上面的开关,灯泡依然是亮的,由于左边的或非门输出仍是 0,而或非门只要有一个输入是 1,输出就是 0 。
此时若是闭合下面的开关,灯泡就会熄灭,再断开下面的开关灯泡仍旧不亮。
能够看到这个电路是有记忆功能的,你看若是你发现此时的灯泡是亮的,你就能推断上一次闭合的是上面的开关,若是此时灯泡是暗的那么上次闭合的就是下面的开关!
这种电路叫触发器,其实上面的开关就等于置位(set),下面的开关等于复位(Reset),因此这也叫 R-S触发器。
不过更有用的电路应该能记住某个特定时间点的上上一个信号是 0 是 1。
因此还须要搞个保持位,使得保持位关了以后,上下两个开关随意拨动都不影响以前保持结果(下面的图复位和置位位置和咱们电路图是相反了,不过没影响同样的)。
其实就是当保持位 0 的时候,复位和置位经过与门的输出确定是 0 根本影响不到以前的结果。
可是这样就有三位输入了,比较麻烦。从上面的观察来看有意义的输入实际上是上面开下面关,或者上面关下面开,因此必定是相反的。因此搞个反向器这样就只有两个输入了。
这个叫电平触发的D型触发器,D表示 Data,数据的输入。电平触发就是当保持位为某一个特定电平时 (例子是 1),触发器就会保存数据端的输入值。
理解了保持位以后,咱们须要引入时钟(标志为 clk),一个有规律的来回变化的时钟,当时钟从 1 切换到 0 的时候上一次操做的内容就被保存了,因此把保持位的输入替换成时钟输入。
这样的电路叫作电平触发的D型锁存器,它表示电路锁存住一位数据,并保持到未来使用,它也称之为 1 位存储器。
有了 1 位存储器,那多位存储器就很简单了,就是将多个锁存器合在一块儿,以下图是八位锁存器。
这里还须要提一下边沿触发器,不一样于电平触发器的是边沿触发器是在 0 变成 1 的瞬间记录结果,像电平触发器是在 1 的时候每一个结果都会被覆盖性的记住,在某些场景下边沿触发器的瞬时性更合适。
电路图以下,由两级 R-S 触发器连接而成,其实这种电路看不的很乱以为很复杂没事,知道结果就好了。
简化的画法以下:
而后咱们再来看下这个电路:
将振荡器的输出做为时钟的输入,而后反向 Q 端(上图中下面的Q表明反向Q,图少了一横)的输入又做为 D 的输入。
出来的波形图是这样的,能够看到 Q 的输出频率是时钟的一半,因此这种电路称为分频器。
而分频器的输出又能够是下一个分频器的输入,咱们再来看下这个图:
出来的波形图是这样的:
再填上 0 和 1:
从 Q3 开始每一列从下往上看,是否是 0000、000一、0010.... 这就是计数器,把 8 个集成一下放在黑盒中,就构成了 8 位的计数器。
固然这个计数器是异步的,后面的得等前面的通知,比较不许确,因此更好的是同步计数器,不过比较复杂,这里就不介绍了。
简单组装一下
至此咱们已经有了加法器、振荡器(时钟)、锁存器(触发器)和计数器,接下来咱们就开始组装一下它们。
好比如今咱们有一个灯泡,想测试一下八个锁存器,八个锁存器的话那么须要 3 个开关来表示具体选择哪一个锁存器,2 的 3 次方等于8。
中间的黑盒确定是拿来选择的,经过开关来控制通路,比较复杂我以为稍微看看就行,反正就是电路选择。
输入的话也不用直接用八个,因此也搞个三个开关。
内部构造我就不贴了,也和选择器同样复杂,这叫译码器,最终完整电路图以下:
而是S0、S一、S2 其实就是地址,经过地址来选择写入哪一个锁存器中,而且对应输出结果,这种配置叫读/写存储器,也称为随机访问存储器即 RAM。
由于它能保存信息,因此叫存储器,由于能根据地址选择来写入读取因此是随机。
上图电路简化图以下,能存储 8 个独立的 1 位数据。
两个 8*1 RAM 结合一下就能表示存储 8 个独立的 2 位数据。
若是是经过下面这样的组合,则能表示 16*1 RAM,那个 DI 其实就是第四根地址线,因此是 2 的 4 次方。
能够看到 RAM 阵列的存储容量等于 2 的地址数次方,而后注意下咱们图是简化了的,里面其实有不少继电器的,像逻辑门都是由继电器构成的,当断电以后电磁效应就没了,全部的触点都回归原样,这就是 RAM 为何是易失性存储介质的缘由。
我们如今已经把内存给搞出来了。
接下来咱们的目标就是把要计算的数据输入内存中,而后让加法器计算了以后把结果写回内存,而且能够再经过内存查看结果,大体的组装样子以下:
而后咱们能够将加法器和锁存器结合起来做为一个累加器,即每次加法的值存储到锁存器中并做为下一次累加的值。
有了累加器以后,咱们能够将存储器的值传到累加器中,称为 Load 装载,把下一个值添加到累加器中,称为 Add,而后将结果保存在某个位置,称为 Store。
能够经过控制面板先往存储器里面写好要操做的值而且能够经过控制面板上的灯来查看内存写入结果,而后一开始访问存储器的地址为 0000,由计数器来驱动地址的前进,而后进行相加,最终将结果存储回 RAM 阵列中,固然也须要设置中止信号。
把咱们前面定义的 Load 等操做码,转化为特定的代码来控制总体的流程(你就认为这代码会指示电路作某种操做,不必细想反正就是经过逻辑门组合产生的)。
这个操做码仅是个助记符,由于地址是固定的,而且操做码指令字节是固定长度(1个字节),因此咱们能够在每条操做后面跟上地址,总的而言每条指令(除中止)须要 3 个字节。
简单的看下图,就是在存储器地址0000处存入如下“代码”。
而且能够搞个 Jump 指令用来跳转地址,能够经过设置计数器来达成跳转地址的功能,有了跳转咱们就能作循环操做了。某些重复的指令只须要编写一次,经过条件跳转来完成循环,最终的组装示意图以下:
2-1 选择器是切换计数器的地址输入或者是计算得出的输入,经过三个 8 位锁存器来分别表明代码,地址高位和低位,上图来看可能有点绕,不理解细节也没有关系,大体的流程仍是简单的。
至此咱们其实已经组装了一台计算机了,之因此能叫计算机而不是计算器是由于它能够根据你写入存储器的指令自动取指执行,而且能够进行条件跳转和循环执行自动中止。
计算机的处理器就是咱们上面的累加器,能够称之为算数逻辑单元,即 ALU。
那个计数器就是咱们的程序计数器PC。
存储器就是内存了,输入就是控制面板,输出就是控制面板上的灯。
计算机几个核心模块就都有了。
至于前面咱们定义的操做码其实就是机器语言,而人类为了好记就会搞一些助记符来标识,发展到后来就是汇编语言,而汇编语言又太麻烦了,所以又抽象搞了高级语言,好比 C、Java 等等。
最后
这篇文章最终所描述的计算机实际上是至关简陋的,真正的计算机也确定不会这样造的,好比不会用继电器,线路也会用各类总线啥的搭建起来各类集成电路等等,ALU 也不会如此简单,会有各类并行计算等等。
主要是想借此大体的说下计算机基本的运行原理和构成,由于本质上的道理是同样的。若是要我把不少细节都说出来我也不会,我也就懂一点点点点皮毛,我也不是搞硬件的,啥模电的课我也没上过,我就会装装机的水准。
本文大量借助了《编码的奥义》一书的例子,或者说是对此书一些章节的梳理和总结,若是对原文有兴趣的能够本身购买书籍,若是以为囊中羞涩能够后台回「233」,我来帮你想一想办法。
我是 yes,从一点点到亿点点,咱们下篇见。
本文分享自微信公众号 - yes的练级攻略(yes_java)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。