0%

CPU是怎么运行程序的?

你知道a = 1 + 2在CPU是怎么执行的吗?32位和64位的操作系统有什么区别?64位的软件可以在32位的操作系统上面运行吗?

图灵机的工作方式

要想了解程序是怎么执行的,我们先来看下图灵机的工作方式。

图灵机

图灵的基本思想是用机器来模拟人们用纸笔进行数学运算的过程,他把这样的过程看作下列两种简单的动作:
1、在纸上写上或擦除某个符号;
2、把注意力从纸的一个位置移动到另一个位置。
而在每个阶段,人要决定下一步的动作,依赖于 (1) 此人当前所关注的纸上某个位置的符号和(2) 此人当前思维的状态。

那么我们来看下图灵机是怎么执行1 + 2的。

  • 首先,在纸上写入「1、2、+」这三个字符,并读写头停放在1字符对应的格子上。
  • 接着,读写头将1写入到存储设备中
  • 读写头移动到下一格,读取2到存储设备中
  • 读写头移动到下一格,读取+,判断加未运算符指令,变将存储设备中的1和2相加,得到3,并将3写入到存储设备中
  • 读写头移动到下一格,将存储设备中的3写入到此格。

现代计算机的工作方式与图灵机执行1+2的工作方式基本一致,我们来看下冯诺依曼模型。

冯诺依曼模型

冯诺依曼遵循图灵机的设计,使用电子元件构造计算机,约定用二进制进行计算和存储,并定义了冯诺依曼模型

冯诺依曼模型定义了计算机的基本结构有五个部分,运算器控制器存储器输入设备输出设备

冯诺依曼模型

计算器、控制器位于CPU中,并使用总线与存储器和输入/输出设备完成交互。

冯诺依曼模型

寄存器是什么

看上面的冯诺依曼模型中,CPU中有个寄存器的角色,他是用来干嘛的呢?我们知道CPU中放着控制单元与逻辑运算单元,而数据需要依靠总线与存储单元、输入输出单元交互来获得,依靠总线传输数据会增加运算时间,那么就需要将正在计算的数据存放到寄存器中,通过减少与总线的交互来提升计算速度。根据存储的数据不同,常见的寄存器有以下几种。

  • 通用寄存器,存放进行运算的数据。
  • 程序计数器,存放CPU要执行的下一条指令的地址。
  • 指令寄存器,存放程序计数器指向的指令。

总线

总线用于CPU与内存以及其他设备之前的通信。分为以下三种:

  • 地址总线,用于指定CPU将要操作的内存地址。
  • 数据总线,用于读写内存的数据。
  • 控制总线,用于发送和接收信号,比如中断、设备复位等。CPU收到信号后自然进行相应,也需要控制总线。

当CPU读写内存时,首先通过地址总线来指定内存地址,然后通过控制总线来控制读或写命令,最后通过数据总线来传输数据。

线路位宽与CPU位宽

CPU通过总线与内存进行交互,那么总线是怎么传输数据的呢?其实就是操作电压,低电压为0,高电压为1,以此来表达二进制数据,从而完成数据传输,那么表示一个11010的信号则需要5条线路并行传输,线路的条数称为位宽,一般也对应CPU位宽。

CPU想要操作内存地址就需要地址总线。

假设有线路位宽为5,此时CPU想访问内存地址6,那么则通过地址总线传输00110即可,如果此时CPU想访问128,那么传输啥都没用,位宽为5,寻址范围只能在0-31上,可见,位宽决定了CPU能操作的内存大小,32位CPU只能操作2^32(4G)大的内存。

所以不要给32位CPU安装8G内存条哦,4G就够了

线路位宽决定了数据传输的能力,那么CPU位宽呢?

看冯诺依曼模型可知,CPU放着控制单元与逻辑运算单元,CPU位宽决定了这里的计算能力,32位的CPU没法直接加和两个64位大小的数字,需要分高低位加和再组装,而64位计算机则可以直接对64位数字加和,那么CPU位宽则决定了一次能计算多少字节的数据。

  • 32位CPU一次可以计算4个字节;
  • 64位CPU一次可以计算8个字节;

如果计算的数额不超过32位,此时32位CPU与64位CPU其实没差别

程序执行的基本过程

程序实际上是一条条指令,所以程序的运行过程就是把每一条指令一步步的执行起来,负责执行指令的就是CPU。

程序执行的基本过程

  1. CPU读取程序计数器的值,该值即为指令的内存地址,然后控制单元操作地址总线指定需要访问的内存地址,接着通知内存准备数据,数据准备好后通过数据总线传输给CPU,CPU收到数据后将该指令存放到指令寄存器

  2. CPU分析指令寄存器中的指令,确定指令的类型以及参数,若是计算类型,则交由逻辑运算单元,若是存储类型,则交由控制单元

  3. 当该指令执行完成后,程序计数器自增,自增大小有位宽决定,如果是32位宽,则自增4。

以上过程不断循环,程序便跑起来了,这个循环过程成为CPU的指令周期

a = 1 + 2执行具体过程

CPU可不认识什么a = 1 + 2,想要程序跑起来,首先要将其翻译成汇编语言,此过程称为汇编代码。

针对汇编代码,我们还需要用汇编器翻译成机器码,即01000101011这样的机器语言,此时才可以交由CPU完成执行。

汇编分为数据段指令段,分别存放数据与指令。

数据段与正文段

编译器会把a = 1 + 2翻译成4条指令,存放到正文段中。如图,这 4 条指令被存放到了 0x200 ~ 0x20c 的区域中:

  • 0x200 的内容是 load 指令将 0x100 地址中的数据 1 装入到寄存器 R0;
  • 0x204 的内容是 load 指令将 0x104 地址中的数据 2 装入到寄存器 R1;
  • 0x208 的内容是 add 指令将寄存器 R0 和 R1 的数据相加,并把结果存放到寄存器 R2;
  • 0x20c 的内容是 store 指令将寄存器 R2 中的数据存回数据段中的 0x108 地址中,这个地址也就是变量 a 内存中的地址;

指令

编译期在编译程序时,会构造指令,这个过程成为指令的编码。CPU执行程序时,就会解析指令,这个过程称为指令的解码。

现代CPU使用流水线方式执行指令,流水线方式将一条指令拆成四个阶段,如下图:

流水线执行模式

  1. Fetch -> CPU通过程序计数器读取对应的内存地址的指令(获取指令)

  2. Decode -> CPU对指令进行解码(指令译码)

  3. Execution -> CPU执行指令(执行指令)

  4. Store -> 将计算结果存回到寄存器或者将寄存器的值存回内存(数据回写)

上面4个阶段,称为指令周期,CPU的工作就是一个接着一个执行,周而复始,事实上,不同的阶段有计算机的不同组件完成:

指令周期

  • 取指令的阶段,我们的指令是存放在存储器里的,实际上,通过程序计数器和指令寄存器取出指令的过程,是由控制器操作的;
  • 指令的译码过程,也是由控制器进行的;
  • 指令执行的过程,无论是进行算术操作、逻辑操作,还是进行数据传输、条件分支操作,都是由算术逻辑单元操作的,也就是由运算器处理的。但是如果是一个简单的无条件地址跳转,则是直接在控制器里面完成的,不需要用到运算器。

指令的类型

指令从功能角度划分,可以分为 5 大类:

  • 数据传输类型的指令,比如 store/load 是寄存器与内存间数据传输的指令,mov 是将一个内存地址的数据移动到另一个内存地址的指令;
  • 运算类型的指令,比如加减乘除、位运算、比较大小等等,它们最多只能处理两个寄存器中的数据;
  • 跳转类型的指令,通过修改程序计数器的值来达到跳转执行指令的过程,比如编程中常见的 if-elseswtich-case、函数调用等。
  • 信号类型的指令,比如发生中断的指令 trap
  • 闲置类型的指令,比如指令 nop,执行后 CPU 会空转一个周期
-------- 本文结束 感谢阅读 --------