您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

代码原来是这样被CPU跑起来的

时间:2022-01-06 10:15:45  来源:  作者:小心程序猿QAQ

CPU对我们来说既熟悉又陌生,熟悉的是我们知道代码是被CPU执行的,当我们的线上服务出现问题时可能首先会查看CPU负载情况。陌生的是我们并不知道CPU是如何执行代码的,它对我们的代码做了什么。本文意在简单解释我们代码的生命周期,以及代码是如何在CPU上跑起来的。

简单!代码原来是这样被CPU跑起来的

 

编译-让计算机认识我

一个漂亮 control+c 加上一个漂亮的 control+v,啪~,我们愉快的写下了代码,当代码被保存后,它就被存在我们磁盘的某个地方,它可能是像JAVA或者Python这些高级语言写的,也可能是像c这种古老语言写的,但是现在它肯定没法被运行,因为计算机不认识它们,计算机只认识0、1这样的二进制,简称机器码,那为什么我们不直接写机器码?如果你有这样的思考,我只能呵呵了,请你帮我翻译下以下机器码:

001010100101001001001
100100101000101010101
复制代码

很明显作为高质量人类的我们也无法识别出这段代码写的是什么,于是出现类似java这样的高级语言,它们给机器码穿上了一层外衣,然后交给伟大的程序员来创造未来。

所以反过来我们的代码需要被替换成机器码,这样才能被计算机认识,计算机才能帮我们干事。这个转换的过程我们通常叫编译

#include <studio.h>
int main() 
{
   printf("Hello Worldn");
   return 0;
}
复制代码

这是一段应该每个程序员都写过的代码(hello.c),在linux下,当我们使用GCC来编译Hello World程序时,只需要最简单的命令:

gcc hello.c
./hello
# Hello World
复制代码

看似很简单的一行,但是其实编译的过程很复杂,并不是我们想象中的编译,真实是分为4个步骤,分别是预处理(Prepressing)、编译(Compliation)、汇编(Assertmbly)和链接(Linking)。

简单!代码原来是这样被CPU跑起来的

 

  1. 预编译:这个过程主要是处理源代码中以“#”开始的预编译指令,比如“#include”、“define”等。
  2. 编译:这个过程就是把预处理完的文件进行词法分析、语法分析、语义分析及优化后生产成相应的汇编代码,这个过程是最复杂的。
  3. 汇编:这个过程就是将汇编代码转换成机器码,也就是上图的目标文件hello.o
  4. 链接:我们的代码程序经常是由多个代码文件组成的,当每个文件都被汇编成“.o”文件时,需要一套机制将它们组装在一起,这个过程就叫做链接。

好吧,原来编译是这么回事,通过这一整套的编译操作,我们代码终于能执行了,我们简简单单的运行./hello.out即可输出Hello World。等等,这个简简单单的过程发生了什么?

连接-中转站和高速公路

ok,ok,通过编译,我们的程序终于能执行了,接下来让我们站在CPU的视角来看看Hello World是如何被打印出来的。

首先编译好的文件是存在磁盘上的,得先加载到内存中,这里你可能会问:为什么CPU不能直接读取磁盘的程序运行而要经过内存?答案是慢,缓慢的磁盘会影响我们程序执行的速度,因此需要更加快速、离CPU更近的存储,那就是内存。

简单!代码原来是这样被CPU跑起来的

 

内存是一大块存储空间,可以存储很多数据信息,那么如何找到我们要写的程序呢?答案是地址,其实每个字节在内存中都有一个地址,这样当CPU去内存中读我们的程序时,只需要根据对应的地址就可以知道我们程序的具体内容。

简单!代码原来是这样被CPU跑起来的

 

等等...,这里似乎又有个问题,CPU是如何与我们的内存、磁盘通信的?应该有个媒介之类的吧。没错,这个媒介就是主板上的总线芯片组,总线好理解,就像高速公路,数据信息可以通过这条高速公路传递到CPU中,这个芯片组是个什么玩意?电脑主板上芯片很多,这里说的主要是南桥芯片和北桥芯片。先来个解释:

  1. 北桥芯片:北桥负责高速设备和CPU之间的沟通,主要就是CPU和内存、显卡之间的通信,但是随着技术的迭代,主板上的北桥芯片已经被内置到了CPU里了。
  2. 南桥芯片:南桥负责低速设备和北桥之间的通信,主要负责I/O总线之间的通信,如USB、LAN、ATA、SATA、音频控制器、键盘控制器、实时时钟控制器、高级电源管理等。

嗯... 为什么CPU与高速设备、低速设备之间的通信需要这两个芯片?CPU自己不能干吗?这里还是类似拆分任务的功能,如果把所有的任务都交给CPU来处理,CPU会太忙了,还有比较重要的一点,如果南桥芯片坏了,那么我们可以直接更换南桥,而不用换掉整个CPU。

简单!代码原来是这样被CPU跑起来的

 

终于CPU通过总线和芯片打通了磁盘、内存之间的通信了,接下来的一切开始交给CPU。

CPU-最强大脑

CPU全称是Central Processing Unit,即中央处理单元,它的本质就是一块超大规模的集成电路。从逻辑上来分,它的内部是由寄存器、控制器、运算器和时钟组成的,下面来解释下各个组成是干什么的。

  • 寄存器:CPU内部其实有很多类型的寄存器,我们只需了解寄存器就是暂存数据、指令等信息的,它的本质是临时存储,由于是直接集成在CPU内部,所以读写它们的速度很快,一般一个CPU内部会有20-100个寄存器,这里给大家列举下常用寄存器与其功能。 累加寄存器:存储执行运算的数据和运算后的数据 标志寄存器:存储运算处理后的CPU的状态 程序计数器:存储下一条指令所在内存的地址 基址寄存器:存储数据内存的起始地址 变址寄存器:存储基址寄存器的相对地址 通用寄存器:存储任意数据 指令寄存器:存储指令,CPU内部使用,程序员无法通过程序对该寄存器进行读写操作 栈寄存器:存储栈区域的起始地址
  • 控制器:控制器负责把数据读出或者写入寄存器,并根据指令的结果来控制计算机。
  • 运算器:从名字就可以猜出来,运算器的主要工作就是运算,运算从内存读入寄存器的值
  • 时钟:它并不是我们见的钟表概念,它代表了你的CPU的工作频率,频率越高说明你的CPU处理的速度越快,但是越快就会带来另一个问题:散热。
简单!代码原来是这样被CPU跑起来的

 

综上所述,CPU的大致工作流程如下:在时钟信号到来的时候,就开始工作,通过控制器把内存的数据读到各个寄存器中,然后如果有计算相关的逻辑,就交给运算器。发现没有,CPU的工作其实挺简单的,本质就是不停的读指令、执行指令。但是CPU是如何读到我们的代码指令的,以及我们的代码里面的if else、函数调用都是如何执行分支判断、函数跳转的,我们来看个例子:

a = 1 #0x0010
b = 2 #0x0011
if a > b { #0x0012
	printf("%s","a") #0x0013
} else { 
	add(a,b) #0x0014
} 
printf("%s","end") #0x0017

func add(int a,int b) { #0x0020
  return a+b
}
复制代码

这是段非常简单的伪代码,有分支判断、有函数跳转。我们来从CPU的角度看看它是如何执行的:

简单!代码原来是这样被CPU跑起来的

 

  1. 首先每段程序都有个开始的地址0x0010,也就是CPU读取程序的入口
  2. 把a=1这个数字读入通用寄存器中,程序计数器(PC寄存器)自动加1,即指向下一条指令 0x0011
  3. 指令寄存器拿到程序计数器的指令地址,把b=2这个数字读入通用寄存器中,程序计数器(PC寄存器)自动加1,即指向下一条指令0x0012
  4. 指令寄存器发现此处是比较逻辑,会执行a-b,此时可能会有三个结果分别是大于0,等于0,小于0,然后把这个结果存到标志寄存器里,这里有个小知识,我们经常说的是CPU是64位或者32位,其实也表示了标志寄存器的长度
简单!代码原来是这样被CPU跑起来的

 

  1. 很明显,a是小于b的,CPU根据标志寄存器的状态值应该跳转到else里面,注意这时程序计数器的值不是加1,而是设置成else的地址 0x0014,当执行到0x0015的时候,需要发生函数跳转,程序计数器会被设置成 0x0020,但是这里并不是简单的函数跳转(专业术语叫做call),因为在函数执行完毕之后,还要返回,也就是程序计数器需要从0x0020再变成0x0017。call执行的时候会把后续要执行的指令地址0x0017存到中。
  2. 当我们的add函数执行完毕之后,会有个return,return的时候会把上一步骤存入栈中的地址0x0017写入程序计数器中
  3. 指令寄存器根据程序计数器当前的地址执行最后的打印(end),结束。

顺序执行的指令代码,程序计数器会自动累加(当然不一定累加的是1),然后找到下一条要执行的指令。

分支判断的时候,程序计数器不是简单的累加地址,需要地址的跳转。

函数调用不仅仅需要跳转地址,还要把函数执行完毕之后要执行的地址存下来,方便折回继续执行。

其实还有个循环执行,也就是我们代码中的for、while之类的,这时程序计数器会不停的在某些地址之间来回切换。


作者:假装懂编程
链接:
https://juejin.cn/post/7022800866788950029
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。



Tags:代码   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
CPU对我们来说既熟悉又陌生,熟悉的是我们知道代码是被CPU执行的,当我们的线上服务出现问题时可能首先会查看CPU负载情况。陌生的是我们并不知道CPU是如何执行代码的,它对我们的...【详细内容】
2022-01-06  Tags: 代码  点击:(0)  评论:(0)  加入收藏
说明面向对象的三个基本特征是:封装、继承、多态。call函数:预定义的JavaScript方法,用来调用所有者对象作为参数的方法。上节内容:面向对象,类、对象、属性、方法,静态属性和方法...【详细内容】
2022-01-04  Tags: 代码  点击:(6)  评论:(0)  加入收藏
JavaScript 的故事很长。在今天,JavaScript 的运行从移动设备到服务器端,无论您是计划在 2022 年学习或使用 JavaScript ,还是目前正在使用JavaScript进行开发,还是已经熟练掌握...【详细内容】
2021-12-30  Tags: 代码  点击:(10)  评论:(0)  加入收藏
Apache Log4j2 远程代码执行漏洞的问题最近闹得沸沸扬扬的,很多人都被大半夜叫起来紧急修复这跟问题。有很多人在微信上问我:这种漏洞还能怎么修?下次有问题还要再升级版本吗?有...【详细内容】
2021-12-30  Tags: 代码  点击:(10)  评论:(0)  加入收藏
前言几乎所有.NET序列化程序的实现基础都是反射。下列代码是Newtonsoft.Json的实现:protectedvirtualJsonPropertyCreateProperty(MemberInfomember,MemberSerializationmemb...【详细内容】
2021-12-28  Tags: 代码  点击:(15)  评论:(0)  加入收藏
在SEO优化中,最重要的评估之一就是确定网站上存在哪些HTTP状态代码。这些代码可能会变得很复杂,成为一个难题,必须先解决这些难题,然后才能完成其他任务。例如,如果你放置的页面...【详细内容】
2021-12-24  Tags: 代码  点击:(7)  评论:(0)  加入收藏
1、通过条件判断给变量赋值布尔值的正确姿势// badif (a === &#39;a&#39;) { b = true} else { b = false}// goodb = a === &#39;a&#39;2、在if中判断数组长度不为零...【详细内容】
2021-12-24  Tags: 代码  点击:(9)  评论:(0)  加入收藏
前言本文提供将视频调整分辨率的Python代码,一如既往的实用主义。环境依赖ffmpeg环境安装,可以参考我的另一篇文章: windows ffmpeg安装部署_阿良的博客-CSDN博客ffmpy安装:pip...【详细内容】
2021-12-14  Tags: 代码  点击:(22)  评论:(0)  加入收藏
大家好, 我是林路,今天就给大家介绍Python代码都是用的什么编辑器写的?Jupyter Notebook ,没有Pycharm,没有Vscode,没有Sublime text。 只有一款工具:Jupyter Notebook 。工欲善其...【详细内容】
2021-12-09  Tags: 代码  点击:(32)  评论:(0)  加入收藏
在这篇文章中,我将与你分享一些关于JS的技巧,可以提高你的JS技能。1.避免if过长如果判断值满足多个条件,我们可能会这么写:if (value === &#39;a&#39; || value === &#39;b&#39;...【详细内容】
2021-11-17  Tags: 代码  点击:(24)  评论:(0)  加入收藏
▌简易百科推荐
CPU对我们来说既熟悉又陌生,熟悉的是我们知道代码是被CPU执行的,当我们的线上服务出现问题时可能首先会查看CPU负载情况。陌生的是我们并不知道CPU是如何执行代码的,它对我们的...【详细内容】
2022-01-06  小心程序猿QAQ    Tags:代码   点击:(0)  评论:(0)  加入收藏
前言小黑在开发中遇到个问题,我负责的模块需要调用某个三方服务接口查询信息,查询结果直接影响后续业务逻辑的处理;这个接口偶尔会因网络问题出现超时,导致我的业务逻辑无法继续...【详细内容】
2022-01-04  小黑说Java    Tags:接口重试   点击:(6)  评论:(0)  加入收藏
我在会议演讲后经常收到的一个问题很奇怪,不是关于我演讲的内容,而是关于我的Linux桌面环境。人们对这个漂亮的发行版更感兴趣,而不是我刚才做的精彩演示我不是在抱怨,我喜欢我...【详细内容】
2022-01-04  木偶跳舞    Tags:Linux 开发   点击:(9)  评论:(0)  加入收藏
写在前面有时候可能做项目组长,负责一个项目开发,但是工作是内网,也没有公司的版本库权限,那这个时候,我们怎么用处理版本控制,可以用集中式的版本库工具SVN,或者分布式的Git,这里和...【详细内容】
2021-12-31  山河已无恙被注册了    Tags:Git版本库   点击:(8)  评论:(0)  加入收藏
1、什么是YAMLYAML是"YAML Ain&rsquo;t a Markup Language"(YAML不是一种标记语言)的递归缩写。YAML的意思其实是:“Yet Another Markup Language”(仍是一种标记语言)。主要强度...【详细内容】
2021-12-29  编程菌zfn    Tags:YAML   点击:(10)  评论:(0)  加入收藏
本文分为三个等级自顶向下地分析了glibc中内存分配与回收的过程。本文不过度关注细节,因此只是分别从arena层次、bin层次、chunk层次进行图解,而不涉及有关指针的具体操作。前...【详细内容】
2021-12-28  linux技术栈    Tags:glibc   点击:(10)  评论:(0)  加入收藏
摘 要 (OF作品展示)OF之前介绍了用python实现数据可视化、数据分析及一些小项目,但基本都是后端的知识。想要做一个好看的可视化大屏,我们还要学一些前端的知识(vue),网上有很多比...【详细内容】
2021-12-27  项目与数据管理    Tags:Vue   点击:(17)  评论:(0)  加入收藏
程序是如何被执行的&emsp;&emsp;程序是如何被执行的?许多开发者可能也没法回答这个问题,大多数人更注重的是如何编写程序,却不会太注意编写好的程序是如何被运行,这并不是一个好...【详细内容】
2021-12-23  IT学习日记    Tags:程序   点击:(11)  评论:(0)  加入收藏
阅读收获✔️1. 了解单点登录实现原理✔️2. 掌握快速使用xxl-sso接入单点登录功能一、早期的多系统登录解决方案 单系统登录解决方案的核心是cookie,cookie携带会话id在浏览器...【详细内容】
2021-12-23  程序yuan    Tags:单点登录(   点击:(12)  评论:(0)  加入收藏
下载Eclipse RCP IDE如果你电脑上还没有安装Eclipse,那么请到这里下载对应版本的软件进行安装。具体的安装步骤就不在这赘述了。创建第一个标准Eclipse RCP应用(总共分为六步)1...【详细内容】
2021-12-22  阿福ChrisYuan    Tags:RCP应用   点击:(10)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条