aboutsummaryrefslogtreecommitdiffstats
path: root/content/posts/从编译原理到物理原理剖析程序的编译与执行.md
diff options
context:
space:
mode:
Diffstat (limited to 'content/posts/从编译原理到物理原理剖析程序的编译与执行.md')
-rw-r--r--content/posts/从编译原理到物理原理剖析程序的编译与执行.md138
1 files changed, 138 insertions, 0 deletions
diff --git a/content/posts/从编译原理到物理原理剖析程序的编译与执行.md b/content/posts/从编译原理到物理原理剖析程序的编译与执行.md
new file mode 100644
index 0000000..a969019
--- /dev/null
+++ b/content/posts/从编译原理到物理原理剖析程序的编译与执行.md
@@ -0,0 +1,138 @@
+---
+categories:
+- 往昔
+date: "2025-07-29T15:55:14+08:00"
+draft: false
+slug: ""
+tags:
+- 底层技术
+- 编译器
+- 编译原理
+title: 从编译原理到物理原理剖析程序的编译与执行
+---
+## 编译过程
+
+以C语言为例,编译成可执行文件一共要经历:**预处理=>狭义编译=>汇编=>链接**,最终成为可执行文件。
+
+### 预处理
+
+输入源代码文件以及其包含的头文件,由预处理器执行展开宏定义、处理,条件编译指令、将包含的头文件直接插入到指令位置,删除注释。
+
+输出纯净的、宏已展开、注释已删除、头文件已包含的中间代码文件,通常为`.i`。
+
+编写一个简单的程序:
+
+```c
+#include <stdio.h>
+#define STRING "Program"
+
+int
+main() {
+ // 打印一些内容
+ printf("Hello world!\n");
+ printf("%s\n", STRING);
+ return 0;
+}
+```
+
+使用gcc的`-E`选项生成中间代码:
+
+`gcc exam.c -E -o ./exam.i`
+
+他生成了非常长的代码
+
+可以验证预处理器是直接把头文件替换进来的。而代码中类似`# 5 "exam.c"`的标记用于记录源代码的位置信息,便于调试器和错误诊断程序追踪代码位置。
+
+### 编译(狭义编译)
+
+#### 词法分析
+
+输入预处理后的源代码文件,有编译器执行词法分析,将字符流分解成有意义的词素(Token),例如:
+
+```c
+int sum = a + b;
+```
+
+分解成`int`, `sum`, `=`, `a`, `+`, `b`等Token。
+
+#### 语法分析
+
+根据语言的语法规则,将Token序列组合成抽象语法树(Abstract Syntax Tree),简称AST,表达了代码的结构和层次关系。
+
+#### 语义分析
+
+检查程序的语义是否正确,例如变量是否声明、类型是否匹配等。
+
+#### 中间代码生成
+
+将AST转换成一种独立于CPU架构的中间表示形式(intermediate Representation),即IR。常见的IR有三地址码、LLVM IR、Java字节码等,为了优化和转移成多种目标机器码。
+
+#### 优化
+
+对IR进行处理,目的是在不改变程序行为的前提下,提高代码效率。包括:
+
+- 常量折叠:如计算`3 + 5`为`8`。
+- 死代码消除:移除永远不会执行到的代码。
+- 循环优化:如循环展开。
+- 函数内联。
+- 寄存器分配:决定哪些变量存储在高速的CPU寄存器中。
+
+输出优化后的中间代码。
+
+### 汇编
+
+输入IR,或直接由编译器生成的汇编代码。由汇编器`as`执行。将汇编指令一对一地转换成特定CPU架构的机器操作指令。处理伪汇编指令,如定义数据段`.data`,定义代码段`.text`,分配存储空间`space`等。最后解析符号、如函数名、变量名等,生成符号表。
+
+输出目标文件,如.o,.obj,包含机器指令、全局变量、静态变量的初始值等、符号表、重定位信息。
+
+### 链接
+
+输入一个或多个.o文件夹+库文件(静态.a/.lib,动态.so/.dll)。由链接器进行符号解析、重定位、库处理,最输出可执行文件或库文件。
+
+---
+
+
+## CPU如何识别指令
+
+CPU执行程序的循环称为Fetch-Decode-Execute Cycle(取指-译码-执行周期)。
+
+### 取指
+
+CPU内部有一个寄存器叫程序计数器(Program Counter, PC),它保存着下一条要执行的指令的内存地址,CPU将PC中的地址发送到地址总线,内存控制器根据地址总线上的地址,找到对应的内存单元,将其存储的指令通过数据总线送回CPU。取回来的指令被放入指令寄存器IR。PC的值自动增加,指向下一条指令的地址。
+
+### 译码指令
+
+CPU的控制单元读取IR中的指令,控制单元包含一指令译码器,译码器分析指令 操作码部分,操作码唯一表示CPU应该执行什么操作,如ADD MOV JMP等。
+
+根据操作码,译码器决定:
+- 操作的性质(算术、逻辑、数据传输、跳转等)。
+- 操作需要多少个操作数。
+- 操作数存放位置(寄存器、内部地址指令本身中的立即数)
+
+译码器激活执行该操作所需要的CPU内部电路通路和控制信号,结果决定了下一个阶段(执行)需要做什么。
+
+### 执行指令
+
+CPU的算术逻辑单元(Arithmetic Logic Unit, ALU)或去他功能单元(FPU MMU)根据译码器产生的控制信号执行实际操作。
+
+操作数可能从寄存器文件中读取,或者从内存中加载,ALU执行加减与或移位等操作,如果指令是JMP/CALL/RET/分支指令等跳转指令,可能会修改PC的值,从而改变下一条指令的位置。计算结果可能写回寄存器,或者通过数据总线写会内存。
+
+上述的操作循环以极高的速度(GHz级别)不断重复,构成了CPU运行程序基础。
+
+## 编译后的指令在物理层面上是什么
+
+答:**内存中的电荷状态**。
+
+程序被操作系统加载到计算机的RAM中,RAM由无数的存储单元(通常是电容)组成,每一个单元可以存储一个bit的信息。
+
+高电平电荷(通常代表1)或低电平电荷(通常代表0)的状态,就表示一个二进制位。
+
+每条指令由多个bit组成。指令序列在RAM中是一系列连续存储单元的电荷状态。当CPU取指令时,PC寄存器的值,也就是一组触发器的电平状态被送到地址总线,也就是一组物理导线。
+
+地址总线上的电平信号激活内存控制器和特定的存储单元。
+
+被选中的内存单元的电荷状态被读出,转换为相应的电平信号,通过数据总线传回CPU。
+
+这些电平信号进入CPU的指令寄存器IR,也是一组触发器的电平状态。
+
+一组一组电平状态激活着CPU中的组件,从而执行指令。