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

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

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

编译-让计算机认识我

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

001010100101001001001<br>100100101000101010101

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

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

<br><span class="hljs-function"><span class="hljs-keyword">int</span>&#xA0;<span class="hljs-title">main</span><span class="hljs-params">()</span>&#xA0;<br></span>{<br>&#xA0;&#xA0;&#xA0;<span class="hljs-built_in">printf</span>(<span class="hljs-string">"Hello&#xA0;World\n"</span>);<br>&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">return</span>&#xA0;<span class="hljs-number">0</span>;<br>}

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

gcc&#xA0;hello.c<br>./hello<br>

看似很简单的一行,但是其实编译的过程很复杂,并不是我们想象中的编译,真实是分为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&#xA0;=&#xA0;1&#xA0;<br>b&#xA0;=&#xA0;2&#xA0;<br><span class="hljs-keyword">if</span>&#xA0;a&#xA0;>&#xA0;b&#xA0;{&#xA0;<br>&#xA0;<span class="hljs-built_in">printf</span>(<span class="hljs-string">"%s"</span>,<span class="hljs-string">"a"</span>)&#xA0;<br>}&#xA0;<span class="hljs-keyword">else</span>&#xA0;{&#xA0;<br>&#xA0;add(a,b)&#xA0;<br>}&#xA0;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"%s"</span>,<span class="hljs-string">"end"</span>)&#xA0;<br><br>func&#xA0;add(int&#xA0;a,int&#xA0;b)&#xA0;{&#xA0;<br>&#xA0;&#xA0;<span class="hljs-built_in">return</span>&#xA0;a+b<br>}

这是段非常简单的伪代码,有分支判断、有函数跳转。我们来从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之类的,这时程序计数器会不停的在某些地址之间来回切换。

Original: https://www.cnblogs.com/jiazhuangdongbiancheng/p/15521829.html
Author: 假装懂编程
Title: 简单!代码原来是这样被CPU跑起来的

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/576488/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 【黄啊码】教你用python画冰墩墩

    python;gutter:true; import turtle</p> <p>turtle.title('PythonBingDwenDwen…

    数据库 2023年6月16日
    058
  • Ajax

    AJAX(Asynchronous Javascript And Xml) 传统请求及缺点 传统的请求都有哪些? 直接在浏览器地址栏上输入URL。 点击超链接 提交form表单 使…

    数据库 2023年6月14日
    099
  • MySQL实战45讲 4,5

    04 | 深入浅出索引(上) 索引的出现实际上是为了提高数据查询的效率,就像一本书的目录一样。 [En] The emergence of the index is actuall…

    数据库 2023年5月24日
    0100
  • linux ftp报530 530 Login incorrect pam_unix(crond:account): expired password for user

    今天用FTP登录服务器,要传些数据文件,一直登录不上,重启之后依然无法登陆。 就提交了工单,阿里云的售后居然在网上给我找了两篇处理530的帮助文档,直接无语! 无奈… …

    数据库 2023年6月14日
    0125
  • 学习笔记——Django项目中的请求

    2022-10-03 url中的位置参数 位置参数存放的位置 是子应用中的自定义的”urls.py”文件中的路由中。 位置参数的设置: 如果位置参数很多,那…

    数据库 2023年6月14日
    058
  • MySQL视图

    视图 常见的数据库对象 视图概述 为什么使用视图 可以帮助我们使用表中的 部分数据,对其修改可以改变原来表中的值 可以简化查询 控制数据的访问(权限) 视图的理解 视图是一个本身没…

    数据库 2023年5月24日
    067
  • Linux远程终端连接工具:SecureCRT

    SecureCRT SecureCRT是一款支持 SSH2、SSH1、Telnet、Telnet/SSH、Relogin、Serial、TAPI、RAW 等协议的终端仿真程序 Se…

    数据库 2023年6月11日
    0104
  • 服务器部署 Vue 和 Django 项目的全记录

    本篇记录我在一个全新服务器上部署 Vue 和 Django 前后端项目的全过程,内容包括服务器初始配置、安装 Django 虚拟环境、python web 服务器 uWSGI 和反…

    数据库 2023年6月14日
    093
  • 通过.frm和.idb文件恢复mysql数据库

    本文对该文章进行参考,地址https://baijiahao.baidu.com/s?id=1675966756498698574&wfr=spider&for=p…

    数据库 2023年5月24日
    099
  • 详解Mysql事务隔离级别与锁机制

    一.概述 我们的数据库一般都会并发执行多个事务,多个事务可能会并发的对相同的一批数据进行增删改查操作,可能 就会导致我们说的脏写、 胀读和不可重复读、幻读这些问题。 这些问题的本质…

    数据库 2023年5月24日
    0146
  • Hadoop_mapreduce统计文本单词

    Hadoop MapReduce是一个软件框架,基于该框架能够容易地编写应用程序,这些应用程序能够运行在由上千个商用机器组成的大集群上,并以一种可靠的,具有容错能力的方式并行地处理…

    数据库 2023年6月11日
    082
  • Facade 外观(结构型)

    Facade 外观 (结构型) 一:描述: Facade 外观模式是为子系统至客户端之间提供简单的一致的接口,来降低耦合度。 二:模式图 三:实现代码简单例子: 1 、业务模块; …

    数据库 2023年6月11日
    099
  • 数据库基本知识和MySQL

    数据库的基本知识和MySQL 数据库 数据库 (database) 指保存有组织的数据的容器,人们一般情况说的数据库指的是数据库软件,即数据库管理系统 (DBMS) ,而数据库实确…

    数据库 2023年6月16日
    0129
  • 【必知必会】手把手教你配置MySQL环境变量——图文详解

    一、先决条件 假设我们已经成功安装MySQL数据库。如果还有小伙伴不知道如何安装MySQL数据库,可以在本文下留言,留言数超20,则出一期”手把手教你安装MySQL数据…

    数据库 2023年6月14日
    056
  • 解决执行npm run dev 后报错 Mix: not found的问题

    开发环境:homestead 宿主机环境:windows10 在homestead虚拟机内运行npm run dev时出现错误信息:mix: not found (此时打开lara…

    数据库 2023年6月14日
    087
  • 数据火器库八卦系列之瑞士军刀随APP携带的SQLite

    来源:云数据库技术 数据库打工仔喃喃自语的八卦历史 为导弹巡洋舰设计,用在手机上的数据库 Small and Simple, and Better 如何看出是自己的娃:产品定位,特…

    数据库 2023年6月11日
    0107
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球