计算机基础之计算机组成原理入门

2,346 阅读12分钟

前言

前端工程师还需要学习计算机基础?答案当然是肯定的。其实对于很多前端工程师来说,都不太重视计算机基础这方面的知识,而是一味的去追逐vue、react等这一类前沿的上层框架技术。框架这一类,属于开发工具,确实极大提高了项目的开发效率,作为前端工程师而言,应该掌握。但,前端的技术日新月异,上层技术是永远也学不完的,而且一直学新的框架,不得累死。学会了,只是学会了一个工具,往下沉淀,才是最重要的。了解 计算机底层的基础知识,或许短时间内对自己的业务能力看不到有多大的提升,但从长远来看,绝对会有质的飞越。我自己,其实对计算机基础这方面的知识是很欠缺的,现在也是开始准备学习下这一块的知识,记录下自己的学习笔记。

计算机的基本硬件组成

CPU、内存、主板、显卡,是计算机的重要组成部分。

  1. CPU,即中央处理器,它是计算机最重要的核心部分,负责计算机所有的计算。
  2. 内存。程序、数据等都是放在内存里的,内存越大,加载的东西越多。CPU会读取内存的程序、数据进行计算,并把计算的数据写回内存。但,CPU不能直接插在内存上。
  3. 主板。主板上有很多插槽,CPU和内存都是插在主板上。CPU会读取内存的数据,那CPU和内存之间是如何通信的呢?就是通过主板的芯片组和总线进行通信的。芯片组控制了数据传输的流转,而数据传输的快慢由总线决定。
  4. 显卡。玩游戏的同学应该都知道显卡的重要,尤其是玩一些大型游戏。做图形渲染对显卡的要求都是很高的。显卡的核心是GPU(图形处理器),它可以做各种计算工作,分担了CPU的压力。现在的电脑,一般在CPU集成了,就是常说的集成显卡,这可以满足通常的需求。但如果需要玩大型游戏、3D建模等,则需要在主板上再安一个显卡,也就是独立显卡。
    除了上述四个重要组成部分之外,还有一些I/O设备(输入/输出设备),比如鼠标、键盘、显示器等。而他们通过“桥”和CPU进行通信。

冯·诺伊曼体系结构

冯·诺依曼体系结构又叫存储计算机。看到存储计算机,是否会想到难道还有不能存储的计算机?是的,确实有。其实计算机还可以这么来分类:可编程计算机、不可编程计算机、存储计算机、不可存储计算机。计算机是由各种门电路组合而成的,通过组装成固定电路板,完成一个特定的计算程序。需要修改功能,就要重新组装电路。这种计算机就是不可编程的,比如说普通计算器,只能做一些设定好的加减乘除。早年的插线板式计算机,在板子上接不同的插头,实现不同的功能,这个就是可编程的,但编写好的程序不能存储下来供以后使用,要用到不同的程序,都要重新接插头。所以,不可编程和不可存储都是效率非常低的。而后也就出现了存储计算机。冯·诺伊曼于1946年提出存储程序原理,把程序本身当作数据来对待,程序和该程序处理的数据用同样的方式储存。冯·诺依曼体系结构冯·诺伊曼理论的要点是:计算机的数制采用二进制;计算机应该按照程序顺序执行。因此被该理论称为冯·诺伊曼体系结构,而冯·诺伊曼也被称为‘现代计算机之父’。

冯·诺伊曼体系结构有五大组成部分

(一)运算器

运算器是由算术逻辑和处理器寄存器组成,用来进行算术和逻辑运算。

(二)控制器

控制器由指令寄存器和程序计数器组成,用来控制程序的流程。
运算器和控制器组成了CPU。

(三)存储器

存储器用来存储程序和数据。存储器可以分为主存储器(内存)和辅助存储器(外存,比如:硬盘)。

(四)输入设备

比如鼠标、键盘

(五)输出设备

比如显示器

小结:计算机程序可以理解为,从输入设备读取输入信息,通过运算器和控制器来执行存储器里的程序,并把结果输出到输出设备。

计算机的核心指标

计算机的两个核心指标:性能和功耗。
无论是否是程序员,其实在日常生活中,用电脑、手机都会提到一个词,“性能”。当电脑、手机卡的时候,我们会说性能差了,性能跟不上了。前端做的web应用,打开速度慢了,会做性能优化。

(一)性能

什么是性能?可以理解为响应时间的倒数。计算机的性能,可以用两个指标来衡量。

(1)响应时间

执行一个程序,需要花长时间叫响应时间。很显然,响应时间越短,性能越好。

(2)吞吐量

计算机在单位时间内完成的任务量。吞吐量越大,则性能越好。

小结:我们可以看到,提升性能可以缩短响应时间、增大吞吐量。缩短响应时间,则需要提升CPU的性能。CPU性能到一定的瓶颈之后,通过减少cpu的执行时间来提升性能,是非常困难的。而提升吞吐量的办法有很多,很多时候多加一下机器就可以了。

(二)功耗

前面讲到,提升性能可以通过缩短CPU执行时间。要缩短CPU执行时间,则需要在CPU上多放一些晶体管。有人可能就会想,那不简单,CPU多放一些晶体管,性能不就提升了吗,但工程师们为什么没有这么做呢?这就是功耗的问题。
CPU在计算时,其实就是让晶体管不停的“打开”、“关闭”,来组合完成各种运算。想要计算的快,一方面可以在CPU多放一些晶体管,即增加晶体管密度;二是,让晶体管“打开”和“关闭”得更快一点,即提升主频。这两种方式都会增加功耗,CPU的温度就会升高,带来耗电和散热的问题。那为什么不把CPU造的大一点呢,CPU变大了,晶体管之间的距离就会变大,这样运算速度也会变慢。因此,CPU并不是造的越大越好。并且,我们需要给CPU散热,就会用到一些散热设备比如抹导热硅胶、装风扇等。所以,在面积一定的情况下,CPU里面能放的晶体管是有限的。要多放一些晶体管,就需要把晶体管造的小一点 这在硬件技术层面上,现在也是非常困难的。
当硬件到达一定瓶颈后,需要再提升性能,就需要从其他方面去实现了。比如加速大概率事件(典型例子:深度学习)、流水线提高性能、预测提高性能。

指令

(一)什么是计算机指令

早期的计算机,是用打孔卡来编程的,在特定位置上打孔或不打孔来表示“0”或“1”,而我们现在写程序,都是用java、js、Python等这样高级语言来写的。其实即使在今天,计算机或者说CPU,,它并不能理解高级语言,还是只能处理“0”和“1”这样的数字,我们称为“机器码”,也就是计算机指令。那我们用高级语言写的程序,计算机又是怎么运行的呢?就是计算机指令来执行的。
前面讲过,CPU是中央处理器,计算机的各种指令,就是CPU来执行的。计算机指令,就好比是CPU能理解的语言,可以称为机器语言。不同的CPU,能理解的语言是不一样的,这就形成了计算机指令集。一个计算程序,是由成千上万条指令组成的。但CPU不能一直存着所有指令,所以程序平时都是存储在存储器中。

(二)代码是怎么变成指令的

我们平时用高级语言写的代码,是怎么变成计算机指令,最后被CPU执行的呢?首先需要把整个程序编译成一个汇编语言,再用汇编器把汇编语言翻译成机器码,这机器码是就是计算机指令。

(三)编译原理

以js为例,敲下一行js代码,到这行代码被执行,发生了什么?
计算机不能直接理解js,需要将其编译成计算机指令,这个过程就叫编译。例如:Chrome浏览器的V8引擎,就是做了编译的事情。但市面上不只是Chrome一种浏览器,不同浏览器 有不同的js引擎,能够理解的js语法也不同。这也是前端需要处理浏览器兼容性问题的原因。但,无论何种浏览器,原理都是差不多的。
编译通常分为三个步骤:

  1. 词法分析。将代码拆分成最小有意义的单元,这些最小单元称为词(token)。
  2. 语法分析。将词法单元装换成代表程序语法结构的树,即抽象语法树,也就是AST。 词法分析和语法分析是交错运行的,并不是等所有的词token生产之后,再语法分析。一般是每生成一个token,就用语法分析器处理。
  3. 生成代码。将AST转成计算机指令。

注:js引擎编译过程基本是上面三步,但V8引擎多了一步,在生成计算机指令前,会先生成字节码,最后再将字节码编译成计算机指令。为什么要先转成字节码?因为直接生成指令太占内存。可能你会想,js怎么没有经过汇编再生成机器码,其实V8引擎自带一个汇编器用来动态生成机器码。

电路

前面讲过,计算机只识别“0”和“1”这样的二进制代码,那为什么计算机会采用二进制?这就涉及到电路。

  1. CPU是由晶体管组成,晶体管的组合形成逻辑电路,即门电路(与、或、非)。电路开关的“接通”和“断开”,正好可以用“1”和“0”表示这两种状态。当电路接通,就会形成电压,电压的高低,转换成二进制,“1”表示高电平,“0”表示低电平。所以,从物理上来看,二进制是最容易实现的。
  2. 二进制的编码、计数、运算规则相对于十进制,更为简单。
  3. “0”和“1”正好可以实现计算机逻辑运算的真和假。

所以,这就是计算机为什么会采用二进制的方式。

CPU是如何执行指令的

CPU只能识别计算机指令,那CPU是怎么执行指令的呢?其实,逻辑上,我们可以理解为,CPU是由一堆寄存器组成,而寄存器就是CPU内部,由多个触发器或锁存器组成的简单电路。触发器里面包含了晶体管。
CPU有很多不同功能的寄存器:
(1)指令地址寄存器:存放下一条需要执行的计算机指令的内存地址。
(2)指令寄存器:存放当前正在执行的指令。
(3)条件码寄存器:存放逻辑运算的结果。
(4)通用寄存器:可以存放数据和地址,比如累加寄存器。
还有很多寄存器,就不一一介绍了。

代码解释

我们来思考一下,写了一段代码,背后它到底是怎么运行的呢?

var a = 1;
var b = a;
var c = a + b;

这段代码是怎么运行的呢?

  1. 编译器会先将这段程序分解成词法单元,然后把词法单元解析成一个树结构也就是AST,然后进行代码生成。
  2. 遇到var a,编译器会查看作用域是否已经有一个该名称的变量存在于同一个作用域的集合中。如果是,编译器会忽略该声明,继续进行编译;否则它会要求作用域在当前作用域的集合中声明一个新的变量,并命名为 a。var b和var c也是同样的道理。
  3. 引擎运行时,会查询a容器,并给它赋值。这其实是系统在分配内存,这里都是基础数据类型,都是存在栈里。b=a,会先查询到a变量的值,然后查询到b容器并进行赋值。在计算a+b的时候,会把a和b从栈内存中取出来,放到寄存器中,然后再通过累加器,计算出a+b的值后,查询c容器并进行赋值。

总结

本文从计算机的基础硬件组成开始讲起,介绍了高级语言写的代码是怎么变成计算机指令,最终被CPU执行的。该篇文章的内容,也是自己的学习笔记,如有不对的地方,请大家指正。