CPU指令封装原理
[toc]
# 引言
程序在编码后由编译器或者解释器生成对应的执行指令,而CPU的职责就是不断地接收并处理这些指令。本文将自底向上地从指令集执行逐步向上封装推演出高级语言的执行过程,通过对本文的阅读,您将对CPU指令执行和封装的思想有深刻的理解和掌握。
我是 SharkChili ,Java 开发者,Java Guide 开源项目维护者。欢迎关注我的公众号:写代码的SharkChili,也欢迎您了解我的开源项目 mini-redis:https://github.com/shark-ctrl/mini-redis。
为方便与读者交流,现已创建读者群。关注上方公众号获取我的联系方式,添加时备注加群即可加入。
# 详解指令到编程语言的演变步骤
# 指令和指令集
CPU所执行的指令通过不同的二进制表示进行表示,例如:
- 加法:0000 0001
- 减法:0000 0010
- 乘法:0000 0011
- 除法:0000 0100
- 从内存读取数据:0000 0101
- 将数据写入到内存:0000 0110
- ......
注意,上述的二进制编码仅仅是举例,实际上的指令集编码相比起来更加复杂,也正是这些二进制构成了指令集,使得我们可以通过不同的指令集进行编排构成各种逻辑运算,这就是所谓的编程。
需要注意的是在不同的CPU下指令集是不同的:
- x86架构是复杂指令集—CISC,即一条指令执行复合操作即完成一个复杂的功能,所以它的指令长度是不固定的。
- ARM架构则是一条指令执行一个基本操作,这种简单指令也就是RISC简单指令集
一般来说,现在的服务器基本是x86架构,如下图,笔者通过uname -m输出x86_64即64位的x86架构服务器:

# 寄存器
有了指令之后就要考虑到程序执行的效率,为保证CPU能够尽快执行指令,设计者们在CPU中内置了寄存器来缓存了一些数据以提升数据读写速度进而提升程序执行的效率,而对应的数据包括:
- 执行的指令地址
- 存储过程中的一些状态信息
- 存储运算要用到的数据
为方便管理寄存器也有相应的分类:
- 记录指令的寄存器EIP(Extended Instruction Pointer)
- 记录堆栈信息的寄存器ESP(Extended Stack Pointer)
- 存储运行状态和控制信息的标志寄存器EFLAGS(Extended FLAGS register)
- 缓存数据的通用寄存器

# 汇编码
基于上述基础,在进行程序编写工作时不得不手动输入一个个二进制机器码,这对于程序员的记忆负担也是非常重的,编码效率十分低下,于是就想到了助记符,即用一个个带有语义的助记符号表示一个个二进制指令,例如:
- 加法:0000 0001=>ADD
- 减法:0000 0010=>SUB
- 乘法:0000 0011=>MUL
- 除法:0000 0100=>DIV
- 从内存读取数据:0000 0101=>LOAD
- 将数据写入到内存:0000 0110=>STORE
- ......
而这种助记符对应的专业术语也就是我们常说的汇编语言,基于汇编语言,我们希望将寄存器上的数字和ebp寄存器对应偏移量为offset的数字相加,就可以通过add eax, [ebp+offset]的方式编码,执行时通过工具转换为机器码由CPU执行:

# 高级语言
但是考虑到各平台的CPU指令集的差异,不同的平台可能存在不同的汇编码指令,为了实现跨平台程序,程序员不得不在各个平台上完成适配,因此考虑到采用更高级别的语言针对这些指令进行语义化的封装,例如上述的add指令中各种寄存器读取、相加、写回都采用一条通用的编码int w11 = w19 + w20,对应的笔者也给出arm架构下加法运算和对应的汇编指令:
int w11 = w19 + w20 //源代码
add w11, w19, w20 //arm架构下转为汇编的指令
2
然后这些高级语言直接通过编译器转为可执行的二进制机器语言,将不同平台的适配工作交由编译器统一处理,减少程序员不同平台的适配负担,由此诞生了C、C++、Java等这些语法规则接近人类自然语言的高级编程语言:

上文提及编译器转换的步骤,因为机器无法直接识别人类所能理解的指令,所以就有了编译器编译将其转换为机器码文件的步骤,由此现代高级语言大体执行过程为:
- 操作系统从硬盘中加载可执行文件到内存,读取可执行文件头部信息确定程序入口点地址
- 操作系统将入口点地址写入CPU的指令寄存器(如EIP或RIP)
- CPU开始从指令寄存器指向的地址执行指令
- 读取指令的电路通过指令寄存器加载指令,交由译码电路执行
- 译码电路根据指令长度定位到下一条执行的指令
- ALU完成本次执行指令的运算
- 将指令执行结果写回对应的位置上
- 重复执行步骤4到7

# 小结
本文自底向上地分析了计算机编程语言的演进之路,从最开始的二进制机器码到语义化汇编语言,然后考虑到跨平台的适配负担又衍生了基于编译器跨平台运行的高级语言,并对高级语言的执行过程进行了深入的讲解。
我是 SharkChili ,Java 开发者,Java Guide 开开源项目维护者。欢迎关注我的公众号:写代码的SharkChili,也欢迎您了解我的开源项目 mini-redis:https://github.com/shark-ctrl/mini-redis。
为方便与读者交流,现已创建读者群。关注上方公众号获取我的联系方式,添加时备注加群即可加入。
# 参考
《趣话计算机底层技术》