logo

2.代码是怎么运行起来的?

作者
Modified on
Reading time
22 分钟阅读:..评论:..

从0和1到代码

现在可以开始回答这个问题了:一个代码到底是怎么执行起来的?CPU内部到底是怎么工作的? 电影片段里黑客出场时一般都少不了如下图所示的“数字雨”特效:

我们先来看看计算机内部的基本元素“0”和“1”。

0和1

那么,计算机中的“0”和“1”到底是什么? 简单,就是低电平和高电平的意思。 啥意思? 0其实就代表低电平,1代表高电平。这里所说的电平,意指电压的大小(单位,福特,用符号V表示),而高低只是一种相对概念。 比如0.2V是低电平的话,那么5V可能就是高电平了。注意,我说的是“可能”,难道单凭电压大小还不能确定是不是高电平了? 是的,没错。高低电平的数值是由具体的电路来确定的一般两者都有一个阈值,当电压大于某个阈值时,即是高电平;当电平小于某个阈值时,即是低电平。 计算机中的0和1是人类为了理解方便,给低/高电平取的别名。其实两种称呼分别代表了数字电路和模拟电路。 数字电路是电路是以“0”和“1”即相应的逻辑符号来表示的,如下图:

模拟电路是电路中以电压高低和电流等参数来表示的,如下图所示:
那么数字电路和模拟电路有啥联系? 唔···其实可以看做建筑施工图和建筑实物图的关系:数字电路主要是表现其逻辑和功能,模拟电路是搞定采用什么材料什么方式来实现数字电路想要达到的结果! 现在可以开始考虑:高低电平如何实现的?

二极管

二极管是用半导体材料(硅、硒、锗等)制成的一种电子器件,具有单向导电性。 什么是单向导电性?先看下图,左图是一个二极管的实物图,右图是逻辑电路图(即抽象的):

注:二极管还可以分为整流二极管、稳压二极管、发光二极管等等,这里只介绍普通二极管。 电流可以从正(+)极流向负(-)极,此时处于导通状态;但反过来却不行,此时处于截止状态。这就是所谓的单向导电性! 附一张二极管的伏安特性图,看不懂也没关系,眼熟就好:
由于单向导电性,二极管就像是一个开关:
当处于导通状态时,开关打开,两边的电压大小一致。 如正极(+)电压为5.2V,那么负极(-)也为5.2V。 注:这里并不准确,因为还涉及正负极的电压大小、阈值电压等等,只考虑理想情况下,其他的可自行百度探索。 如果我们进一步把5.2V定义为高电平,那么此时正负极都是“1”! 当处于截止状态时,开关关闭,两边电压大小不一致。 如果正极只有0.2V,那么此时正负极可能都是低电平,也就是“0”!

逻辑运算与门电路

我们通过二极管可以获得“0”和“1”,但是光有“0”和“1”还不够,计算机怎么进行亿万级别的运算的? 这得慢慢来,要进行运算离不开逻辑运算也就是门电路,常见的逻辑运算有与、或、非、异或、同或。它们的真值表与逻辑符号如下。

不用管逻辑符号,重点是真值表。 先以“与”来讲解: “与”,即当且仅当所有的输入都为“1”时,输出才为“1”,否则输出为“0”。现在看真值表,是不是这个意思?
等等,好像我们是从二极管过渡到这的吧?这个与门可以用二极管实现? 当然啦!下图就是一个利用二极管和电阻(不用管)实现的简单与门!
其中,A、B为输入,Y为输出。 我们来分析一下: 若A为高电平“1”,B为低电平“0”,此时,D1截止状态,D2处于导通状态,Y就和B电压一致,也为低电平“0”,所以输出为“0”; 若A为低电平“0”,B为高电平“1”,此时,D1处于导通状态,D2截止状态,Y就和A电压一致,也为低电平“0”,所以输出为“0”; 若A为高电平“1”,B为高电平“1”,此时,D1和D2都处于截止状态,Y就和VCC电压基本一致,即是高电平“1”,所以输出为“1”; 注:判断电压的大小涉及电路中电流和电压的关系,简单来说,在一个电路中,电阻越大,那么该电阻所占据的电压也就越大,当二极管处于截止状态时,电阻可以看做超级大,所以R1分的电压就很小,输出Y和电源VCC基本一致。若想进一步了解请百度探索或留言,我继续更新。 与门在数字电路中还可以表示为“&”:
再来分析一个或门:当输入中至少有一个“1”时,输出为“1”,若全为“0”,则输出“0”。 刚刚的与门展示的是两个输入,现在来看看四个输入!
简单分析一下,当ABCD四个都是输入低电平“0”时,四个二极管都处于截止状态,此时输出即为低电平“0”。 只有当其中任意一个不为低电平时,若A为高电平“1”,此时D1二极管导通,输出即为A的电平,即高电平“1”。 或门在数字电路中还可以表示为:
后面的非门之类的,单靠二极管就不太行了,就需要请出它的老哥——“三极管”,这里暂时不介绍,我们只需要知道:我们可以利用这些元器件构成如上表所展示的那些门电路,现在我们要做的就是思考如何利用这些门电路来进行代码的执行!

加减乘除四则运算

有了以上的逻辑运算门电路,我们来不如考虑如何做加减乘除?为方便回忆,现在把我们已有的门电路工具组再次拿出来!

加法器 首先咱们得明白,计算机中所有的运算其实都是基于二进制(进制的转换之后再讲)的,也就是加法器的输入和输出只有“0”和“1”。 值得注意的是设计逻辑电路时最好的方法是先写真值表,对于一个简单的加法器而言有两个输入(A/B)和一个输出(S)和一个进位(C)。 **简单分析一下,**输入两个1,输出当前位0,进位为1;输入两个0,当前位为0,进位为0;输入一个0一个1,输出当前位0,进位为0。

ABSC
0000
0110
1010
1101

只看当前位,去看看门电路工具组是不是有点眼熟?异或门! 只看进位,去看看与门是不是有点眼熟! 也就是说加法器可以由异或门和与门构成!如下图所示:

但是现在这个只能称为“半加器”,最多只能计算1+1,如何更进一步,比如1+2,2+2? 这就需要在原本的加法器增加一个进位接口,如下就是“全加器”:
Copy一个百度图片来简化一下上图的结构:
将两个加法器串联在一起,就得到一个可以进行2位(3+3)加法的加法器。
要几位加法就用几个加法器串联,如四位(15+15):
注:上图展示的是串行进位加法器,又称波纹加法器。还有一种超前进位加法器,不再是从右到左一次计算,比前者更快。
参考: https://wenku.baidu.com/view/9de55e1ce53a580217fcfe93.html 减法器 减法器其实是本质还是通过加法器实现的,比如15-15,其实就是15+(-15),这里的-15是所谓的补码,后面再说。 乘法器 加减解决了,乘法怎么弄? 先看看二进制的乘法是怎么计算的,如下:
上图是4位的乘法,先考虑2位的乘法,例如a2a1×b2b1:
为什么输出有四位?因为二位乘法最大就是四位结果!比如11(二进制)乘11(二进制),结果是多少?1001! 结合上图的乘法过程,我们可以利用与门以及加法器来组成乘法器,如下图所示:
乘1?原值。 乘2?由于是二进制,在末尾加0即可。 乘3?先加0,在做一次加法可行。 乘4?加两个0。 ...... 除法器 除法器的设计就比较麻烦了,这里不进行展开,有兴趣的同学可以参考以下链接: 乘法器除法器的制作——百度文库 https://wenku.baidu.com/view/b30ce26ab84ae45c3b358c25.html ...... ok!以上一通分析之后我们得到了加减乘除四则运算的器件了,我们接下来可以进行更高难度的计算了。 比如: A×B+C 这就需要先保存A×B的结果,在与C相加,等等...保存? 话说在计算机内部是用什么方式保存数据的呢

寄存器

这就得讲讲寄存器的相关知识。 由于保存数据的重要性,科学家一直在思考在电路中使用何种方式可以保存数据,比如使某个器件一直输出高电平,那不就是“1”了吗?一直输入低电平,那不就是“0”了吗?而且还要可以自由切换“0”和“1”。 经过一阵鼓捣,科学家们发明了锁存器这个东西,它有什么用?它可以根据输入信号来改变输出状态。 锁存器是具有记忆功能(也就是存储功能)的基本单元,最简单的锁存器可以存储一位二进制信息。 它有两个特点

  • 有两个能稳定保持的状态,分别用来表示逻辑0和逻辑1;
  • 在适当的输入信号下,可以从一种状态变为另一种状态(0-1或1-0)

最简单的S-R锁存器它是由两个“或非”门电路或者“与非”门电路构造出来的:

R、S是锁存器的输入端,前者一般被称为复位(Reset)端,后者为置位(Set)端,Q和Q加一杠是互为相反的输出(你0我1,你1我0)。 注意到这个电路跟之前我们看到的都不一样了,其门电路的输出会作为自身的输入,这种结构被称为反馈电路。 分析一下“或非”门电路构成的锁存器的工作原理,这其中涉及稳定态和不稳定状态,假设输入之前的Q为1,杠Q为0

  • 当输入R、S都为0,诶?好像这Q和杠Q不会变?是的,此时锁存器输出不变,这有什么用?那就保存了之前数据!比如此时Q一直输出1。
  • 当输入R为0,S为1,此时输出Q也没变,仍为1。难道跟上一个输出一样?不是的。更改一下前提:若之前的Q为0,杠Q为1,这个输入之后,输出Q是多少?是1!这就是所谓的置位。
  • 当输入R为1,S为0,不管之前的Q是多少,最终的Q都是0,也就是复位。
  • 当输入R、S都为1,输出状态不定,因此这个输入是会被电路所规避的。

不太明白?那就祭出真值表吧!下图是RS锁存器的真值表:

由S-R锁存器派生出各类其他的锁存器和触发器,比如D锁存器、S-R触发器、D触发器等等,有兴趣的可以看看文末的参考链接。 顺带一提,触发器引入了时钟信号的概念,这有什么意义? 其实时钟信号是为了规范计算机中的各类电路的执行时机,就像小时候玩的“321不许动”,只有当固定的时钟信号来临时,各元件才会改变自身的状态。如果没有时钟信号来管理时序,那么各元件的变化就会杂乱无章,极不稳定。 有了锁存器和寄存器,就可以并联构成寄存器,一个锁存器可以存储1位,4个并联就可以存储4位......总之,我们获得了一个可以存储数据的东西了。

译码器与选择器

突然发现我们现在手头可用的元件越来越多的,这就有一个问题,我们如何控制使用哪个元件呢?总不能自己手动更改线路连接吧? 注:以前真的是手动更改选择的电路。 我们能不能通过输入几位二进制数,就可以实现使用对应的元件呢?就以3位举例。 3位二进制数最多可以表示8个输出(2^3)。

内部怎么实现的?3位的太复杂,找了个2位的将就看看。
上面介绍的这个东西被称作译码器,它将输入的二进制码译成对应的输出高、低电平信号,实现选择逻辑电路的作用。 还有一个问题,**那就是这么多的元件,我们到底选择哪一个的输出呢?就好比水坝放水选择哪一个闸门一样,引出选择器的概念。**选择器可以根据输入二进制码的状态,选择其中一个作为输出信号。 利用门电路我们可以构造它,有了它就可以获得对应的输出:
sel输入0,那么out就输出a的值,输入1就输出b的值。 上面只能二选一,还可以进一步四选一、八选一等等,下图是四选一选择器:
如果我们将之前得到的各个四则运算器、锁存器、寄存器等等加一个输入端口,该端口(激活引脚)为1,这个元件才能用,利用选择器和激活引脚,我们就可以控制选择的的元器件了。 其实写到这,计算机底层的基础知识也就差不多了,最后再讲一讲编程语言与以上我们所讲解的东西之间的联系。

“终”章

假如有一个CPU,仅有8个引脚,其中4个数据引脚,4个指令引脚。 数据引脚是可以输入数据,指令引脚是用来选择执行的操作。我们定义,当指令引脚输入0001 读取数据,将数据引脚的数据读入寄存器; 0010 选择加法器,将数据引脚的数据与寄存器数据相加,结果在寄存器; 0100 选择乘法器,将寄存器的数据乘以数据引脚的数据,结果在寄存器; 1000 清空寄存器。 也许会疑惑怎么通过指令来执行相应的操作呢?不要忘了我们已经有很多可用器件,比如译码器,就可以通过4位的二进制数据,来选择对应的加法器、乘法器等等。 以上就是最简单的指令译码过程,实际场景下的指令当然不止这几种,而是成千上万,其中的逻辑电路就复杂的令人发指了。我们不用考虑复杂场景,仅从最基本的入手,毕竟再复杂的场景也是由基本的元器件构成的。 假如0001 0001 就是输入0001将它保存到寄存器中。 好了,我们以一个计算题来举例:3×5+6,输入依次为: 0001 0011 0100 0101 0010 0110 ok,我们再计算一个“稍微”复杂一点的:3*6+8+9+10+11×5×6×7×8×9+15。 指令输入我就不写了,太麻烦了。这还是我们只定义了4种指令,要是成千上万种这谁顶得住? 我们不妨对指令稍微包装一下,规定: 0001 用MOV表示 0010 用SHL表示 0100 用ADD表示 并且假设现在又多了一个元件可以实现十进制到二进制的转换,那么命令应该为: MOV 3; SHL 5; ADD 6; 稍微好受一点了,这就是所谓的汇编语言,之前仅有0和1的语言称为机器语言。 但是还是觉得有点不顺畅,明明就是计算一个3×5+6,我为啥非得要写三行,一行不行么?! 比如直接输入: print("%d:/n",3×5+6); 上面的这个就是大名鼎鼎的C语言,在计算机中上面的输入其实是经过一系列称之为“编译和汇编”的操作,编译将C语言转换为汇编语言汇编将汇编语言转换为机器语言。 类似于C这样的语言还有很多,比如java、golong、C++、python等等,它们都有一个共同的名字——高级语言。 高级?高级就在于使用这些语言来编程,相比于机器语言和汇编语言已经很人性化了,具有很强的可读性。 如果以后出现“终极语言”、“天选语言”也别奇怪,在计算机这条路上前人都是为后人铺路,无论是二极管、门电路、逻辑电路、寄存器、译码器,还是机器语言、汇编语言、高级语言等等,后来者都是在前人的基础之上再造、浓缩、改编、起舞。 这是一个螺旋上升永无止境的迭代之路,这也是为什么标题的“终”要加一个引号的原因,计算机的道路永无止境,个人的学习也是永无止境的。

计算机组成

我们所熟知的计算机组成结构很简单:主机+显示屏+键盘+鼠标。音乐发烧友或者游戏爱好者对音乐要求较高,所以还要加上一个高品质的耳机;程序员可能需要经常拷贝程序,所以还要加上移动硬盘什么的······ 上面其实只提到了计算机的硬件部分,一个完整的计算机系统包括硬件部分和软件部分。硬件部分就是你所能看见的、摸得着所有设备、芯片、硬盘、键盘等。软件部分就是电脑启动、电脑运行过程中那些可见或不可见的程序。如图所示:

上图里的东西太多了,我们将其简化为一下四个部分:CPU、存储器、IO(输入输出设备)、软件。 一句话简单介绍几个部分之间的关系:CPU从存储器中获得软件代码并执行,还利用IO设备与用户交互。

CPU

CPU可以说是计算机的灵魂和核心,起着操控全局、数据运算(控制器、运算器)的功能。有关它的实际硬件构成详见之后的“从沙子到CPU”,这里只对其逻辑上的构成进行介绍,即控制单元、存储单元、运算单元。 ·控制单元是CPU的控制中心,负责执行指令和操作控制电路等。 ·存储单元是存储CPU运行中、运行后的结果元素。 ·运算单元主要是对存储单元存储的元素进行逻辑(或、与、非等)运算。 三部分的关系如下:

控制单元 控制单元的组成包括包括: ·指令寄存器IR(Instruction Register):存放指令的仓库,减小CPU去内存中读取指令的次数; ·指令译码器ID(Instruction Decoder):将指令转换为机器码,使得运算单元和寄存器可以理解(进行相应的电路操作); ·操作控制器OC(Operation Controller)等 它依次从存储器(内存/外存)中取出各条指令,放在指令寄存器IR中,通过指令译码器ID确定应该进行什么操作,然后通过操作控制器OC,按确定的时序,向相应的部件发出微操作控制信号(以后会讲)。 存储单元 存储单元的组成包括: ·CPU片内缓存 ·寄存器组 它是CPU中暂时存放数据的地方,里面保存着待处理或已处理的数据,CPU访问寄存器所用的时间要比访问内存的时间短。寄存器组可分为专用寄存器和通用寄存器。专用寄存器的作用是固定的,分别寄存相应的数据。而通用寄存器用途广泛并可由程序员规定其用途,通用寄存器的数目因微处理器而异。 采用寄存器,可以减少CPU访问内存的次数,从而提高了CPU的工作速度。但因为受到芯片面积、集成度以及成本所限,寄存器组的容量不可能很大。在任务管理器中点击性能页面,即有如图所示界面:
从图片右下角可以看到,该CPU有三级缓存,并且容量逐渐增大。 运算单元 运算单元可以执行算术运算(包括加减乘数等基本运算及其附加运算)和逻辑运算(包括移位、逻辑测试或两个值比较)。运算单元所进行的全部操作都是由控制单元发出的控制信号来指挥的。 CPU工作原理大致如下图:

存储器

计算机中的存储器分为内存储器与外存储器。CPU与内存储器直接打交道,即我们常说的内存,它其中存放着当前正在运行的程序与数据,一旦断电,其中的数据就丢失,速度较快。外存储器也是存放数据的媒介,即我们常说的软盘、硬盘、光盘和磁带等,可以长时间保存数据,速度较慢。

IO设备

IO设备即是计算机与用户交互的媒介。如常见的键盘、鼠标、打印机、扫描仪、显示器、读卡机、纸带、耳机等等··· 简单来说,就是用户可以看到计算机想我们看到的东西或者输入某些计算机想获取的东西。

软件

软件即是计算机中的程序,它是用户与硬件之间的接口。如果是除软件之外的三个部分是“躯壳”,那么,软件就是“灵魂”,只有当“躯壳”和“灵魂”皆有,这才是一个真正的计算机。 软件还可进一步分为系统软件和应用软件。 前者即常听的Windows、Linux、Android、IOS等等。后者就是用户启动或其他程序调用的软件。比如Word、Excel、QQ、微信等等。

参考链接 灵感来自: CPU 是怎么认识代码的? - Zign的回答 - 知乎 https://www.zhihu.com/question/348237008/answer/843382847 【数电】(二) 基本逻辑运算与逻辑门电路 https://blog.csdn.net/as480133937/article/details/104554160/ 计算机组成与设计 https://www.cnblogs.com/lfri/p/10046360.html 数字电路锁存器详解 https://wenku.baidu.com/view/f7360766cc22bcd127ff0cc0.html 锁存器、触发器、寄存器的区别联系 https://blog.csdn.net/surgeddd/article/details/4683657 数据选择器 https://blog.csdn.net/vivid117/article/details/100747939

从沙子到CPU

https://www.jianshu.com/p/14c800dffe18 https://www.jianshu.com/p/14c800dffe18 https://wenku.baidu.com/view/8c9fadd476eeaeaad1f3306c.html