[操作系统]/硬件结构篇/CPU 是如何执行程序的?

发布时间:2026/7/24 19:12:53

[操作系统]/硬件结构篇/CPU 是如何执行程序的? 提纲图灵机的工作方式冯·诺依曼模型。线路位宽与CPU位宽程序执行的基本过程a 1 2 执行具体过程图灵机的工作方式图灵的基本思想是用机器来模拟人们用纸笔进行数学运算的过程而且还定义了计算机由哪些部分组成程序又是如何执行的。图灵机的基本组成如下有一条「纸带」纸带由一个个连续的格子组成每个格子可以写入字符纸带就好比内存而纸带上的格子的字符就好比内存中的数据或程序有一个「读写头」读写头可以读取纸带上任意格子的字符也可以把字符写入到纸带的格子读写头上有一些部件比如存储单元、控制单元以及运算单元1、存储单元用于存放数据2、控制单元用于识别字符是数据还是指令以及控制程序的流程等3、运算单元用于执行运算指令简单数学运算的 1 2 作为例子写入字符到纸带——读入1到存储设备图灵机的状态——读入2——号如图……冯诺依曼模型计算机具体实现的报告遵循了图灵机的设计而且还提出用电子元件构造计算机并约定了用二进制进行计算和存储计算机基本结构运算器、控制器、存储器、输入设备、输出设备——冯诺依曼模型运算器、控制器——中央处理器存储器——内存输入输出设备——计算机外接的设备键盘就是输入设备——要用到控制总线显示器就是输出设备存储单元和输入输出设备要与中央处理器打交道的话离不开总线。内存存储程序和数据——存储的区域是线性的数据存储——基本单位是字节——每一个字节都对应一个内存地址内存的地址——从 0 开始编号的——自增排列最后一个地址为内存总字节数 - 1——内存的读写任何一个数据的速度都是一样的中央处理器——CPUCPU 的位宽32 位和 64 位 CPU 最主要区别在于一次能计算多少字节数据32 位 CPU 一次可以计算 4 个字节64 位 CPU 一次可以计算 8 个字节为了能计算更大的数值CPU 位宽越大可以计算的数值就越大内部还有一些组件寄存器分为多种类每种寄存器的功能又不尽相同主要作用是存储计算时的数据内存离 CPU 太远了、控制单元控制 CPU 工作和逻辑运算单元计算常见的寄存器种类通用寄存器存放需要进行运算的数据程序计数器存储 CPU 要执行下一条指令「所在的内存地址」指令寄存器指令被执行完成之前存放当前正在执行的指令总线CPU 和内存以及其他设备之间的通信总线可分为 3 种地址总线用于指定 CPU 将要操作的内存地址控制总线用于发送和接收信号比如中断、设备复位等信号CPU 收到信号后自然进行响应这时也需要控制总线数据总线用于读写内存的数据——传输数据输入、输出设备线路位宽与 CPU 位宽数据通过线路传输操作电压低电压表示 0高压电压则表示 1串行一位一位传输的方式 下一个 bit 必须等待上一个 bit 传输完成才能进行传输eg地址总线只有 1 条一条线路101传三次并行传输线路的位宽最好一次就能访问到所有的内存地址eg如果地址总线只有 1 条CPU 能操作的内存地址最大数量为 22^1个注意不要理解成同时能操作 2 个内存地址如果地址总线有 2 条 CPU 能操作的内存地址最大数量为 42^2个所以CPU 操作 4G 大的内存那么就需要 32 条地址总线因为2 ^ 32 4GCPU 的位宽最好不要小于线路位宽32 位 CPU 并不能一次性计算出加和两个 64 位数字的结果需要把这 2 个 64 位的数字分成 2 个低位 32 位数字和 2 个高位 32 位数字来计算先加个两个低位的 32 位数字算出进位然后加和两个高位的 32 位数字先加个两个低位的 32 位数字算出进位然后加和两个高位的 32 位数字64 位 CPU 就可以一次性算出加和两个 64 位数字的结果因为 64 位 CPU 可以一次读入 64 位的数字并且 64 位 CPU 内部的逻辑运算单元也支持 64 位数字的计算。如果计算的数额不超过 32 位数字的情况下32 位和 64 位 CPU 之间没什么区别的只有当计算超过 32 位数字的情况下64 位的优势才能体现出来。32 位 CPU 最大只能操作 4GB 内存就算你装了 8 GB 内存条也没用64 位 CPU 可以寻址更大的物理内存空间程序执行的基本过程第一步CPU 读取「程序计数器」的值这个值是指令的内存地址然后 CPU 的「控制单元」操作「地址总线」指定需要访问的内存地址接着通知内存设备准备数据数据准备好后通过「数据总线」将指令数据传给 CPUCPU 收到内存传来的数据后将这个指令数据存入到「指令寄存器」。第二步「程序计数器」的值自增表示指向下一条指令。这个自增的大小由 CPU 的位宽决定比如 32 位的 CPU指令是 4 个字节需要 4 个内存地址存放因此「程序计数器」的值会自增 4第三步CPU 分析「指令寄存器」中的指令确定指令的类型和参数如果是计算类型的指令就把指令交给「逻辑运算单元」运算如果是存储类型的指令则交由「控制单元」执行CPU 会根据程序计数器里的内存地址从内存里面把需要执行的指令读取到指令寄存器里面执行然后根据指令长度自增开始顺序读取下一条指令。CPU 从程序计数器读取指令、到执行、再到下一条指令这个过程会不断循环直到程序执行结束这个不断循环的过程被称为CPU 的指令周期。a 1 2 执行具体过程CPU 是不认识a 1 2这个字符串需要把整个程序翻译成汇编语言的程序这个过程称为编译成汇编代码。我们还需要用汇编器翻译成机器码这些机器码由 0 和 1 组成的机器语言这一条条机器码就是一条条的计算机指令这个才是 CPU 能够真正认识的东西。下面来看看a 1 2在 32 位 CPU 的执行过程。程序编译过程中编译器通过分析代码发现 1 和 2 是数据于是程序运行时内存会有个专门的区域来存放这些数据这个区域就是「数据段」。如下图数据 1 和 2 的区域位置数据 1 被存放到 0x200 位置数据 2 被存放到 0x204 位置注意数据和指令是分开区域存放的存放指令区域的地方称为「正文段」。编译器会把a 1 2翻译成 4 条指令存放到正文段中。如图这 4 条指令被存放到了 0x100 ~ 0x10c 的区域中• 0x100 的内容是load指令将 0x200 地址中的数据 1 装入到寄存器R0• 0x104 的内容是load指令将 0x204 地址中的数据 2 装入到寄存器R1• 0x108 的内容是add指令将寄存器R0和R1的数据相加并把结果存放到寄存器R2• 0x10c 的内容是store指令将寄存器R2中的数据存回数据段中的 0x208 地址中这个地址也就是变量a内存中的地址编译完成后具体执行程序的时候程序计数器会被设置为 0x100 地址然后依次执行这 4 条指令。上面的例子中由于是在 32 位 CPU 执行的因此一条指令是占 32 位大小所以你会发现每条指令间隔 4 个字节。而数据的大小是根据你在程序中指定的变量类型比如int类型的数据则占 4 个字节char类型的数据则占 1 个字节。指令事实上指令的内容是一串二进制数字的机器码每条指令都有对应的机器码CPU 通过解析机器码来知道指令的内容。不同的 CPU 有不同的指令集也就是对应着不同的汇编语言和不同的机器码选用最简单的 MIPS 指集来看看机器码是如何生成的这样也能明白二进制的机器码的具体含义。MIPS 的指令是一个 32 位的整数高 6 位代表着操作码表示这条指令是一条什么样的指令剩下的 26 位不同指令类型所表示的内容也就不相同主要有三种类型R、I 和 J。R 指令用在算术和逻辑操作里面有读取和写入数据的寄存器地址。如果是逻辑位移操作后面还有位移操作的「位移量」而最后的「功能码」则是再前面的操作码不够的时候扩展操作码来表示对应的具体指令的I 指令用在数据传输、条件分支等。这个类型的指令就没有了位移量和功能码也没有了第三个寄存器而是把这三部分直接合并成了一个地址值或一个常数J 指令用在跳转高 6 位之外的 26 位都是一个跳转后的地址接下来我们把前面例子的这条指令「add指令将寄存器R0和R1的数据相加并把结果放入到R2」翻译成机器码。加和运算 add 指令是属于 R 指令类型add 对应的 MIPS 指令里操作码是000000以及最末尾的功能码是100000这些数值都是固定的查一下 MIPS 指令集的手册就能知道的rs 代表第一个寄存器 R0 的编号即00000rt 代表第二个寄存器 R1 的编号即00001rd 代表目标的临时寄存器 R2 的编号即00010因为不是位移操作所以位移量是00000把上面这些数字拼在一起就是一条 32 位的 MIPS 加法指令了那么用 16 进制表示的机器码则是0x00011020。编译器在编译程序的时候会构造指令这个过程叫做指令的编码。CPU 执行程序的时候就会解析指令这个过程叫作指令的解码。CPU 都使用来流水线的方式来执行指令所谓的流水线就是把一个任务拆分成多个小任务于是一条指令通常分为 4 个阶段称为 4 级流水线如下图具体含义CPU 通过程序计数器读取对应内存地址的指令这个部分称为Fetch取得指令CPU 对指令进行解码这个部分称为Decode指令译码CPU 执行指令这个部分称为Execution执行指令CPU 将计算结果存回寄存器或者将寄存器的值存入内存这个部分称为Store数据回写上面这 4 个阶段我们称为指令周期Instrution Cycle事实上不同的阶段其实是由计算机中的不同组件完成的实际上通过程序计数器和指令寄存器取出指令的过程是由控制器操作的指令的译码过程也是由控制器进行的指令执行的过程无论是进行算术操作、逻辑操作还是进行数据传输、条件分支操作都是由算术逻辑单元操作的也就是由运算器处理的。但是如果是一个简单的无条件地址跳转则是直接在控制器里面完成的不需要用到运算器。指令的类型从功能角度划分可以分为 5 大类数据传输类型的指令比如 store/load 是寄存器与内存间数据传输的指令mov 是将一个内存地址的数据移动到另一个内存地址的指令运算类型的指令比如加减乘除、位运算、比较大小等等它们最多只能处理两个寄存器中的数据跳转类型的指令通过修改程序计数器的值来达到跳转执行指令的过程比如编程中常见的 if-else、switch-case、函数调用等。信号类型的指令比如发生中断的指令 trap闲置类型的指令比如指令 nop执行后 CPU 会空转一个周期指令的执行速度CPU 的硬件参数都会有GHz这个参数比如一个 1 GHz 的 CPU指的是时钟频率是 1 G代表着 1 秒会产生 1G 次数的脉冲信号每一次脉冲信号高低电平的转换就是一个周期称为时钟周期。对于 CPU 来说在一个时钟周期内CPU 仅能完成一个最基本的动作时钟频率越高时钟周期就越短工作速度也就越快。一个时钟周期一定能执行完一条指令吗答案是不一定的大多数指令不能在一个时钟周期完成通常需要若干个时钟周期。不同的指令需要的时钟周期是不同的加法和乘法都对应着一条 CPU 指令但是乘法需要的时钟周期就要比加法多。如何让程序跑的更快程序执行的时候耗费的 CPU 时间少就说明程序是快的对于程序的 CPU 执行时间我们可以拆解成CPU 时钟周期数CPU Cycles和时钟周期时间Clock Cycle Time的乘积。时钟周期时间就是我们前面提及的 CPU 主频主频越高说明 CPU 的工作速度就越快比如我手头上的电脑的 CPU 是 2.4 GHz 四核 Intel Core i5这里的 2.4 GHz 就是电脑的主频时钟周期时间就是 1/2.4G。要想 CPU 跑的更快自然缩短时钟周期时间也就是提升 CPU 主频但是今非彼日摩尔定律早已失效当今的 CPU 主频已经很难再做到翻倍的效果了。另外换一个更好的 CPU这个也是我们软件工程师控制不了的事情我们应该把目光放到另外一个乘法因子 —— CPU 时钟周期数如果能减少程序所需的 CPU 时钟周期数量一样也是能提升程序的性能的。对于 CPU 时钟周期数我们可以进一步拆解成「指令数 x 每条指令的平均时钟周期数Cycles Per Instruction简称 CPI」于是程序的 CPU 执行时间的公式可变成如下因此要想程序跑的更快优化这三者即可指令数表示执行程序所需要多少条指令以及哪些指令。这个层面是基本靠编译器来优化毕竟同样的代码在不同的编译器编译出来的计算机指令会有各种不同的表示方式。每条指令的平均时钟周期数 CPI表示一条指令需要多少个时钟周期数现代大多数 CPU 通过流水线技术Pipeline让一条指令需要的 CPU 时钟周期数尽可能的少时钟周期时间表示计算机主频取决于计算机硬件。有的 CPU 支持超频技术打开了超频意味着把 CPU 内部的时钟给调快了于是 CPU 工作速度就变快了但是也是有代价的CPU 跑的越快散热的压力就会越大CPU 会很容易奔溃。很多厂商为了跑分而跑分基本都是在这三个方面入手的哦特别是超频这一块。

相关新闻