03.08 CPU是怎么认识代码的?

邢小燕


提前备注:回答比较硬核,我会尽量软化,但想了解知识还是需要耐心。CPU内传输的信号有两种:高电压和低电压,分别代表数字信号“1”和“0”,因此CPU唯一能理解(问题中的“认识”)的语言就是由“1”和“0”写成的机器语言。

由于程序(代码)存储在电脑硬盘中时,也是“1”和“0”的形式,是否就意味着,只要程序存到硬盘中,CPU就能认识呢?

答案是CPU仍然看不懂这些程序,因为以“1”和“0”形式存储的程序和以“1”和“0”写成的语言完全是两回事,两者的区别类似于汉语书和英语书都用纸和油墨印制,但依然是两种不同的语言,不会英语的依然看不懂英语书。

要让CPU能看懂代码,要做相当多的工作。

现在的程序都是由C++和Java等高级语言写成,这些语言是为方便人类编程发明的,不是为方便电脑执行而设计。

说到这里,需要进一步说说机器语言和高级语言的差别。机器语言的最大特点是面向计算机硬件编程,简单说就是程序员需要通晓计算机硬件知识,写的程序要真实表示数据是如何被计算机操纵的。对程序员来说这就比较头大,毕竟上得了“厅堂”下得了“厨房”只有少数大神能做到,加上机器语言纯用“0”和“1”序列组成,既对视力是一种摧残,也是对编程趣味的扼杀。

于是,有一帮人开始琢磨了:能不能将计算机硬件从编程中分离出来,让硬件知识小白也能编程?

最先开窍的是蓝色巨人IBM,它在其System/360计算机中引入了ISA(Instruction Set Architecture)概念,将编程所需要了解的硬件信息从硬件中抽象出来,这样编程人员就可以面向ISA编程。由于ISA是用来描述编程时用到的抽象机器(不是具体的电脑CPU),包括了一套指令集和一些寄存器,因此,程序员只要知道ISA,不需要了解具体的硬件知识(每一两年硬件都会换新),就可以编写程序,在ISA相同的电脑上运行。

这样一来,程序员不必了解过于专业的计算机硬件知识,不需要下得了厅堂,可以专心在“厨房”烹调程序大餐。

由此也可以看出,程序员很多都是不了解计算机硬件的,所以妹子们不要指望自己的程序员男朋友给你DIY电脑,或者电脑坏了,他能给你省下一笔修理费。他说不会修,那就是真的不会修。

极客漫画《编程语言之战》。


自从不用懂硬件也能编程的高级语言出现后,人类开始了编程上的放飞自我,经过数十年发展,高级编程语言已超过2500种。

但矛盾出现了,CPU能理解的机器语言还是那个机器语言,几十年来没有变化,怎么办?

其实,早在高级语言出现之前的汇编语言时代,聪明的计算机研发人员就开发出了专门的程序,用来将汇编语言和高级语言翻译成机器语言,其过程相当于将英语名著翻译成汉语著作。这种翻译程序相当于人类中的翻译家。

编译器有两种方法用于翻译:编译和解释,相应的名称是编译器和解释器。

两者的区别是,编译是在执行前把整个源程序(高级语言程序)翻译成目标程序(机器语言程序),而解释是一次只翻译和执行源程序中的一行。

打个形象的比方,解释器相当于发布会的实时翻译,演讲的嘉宾说一句,实时翻译马上翻译一句。编译器则相当于著作翻译家,整本翻译完成后,再让出版社印刷上市。

将高级语言翻译成机器语言的过程。个人手绘比较粗糙,大家凑合看。


重点来了,从以上的内容可以看出,由于CPU不能直接理解用高级语言写成的代码,必须由翻译程序翻译成机器语言,因此翻译程序可以极大地影响甚至决定处理器性能的发挥。如果没有一个好的翻译程序,那么CPU的性能再强大,也好比茶壶装的饺子,倒不出来。

正因为如此,谷歌在安卓4.4之后,抛弃了Dalvik虚拟机,改为ART,实际是将翻译程序从解释器切换到编译器,发挥了芯片的性能,提高了程序运行效率。

现在,手机大厂包括华为、OPPO和vivo都开始重视编译器开发,说到底就是为了发挥芯片性能,让它不再成为倒不出饺子的茶壶。

最后总结一下,用翻译程序把程序员编写的程序翻译成二进制代码的机器语言后,CPU就能认识了,而且翻译程序的优劣可以影响乃至决定CPU性能发挥。


魔铁的世界


其实,CPU也不知道我们敲得代码是什么意思。想要让一段代码编程可执行的程序,需要进行一系列的操作。

关于CPU识别程序的问题,细讲起来是比较麻烦的一件事情,我们来分步骤逐一解释。

1.CPU的基本工作原理——数字电路

在具体将这个问题之前,我们先来了解一下半导体的特性。

顾名思义,半导体就是一种介于导体和绝缘体中间的物质,它具有以下特性。

比如上图,如果电流是从A端流向C端,则电路通畅;反过来的话就不行了。大家可以把它理解一种单方向控制电流的设备。

电流只有两种情况:开路和闭路,将开路规定为0,闭路规定为1,这也就是我们所熟知的二进制。

根据这种特性,设计者们开发出了“与”,“或”,“非”,“异或”四种情况:

  • 与门:只有同时具备条件A和条件B时,才能得出结果1,当其中任意一个条件为0时,结果为0。

1+1=1; 1+0=0; 0+1=0; 0+0=0

  • 或门:只有条件A或条件B都为0时,才能得出结果0;当其中任意条件为1时,结果为1;

1+1=1; 1+0=1; 0+1=1; 0+0=0

  • 非门:就是相反的意思,条件A的反为B,条件B的反为A;

输入x 输出y

0 1

1 0

  • 异或门:当两个条件取值相异(0和1)时,结果为1;反之为0

1⊕1=0;

1⊕0=1;

0⊕0=0;

根据这几种门电路,还衍生出了一些电路,比如与非门,或非门等等。但基本的电路状还是不会变的。这些门电路构成了CPU的基本工作原理——数字电路。

无论是数据的计算,还是指令的调度,CPU都是要通过运算来实现的。门电路的目的就是为了通过控制电流的状态,来实现计算的原理。当然了CPU上的电路是非常复杂的,如何将由门电路构造成的加法器,乘法器之类集成到一块指甲盖大小的电路板上绝非易事,这也是为什么芯片技术被称为最难突破的尖端技术的主要原因之一。

在简单了解CPU的工作原理后,就是关于高级语言和机器语言的转换问题了

机械语言和高级语言

早期的计算机编程是一件非常痛苦的事情,只要涉及到需要位移量的运算时,例如乘法运算,就要做大量的插线工作。也许一个几分钟就能算完的程式,插拔线路就得花上半个小时,还不能插错,否则就白忙活了。所以当时做这些工作的都是一些靓丽的摩登女郎,穿着漂亮的制服以缓解工作人员的压力……

随着计算量不断地增大,这种插拔的方式已经满足不了实际需求。,人们开始考虑:能不能设计一种语言来更高效的设计程序呢?由此,高级语言诞生了。

高级语言:一种接近于人类自然语言的表达方式,通过一些较为直观的单词,符号来表示低级语言。从而使编程变得明了易学,可读性强。

同时,高级语言分为面向过程和面向对象两类。前者在同一个功能每实现一次,都得重新编写一次代码,所以代码的重复利用率比较差;后者面向由于引入了类的概念,只要只要编写一次代码,后面便可以通过调用类的方式多次使用,大大提升了效率。所以java能成为最受欢迎的编程语言不是没有道理的。

机器语言:计算机最原始的语言,全部由0和1构成的数字串构成,也是cpu唯一可以识别的语言。

另外,还有一种汇编语言,很多人存在一个误区,觉得汇编语言就是机器语言,其实不然,CPU还是不知道汇编语言是个什么东西。汇编语言本质上还是一种计算机低级语言,通过汇编语言我们可以了解CPU 到底干了些什么,以及代码的运行步骤。

如何将高级语言转换成计算机可以识别的机器语言呢?这时候就需要编译器的发挥作用了:

简而言之,编译器就像是一个中转平台,就是将程序员使用的高级语言翻译为计算机可以识别运行的机器语言。其主要工作流程可以具象为:源代码 (source code) → 预处理器 (preprocessor) → 编译器 (compiler) → 目标代码 (object code) → 链接器(Linker) → 可执行程序 (executables),也就是我们最终使用的后缀名为.exe这样的程序。

最后说个题外话,编译器对于程序的所起的影响可谓举足轻重,甚至可能影响到CPU的执行效率。所以现在厂家除了绞尽脑汁的提升CPU性能外,也将目光投向了编译器,编译器极有可能在未来成为新的热门导向。



爱思考的奥特曼


如果你是一个程序员,你写过很多代码,那么现在要你写一段程序,计算50.00*0.1的值。

你也许会这样写:

代码你很快写完了,如果没有和其他人交流,可能只是你自己看了,而抱歉的是CPU也不认识你写的是啥。

CPU并不认识你写的程序

CPU不知道也不管你是用什么语言写的代码,那是给人阅读的,不是给CPU阅读的,CPU只懂得编译过的二进制指令,也就是一大串的0和1,还必须按照商家设计好的指令体系编制的指令序列才行。至于你写的那些牛B的代码,CPU并不买账,它好不好用在于编译器或解释器把这些人类代码翻译为机器码有多高明。

那CPU是怎么去执行我们的程序呢?

这就是编译器存在的原因了,编译器将我们写的程序编译成可供CPU执行的二进制代码,并且分配指令和内存的地址等这类复杂的工作。

程序大体的执行流程

一个程序的执行流程总体上可以分为3个步骤:磁盘->RAM(内存)->CPU

即程序从磁盘拷贝到内存中,CPU从内存中取指令,然后解码,之后执行,最后将执行结果写回内存。

CPU又是如何执行指令的呢?

硬件产商约定好了一套指令体系,不管什么编程语言都通过某种方式把代码编译为CPU能懂的二进制指令指挥CPU工作。

比如:有一串指令过来了,如果是以1开头,则表示做什么,比如做加法,那么后面接着会发过来两个数,CPU做加法后把结果存入既定的寄存器,程序会有后续的指令去这个寄存器提取结果,放入指定的内存中。CPU就是按照这样的约定解析传入的一连串的二进制数据,并一一的去执行。这就好比航海的船通过约定的灯语(灯的有规律亮、灭)来相互通信的道理是一样的。

总结

所以说,CPU不是认识代码,而是记住代码,数据总线送过来一个指令,是什么要如何去执行,已经固化在了CPU里了。如何执行和固化?是通过复杂的组合,以门、非门、与非门这三种复杂的组合,实现了复杂的逻辑关系。

以上是我个人的一些经验和总结,希望可以帮助到大家,如果有不同意见可以评论区留言一起讨论。


汇聚魔杖


什么是代码?代者,替换也。

假如你设计一个数字电路,包括一个加法器和一个乘法器。

你希望根据需要,有时对输入数据做加法,有时对输入做乘法,该怎样做呢?

显而易见的方法是手工控制加法器和乘法器的使能端,每次只让一个电路工作。那么当两个使能端为01,就是加法器工作,是10,就是乘法器工作。

然后为了让电路根据要求的顺序做加法和乘法,你把一系列01、10存储起来,当你要做加法,加法,乘法,加法时,存储的就是01011001。你增加了一个时序电路,每次读出两个位,把高低电平加在两个使能端上,替代你的手工接线。01和10这两个数码能控制电路的行为,起名叫机器码。

然而这样二进制的东西并不好记忆。于是你给01起了个名字,叫add,10起名叫mul。你的这组运算就记为add,add,mul,add。

add,mul这种助记符,就叫代码,具体说是汇编代码;你会找一个悲催的助手,帮你把代码替换成01,10这种机械码。

由代码翻译成机器码的过程,后来也由机器来做,这种机器(软件)就叫编译器。

因此所谓cpu认识代码,是一种拟人化的修辞,偏文艺了。cpu只是一种根据高低电平输入,产生特定的高低电平输出的机器。代码不过是助记符。

后来你觉得add,add,add,add,add这类写法太烦,于是发明了五连加,7连乘之类的写法——高级语言代码诞生了。


味冷


你看看海上船只之间通过打灯光沟通是怎样的,CPU执行代码也是那个原理。

关键是约定一套信号协议。CPU只按传入的二进制代码执行指令,商家约定好了一套指令体系,不管什么编程语言都通过某种方式把代码编译为CPU能懂的二进制指令指挥CPU工作。协议跟人与人的交流一样,是有规矩的。举个例子,发出一串指令,如果是以1开头,则表示做什么,比如做加法,那么後面接着会发过来两个数,CPU做加法後把结果存入特定的寄存器,程序会有後续指令去这个寄存器提取结果,放入指定的内存中,CPU按照这样的约定解析传入的一连串二进制数据,并一一执行。CPU能直接做什么,根据这套指令体系来,不能直接做的,程序员负责编写完整解决方案让CPU能做出来,这就叫编程,整个指令序列叫程序。

CPU不认识你什么语言代码,那是给人阅读的,不是给计算机阅读的,计算机只懂编译过的二进制指令,还必须是按照商家设计好的指令体系编制的指令序列才行。没有什么编程语言是高大上的,它好不好用,在于编译器或解释器把这些人类代码翻译为机器码有多高明,守恒律在这方面仍然起作用,你写的高级语言代码越少,翻译给机器执行的指令实际上越多。C入门最基础的hello world程序,不要以为写的很短就很得意,这程序编译後的二进制机器码有5K,printf()函数的源代码有多长,可以自己去看看,但实际上你输出一个字符串用不着完整的printf()功能,这函数其实一个低效的解释型程序。


TonyDeng


高级语言通过编辑器转为汇编代码,程序运行时由另一段程序(操作系统)加载到内存中, 内存与cpu通过地址线、控制线、数据线与内存连接。cpu通过叫做IP寄存器(寄存器有很多种,可以把寄存器看做一个速度很快的内存)的硬件读取到下一条指令在内存中的地址然后把这个地址信号通过控制线发送给内存控制器,内存控制器取出相应地址的数据,通过数据线发送给cpu,这就取到了一条机器指令。 cpu将这个机器指令分成两段: 一段是操作数 ,一段是操作码,cpu通过操作数操作码来控制寄存器访问内存、访问寄存器、启动cpu内置的计算相关的电路(ALU)最后更新ip寄存器的内容, 再通过ip寄存器发出的信号找到下一条指令并且执行, 周而复始直到程序遇到特殊的指令来结束这个程序。


A骑着白马的悟空


Cpu说,我只会 洗衣服,做饭,扫地 三件事,你只要告诉我要做哪件事,别的我一个字都不听。这三件事就是CPU的指令集。

所以,CPU根本不认识代码,只是代码按照CPU给定的指令集去调用而已


alex136442470


准确的说,CPU不是认识代码,而是记住代码,数据总线送过来一个指令,是什么如何执行,已经固化在CPU里了,如何执行和固化?是通过复杂的组合,以门,非门,与非门这三种复杂的组合,实现了复杂的逻辑关系。


繁华落尽任浮沉


CPU不认识代码,只认识有电(1)和没电(0),代码经过转换最终变成开关信号控制三极管的通与断!


分享到:


相關文章: