计算机运行原理:从1+1等于2看电脑是如何干活的?

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 人类大脑白天工作,晚上休息;电脑白天随人一起运转,到了晚上人类按下电源键,它们也开始休息。如果CPU中的电子元件有意识,那么它们觉察到自身的硬件性能一代比一代强,软件功能一代比一代复杂,它们能意识到在它们之上,有人类在指挥和操控着这一切吗?

image.png

人类大脑白天工作,晚上休息;电脑白天随人一起运转,到了晚上人类按下电源键,它们也开始休息。如果CPU中的电子元件有意识,那么它们觉察到自身的硬件性能一代比一代强,软件功能一代比一代复杂,它们能意识到在它们之上,有人类在指挥和操控着这一切吗?


今天计算机已经可以处理很多复杂的工作了,复杂的问题我们先不考虑,先想一个最简单的问题:

计算机是如何计算 1+1=2 的?


这个问题看似简单,但通过它可以见微知著。可以说,所有计算机表面上复杂的软件运行,都是底层简单的节字叠加。通过对这个问题的剖析,我们可以了解一些底层最基础的编程基本概念,和电子计算机的运行原理。


1+1,对任何一位读者来讲都很容易,但这项数学技能是人类后天通过学习而习得的,人类生来并不会计算。那么计算机的 1+1 计算能力是如何拥有的呢?


我们可以说,是通过编程语言实现的吗?那么,什么又是编程语言呢?


像 C、C++、Java,C#、Python,这些都是编程语言,还有我们在《微信小游戏开发》中学习的 JavaScript、Go 语言,也是编程语言,并且 Go 语言还可以说是一个现代高级编程语言,相当于是互联网时代的 C 语言。


编程语言的作用是什么?为什么人类不用自己的自然语言进行编程呢,这样程序员不是就少了一项学习任务了吗?


人类语言,是人与人之间沟通的桥梁,编程语言是人与计算机之间交流的工具。计算机听不懂人类的话语——至少在目前这个时代是不可以的,人类想控制计算机,想与计算机交流,就必须通过中间角色翻译。


早期科学家通过拨动开关编程,后来通过纸带打孔编程,这些方式效率都极其低下。后来程序员发明了汇编语言,这已经是一个巨大的进步了,但汇编语言晦涩难懂,表意性又不强,编程效率不高。直到 1972 年,有个老外叫丹尼斯・里奇,他在贝尔电话实验室设计出了通用的 C 语言,编程才算彻底走出了蛮荒时代。


自 C 语言诞生之后,新的编程语言层出不穷,到目前为止全世界至少已经有 600 种编程语言。这些语言虽然复杂,但大多数并不是通用的语言,很多编程语言只是为了解决某个特定领域的具体问题而设计的,所以并不是每个语言都具备普通大众深入学习的价值。编程语言种类虽然繁多,但它们像人类的自然语言一样,有它们的共通性,往往学习了一门语言,其它语言也能触类旁通。


老话说“弱水三千,只取一瓢饮”,现在我们就以 JS——也就是 JavaScript 为例,来探究一下计算机是如何计算 1+1=2 的?对这个问题的探索,分为以下 4 步。


1 在浏览器中进行算式实验


首先,打开谷歌浏览器,右健单击页面的空白处,选择 “检查”,如图 1-1 所示:


image.png

图 1-1 谷歌浏览器右键菜单


然后,在打开的浏览器自带的开发者工具面板中,选择 Console 面板,如下面图 1-2 所示,这是一个浏览器环境下的调试面板:


image.png

图 1-2 Console 面板


除了通过右键菜单打开 Console 面板,还可以通过快捷键 Command+Shift+D(macOS)或者 Ctrl+Shift+D(Windows)打开,效果是一样的。


现在,我们在 Console 面板内输入我们的算式:


1 + 1

然后按下回车键,不出意外的话,我们会看到浏览器输出了结果 2,如图 1-3 所示:


image.png

图 1-3


1+1 计算结果


这个过程看起来很快,几乎是一瞬间完成的,但计算机内部其实已经经过许许多多复杂的运算操作了。

纵观人类进化史,从学会使用石器,到学会结绳记数,差不多用了 100 万年。但自 1946 年第一台电子计算机 ENIAC(埃尼阿克)诞生,到 1990 年第一个浏览器诞生,仅仅用了 44 年,那么,浏览器是怎么知道 1+1 等于 2 的呢?


2 开始与浏览器对话,浏览器并没有计算能力


问题探索到这里,为了使探索过程不致太过枯燥,请允许作者将涉及的相关对象拟人化。我是这样想的,浏览器应该知道答案,因为这个结果是它告诉我的。


于是我就问谷歌浏览器:“浏览器,请你告诉我,你是怎么知道 1+1 等于 2 的?”

浏览器一脸懵懂啊:“我不知道啊,是 V8 告诉我的。”


我问:“V8 又是谁,是台球馆里新的台球号码吗?”


浏览器说了:“不是的,V8 是谷歌研发的 JavaScript 引擎,你发给我的 JS 代码,都是由它执行的。”


注意:这里我们对 V8 做一个小小的拓展,如果不感兴趣可以略过不看。V8 最早是为某种有八个气缸的 V 型汽车发动机而命名的,用这个名字命名谷歌浏览器的 JS 引擎,预示着它的性能极高。它采用 C++编写,是一款开源、高性能的 JavaScript 和 WebAssembly 引擎,已被广泛用于 Google Chrome(谷歌浏览器,非开源)、Chromium(谷歌面对开发者开源的浏览器项目)和 Node.js 中。当人民使用 Goolge Chrome 浏览Web页面的时候,V8 引擎负责处理及执行页面里的 JS 代码。

JS 引擎与浏览器宿主环境是分开独立的,V8 仅提供了执行 JS 的能力,像 DOM 和其他 Web API 则由浏览器提供。开源分享精神推动了 Node.js 的兴起,2009 年 V8 开始作为 Node.js 的宿主环境引擎,得益于 V8 卓越的性能,Node.js 一经推出就获得了爆炸性发展,很多服务器端接口程序,都开始基于 Node.js 快速编写。除了 Node.js,V8 还通过 Electron 等框架,为桌面应用程序开发提供了支持,开发桌面软件也开始像编写 Web 网页一样随心所欲了。


于是我又去问 V8 引擎:“听浏览器说,计算结果是你给我的, 你是怎么知道 1+1 等于 2 的呢?人类世界上最聪明的孩子降生时,都不知道 1+1 等于 2,你是怎么知道的呢?”


V8 引擎双手一摊:“尊敬的人类大哥,我并不知道 1+1 等于几,我所有结果都是基于您的输入给出的呀?”


我一听,有点懵了,“噢,是吗?那当浏览器把 1+1 发给你以后,你做了什么事情呢?”


V8 引擎说道:“我就是进行了常规的词法分析、语法分析,然后建立了语法树、符号表等……”


浏览器见 V8 在掉书袋,直接打断它了:“这些都不要说了,这些工作都是身为编译器 和解释器的份内之事,大家都是这么干的,我解析 HTML 标签不也是这么干的吗?直接和人类大哥说,你解析完了干了什么事?”


V8 引擎被打断也不生气,接着说道:“解析完了,我就使用了一个叫 MacroAssembler 的类库……”


注意:这里再小小拓展一下 MacroAssembler,它简写为 MASM,也称作 Microsoft Macro Assembler,它是微软为 x86 微处理器家族编写的一套宏汇编器,算是高端汇编器的一个范本。早期它原本是由微软公司维护的一个商业套件,20 世纪 90 年代未,从 6.12 版开始不再单独销售,开始免费分发。现在 MASM 的新版本,已经包含在了微软的开发工具 Visual Studio 里面了。

如果不是免费分发,MASM 或许还不能具备如此广泛的市场份额,谷歌应该也不会在 V8 引擎中使用它。


说到这里,浏览器又打断 V8 说道:“MacroAssembler 库就是缩写为 MASM 的 C++ 汇编库吧?我去年在老友 Strongtalk VM 那里见过它,嘿嘿,要知道 Strongtalk VM 可是大大鼎鼎的 Java 虚拟机 HotSpot JVM 的前辈呢!Java 的执行效率可是比 JS 快得多呢!”


从这里我们可以看出来,浏览器是一个见多识广的人,要知道自 1990 年以来全世界的 Web 网页都在它上面展示,也怪不得它见识广泛。


大家都不喜欢浏览器的打断,毕竟这阻碍了我们探索问题,于是我说道:“浏览器你别打岔,V8 你继续讲,使用 MASM 干了什么事情?”


V8 说道:“MASM 提供了很多方法,可以理解为,和 JS 的语法能力是一一对应的,JS 代码里的语句是什么,就对应调用 MASM 里面的某个方法。例如 1+1 这句代码,对应调用了 MASM 的 C++ 代码,它们是这样的(如下所示):”


#define __ masm.
__ mov eax 1
// 在这里__是一个宏
// 在预处理之后将被统一替换为 masm.
// 这一句是将寄存器 eax 设置为 1
__ add eax 1  // 这一句将寄存器的值加 1
__ ret eax // 这里返回寄存值的值
// 注:以上仅是伪代码示例


“上面这些是 C++ 代码,在内存里生成下面这样的机器码,大概就是人类谁也看不懂的样子啦(如下所示):”


B8 01 00 00 00  // mov eax 1
83 C0 01  // add eax 1


听了 V8 的解释,感觉 V8 貌似又比浏览器见多识厂了。


浏览器打断 V8 说道:“胡说!机器码都是像 010110010 这种二进制格式,你这种 B8、01、00、83,这些字母加数字是什么鬼?”


V8 说道:“你整天只跟网页打交道,哪里知道计算机的底层逻辑。像 B8、01、00、83 这些是二进制机器码的 16 进制表示,人类使用 16 进制阅读二进制更节省屏幕空间,所以很多地方都以这种格式在屏幕上呈现文本的。但我和 CPU 之间的交流,却是以 010110010 这种二进制方式进行的。”

注意:这里再小小拓展一下关于 CPU 的知识,CPU 是 Central Processing Unit 的简称,翻译成中文就是中央处理器。现代一块普遍的CPU 往往都是包含了数十亿电子元件的集成电路。CPU是计算机的运算核心和控制核心,CPU 的功能主要是解释计算机指令,以及处理计算机软件中的数据。从组成结构上看,CPU 主要包括算术逻辑运算单元、高速缓冲存储器,及控制单元等部分。


浏览器不说话了。此时 CPU 听到 V8 在谈论中提到了它的名字,于是说道:“V8,那串十六进制代码里面的 mov eax 1 是什么意思?我怎么从来没见你提起过?”


V8 解释道:“mov eax 1 是机器码注释,是给人类大哥看的,我给你看的都是二进制字节码,是 010110010 这种的。具体解释一下,像 mov,它是诸如 1010 这种汇编指令的编程语言代名词。人类编写的是 mov,汇编编译器译完,就是 1010 这种格式了。”


大家看到了吧,这也就是编程语言的作用,在人类和计算机中间充当了一名翻译官。


V8 继续解释道:“eax 是寄存器地址,第 2 行代码 mov eax 1 这句指令就是将寄存器的值设为 1。同时,它下面第 6 行那句代码 add eax 1 是将寄存器里面的数值加 1。add 与 mov 不就是你的两个指令吗,CPU 大哥?如果我发错了指令,你可是从来不会理我的啊!”


CPU 重重点了点头,“嗯”了一声,表示赞同。(至于它作为一个硅基方块,没有头和手脚,是怎么点头的,我们就不要深究了,反正在意识上它点头了。)


看到了吧,原来一句简简单单的代码,要经过词法解析、语法解析、转为汇编代码,汇编代码还要再转为机器码,经过许多步骤之后,到达 CPU,最终才能执行。


浏览器又说道:“好啊 V8,网民每天都骂网页慢,罪魁祸首原来在你这里!你将 JS 代码先转成汇编代码,再将汇编代码转成机器码,一件事转好几道手续,这样能不慢吗?为什么你不直接将人类大哥写的代码,转为二进制机器码呢?”

浏览器貌似找到了一次反击的机会,“嘿嘿”笑了两声,喜形于色。

“哈哈哈!”V8 大笑道,“浏览器,你只知其二,不知其二。JS 是解释型语言,如何直接编译成机器码?如果是这样,它不就和 Java 一样,是编译型语言了吗?”


浏览器不服气,虽然 JS 是解释型语言,可它为什么不能先编译再执行呢?在 Java 版 JS 解释器 rhino「ˈraɪnəʊ」 中,JS 脚本不就是被编译为 Java 字节码执行的吗?


……

探讨已经过去半个时辰了,我感觉讨论有点偏离主题了,于是说道: “好了,书归正传。V8,浏览器给你的 JS 代码,你是读一行调用 MASM 转化一行,还是读完了整体代码之后,统一调用 MASM 再转化的呢?”


V8 回答道:“是统一转化的,但这一切都是在内存那里折腾的。我有两个助手,一个叫初级全码编译器(英文学名叫 Full Code Generator),它将所有 JS 代码依次调用 MASM 全部在内存中走了一遍;另一个叫优化能手编译器(英文学名叫 Crankshaft「ˈkræŋkʃɑːft」),它针对运行多次的代码,以初级全码编译器的编译结果为基础,再作一次优化编译,目的是使代码的执行效率更高。”


注意:这里对内存先做一个小小的拓展,内存(Memory)也被称为内存储器,其作用是用于暂时存放 CPU 中的运算数据,还有就是存放准备与硬盘等外部存储器做交换的数据。内存是由内存芯片、电路板、金手指等很多部分组成的。注意这里的金手指,和网文小说里的金手指不是一回事。

听了 V8 的发言,这时候一直默默无声的内存说话了:“是啊,每次都把我折腾的晕头转向。别的 EXE 执行文件,是先于我这里加载、后交给 CPU 运行,都是一次搞定的。唯有 V8 交给我的执行文件,连个名字都没有,执行代码忽长忽短,变化莫测。”

“哼!”内存用鼻子表达了它对 V8 引擎的不瞒情绪。

注意:这里拓展一下 EXE,EXE 是 Windows 系统下可执行文件——也就是程序的后缀名,英文全名是 Executable「eksɪˈkjuːtəbl」File。


CPU 说道:“但我感觉,V8 交给我的机器码和普通 EXE 执行文件交给我的机器码没有什么区别,在我这里,它们都是合法公民。只要指令正确,我就能给出正确的运行结果。”


探讨进行到这里,我们可以看出来了,V8 并不知道 1+1 为什么等于 2,它只是将浏览器交给它的代码,在内存中编译一遍,然后交给 CPU 执行。V8 为了执行 JS 更快,平白多占用了很多内存,是用“空间换时间”的手法,在计算机世界博得了“V8 引擎执行 JS 就是快”的美名。具体为什么 1+1 等于 2,这件事还需要再问问 CPU。


3 拷问 CPU,从 CPU 的视角看“加法器”


于是我向 CPU 问道:“CPU,你说只要指令正确,你就能返回正确结果。那么 V8 将 1+1 的机器码传给你以后,你又都做了什么事情呢?”


CPU 说道:“报告人类大人,我什么都没有做。我做的一切,都是按照您的指令完成的,这一切都是您的智慧啊!”


CPU 此时态度很诚恳,并不像在拍马屁。真相只有一个,CPU 是人类科学家设计出来的。

CPU 继续说道:“首先,当我看到 mov eax 1 这条指令的时候,我就知道这是叫我将数值 1 移动到寄存器 eax 这个地方。”


内存这时候插了一嘴:“你是怎么调度指令的?又怎么知道什么指令应该如何执行的呢?”


CPU 答道:“我的内部有一个助理,叫指令指挥官,它负责指令的分类与调度。假设它看到的指令是 010100010010,首先从前 4 位 0101 判断,这是一个寄存器设置指令,于是就打电话通知寄存器老头来领取数据包裹;如果看到前 4 位是 1010,就知道这是一个加法指令,就打电话通知算术运算单元加法器来领取任务。待加法器计算完了,它又会将运算结果发给寄存器老头保存。”


这时浏览器貌似对 CPU 的工作原理也起了好奇之心,也插了一嘴:“不要说人话,请讲机器语言。什么寄存器算术运算单元都是你单位的员工,指令指挥官是如何给你的单位职员分派任务的?他看到 0101,是怎么知道应该吧这个任务分派给寄存器老头的?”


CPU 说道:“我用拟人化的指令指挥官作类比,是为了方便人类大哥理解。从计算机角度讲,比如 0101 这 4 个 Bit——也就是位,依次代表 4 个路口,每个路口有两个岔,0 向左转,1 向右转,这样 0101 一路在电路板上走下来,不就知道是哪个职员应该负责了吗?”


大家看到了吧,指令分派确实简单,问题关键还在加法器上,1+1 等于几其实是它算出来的。

浏览器又问道:“CPU,那加法器是如何计算 1+1=2 的呢?”


CPU 说道:“这就没那么简单了。加法器并不知道 1+1 等于几,加法器是由半加器累加而成的,而半加器是由一个异或门加一个与门组成的。(如下面图 1-4 所示)就是一个半加器:”


image.png图 1-4 半加器

注意:在这张图中,A、B 是输入,S 是最终结果,C 是进位结果。


CPU 继续说道:“其中异或门的逻辑是这样的:”


负负得负、正负得正、负正得正、正正得负。


“如果说异或门电路有点复杂的话,那么还可以拆开看,一个异或门可以用 4 个与非门表示。(如下面图 1-5 所示)是 4 个非门:”

image.png图 1-5 4 个非门


“综合以上这些,加法器是由半加器组成的,而半加器又是由异或门与非门组成的,异或门又可以由与非门组成,所以,整个加法器都可以看作是由与非门组成的,而一个与非门简单电路的物理设计是怎么样的呢,大家知道吗?”


注意:这里拓展一下,事实上任何一个算术逻辑。都可单独由“与非逻辑”或“或非逻辑”来实现。


CPU 看了看大家,继续说道:“(如下面图 1-6 所示)与非门是由开关设计实现的。x、y 是两个开关,它的开状态相当于 1,关状态相当于 0。x、y 相当于与非门中的 A、B。x、y 状态全开,以及任何一个状态为开,电路都是不通的。只有当 x、y 状态全为关,电路才是通的。”

image.png

图 1-6 与非门的电路设计


“既然与非门,可以由开关设计组成,异或门同样也可以。异或门加一个与非门组成了半加器,多个半加器累加到一起,就组成了全加器(如下面图 1-7 所示):”

image.png

图 1-7 全加器的组成


“半加器是怎么组成全加器的呢?低位半加器的进位结果,恰是高位半加器的输入,多个半加器合在一起就组成了一个多位全加器。所以,我的算术运算单元的运算能力也不是无限的,能算多大数字是由我包含了多少硬件决定的。


CPU 解释完了,空间一片寂静,大家都听得入神了。


看到这里,相信书本前的你也明白了,CPU 并不知道 1+1 等于 2,之所以 1+1 能算出等于 2,是人类在设计 CPU 的时候赋能给它的。 而 CPU 内所有的逻辑运算,归根结底,在最底层又都是简单的开关的开合。从这点来看,计算机的鼻祖竟然是小小的开关啊。


4 计算机实现加减乘除,及呈现文字影像的原理


这时候浏览器举手问道:“CPU,你刚才说,你的加法器是由众多开关实现的。那减法运算、乘法运算、除法运算又是怎么实现的?”


CPU 说道:“减法在我这里也是加法,乘法是换算为多位加法累加的,除法又可以换算为乘法,所以,你们明白了吧,所有数学上的四则运算,都可以由加法变换实现。包括文字、音频和视频信息的处理,在我这里最终也都是二进制的加减乘除,与逻辑与非。”


浏览器又问道:“那这样说,在你内部,岂不是有很多很多的开关喽?”


CPU 说道:“是的,人类发明了一种双极型三极管,简称晶体管(如下面图 1-8 所示)。”


image.png

图 1-8 各种晶体管


“每个晶体管相当于一个电路开关,以我为例,我的型号是 2010 年出厂的酷睿 i7-980X,晶体管数量不多,也就 11 亿多个吧。

注意:这里拓展一下晶体管,现代计算机 CPU 里用的是更高级、更微小的场效应管,本质上它也是一种晶体管。晶体管的发明,是 20 世纪中叶科学技术领域具有划时代意义的一件大事。为此,1956 年诺贝尔物理学奖授予了美国的肖克利、巴丁和布拉坦,以表彰他们对半导体和晶体管效应的研究发现。

听了 CPU 的解释,我不禁感喟,原来人类在浏览器的 Console 面板里简单敲了一行 1+1,CPU 内部噼里啪啦竟然做了那么多事情。在电子计算机世界面前,人类就是上帝的存在啊,人类动动小手指头,这个世界就翻云覆雨一阵变幻。


计算机并没有智能,我们从宏观上看,计算机仿佛拥有了智能一般,其实都是通过数以亿计的场效应晶体管,通过很小很小的开关电路实现的,并且这种能力也都是人类赋予它的。


在人类的大脑中,也有几十亿个神经元,它们就像计算机里面的场效应晶体管一样,人类为什么拥有智能呢,或许人类根本也没有智能,在“上帝”那里——如果有“上帝”存在的话,我们的大脑也可能只是按照他老人家的设计,表现出来的一种开关状态而已。


你觉得在人类之上,有更加复杂的文明存在吗?


以上内容摘自机工出版的《微信小游戏开发》,李艺著,该书已在京东上架,内容为适合网络发表有少量修改。

目录
相关文章
|
4月前
|
存储 调度 C++
【操作系统】进程与线程的区别及总结(非常非常重要,面试必考题,其它文章可以不看,但这篇文章最后的总结你必须要看,满满的全是干货......)
【操作系统】进程与线程的区别及总结(非常非常重要,面试必考题,其它文章可以不看,但这篇文章最后的总结你必须要看,满满的全是干货......)
101 1
|
2月前
|
安全 Linux 文件存储
在Linux中,服务器开不了机怎么解决⼀步步的排查?
在Linux中,服务器开不了机怎么解决⼀步步的排查?
|
5月前
|
缓存 Unix Shell
回顾计算机运行中发生的事情
【5月更文挑战第1天】本文介绍了计算机启动和操作系统运行的过程。当计算机启动时,BIOS引导加载程序加载内核到内存,然后内核初始化并探测硬件。启动后,init或upstart程序启动守护进程和系统服务,包括X服务器。用户登录时,身份验证后,用户获得相关权限并启动Shell或图形桌面。程序运行时,内核通过系统调用管理进程,实现多任务处理。内存管理通过虚拟内存和内存层次结构,如寄存器、缓存和主存,确保进程安全并有效地共享资源。如果进程试图访问非法内存,会触发段错误。文件权限则进一步保护系统免受恶意程序影响。
115 8
回顾计算机运行中发生的事情
|
4月前
|
算法 Unix Linux
进程之舞:操作系统中的启动、状态转换与唤醒艺术
进程之舞:操作系统中的启动、状态转换与唤醒艺术
48 0
|
5月前
|
数据采集 缓存 前端开发
LabVIEW用了多线程,程序是不是会跑的更快些
LabVIEW用了多线程,程序是不是会跑的更快些
66 0
|
5月前
|
存储 算法 Unix
操作系统复习篇一分大小单双方式方法
用户程序访问操作系统的程序和数据 • 地址映射:逻辑地址转换为物理地址 • 内存扩充(虚拟存储技术):请求调入功能、置换功能 设备管理功能一分大小单双方式方法 《罔 F C W 2 3 ●VIP》【企鹅Q1344-687】 • 缓冲管理:缓冲区机制 • 设备分配 • 设备处理:设备驱动程序 主要任务:完成I/O请求、提高CPU和I/O设备的使用率 文件管理功能 • 文件存储空间的管理 • 目录管理:按名存取 • 文件的读/写管理和保护:文件的读/写管理、文件保护
|
人工智能 Linux Windows
计算机中那些事儿(二):如何神操作你的电脑
计算机中那些事儿(二):如何神操作你的电脑
|
存储 算法 机器人
计算机的组成是什么样的?计算机的指挥中心CPU为啥那么强大?
计算机的组成是什么样的?计算机的指挥中心CPU为啥那么强大?
242 0
第七次笔记:程序的机器级代码表示
第七次笔记:程序的机器级代码表示
90 0
第七次笔记:程序的机器级代码表示
|
存储 自然语言处理 前端开发
解读《深入理解计算机系统 (CSAPP)》第 3 章程序的机器级表示
C语言机器级编程通过让编译器产生机器级程序的汇编代码表示,了解了编译器以及机器、数据类型和指令集
解读《深入理解计算机系统 (CSAPP)》第 3 章程序的机器级表示