01 CPU是什么
1.1 CPU 的内部结构
组成层面:CPU和内存是由许多晶体管组成的电子部件,通常称为IC(Integrated Circuit,集成电路)
功能层面:
- CPU内部由寄存器、控制器、运算器和时钟四个部分构成,各部分由电流信号相互连通
- 寄存器可暂存指令、数据等处理对象,可看作是内存的一种,一个CPU内部有20~100个寄存器
- 控制器负责把内存上的指令、数据等读入寄存器,并根据指令执行结果控制整个计算机
- 运算器负责运算从内存读入寄存器的数据
- 时钟负责发出CPU开始计时的时钟信号,有些计算机的时钟位于CPU外部
通常所说的内存即计算机的主存储器(主存),通过控制芯片与CPU相连,由可读写且每个字节带地址编号的元素构成,负责存储指令和数据供CPU按地址读写,其存储内容会随计算机关机自动清除。
程序启动后,控制器依时钟信号从内存读取指令和数据,运算器对数据运算,控制器据运算结果控制计算机,控制主要是数据输入输出时机处理,如内存、磁盘等存储设备及键盘、显示器等外设的输入输出。
1.2 CPU 是寄存器的集合体
在CPU的四个构成部分中,程序员只需了解寄存器,其余三个无需过多关注。这是因为程序直接以寄存器为操作对象,而其他部分的功能由硬件自动实现,程序员无需直接干预。
汇编语言用助记符(多为英文单词缩写,如mov、add)编写程序,与机器语言指令一一对应,这和高级编程语言不同,因此常用其说明CPU运行。将汇编程序转成机器语言叫汇编,反之将机器语言程序转成汇编语言叫反汇编。
mov eax, dword ptr [ebp-8] ; 将内存地址ebp-8的数据存入eax寄存器
add eax, dword ptr [ebp-0Ch] ; 用eax寄存器与内存地址ebp-0Ch的数据相加
mov dword ptr [ebp-4], eax ; 将运算结果存回内存地址ebp-4处
通过阅读汇编语言代码可了解机器语言程序运行情况,对程序员而言“CPU是寄存器的集合体”,寄存器用于处理数据(如存储、加法运算等),其种类通过名字区分(如示例中的eax、ebp),内存存储场所则通过地址编号区分;无需掌握所有寄存器种类和汇编语言,只需大致知晓高级语言程序编译为机器语言后,由CPU内部寄存器处理(如“a=1+2”转化为机器语言后靠寄存器完成运算和存储)。
不同类型CPU的寄存器在数量、种类及数值范围上存在差异,但按功能可大致分为八类;寄存器可存储指令或数据,数据分为“运算数值”和“内存地址数值”,前者由累加寄存器存储,后者由基址寄存器、变址寄存器等存储(如代码示例中的eax是累加寄存器,ebp是基址寄存器)。
| 种 类 | 功 能 |
|---|---|
| 累加寄存器(Accumulator Register) | 存储执行运算的数据和运算后的结果数据。 |
| 标志寄存器(Flag Register) | 存储运算处理后的 CPU 状态(如进位、溢出、零值等标志)。 |
| 程序计数器(Program Counter) | 存储下一条待执行指令在内存中的地址,控制程序执行流程。 |
| 基址寄存器(Base Register) | 存储数据内存区域的起始地址,用于内存地址的基准定位。 |
| 变址寄存器(Index Register) | 存储基址寄存器的相对偏移地址,用于动态计算内存地址(如数组索引)。 |
| 通用寄存器(General Purpose Register) | 存储任意类型的数据,可灵活用于运算、地址存储等场景。 |
| 指令寄存器(Instruction Register) | 存储当前正在执行的指令,供 CPU 内部译码和执行,程序员无法通过程序直接读写。 |
| 栈寄存器(Stack Register) | 存储栈区域的起始地址或栈顶指针,用于管理函数调用、数据压栈/出栈等操作。 |
对程序员而言,CPU 是由具有各种功能的寄存器组成的集合体,其中程序计数器、累加寄存器、标志寄存器、指令寄存器和栈寄存器通常各只有一个,其他寄存器(如通用寄存器)一般有多个;程序计数器和标志寄存器比较特殊,后续说明;程序员主要关注与数据处理相关的寄存器,而指令寄存器等无需过多关注。
1.3 决定程序流程的程序计数器
程序启动时,Windows等操作系统会将硬盘中的程序复制到内存(如示例中实现123+456并输出结果的程序),内存通过地址划分存储指令和数据(图中简化为一个地址存一条指令/数据,实际可能占多个地址);操作系统将程序计数器设为程序起始地址(如0100),CPU每执行一条指令,程序计数器自动加1(若指令占多个地址则按长度递增),其数值决定程序执行流程。
1.4 条件分支和循环机制
程序流程包括顺序执行、条件分支和循环三种:
- 顺序执行:按内存地址顺序执行指令,程序计数器自动递增(如+1或按指令长度);
- 条件分支与循环:通过跳转指令根据条件(如累加寄存器运算结果的正负、零值等)修改程序计数器为任意地址,实现流程跳转或重复执行,条件判断依赖标志寄存器记录的运算状态(如正负、溢出等)。
(例:累加寄存器值为正数时,跳转指令可使程序跳过部分代码,直接执行目标地址指令。)
条件分支和循环中的跳转指令通过标志寄存器判断是否执行:标志寄存器记录累加寄存器运算结果的状态(如正负、零值、溢出、奇偶性等),跳转指令依据这些状态信息决定是否修改程序计数器以改变程序执行流程。
CPU运算时,标志寄存器会根据结果自动设定数值(其三个位分别对应正、零、负状态);条件分支的跳转判断基于标志寄存器:执行比较指令时,CPU内部通过两数相减(如XXX-YYY)更新标志寄存器,结果为正/零/负分别表示前者大于/等于/小于后者,进而决定是否执行跳转指令。
1.5 函数调用机制
函数调用通过 call 指令实现,该指令在跳转至函数入口地址前,会将调用点的下一条指令地址(如示例中的 0154)存入栈中;函数执行完毕后,return 指令从栈中取出该地址并恢复到程序计数器,确保流程正确返回到调用点,解决了单纯跳转指令无法回溯的问题。
高级编程语言的程序经编译后,函数调用会转换为call指令(用于保存调用点的返回地址并跳转至函数入口),函数结束则转换为return指令(从栈中读取返回地址并恢复程序计数器)。
1.6 通过地址和索引实现数组
基址寄存器和变址寄存器可用于划分主内存特定区域以实现类似数组的操作:基址寄存器存储内存区域起始地址(如10000000),变址寄存器存储相对偏移量(如00000000~0000FFFF),CPU通过“基址寄存器+变址寄存器”计算实际内存地址,变址寄存器功能类似高级语言中数组的索引。
1.7 CPU 的处理其实很简单
下表按照功能对 CPU能执行的机器语言指令进行了大体分类。这里没有列出指令的具体名称(汇编语言的助记符)。看完表后你会惊奇地发现,原来 CPU 可以进行的处理非常少。虽然高级编程语言编写的程序看起来非常复杂,但 CPU 实际处理的事情就是这么简单。
|机器语言指令的主要类型和功能||
| 类 型 | 功 能 |
|---|---|
| 数据转送指令 | 完成寄存器与内存、内存与内存、寄存器与外围设备间的数据读写操作。 |
| 运算指令 | 通过累加寄存器执行算术运算、逻辑运算、比较运算和移位运算。 |
| 跳转指令 | 实现条件分支、循环及强制跳转等程序流程控制。 |
| call/return 指令 | 处理函数调用(保存返回地址并跳转至函数入口)及返回(恢复调用点地址)。 |