<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>编译器 on 映屿</title>
    <link>https://blog.verdant.ee/tags/%E7%BC%96%E8%AF%91%E5%99%A8/</link>
    <description>Recent content in 编译器 on 映屿</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    
      <managingEditor>i@glowisle.me (五葉地錦)</managingEditor>
    
    
      <webMaster>i@glowisle.me (五葉地錦)</webMaster>
    
    
    
    <lastBuildDate>Tue, 29 Jul 2025 15:55:14 +0800</lastBuildDate>
    
    
    <atom:link href="http://blog.verdant.ee/tags/%E7%BC%96%E8%AF%91%E5%99%A8/atom.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>从编译原理到物理原理剖析程序的编译与执行</title>
      <link>https://blog.verdant.ee/posts/%E4%BB%8E%E7%BC%96%E8%AF%91%E5%8E%9F%E7%90%86%E5%88%B0%E7%89%A9%E7%90%86%E5%8E%9F%E7%90%86%E5%89%96%E6%9E%90%E7%A8%8B%E5%BA%8F%E7%9A%84%E7%BC%96%E8%AF%91%E4%B8%8E%E6%89%A7%E8%A1%8C/</link>
      <pubDate>Tue, 29 Jul 2025 15:55:14 +0800</pubDate><author>i@glowisle.me (五葉地錦)</author>
      <guid>https://blog.verdant.ee/posts/%E4%BB%8E%E7%BC%96%E8%AF%91%E5%8E%9F%E7%90%86%E5%88%B0%E7%89%A9%E7%90%86%E5%8E%9F%E7%90%86%E5%89%96%E6%9E%90%E7%A8%8B%E5%BA%8F%E7%9A%84%E7%BC%96%E8%AF%91%E4%B8%8E%E6%89%A7%E8%A1%8C/</guid>
      <description>&lt;h2 id=&#34;编译过程&#34;&gt;编译过程&lt;/h2&gt;&#xA;&lt;p&gt;以C语言为例，编译成可执行文件一共要经历：&lt;strong&gt;预处理=&amp;gt;狭义编译=&amp;gt;汇编=&amp;gt;链接&lt;/strong&gt;，最终成为可执行文件。&lt;/p&gt;&#xA;&lt;h3 id=&#34;预处理&#34;&gt;预处理&lt;/h3&gt;&#xA;&lt;p&gt;输入源代码文件以及其包含的头文件，由预处理器执行展开宏定义、处理，条件编译指令、将包含的头文件直接插入到指令位置，删除注释。&lt;/p&gt;&#xA;&lt;p&gt;输出纯净的、宏已展开、注释已删除、头文件已包含的中间代码文件，通常为&lt;code&gt;.i&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;p&gt;编写一个简单的程序：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e67e80&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#e67e80;font-style:italic&#34;&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#e67e80&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e67e80&#34;&gt;#define STRING &amp;#34;Program&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b2c98f&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#859289;font-style:italic&#34;&gt;// 打印一些内容&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b2c98f&#34;&gt;printf&lt;/span&gt;(&lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;Hello world!&lt;/span&gt;&lt;span style=&#34;color:#b2c98f&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b2c98f&#34;&gt;printf&lt;/span&gt;(&lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;%s&lt;/span&gt;&lt;span style=&#34;color:#b2c98f&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#b2c98f&#34;&gt;&amp;#34;&lt;/span&gt;, STRING);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e67e80&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#d699b6&#34;&gt;0&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;使用gcc的&lt;code&gt;-E&lt;/code&gt;选项生成中间代码：&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;gcc exam.c -E -o ./exam.i&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;他生成了非常长的代码&lt;/p&gt;&#xA;&lt;p&gt;可以验证预处理器是直接把头文件替换进来的。而代码中类似&lt;code&gt;# 5 &amp;quot;exam.c&amp;quot;&lt;/code&gt;的标记用于记录源代码的位置信息，便于调试器和错误诊断程序追踪代码位置。&lt;/p&gt;&#xA;&lt;h3 id=&#34;编译狭义编译&#34;&gt;编译（狭义编译）&lt;/h3&gt;&#xA;&lt;h4 id=&#34;词法分析&#34;&gt;词法分析&lt;/h4&gt;&#xA;&lt;p&gt;输入预处理后的源代码文件，有编译器执行词法分析，将字符流分解成有意义的词素（Token），例如：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d6cbb4;background-color:#252b2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#dbbc7f&#34;&gt;int&lt;/span&gt; sum &lt;span style=&#34;color:#7a8478&#34;&gt;=&lt;/span&gt; a &lt;span style=&#34;color:#7a8478&#34;&gt;+&lt;/span&gt; b;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;分解成&lt;code&gt;int&lt;/code&gt;, &lt;code&gt;sum&lt;/code&gt;, &lt;code&gt;=&lt;/code&gt;, &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;等Token。&lt;/p&gt;&#xA;&lt;h4 id=&#34;语法分析&#34;&gt;语法分析&lt;/h4&gt;&#xA;&lt;p&gt;根据语言的语法规则，将Token序列组合成抽象语法树（Abstract Syntax Tree），简称AST，表达了代码的结构和层次关系。&lt;/p&gt;&#xA;&lt;h4 id=&#34;语义分析&#34;&gt;语义分析&lt;/h4&gt;&#xA;&lt;p&gt;检查程序的语义是否正确，例如变量是否声明、类型是否匹配等。&lt;/p&gt;&#xA;&lt;h4 id=&#34;中间代码生成&#34;&gt;中间代码生成&lt;/h4&gt;&#xA;&lt;p&gt;将AST转换成一种独立于CPU架构的中间表示形式（intermediate Representation），即IR。常见的IR有三地址码、LLVM IR、Java字节码等，为了优化和转移成多种目标机器码。&lt;/p&gt;&#xA;&lt;h4 id=&#34;优化&#34;&gt;优化&lt;/h4&gt;&#xA;&lt;p&gt;对IR进行处理，目的是在不改变程序行为的前提下，提高代码效率。包括：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;常量折叠：如计算&lt;code&gt;3 + 5&lt;/code&gt;为&lt;code&gt;8&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;li&gt;死代码消除：移除永远不会执行到的代码。&lt;/li&gt;&#xA;&lt;li&gt;循环优化：如循环展开。&lt;/li&gt;&#xA;&lt;li&gt;函数内联。&lt;/li&gt;&#xA;&lt;li&gt;寄存器分配：决定哪些变量存储在高速的CPU寄存器中。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;输出优化后的中间代码。&lt;/p&gt;&#xA;&lt;h3 id=&#34;汇编&#34;&gt;汇编&lt;/h3&gt;&#xA;&lt;p&gt;输入IR，或直接由编译器生成的汇编代码。由汇编器&lt;code&gt;as&lt;/code&gt;执行。将汇编指令一对一地转换成特定CPU架构的机器操作指令。处理伪汇编指令，如定义数据段&lt;code&gt;.data&lt;/code&gt;，定义代码段&lt;code&gt;.text&lt;/code&gt;，分配存储空间&lt;code&gt;space&lt;/code&gt;等。最后解析符号、如函数名、变量名等，生成符号表。&lt;/p&gt;&#xA;&lt;p&gt;输出目标文件，如.o，.obj，包含机器指令、全局变量、静态变量的初始值等、符号表、重定位信息。&lt;/p&gt;&#xA;&lt;h3 id=&#34;链接&#34;&gt;链接&lt;/h3&gt;&#xA;&lt;p&gt;输入一个或多个.o文件夹+库文件（静态.a/.lib，动态.so/.dll）。由链接器进行符号解析、重定位、库处理，最输出可执行文件或库文件。&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;cpu如何识别指令&#34;&gt;CPU如何识别指令&lt;/h2&gt;&#xA;&lt;p&gt;CPU执行程序的循环称为Fetch-Decode-Execute Cycle（取指-译码-执行周期）。&lt;/p&gt;&#xA;&lt;h3 id=&#34;取指&#34;&gt;取指&lt;/h3&gt;&#xA;&lt;p&gt;CPU内部有一个寄存器叫程序计数器（Program Counter, PC），它保存着下一条要执行的指令的内存地址，CPU将PC中的地址发送到地址总线，内存控制器根据地址总线上的地址，找到对应的内存单元，将其存储的指令通过数据总线送回CPU。取回来的指令被放入指令寄存器IR。PC的值自动增加，指向下一条指令的地址。&lt;/p&gt;&#xA;&lt;h3 id=&#34;译码指令&#34;&gt;译码指令&lt;/h3&gt;&#xA;&lt;p&gt;CPU的控制单元读取IR中的指令，控制单元包含一指令译码器，译码器分析指令 操作码部分，操作码唯一表示CPU应该执行什么操作，如ADD MOV JMP等。&lt;/p&gt;&#xA;&lt;p&gt;根据操作码，译码器决定：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;操作的性质（算术、逻辑、数据传输、跳转等）。&lt;/li&gt;&#xA;&lt;li&gt;操作需要多少个操作数。&lt;/li&gt;&#xA;&lt;li&gt;操作数存放位置（寄存器、内部地址指令本身中的立即数）&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;译码器激活执行该操作所需要的CPU内部电路通路和控制信号，结果决定了下一个阶段（执行）需要做什么。&lt;/p&gt;&#xA;&lt;h3 id=&#34;执行指令&#34;&gt;执行指令&lt;/h3&gt;&#xA;&lt;p&gt;CPU的算术逻辑单元（Arithmetic Logic Unit, ALU）或去他功能单元（FPU MMU）根据译码器产生的控制信号执行实际操作。&lt;/p&gt;&#xA;&lt;p&gt;操作数可能从寄存器文件中读取，或者从内存中加载，ALU执行加减与或移位等操作，如果指令是JMP/CALL/RET/分支指令等跳转指令，可能会修改PC的值，从而改变下一条指令的位置。计算结果可能写回寄存器，或者通过数据总线写会内存。&lt;/p&gt;&#xA;&lt;p&gt;上述的操作循环以极高的速度（GHz级别）不断重复，构成了CPU运行程序基础。&lt;/p&gt;&#xA;&lt;h2 id=&#34;编译后的指令在物理层面上是什么&#34;&gt;编译后的指令在物理层面上是什么&lt;/h2&gt;&#xA;&lt;p&gt;答：&lt;strong&gt;内存中的电荷状态&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;程序被操作系统加载到计算机的RAM中，RAM由无数的存储单元（通常是电容）组成，每一个单元可以存储一个bit的信息。&lt;/p&gt;&#xA;&lt;p&gt;高电平电荷（通常代表1）或低电平电荷（通常代表0）的状态，就表示一个二进制位。&lt;/p&gt;&#xA;&lt;p&gt;每条指令由多个bit组成。指令序列在RAM中是一系列连续存储单元的电荷状态。当CPU取指令时，PC寄存器的值，也就是一组触发器的电平状态被送到地址总线，也就是一组物理导线。&lt;/p&gt;&#xA;&lt;p&gt;地址总线上的电平信号激活内存控制器和特定的存储单元。&lt;/p&gt;&#xA;&lt;p&gt;被选中的内存单元的电荷状态被读出，转换为相应的电平信号，通过数据总线传回CPU。&lt;/p&gt;&#xA;&lt;p&gt;这些电平信号进入CPU的指令寄存器IR，也是一组触发器的电平状态。&lt;/p&gt;&#xA;&lt;p&gt;一组一组电平状态激活着CPU中的组件，从而执行指令。&lt;/p&gt;&#xA;</description>
    </item>
  </channel>
</rss>
