第2集:计算机是如何执行程序的?

学习目标

通过本集的学习,你将能够:

  • 理解从高级语言到机器语言的完整转换过程
  • 描述 CPU 的基本工作原理
  • 区分内存和寄存器的作用
  • 读懂简单的机器语言和汇编代码

2.1 从高级语言到机器语言

在上一集,我们知道编译器把高级语言翻译成机器语言。让我们更详细地看看这个过程:

┌─────────────────────────────────────────────────────┐
│  第1层:高级语言 (C、Java、Python)                  │
│  int a = 1 + 2;                                    │
└─────────────────────┬─────────────────────────────┘
                      │ 编译器
                      ▼
┌─────────────────────────────────────────────────────┐
│  第2层:汇编语言 (Assembly)                         │
│  mov $1, %eax                                       │
│  add $2, %eax                                       │
│  mov %eax, a                                        │
└─────────────────────┬─────────────────────────────┘
                      │ 汇编器
                      ▼
┌─────────────────────────────────────────────────────┐
│  第3层:机器语言 (Machine Code)                      │
│  01011001 00000001 00000000 00000000             │
│  00000101 00000010 00000000 00000000             │
│  10001001 00000000 01000000 00000000             │
└─────────────────────────────────────────────────────┘

注意:实际上,现代编译器通常直接从高级语言生成机器码,汇编语言只是一个中间表示,方便人类阅读。

2.2 CPU 是如何工作的?

CPU(中央处理器)是计算机的"大脑",它的工作可以用一个简单的循环来描述:取指-译码-执行(Fetch-Decode-Execute)。

让我们用工厂流水线来类比:

┌─────────┐    ┌─────────┐    ┌─────────┐
│  取指   │ →  │  译码   │ →  │  执行   │
└─────────┘    └─────────┘    └─────────┘
     ↑              ↓              ↓
   从内存        理解指令        执行指令
  读取指令        做什么        完成工作

详细步骤:

  1. 取指(Fetch):CPU 从内存中取出下一条要执行的指令
  2. 译码(Decode):CPU 弄明白这条指令要做什么
  3. 执行(Execute):CPU 执行这条指令
  4. 更新:更新程序计数器,准备取下一条指令
  5. 重复:回到步骤1,继续执行下一条

这个循环每秒可以执行数百万次甚至数十亿次!

2.3 内存、寄存器与指令

让我们认识计算机中的几个关键组件:

内存(Memory)

  • 特点:容量大,速度相对较慢
  • 作用:存储程序和数据
  • 类比:像一个大仓库,可以放很多东西,但找东西需要时间

寄存器(Register)

  • 特点:容量极小,速度极快
  • 作用:CPU 执行计算时临时存放数据
  • 类比:像 CPU 手上的小记事本,随手就能拿到

指令(Instruction)

  • 特点:告诉 CPU 做什么的命令
  • 组成:操作码 + 操作数
  • 例子:加法、加载数据、存储数据

让我们用图来展示它们的关系:

┌─────────────────────────────────────────────────┐
│                    内存                         │
│  ┌──────┐  ┌──────┐  ┌──────┐  ┌──────┐   │
│  │数据A │  │数据B │  │指令1 │  │指令2 │  ...│
│  └──────┘  └──────┘  └──────┘  └──────┘   │
└────────────────────┬────────────────────────┘
                     │
         ┌───────────┴───────────┐
         ▼                       ▼
┌──────────────────┐    ┌──────────────────┐
│   取指/存数      │    │      CPU         │
│  (加载/存储)   │    │  ┌────────────┐ │
└──────────────────┘    │  │  寄存器    │ │
         ▲              │  │ %rax %rbx  │ │
         │              │  │ %rcx %rdx  │ │
         └──────────────┤  └────────────┘ │
                        │  ┌────────────┐ │
                        │  │  ALU(运算)  │ │
                        │  └────────────┘ │
                        └──────────────────┘

2.4 简单的机器语言示例

让我们看一个非常简单的例子:计算 1 + 2,然后把结果存起来。

高级语言版本

int result = 1 + 2;

汇编语言版本(x86-64)

mov $1, %rax      ; 把数字 1 放到寄存器 rax 中
add $2, %rax      ; 把数字 2 加到 rax 中(现在 rax = 3)
mov %rax, result  ; 把 rax 的值存到 result 变量中

机器语言版本(简化示意)

10111000 00000001 00000000 00000000 00000000   ; mov $1, %rax
00000101 00000010 00000000 00000000 00000000   ; add $2, %rax
10001001 00000101 00000000 01000000 00000000   ; mov %rax, result

虽然机器语言都是 0 和 1,但 CPU 能完美理解它们!

2.5 一个完整的执行流程

让我们跟踪 1 + 2 这个简单计算的完整执行过程:

初始状态:
  内存中有指令和数据
  寄存器全是 0
  程序计数器(PC)指向第一条指令

步骤1:取指
  CPU 从 PC 指向的位置取出指令:"mov $1, %rax"
  PC 自动指向下一条指令

步骤2:译码
  CPU 理解:这是一条"移动"指令
  源操作数是 1
  目标是寄存器 rax

步骤3:执行
  CPU 把数字 1 放到 rax 寄存器中
  现在 rax = 1

步骤4:取指(第二条指令)
  CPU 取出:"add $2, %rax"
  PC 继续前进

步骤5:译码
  这是一条"加法"指令
  要把 2 加到 rax 上

步骤6:执行
  rax = rax + 2 = 1 + 2 = 3
  现在 rax = 3

步骤7:取指(第三条指令)
  取出:"mov %rax, result"

步骤8:译码
  把 rax 的值存到内存的 result 位置

步骤9:执行
  把 3 从 rax 存到内存中的 result 变量

完成!result = 3

2.6 常见的指令类型

虽然不同的 CPU 架构有不同的指令集,但基本的指令类型都差不多:

指令类型 作用 例子
数据传输 在寄存器和内存间移动数据 mov, load, store
算术运算 加减乘除等运算 add, sub, mul, div
逻辑运算 与、或、非、异或等 and, or, not, xor
比较 比较两个值 cmp
跳转 改变执行顺序 jmp, je, jne
函数调用 调用函数 call, ret

2.7 自测一下

问题 1

CPU 执行指令的基本循环是?
A) 读-写-存
B) 取指-译码-执行
C) 输入-处理-输出
D) 编译-汇编-链接

问题 2

关于寄存器和内存,以下说法正确的是?
A) 寄存器容量大,速度快
B) 内存容量小,速度快
C) 寄存器容量小,速度快
D) 内存和寄存器一样快

问题 3

什么是机器语言?

问题 4

请描述"取指-译码-执行"循环的三个步骤。


答案

  1. B
  2. C
  3. 机器语言是由 0 和 1 组成的、CPU 可以直接执行的语言
  4. 取指:从内存读取指令;译码:理解指令要做什么;执行:完成指令的工作

2.8 下集预告

下一集,我们将探索:编程语言家族树

我们会了解:

  • 机器语言、汇编语言、高级语言的区别
  • 编译型语言 vs 解释型语言
  • C、Java、Python 各有什么特点
  • 语言的抽象层次是如何发展的

准备好了吗?我们下集见!


参考资料

  • 《计算机组成与设计:硬件/软件接口》
  • 《深入理解计算机系统》
  • x86 汇编语言教程
« 上一篇 什么是编译器?——从源码到可执行文件的魔法 下一篇 » 编程语言家族树