likes
comments
collection
share

android 筑基 - 计算机导论

作者站长头像
站长
· 阅读数 8

android 筑基 - 计算机导论

肯定有人会问为啥要知道 CPU 结构这块的内容,不知道我也不一样可以写代码嘛~ 的确如此,但是很多知识点和CPU硬件知识是关联的,比如多线程,内存管理,中断,汇编,字节码等

所以我特意去看了这块内容,这块知识点在大学里是大一:计算机组成原理这门课,但是吧我专门看了B站上大好几所大学的计算机课程,这块讲的是真的烂,既浪费时间,也没会学,有志于此的同学,我们推荐大家看国外的这门课:国外资料:40集全/精校-计算机科学速成课

另外多二进制,位计算不熟悉的话看看这个:二进制菜鸟教学

计算机硬件的一些单位和概念

后面会涉及到,有需要的可以返回来再看这里,这里集中统一写一下

  • hz 频率,1秒内执行的次数,比如 10HZ就是1秒内干了10次活

  • 时钟 CPU 是每隔一小段时间就供电一次的,结算结果就是电子流动到哪里的问题。所以 CPU 需要一种有规律的能控制的振动器来控制电源提供稳定的间断性供电,这东西就是时钟,也可以叫振晶

  • 时钟周期 CPU 时钟的振动频率,一般 1G=10亿次每秒

  • 指令周期 一个指令从取指令,解码,到执行完毕的过程,和时钟周期不能画等号,早期计算机性能不行,乘除法一次指令操作往往需要好几个指令周期,从内存读取数据也是需要复数的时钟周期的


计算机中的数学基础

人类科技其实都是基于数学这门基础科学的,计算机更是如此了

1. 逻辑数学

1847年,英国数学家布尔发表了《逻辑的数学分析》,建立了布尔代数,并创造一套符号系统,这些符号就是我们熟悉的:或、于、非了,这个不用我说了吧,记住这是现代计算机真正的基础,没有逻辑代数,就没有现代计算机的诞生

2. 二进制

二进制这个大家都知道,01010101

  • 10进制是逢10进一,所以10进制没有10这个字符,而是用进1位表示10
  • 2进制是缝2进1,没有2这个字符,而是进1位用10表示2

3. 二进制在物理层面的实现

为什么计算机要用二进制而不是3进制或是更多进制,甚至10进制呢,10进制多好啊,平时用的就是10进制,看的舒爽,接受也是最快的

不是不想用,而是受物理材料的限制。不管是继电器还是真空管都只能有2个状态,既通电不通电

计算机基本计算材料都是遵循这样的思路设计的:

  • 有2个电极
  • 中间有1个控制器,控制器控制2个电极之间能不能联通

我们给电极A通电,然后规定在一定时间内(这个时间很短,具体时间受材料自身限制)使用控制端开关设备,另一个电极B若是有电子通过就算是1或是true,若是没电子通过就算是0或是false

晶体管虽然能任意通电,但是电压差距小的话根本分辨不出来,只能有效分别0V和5V,这样看还是2个状态,所以这2进制就彻底定下来了,成了计算机通用的基础

  • 0V: 不通电,表示二进制中的数字0,逻辑代数中的false
  • 5V: 通电,表示二进制中的数字1,逻辑代数中的true

计算机的发展是随着电的发现于应用开始的,大家往后看就会有深刻思考的,这块大家不理解的下面还会说


人类早期的那些计算设备们

我喜欢西方人的思路,喜欢西方人不管讲什么,都先捋一遍历史发展的脉络。是的,不了解历史,你是不会深刻认识到现阶段设备的优势,你就不会深刻的理解它,欣赏它

计算机广义上讲是一种计算设备,区别于早期的是,现代计算机是通用化的,可以连续的,按顺序运行多段不同操作的,自动化的设备。人类早期的计算设备用现在的眼光来看都是和人们手里的计算器一样的家伙,了解这些老家伙,有助于我们更深刻的理解现在的计算机

1. 人类最早的计算设备:算盘

是的,你没看错,就是算盘,只不过这里说的不是我们中国的算盘,是更早的算盘。它来自公元前2500年的美索不达米亚,看图: android 筑基 - 计算机导论 android 筑基 - 计算机导论

上面是现代复刻出的样子

这是人类所有算盘的雏形,是最早出现的,咱们中国人的算盘也是归类在这种算盘之中的

这种设备的特点:

  • 全手工操作
  • 可以进行加减运算
  • 采用十进制,每一行算珠代表一次进位
  • 可以存储计算状态,类似于现在的硬盘

看东西我们要更深刻的去理解,这种设备在刚出现的时候,绝对是黑科技,它不光可以计算,最重要的是还能储存数据,在没有记载工具,书写工具的人类早期历史时代,这是多么的黑科技

为啥在公元2500年,距今4500前会出现这种计算设备呢,是因为当时美索不达米亚文明的高度发展。当人们成千上万聚集在一起时,一切的数字都变得开始很大了起来,注意这个很大是相对于原始人类的认知范围的,当社会规模超出了心算能力上限这个前置条件达成后,计算设备的出现也只是时间问题了。人类社会的进步就是工具和由工具进步带来的相互关系的变化

马克思说过:生产工具决定生产关系,现在再回味,真是至理名言啊,我真的懂这句话的含义了。在古代大学教育都是真正的成人教育,没有一定的社会阅历,很多理论你是理解不了的

2. 其后的4000年,人类发明了很多专项计算设备

工具的产生是为了简化我们的工作,早期的人类达不到计算工具的通用化,所以只能实现在特定计算领域的工具化,比如:

  • 星盘: 这东西大家都知道航海用的,作用是计算维度,以实现在茫茫大海上的定位问题。你看由于计算工具的进步,让以前不可能的事变成可能,由此大大改变了人类社会的历史进程,从这个角度看历史,其实挺有意思的
  • 计算尺: 辅助加减乘除运算,可以理解成9*9乘法表一类的东西
  • 各种时钟: 除了记录时间,就是计算天文事件了,西方历史上的天文预测比中国现金,就是因为大量使用了专业的计算工具

android 筑基 - 计算机导论

这些计算设备的出现其实深刻的影响了我们人类社会的变迁,大航海时代,奴隶贸易,殖民时代和由此带来的一系列战争,我们仔细想想,其实都是因为计算工具的进步,让不可能变成可能带来的

3. 古代最先进的计算器:手摇式步进计算器

步进计算器,记住这个名字,它可以算是现代计算机的鼻祖了,就算不是鼻祖,其思想也为现代计算机的诞生奠定了基础

看图:

  • 这是只能做加减法的步进计算器 android 筑基 - 计算机导论
  • 这是可以做加减乘除4则运算的步进计算器 android 筑基 - 计算机导论

1694年,在德国,一个聪明人在前人们的启发下,发明了手摇式步进计算器,其特点:

  • 手摇机械式
  • 十进制
  • 每一个齿轮有10个齿,低位的齿轮转一圈会带动高位齿轮转一个刻度,这就是这种机器的进位方式

早期的机器只能做加减法,后来人们给他加上更多的复杂机械后,可以做乘除法了。它处理乘除法的方式其实还是加减法,比如:17/5=17-5-5-5=2 就是这么计算的

因为这个机器是手摇式的,累人啊,因此用手摇步进计算器计算复杂公式还是很慢的,算一个结果可能要几个小时甚至几天。还因为是纯手工制造的精密机械,所以价格极其昂贵,限制了他的普及

但是不要小看手摇步进计算器,他在古代算是非常成功的设计了,以至于一直沿用了长达300年的时间,为此还诞生了一种职业:computer。现代这个词专指电脑,但是在古代,这个词是一种职业,一种利用手摇步进计算器这种专业计算机器进行计算的人。大概在 1900年,computer这个词才开始转变成计算机器的代指了

4. 计算表

计算表 的诞生是基于步进计算器成功的基础上的。因为步进计算器的成功,所以人们开始大量使用科学计算参与日常工作,科学计算开始深入各方各面,因为步进计算器计算一个数字需要的时间还是较长,为了提高工作效率,人们把日常需要的,所涉及到的计算结果统计到一张表里,有需要的时候直接查阅即可,比如:求8675309的平方根

  • 计算表 android 筑基 - 计算机导论
  • 火炮炮表 android 筑基 - 计算机导论

火炮炮表计算表最出名的应用了,一直沿用到现在

5. 人类首次对通用计算器的尝试:差分机

步进计算器的效率还是太低了,步进计算器一次只能进行加减乘除级别的计算,一个复杂的公式往往要耗时几个小时甚至几天才能完成计算,随着时间的发展,人们越来越难以接受这个设定了,虽然计算表的诞生缓解了这个问题,但是还是有聪明人想从根本上也就是机器层面去改进

其想法是这样的,做一台机械:

  • 它可以自由传入数据
  • 它可以自由传入计算公式
  • 它可以保存计算结果并打印出来

1823年,有一名教授把这种机器命名为:差分机,并进行了为期20年的研究,但是因为太过于复杂,最终失败了,但是这种思想开始广为传播并被当时的科学界接受,甚至英国的一位女性教授还写出了人类历史上第一段程序代码,是不是感觉很神奇,这才是18XX年,这在当时绝对的黑科技了,用现在人的说法,妥妥的外星科技啊...

  • 1991年,后人搞出来的前人未完成的差分机 android 筑基 - 计算机导论
  • 人类历史上第一段程序 android 筑基 - 计算机导论

差分机可以算是现代计算机的直接鼻祖了,其可输入公式在现在看来就是程序代码了

6. 打孔输入设备的出现:分析机

看图: android 筑基 - 计算机导论

以打孔的卡片作为数据的输入,这也是一种非常重大的进步了,它的首次出现是1890年美国人口普查,因为这种打孔输入设备参与,大大加快了工作效率。打孔输入再机器统计这种方式在当时可以做到人力统计的10倍,尤其对于人口普查这种国家级任务来说,可以大大减少人力成本支出

做这个设备的人因为这次非常成功的应用开了一家专业公司专门生产这个设备,1924年该公司改名:IBM

7. 吐槽...

惊不惊喜,意不意外,历史就是这么有意思

上面说的都是现代计算机产生之前,人类的各种尝试,没有前人不停的尝试,思考,也就没有现代计算机的诞生,大家都是站在前人的肩膀上慢慢进步的,没有什么重大技术进步是一撮而就的,这是古代中国没有产生现代科技文明的根本原因:对于我们来说,这都是奇技淫巧,低人一等,上不得台面的东西

我们从来没有思考,重视过因工具进步带来的生产力的进步,因为不重视甚至鄙视,所有相关人员就没有社会地位,没有社会地位谁又会以此为荣呢,久而久之我们落后了

其实战国时代,工匠的地位是非常高的,仅次于士大夫阶层,厉害的工匠也是有官当的,出成绩也会有丰厚的赏赐去激励工匠继续进步

但是儒家的出现打断这个历史进成,儒家崇尚的愚民思想,把持思想高地,禁绝其他一切思想,而自己天天沉醉于几个哲学家而不再睁眼看世界。这是中华民族越来越衰弱的根基

思想是什么样的,人就会什么样,懦弱的人怎么可能奋勇杀敌,愚蠢的人怎么能战胜敌人,固步自封的怎么能战胜手拿跨时代武器,学着新时代战术思想的人。想想清末的战争,也不是没有敢于死战的勇士,将军,但是我们一样还是输的裤衩都没有,究其愿意:不专业啊!,表面原因就是这么简单,专业的干死外行很正常不是


计算机核心组成部件的发展

首先我们要明确一个概念:计算机是一个物理设备,既然是物理设备,那么自然有其核心的组成部件,这些部件的进步是计算机进步的基础,我管这些叫计算机核心部件

计算机核心部件经历了3代:继电器-真空管-电子管

注意他们都带一个字,所以他们都是人类发现电,使用电后才出现的,感谢先贤们的伟大探索,要不说基础研究决定科技水平呢...

1. 继电器

看图:

  • android 筑基 - 计算机导论
  • android 筑基 - 计算机导论

继电器是机械控制的,有控制线路的,可以开关闭合的2个连着的电极,开时2个电极分离不通电,闭合时2个电极联通通电。这个开关与否使用控制端控制的,使用机械手段实现

  • 通电,电极联通,可以输出电子,表示1或true
  • 通电,电极不联通,不能输出电子,表示0或false

一般认为,继电器有2个电极,1个控制器

继电器是随着法拉第发现电磁感应现象一同发明的,时间大概是1837年,二战时开始大规模使用继电器制造计算机,但是现在就不是了,现在都是电子管。虽然计算机不再使用继电器了,但是继电器并没有原理我们,电力行业中还在大量使用继电器呢,家里用的空气开关就是继电器

这里有个继电器的演示图: android 筑基 - 计算机导论

2. 真空管

看图: android 筑基 - 计算机导论 android 筑基 - 计算机导论

用作计算机的真空管都是三极管,和继电器一样有2个电极和一个控制段,继电器开关是通过机械开关实现的,到真空管这就不是机械了,而是存电子手段了

真空管的原理是2个电极之间有灯丝,灯丝加热后会形成磁场,这样电子就可以从电极的一端到达另一端形成电流了。出了开关原理不一样外,真空管和继电器没啥不一样的,都是2个电极,一个控制器

真空管1904年诞生的,但是这东西发热量太大,很容易损坏,对然开关频率比继电器高d上不少(每秒上千次开关),但是因为太容易坏,在计算机领域并没有实现对继电器的替代,同时代的计算机先贤们既有用继电器实现的,也有用真空管实现的

目前真空管用于专业音频设备和无线电领域,计算机领域这东西早就被彻彻底底的淘汰了

3. 晶体管

看图: android 筑基 - 计算机导论 android 筑基 - 计算机导论

晶体管一样也是2个电极,1个控制端,晶体管也叫半导体材料,为啥呢,因为半导体材料的导电性可控,通电就能导电,不通电就不到电。在晶体管中2个电极之间填充的半导体材料,给这些半导体材料通电就能实现2个电极之间的联通,要是不给半导体材料通电,那不管2个电极之间加多大的电压电流,2个电极之间也不会联通

这就是半导体的含义,导电性电可控,记住晶体管中,只要给控制器通电,电极2端就能联通,电流就能通过

4. 延伸

计算机的发展就是利用这3种材料一步步走出来的,可以看出电是计算机的基础,没有电的发明和利用就没有计算机的诞生,电就是基础科学,计算机是应用科学,数学也是基础科学,没有二进制,没有布尔代数,有电有个屁用

不管是继电器,还是真空管和晶体管,都是通过控制端实现2个电极之间的联通,在一定时间内,电极联通了有电子过来了就算是1是true,电极没联通没电子过来就算是0是false,一个基础可以保存一个数,那么我们用几个组合在一起,就能保存,传递几位数,比如把8个电子管组合在一起就能输出一个8位的二进制数,那我们搞2个这样的电子管组合出来,就能来回传递8位的二进制数了,这就是计算机的基础

大家注意,XX管用于二进制数的储存和传递时不是要求一直过电的,的2个电极之间只有在一段时间内的变化才能算做是0或1,一直通电的话那就是电线了,而不是用作计算的计算机了,这一段时间的变化也叫一次开关,和字面意思一样,管材料2个电极之间平时是不通电的,只在一段时间内做一次通电操作,这就叫一个开关

显然开关的次数就很重要了,开关的次数就叫做上文说的HZ 赫兹HZ越大,计算性能越好,继电器因为是机械操控的开关,100HZ/S 就是天了,真空管则可以达到 5000HZ/S,而现代的晶体管那都是10亿HZ/S起步的...

一般我们规定认为,一次开关算作是一次任务,一次计算,注意这个计算并不等同于加减乘除,而是电子硬件领域最小单位的计算,我们管这个不可分割的,而加减乘除操作通常不是一次


现代计算机的前辈们

1. 继电器时代

看图:

android 筑基 - 计算机导论 android 筑基 - 计算机导论 android 筑基 - 计算机导论

继电器时代计算机很少,最著名的是马克1号,IBM于1944年生产,应用于曼哈顿计划。性能还可以,1秒3次加减法,乘法6秒,除法15秒,三角函数1分钟往上。

使用3500个继电器,组成了72个累加器,每一个有自己的算术单元,及23位数的寄存器

它的主要问题是继电器机械磨损严重,还非常爱吸引虫子。业界著名的bug一词就来源于继电器时代,bug本意是虫子,因为当年总是在继电器部件中发现虫子,老鼠之类的,说习惯了于是大家就把bug当做计算机出问题的代称了

2. 真空管时代

用真空管做的计算机还是挺多的,最著名的几个是:

  • 巨人1号 - 1943投入使用,用来破解纳粹通信,它有1600个真空管,是世界上第一个可编程计算机,编程方法是把几百跟线插入插板,以此来编程计算机操作
  • ENIAC - 1946年,每秒5000次加减法
  • AN/FSQ-7 - 1955年,用于美国空军的弹道导弹防御计划

android 筑基 - 计算机导论 android 筑基 - 计算机导论 android 筑基 - 计算机导论

真空管这东西按说性能比继电器强多了,但是真空管太爱坏了,时刻都得更换真空管部件,所以即便是194X年代,曼哈顿计划还是选择使用了继电器计算机

3. 晶体管时代

现在计算机也是晶体管的,这里咱们说说晶体管计算机的开端:IBM 608,1957年投入使用,第一个全晶体管计算机,有3000个晶体管,每秒4500次加减法,80次乘除法

这家伙最NB的是全球第一台面向个人的计算机... android 筑基 - 计算机导论


计算机电路设计的基本单元 - 逻辑门电路

上文书我们说计算机元器件只能表示2个状态,所以才使用2进制,但是这不是全部。另一个决定性因素是对于2进制,我们早早的就有了专门的一个数学分支处理true和false,疏通解决了其中的逻辑运算法则,这个数学分支叫布尔代数。来自于19世纪英国数学家,逻辑代数拓展自古典数学家亚里士多德基于哲学的逻辑方法

布尔代数第一次出现是在逻辑的数学分析这本书中,详细阐述了逻辑数学的逻辑方式,我们先回忆一下:

  • NO: 取反,1个输入数据
  • AND: 与,2个输入数据
  • OR: 或,2个输入数据
  • XOR: 异或,2个输入数据。这个解释一下,就是2个数据不一样才是true,否则就是false

记住,先有理论基础,才有实践工程。计算机是先产生了相关的数学基础,后手才出现的工程发明应用。若是想实现3进制计算机,那么先得把相关的3进制数学逻辑理论搞出来才行,要不也没法设计硬件电路

大家再回忆下上文我们说的电子设备:继电器、真空管,他们的通用设计思路,2个电极,中间插一个控制端控制电路是不是能闭合。电极一头接电,另一头输出电子,以有电子通过和没电子通过代表2种状态,以此来表示2进制

控制器是重点,因为她可以控制整个电路,因为电极的一端永远是接电的,一端永远是不接电,等待电子通过的,所以只有控制端是我们可以玩出花样的

我们把给控制端通一次电作为一个数据输入来看待,给控制端接电那就代表给电路输入一个1或true的数据,不给控制端接电那就代表给电路输入一个0或false的数据

但是晶体管和他俩有区别,大家不要被上面晶体管的图误导,那是把晶体管作为单独的电路板上面的元器件使用的样子,用于计算机制作的晶体管其实是有区别的,大家把晶体管按照电路图来理解就合理了,CPU中的晶体管都是相互连接的,大家就当是在看电路图,晶体管不再是单个的器件了

先看图: android 筑基 - 计算机导论

CPU中的晶体管里面的2个电极其中一个不再作为输入电流使用,晶体管专门有一个电源输入的接口,此接口还用于连接其他的晶体管(连接的是另一个晶体管的输出电极),晶体管的2个电极全部用于输出数据,我们根据情况选取一个电极作为输出数据端

下面的电路我们都以晶体管为例

1. NOT 逻辑门电路

NOT逻辑电路实现很简单,取反就行了呗,因为只有一个输入数据所以用一个晶体管就行了,看图: android 筑基 - 计算机导论

这里我们使用上面那个电极作为输出端,实现逻辑如下:

  • 控制器接电: 输入数据为:true/1,整个电路会联通,电流会从下面的电极流出,上面电极的没有电流输出,所以输出数据是false/0
  • 控制器不接电: 输入数据为:fasle/0,整个电路会中断,电流只能从上面的电极流出,所以输出数据是true/1

这就是NOT逻辑电路,很简单不是,一个晶体管搞定

NOT 逻辑门在电路设计中使用这个符号替代: android 筑基 - 计算机导论

2. AND 逻辑门电路

AND就是2个数据了,因为1个晶体管只有1个输入端,1个输出端,所以这里我们需要使用2个晶体管,看图: android 筑基 - 计算机导论

2个晶体管串联,前一个电极输出端和下一个电极相联,为的就是能让上一个电极的电流也就是结果可以教给下一个电极,CPU设计中基本都是这种晶体管相联的设计思路,要习惯,2个控制端作为2个数据输入端,最后一个电极的输入端为最终结果的输出端,逻辑如下:

  • 2个控制端都给电,那么输入数据都是true/1,因为晶体管是串联的,电路都是联通的,所以最后有电流输出,结果是:true/1
  • 不管是第一个还是后一个控制器还是2个都不给电,也就是只要有一个输入数据是false,整个电路就不是同的,结果是:false/0

AND 逻辑门在电路设计中使用这个符号替代: android 筑基 - 计算机导论

3. OR 逻辑门电路

一样因为有2个数据,所以要使用2个晶体管,看图: android 筑基 - 计算机导论

这里就不是串联了,而是2个晶体管并联,逻辑很简单直接说了,只要有个晶体管的电路是通的,那么最后结果就是true/1

OR 逻辑门在电路设计中使用这个符号替代: android 筑基 - 计算机导论

4. XOR 逻辑门电路

到这里就开始有难度了,因为逻辑复杂度上升了,2个晶体管就不够用了,逻辑代数里 XOR 的逻辑是用 AND/OR/NOT 逻辑来实现的,所以 XOR 的逻辑电路就用的是上面3个逻辑电路的组合合成的,看图: android 筑基 - 计算机导论

XOR的逻辑是2个输入必须不一样结果才是true,否则即便2个输入全是true,结果一样也是false,电路逻辑如下:

  • 2个输入全是true: 那么1的结果是true,2的结果是false,3的结果是true,4的结果是false
  • A是true,B是false: 那么1的结果是false,2的结果是true,3的结果是true,4的结果是true
  • A是false,B是true: 那么1的结果是false,2的结果是true,3的结果是true,4的结果是true
  • 都是false 那么1的结果是false,2的结果是true,3的结果是false,4的结果是false

XOR 逻辑门在电路设计中使用这个符号替代: android 筑基 - 计算机导论

最终 XOR 逻辑门电路用了4个逻辑门电路,共7个晶体管

5. 总结

到这里大家有什么感觉没有,有的人可能会问,逻辑这东西又不是1+1,我们要的是能计算数字的,这东西有啥用?大家还记得2进制不,我们用这几个逻辑门电路就能实现加减发的运算,大家可以自己想想,true 不就是1嘛,false 不就是0嘛,只要我们解决了进位的问题,加减法还是问题嘛~

整体计算机的电路设计基础就是逻辑门电路,区别是用多少个电路实现我们想要的逻辑或是计算结果,用多少个晶体管的事,现代计算机CPU都是几亿个晶体管起步,可以把单个晶体管做到纳米的极小体积,晶体管体积越小,相同面积下就可以放置更多的晶体管,性能就会更好。当然,当制程工艺走到头时,我们就得寻找其他的路来提高CPU性能了

现在AMD是7NM工艺了,理论2NM就是极限了,不知道今后CPU的进化是个什么样子...很期待啊


CPU 电路设计基础一:ALU 计算单元

ALU 计算单元,使用上面我们完成的逻辑门电路,通过组合的方式实现数学计算,比如最简单的加法计算

有的说ALU有2部分:计算单元和逻辑电路单元,从现在的 CPU 设计看大家把 ALU 成纯数学计算单元就成,逻辑电路不在 ALU 中,是另外一个部件:控制器

大家顺着上一节的思路想,其实这很简单,就是非常巧妙的使用了这4种逻辑门电路,不学的话我也不知道原来计算机电路设计中技巧占据如此重要的地位

1. 半加器

我们先实现加法器,使用逻辑门电路组件搭建能够计算加法的电路,这个就是半加器,至于为什么有个半字,大家自己想呀,挺有意思的~

设计思路其实非常简单:

  • 因为要计算2个数据,所以要有2个输入端
  • 2进制每个位上就2个数,不是0就是1,只要进位就用0表示,不进位就是1
  • 因为这个逻辑,所以一个 XOR 逻辑门就行了,0+0 是 false,1+0 是 true,1+1 是 false
  • 1+1之后,个位是0,但是会进位,我们必须告知下一位这里有进位,所以输出端除了要有结果外,还要添加一个端口表示是否进位

所以设计图如下: android 筑基 - 计算机导论

AND 逻辑门计算进位,只有 true+true 结果才是 true 对不,大家想一下,详细不再展开了,说到这个程度大家都能明白,不明白的去看上面的逻辑门电路部分

这个半的意思,就是会输出进位信息,但是在计算数据时不接收进位数据,所以这个半加器可以用来计算个位,那十位,百位呢,自然是有的,这个叫:全加器

半加器用这个符号表示: android 筑基 - 计算机导论

总结下半加器特征:

  • 2个数据输入端口
  • 1个数据输出端口
  • 1个进位输出端口

2. 全加器

上面我们说了半加器只能计算个位,但是我们要是只想计算个位加法的话何必用计算机呢,自己脑子不香吗!!! 所以啊半加器绝对不是我们的最终目标,我们的最终目标是非常大的数字之间的加法计算,这是我们脑子没法很快计算出来的数字,这才是计算机存在切发展的意义,所以我们要面临一个问题,怎么实现个位之上的高位位数的加法

当然了~不用大家自己再去发明创造了,大家思考一下就行了,本文的主要目的还是在于科普,大家跟着我写的思考脉络就行了,这个比半加器更NB能计算高位的就是全加器

直接看图: android 筑基 - 计算机导论

CPU电路设计其实和写代码一样大量使用了封装复用的思路,从逻辑门开始使用一个个晶体管组成,然后一个个逻辑门电路组成了更复杂的逻辑门电路和半加器

同样全加器一样也是在半加器的基础上实现的,半加器的缺点是不能接收低位传过来的进位信息,这里我加上就行了,其他的思路不变

上图的例子是计算十位数字的计算(十位这里不准确没,因为不是十进制,但是这样写大家更好理解)

  • A/B 所在的半加器先本位数字的加法,有进位输出进位
  • C 所在的半加器计算的是本位数字加法进位过后遗留的数字和低位进位的数组的加法
  • 因为2进制的特征,所以不管什么样的数字,进位只有一次,即便再加上低位的进位数字也是一样,进位最多只能是1,所以最后用了一个OR逻辑门来计算进位

其实我们就是用2个半加器,一个OR逻辑门来实现了一个全加器,简单不,难的是前辈门怎么想出来的...

3. 使用半加器和N-1个全加器可以实现N位的加法计算单元

多个全加器+半加器就能计算任意位数的数字的加法了,实现图是这样的: android 筑基 - 计算机导论

这是一个可以实现8位2进制加法的ALU计算单元,详细不解释了,都能看懂

计算单元有自己专属的单词:ALU,每个ALU都是专门设计来实现一种类型的计算,上面我们用加法举例,那么这里就是加法的ALU。不仅是加法,减法甚至乘除法一样可以实现自己专属的ALU计算单元

4. 世界上第一块独立的 ALU:intel 74181

1970年,世界上第一块独立的 ALU:intel 74181 诞生了,它首次对ALU实现了高度封装,使ALU成了一个独立的芯片。在此之前都是把ALU和其他电路混合坐在一起的,修改,重新设计费时费力,于是更高程度的封装诞生了

android 筑基 - 计算机导论

android 筑基 - 计算机导论

当然像 74181 这样的 ALU 都属于早期 ALU,只有独立的加减法计算单元,乘除法虽然可以做,但是像老式步进机一样乘除法使用加减法模拟的,所以计算效率很差,这是早期ALU的特征。优点是设计简单,晶体管少,成本低廉,至今像恒温器,遥控器,微波炉这样的家用电器的ALU还是这种设计的,便宜嘛,智能的就当我没说~

5. ALU 补充

电路设计中 ALU 也是有自己的专用的图标的 android 筑基 - 计算机导论

相对于上面是说的那个8位的加法ALU,完成的设计还包括更多输出端口,有3个:

  • OVERFLOW: 溢出,其实就是进位,比如2个8位的数据计算要是结果是9位,那么这个标记就是true表示数据溢出,一般现代电路碰到这种情况都是自己死机
  • ZERO: 2个数据是否一样,做==这种计算时效率会快很多
  • NEGATIVE: 比大小的,做><这种计算时效率会快很多

这是最传统的电路设计,现代电路设计发展了更多,尤其是CPU电路设计,现代CPU设计中ALU的概念被拓宽了,和我们这里说的有些差异了,但是基本设定没变


CPU 电路设计基础二:内存单元

看了上面我们知道了计算机如何在物理层面计算加法,也能由此想象减法,乘除法的计算了,但是结算的结果肯定是需要保存的,那么计算机物理层面是怎么保存2进制数据的呢?上面的电路都是通电一次输出一个结果,下次通电再计算另外一组数字,也没发看出怎么能够储存数据的啊...

哈哈,这里其实不难想象,学习多线程时我们知道线程能不断执行的核心就是一个不会结束的循环,不管封装层怎么变化,这个循环是不会跑的,那么我们发散下思维,其实循环也是可以用于电路设计中的

1. 存储锁

电路是物理的,不像我们写代码一个for,while(true)就能简简单单实现循环的,电路是电子再导电材料上流动,是有方向性的,是单向的,所以怎么实现电流的循环流动呢?大家想想仓鼠,不挺的转圈圈,呵呵,核心就是我们给电流造一个圈,让它在这个圈里跑就行了

这个圈很好实现,只要把逻辑门的输出和输入连上就行了

  • OR 循环逻辑门: android 筑基 - 计算机导论 OR 的特性,有一个是1结果就是1,A点输入数据,A输入一个1,那么结果不管B是啥,这个1就会在B和output组成的环路中循环跑,只要电不消耗完,就相当于永久储存了一个1位的数字1
  • AND 魂环逻辑门: android 筑基 - 计算机导论 AND 的特性,2个都是1结果才是1,不管A输入什么,只要B是0,这个结果就一直是0,这个B和output组成的环路中就一直是0,只要电不消耗完,就相当于永久储存了一个1位的数字`0``AND-OR

我们利用上面 OR/AND 的闭合逻辑门组合在一起可以实现永久存储一个1位2进制数字的新的电路逻辑:AND-OR 锁,也叫储存锁,因为它锁定了一个值, android 筑基 - 计算机导论 android 筑基 - 计算机导论 允许写入线不开,你写0还是1都不管用,在上面那个 AND 逻辑门就被卡住了,读写是随时都可以的,因为输出线和循环电路相联的

储存锁 有自己的电路图标: android 筑基 - 计算机导论

2. 寄存器

首先明确概念:

  • 储存锁: 只能存储1bit(位)数字
  • 寄存器: 可以储存任意位数的数字,即可以储存一个完整的数字,至于能储存多少位,看设计
  • RAM: 打包封装许多个寄存器,可以存储一大堆数据

这2个概念搞清楚,下面就好理解了

储存锁 毕竟只能存1位数字,这大大脱离了实际,不能满足我们恶需求,于是我们又进一步,我们把多个储存锁连在一起,以实现能够储存多位数据的目的: android 筑基 - 计算机导论

图示是以8位储存举例的,每一个储存锁存1位,8个存储锁加起来就能储存8位数字了,只要能保持同时写入,同时操作就没问题

我们用一根线把8个储存锁的允许写入线连起来,每一个储存锁有专属自己的一根数据写入线。我们要存8位数字的话需要9跟线,要是256位数据呢,需要257跟线。实际上这会大大增加电路设计的难度,线太多是会极大影响性能,占用大量电路面积的,实际很不理想

为了解决这个问题,科学家们奇思妙想,把矩阵应用在了这里: android 筑基 - 计算机导论

android 筑基 - 计算机导论

android 筑基 - 计算机导论

我们把256个储存锁按16*16排列起来,每一行,每一列都有一根通用的允许写入线连接自己所属行或列的储存锁,只有交叉点的那个存储锁的才能写入数据。详细看单个储存锁的电路设计,添加了一个 AND 逻辑门,行和列分别做为2个输入,只有交叉点的储存锁2个值才是1,很巧妙的实现。这样一来,256个储存锁,只用2条线就能搞定

但是大家不要混,这个256个储存锁组成的矩阵还是只能同时写入读取1位数据,只是有256个位置罢了,可以存储256个不同数字的其中1位,这不是寄存器的,注意不要混,不清楚的再去看看概念 android 筑基 - 计算机导论 图示的256位内存,是指能存256个数字,不是指01101100这个8位,图示写的不太好

现在我们还是想存一个8位数字的话,把8个矩阵连在一起就行了,这样就能实现一个存储256个8位2进制数字的内存单元/寄存器了 android 筑基 - 计算机导论

3. 内存地址

上面看着热闹,还有一个点没说,就是内存地址,现实中我们都需要门牌地址才能快速找到一个地方,内存也是,我们淂能快速找到具体的某一个储存锁,就得给设计一套地址系统出来。其实这都是现成的,行数列数就是地址,16行5列,使用矩阵中交叉点的位置,我们给储存锁添加序号

16*16的储存锁矩阵,一共可以存储256个数字,8位2进制数就可以包括了

  • 8个储存锁矩阵连在一起存储8位数据,这个8位叫做:内存位宽
  • 储存锁矩阵大小16*16,可以存256个数字,这个256叫做:内存地址范围

计算时内存地址范围要是超出了,比如设计是256,你来个257,这叫做内存溢出,电路设计时会直接死机,即便现在也是如此,android 手机不就是内存经常溢出嘛,你搞个循环试试,一会内存就满了

4. 1980年一个4M的内存

我们结合一个真实的RAM内存来看看

  1. 这是一个4M的内存,1980年生产,PCB基板上封装有8个内存颗粒 android 筑基 - 计算机导论

  2. 放大每个内存颗粒,分成32个块,每一个块就是一个完整的,单独的寄存器 android 筑基 - 计算机导论

  3. 放大每个寄存器,分成4块 android 筑基 - 计算机导论

  4. 再放大每一块,这每一块就是上面说的锁存器矩阵了 android 筑基 - 计算机导论

可以看到图2中,被电路引脚隔开的就是单独的寄存器结构了,图3中每一个寄存器有4个锁存器矩阵,那么可以知道该内存的位宽是4,联想上面介绍的 inter 74181 这块ALU就是4位的,时间也对的上,都是同一个时代的东西,所以这样看是没有错的,内存的位宽都是和ALU处理的最大位宽一致的

  • 每一个锁存器矩阵是 64 @ 64 = 4096 的
  • 每一个寄存器的大小就是 4096 @ 4 = 16384
  • 每一个内存颗粒的大小是 16384 @ 32 = 524288
  • 整个内存的大小就是 524288 @ 8 = 4194304 / 1024 = 4096K / 1024 = 4M

内存的发展很快,SDRAM,DDR1,2,3,4,5,6,他们的区别是用不同的逻辑门,电容器,电荷捕获或忆阻器来做锁存器,但是基本的矩阵结构思路都是一样的

经过上面的学习,大家其实都能感觉到了,现代计算机的设计就是再晶体管上一层层精妙的抽象,底层实现都很简单,经过大量的逻辑抽象、封装,来完成越来越复杂的设计,这点和代码是一样的,因为不管是电路设计也好,还是软件编程也好,都是属于工程设计工作,不是研发,所以有趋同的逻辑,手段,不知道大家知不知道代码中著名的设计模式就是从建筑行业借鉴过来的


经典计算机结构

计算机核心的东西就是 CPU 和 内存,看懂了上面的同学应该都明白其实我们已经实现了 CPU 和 内存 核心的电路实现,更多的是更加精巧的设计,但是基本原理是不会变的

1. 冯诺依曼结构

冯诺依曼结构是现代计算机构型的基础思想,既:程序和数据都存储在一起,就是内存中

电脑大家都熟悉,不同的软件可以干不同的事,提供不同的功能,这实现了可执行程序的动态切换,这在早期计算机上是不可能实现的

30-50 年代的电脑没有这么高级,那会没有现在的高级语言,也木有现在的程序 app,程序逻辑都是用硬件电路来实现的,机器想执行不同的程序需要更换硬件插板,和红白机玩游戏换卡带一个意思,后来冯诺依曼这个人就提出了一种设想,把程序像数据一样存在内存中,只要内存够大,我们就可以灵活的切换程序了,这样可以大大减少计算机体积和成本

2. 机器指令

为了实现冯诺依曼结构这个巧妙的设计,我们就必须使用一种方式把我们想要的逻辑操作用2进制的方式存在内存中,所以机器指令诞生了

机器指令:给计算机看的一种语言。在计算机中是最根本的,直接操作硬件执行逻辑操作的一段段命令,适用2进制的方式存储。机器指令一般也叫机器码,有自己的位数,一般位数都是计算机CPU一次能处理的最大数据位数相同的,这样做是为了性能考虑

以8位指令为例: android 筑基 - 计算机导论

  • 前4位表示要执行的操作类型
  • 后4位代表数据在内存中的地址
  • 一般现在都是32/64位指令的,看操作系统走,除了前面的操作码,后面可以跟多个数据的内存地址,具体要看指令是什么样的,计算机发展到现在已经有几千条指令了...

最简单的操作码看这个,这个是最早期的了 android 筑基 - 计算机导论

比如:LOAD_A / 0010 1100这个机器码的意思是把数据内存中1100这个位置的数据加载到寄存器A中

OK,大家先这么理解

3. CPU 经典构型

上面我们学习过了ALU内存,寄存器了,但是这些硬件电路都只能完成各自独立的任务,比如ALU只能完成计算任务,自然有人会想到把让门组合到一起形成一个完成的功能回路,CPU因此诞生了

这是最早的独立CPU,inter 4404,1971年诞生的,自此奠定了CPU的样子 android 筑基 - 计算机导论

CPU 总的来说:是由一堆各自完成特定功能的硬件组件和指挥协调他们的控制器组成的

经典的CPU结构图如下(这是早期8位cpu的): android 筑基 - 计算机导论

  • ALU: 这个大家都熟悉了,接收数据计算的,根据指令集的不同,ALU会有完成不同的计算任务的电路组件,这些组件统一在一起形成了完整的ALU
  • 寄存器: 这个用来临时存储数据的,比如执行一个加法计算的,需要把数据从内存中读出来,保存结果写会内存中,这里寄存器就起到一个临时仓库的功能
  • 缓存: 这个大家更了解了,内存因为离着CPU远,硬件上也不是最好的,所以内存的读写速度很慢,读一个数据都要CPU等待多个时钟周期才能完成,CPU要是不搞个缓存把需要的数据都打包一份过来,光是等内存读写都能让人绝望,再者CPU现在都是有多级缓存的,并且还是性能最好的缓存,为此名字都不一样,内存就要内存,性能再好一个档次的叫告诉缓存,最好的那部分才能叫寄存器,这里寄存器都是泛指一类事物了
  • 控制器: 下面会仔细说

4. 控制器

控制器要细说一下,因为大家熟悉也不知道这个东西是干啥的。ALU、内存都只能干自己的事,无法完成CPU的整体逻辑,所以就需要一个指挥中枢来控制CPU中的各个部件,所以控制器就诞生了

不仅如此,控制器还要区分出指令是要做什么事,然后操作CPU中的每个组件来完成具体的功能,是功能落地的直接体现,那么控制器是如何区别指令的

指令上面我们看到了,就是一串二进制数:0010 11000010代表具体的指令,控制器是硬件的,又不是人怎么知道0010是个啥操作,所以这里我们有回到逻辑门了,计算机的一切都离不开逻辑门,这里也是一样的

控制器针对每一种指令都会设计一个专门的逻辑门电路,针对不同的功能,这块逻辑门电路会连接不同的硬件部分,开启不同的控制线以完成任务

用 0010 举个例子,0010 是 LOAD_A,功能是把内存中指定位置的数据读取到寄存器A中:

首先:控制器会有一块专门的逻辑门电路来判断是不是0010这个二进制数 android 筑基 - 计算机导论

大家看到这个逻辑门电路了没,就是这么简单暴力哈。所以啊 CPU 支持的指令越多,控制器电路越多,面积就越大,而 CPU 的大小一般不会变化,所以控制器占的面积越大,留给 ALU 计算单元的面积也就越小。大家都知道 GPU 的计算能力远远高于 CPU 吧,其中一个原因就是 CPU 支持的指令往往有数千个,控制器的面积小不了,没法像 GPU 那样存催为了计算而设计

所以 CPU 也叫通用计算器,这里又可以牵扯出复杂指令集和简单指令集来了,他们的区别是简单指令集使用简单指令来模拟复杂指令的计算,这样以减少控制器的面积和设计难度,但是这样性能上就要弱不少。像X86 桌面级别 CPU 都是复杂指令集的,有几千条指令。ARM 和 龙芯 CPU 都是简单指令集,虽然 CPU 面积小,功耗低,但是性能比桌面 CPU 差很多就是因为控制器面积这个原因

然后:指定逻辑门电路会连接不同硬件 android 筑基 - 计算机导论 红框圈出来的部分就是,会打开内存读控制线,连通内存数据线到寄存器A

5. 一个指令的执行过程

CPU 的执行过程基本上就是:取指令,解码,执行 这3件事,CPU淂知道要知道上级下达的指令吧,所以淂把内存中的

还是上面那个 0010 1100 为例,大家看图: android 筑基 - 计算机导论

  1. 指令地址寄存器从内存中读取指令,该寄存器保存了指令在内存中的地址,程序也是保存在内存中的嘛,所以读取指令和读取 数据是一样的方式,只不过程序是一段代码,是一串指令组成的,这串指令在内存中是顺序存储的,所以这里指令地址寄存器只要存储开头指令的位置就行啦,其实这个也叫程序计数器
  2. 指令寄存器保存指令,从内存中拿到的指令淂保存在CPU中才行
  3. 控制器解析指令,上面说过控制器怎么识别指令的了
  4. 开打内存读取控制线,联通寄存器A
  5. 寄存器A保存数据
  6. 指令地址寄存器+1,不加一怎么指向下一条指令呢

OK,过程就是这样子的,计算机看着很玄幻,怎么就能自己计算呢,其实也是相关硬件严格按照操作顺序一步步执行的,和流水线其实是一样的,任何事情搞清楚原理其实都不难

6. 专业术语

  • Ad(Address) 形式地址
  • DR(Data Register) 数据寄存器
  • AR(Address Register) 地址寄存器(MAR)
  • IR(Instruction Register) 指令寄存器
  • BR(Buffer Register) 缓冲寄存器(MBR)
  • ID(Instruction Decoder) 指令译码器
  • PC(ProgramCounter) 程序计数器

1. 取指令阶段: 取指令(Instruction Fetch,IF)阶段是将一条指令从主存中取到指令寄存器的过程。程序计数器PC中的数值,用来指示当前指令在主存中的位置。当一条指令被取出后,PC中的数值将根据指令字长度而自动递增:若为单字长指令,则(PC)+1àPC;若为双字长指令,则(PC)+2àPC,依此类推

//PC -> AR -> Memory
//Memory -> IR

2. 指令译码阶段: 取出指令后,计算机立即进入指令译码(Instruction Decode,ID)阶段。在指令译码阶段,指令译码器按照预定的指令格式,对取回的指令进行拆分和解释,识别区分出不同的指令类别以及各种获取操作数的方法。在组合逻辑控制的计算机中,指令译码器对不同的指令操作码产生不同的控制电位,以形成不同的微操作序列;在微程序控制的计算机中,指令译码器用指令操作码来找到执行该指令的微程序的入口,并从此入口开始执行

//                        { 1.Ad
//Memory -> IR -> ID ->   { 2.PC变化 
//                        { 3.CU(Control Unit)

3. 访存取数阶段: 根据指令需要,有可能要访问主存,读取操作数,这样就进入了访存取数(Memory,MEM)阶段。此阶段的任务是:根据指令地址码,得到操作数在主存中的地址,并从主存中读取该操作数用于运算

//Ad -> AR -> AD -> Memory

4. 执行指令阶段: 在取指令和指令译码阶段之后,接着进入执行指令(Execute,EX)阶段。此阶段的任务是完成指令所规定的各种操作,具体实现指令的功能。为此,CPU的不同部分被连接起来,以执行所需的操作。例如,如果要求完成一个加法运算,算术逻辑单元ALU将被连接到一组输入和一组输出,输入端提供需要相加的数值,输出端将含有最后的运算结果

//Memory -> DR -> ALU 

5. 结果写回阶段: 作为最后一个阶段,结果写回(Writeback,WB)阶段把执行指令阶段的运行结果数据“写回”到某种存储形式:结果数据经常被写到CPU的内部寄存器中,以便被后续的指令快速地存取;在有些情况下,结果数据也可被写入相对较慢、但较廉价且容量较大的主存。许多指令还会改变程序状态字寄存器中标志位的状态,这些标志位标识着不同的操作结果,可被用来影响程序的动作

//DR -> Memory

6. 循环阶段: 在指令执行完毕、结果数据写回之后,若无意外事件(如结果溢出等)发生,计算机就接着从程序计数器PC中取得下一条指令地址,开始新一轮的循环,下一个指令周期将顺序取出下一条指令

7. 发散思维

CAS 原子性,我们在学习多线程时碰到的,每一条机器指令就是计算机不能再分必须完成的最小的指令单元了,


CPU 高级设计

上面说的是 CPU 最基本的设计,但是这么多年过去了,有很多优化手段加入进来了

CPU 中 ALU 的计算能力其实是能够满足我们需求的,晶体管的物理性能其实很早就开发的差不多了,频率 5G 基本是桌面 CPU 的极限了,当然现在一般也不会设计到这么高,一般都是3G左右,那么 CPU 是如何提高性能的呢?

ALU 很多时候是空载的,都在等待数据的加载,数据总线相比 ALU 本身的计算是很慢的,内存大家觉得很快吧,SSD 用的爽起。但是其实从内存中读写一个数据往往需要消耗多个时钟周期,在这期间 ALU 就是闲着了,硬盘那就更慢了

所以提高 ALU 使用效率就是性能增加的一个重中之重,有一些设计手段,大家需要了解下

缓存命中

每次从内存中取数据都是几个时钟周期,这根本就等不起嘛,但是内存带宽还是很足的,所以我们一次性的把这段时间计算所需要的数据都加载到 CPU 的高速缓存中,这样可以大大减少和内存通信的次数,减少因为数据等待而引起的 ALU 空载了,这个技术就叫做缓存命中

流水线设计

一条指令的执行分为:取码,译码,执行 这3个过程,要执行多条指令如图所示: android 筑基 - 计算机导论

这其中存在极大的浪费,取码是指令地址寄存器去干的,译码是控制器干的,执行是ALU干的,每个部件干活时其他部件都是闲置的,我们要是可以渐进式一级接一级的衔接着干那就不会有硬件闲置的问题了

如图: android 筑基 - 计算机导论

我们给每个 ALU 都配备相应的控制器和指令地址寄存器,那么他们就能组成一个工作组,可以马不停蹄的一起干活啦,谁也不会再闲着啦,ALU 我执行的时候,控制器可以译码下一跳指令,这时指令地址寄存器可以去取下下一条指令,这样就能像工业流水线一样啦,真的做到马不停蹄了。波音著名的脉动生产线其实就是这个思路

乱序执行

指令之间其实有时候有严格的上下文关联,我本次计算的结果需要参与下一次的计算,那么按照上面这样流水线设计是有问题的,可能会产生数据不正确的问题,那么我们可以预先优化指令的执行顺序,只要能保持最后结果没问题就行啦。多线程学习时大家都会碰到这个问题吧,不了解这里很多人学不明白的

这块内容大家仔细理解下,多线程中说的乱序执行就是这东西,CAS 原子性说的是每一条指令,高级语言的一行可以比如 java ,可以被 JVM 编译成多行机器语言