计算机系统的工作原理
这一节是全书的缩影。 它把一件事从头讲到尾:你写的一行 C 代码,是怎么变成 CPU 里一串电平变化的。
后面六章,每一章都是这条链条上某一段的放大:
所以这一节写得薄一点没关系——它的作用是把地图画出来,细节都有各自的落点。
机制
存储程序:机器为什么能”自动”运行
“存储程序”四个字是冯·诺依曼结构的灵魂(1.2.2 五特点的第 5 条):
将指令以二进制代码的形式事先输入计算机主存储器,然后按其在存储器中的首地址执行程序的第一条指令,以后就按该程序的规定顺序执行其他指令,直至程序执行结束。
拆开看,它靠三件事成立:
| 要素 | 作用 | 缺了会怎样 |
|---|---|---|
| 指令预先存在主存里 | CPU 能一条条取出来 | 得像 ENIAC 那样重新接线 |
| 指令按地址顺序存放 | PC 只要 +1 就能找到下一条 | 每条都要人指定位置 |
| PC 有自动加 1 的能力 | 不需要外力推动 | 机器不能”自动”运行 |
第三条是”自动”二字的全部来源。 1.2.2 说过 PC 是一个计数器——正是这个”计数”让程序自己往前走。
而”顺序”是默认而非唯一:转移指令、过程调用、中断都会改写 PC。存储程序不排斥这些,它只规定”没有别的事发生时,下一条就是 PC+1 那条”。
从源程序到可执行文件:五步
flowchart LR S["hello.c<br/>源程序"] -->|"① 预处理<br/>宏展开·插头文件"| I["hello.i"] I -->|"② 编译<br/>★ISA 第一次进场"| A["hello.s<br/>汇编代码"] A -->|"③ 汇编<br/>按 ISA 编码表逐条翻译"| O["hello.o<br/>可重定位目标文件"] O -->|"④ 链接<br/>符号解析 + 重定位"| X["hello<br/>可执行文件"] X -->|"⑤ 装入<br/>★这一步归 OS"| M["内存映像<br/>进程"] classDef src fill:#e3f2fd,stroke:#1565c0 classDef isa fill:#ffcdd2,stroke:#b71c1c,stroke-width:2px classDef os fill:#c8e6c9,stroke:#1b5e20,stroke-width:2px class S,I,A src class O,X isa class M os
| 步 | 输入 → 输出 | 干什么 | 与 ISA 的关系 |
|---|---|---|---|
| ① 预处理 | .c → .i | 宏展开、插入头文件、条件编译 | 完全无关,输出还是 C |
| ② 编译 | .i → .s | 生成汇编代码 | ISA 第一次进场 |
| ③ 汇编 | .s → .o | 按编码表翻译成机器指令 | 逐条对应 ISA |
| ④ 链接 | .o + 库 → 可执行文件 | 符号解析 + 重定位 | 基本不看指令语义 |
| ⑤ 装入 | 可执行文件 → 内存映像 | 建立地址空间、设置入口 | 归操作系统 |
两个必记的分界:
第一,②③ 之间隔着”汇编语言”这一层,而汇编语法不属于 ISA(1.2.3 的 callout)。
第二,④⑤ 之间隔着 CO 与 OS 的边界。 链接是工具链的事,装入是操作系统的事。
疑问点:ISA 的边界在哪;能不能直接讲讲程序编译链接的过程
这条提问的两半分属两节,而且第二半就是本节的内容。
提问里提到”看一个博主把汇编程序翻译成二进制,就是直接看那种硬件手册”——这个观察抓到了要害。
那位博主做的正是上面的第 ③ 步(汇编),手工版。 他查的那份”硬件手册”就是 ISA 手册的编码表章节。
这件事之所以能手工做,恰恰说明了 ISA 是什么:它是一份完全公开、逐位定义的契约。它必须公开,否则编译器没法写。 反过来,第 ② 步(编译)就没法手工做——那需要的是优化决策,不是查表。
所以”ISA 的边界”可以用这条链条来划:
步 手工可做吗 说明 ISA 是什么 ① 预处理 可以 与 ISA 无关,纯文本 ② 编译 很难 要知道有哪些指令、几个寄存器——ISA 在这里被”使用” ③ 汇编 可以,查表即可 ISA 是一张编码表——这是它最硬的部分 ④ 链接 可以 不看指令语义,只认符号表 一句话:ISA 的边界就是”编译器必须知道、链接器不必知道”的那些东西。
完整展开在 4.1.1,那里还讲了 ISA 与 ABI 的分工、以及为什么同一 ISA 的两个操作系统之间二进制不通用。本节只负责把五步的位置摆对。
顺带说明本笔记的一处编排:王道把”从源程序到可执行文件”放在 1.2.5,但它的机制细节属于第 4 章的 ISA。所以本节写地图、4.1.1 写机制——这与中断的处理方式相同(教材放 7.3.2,原理归 5.5.3)。
指令执行过程:一条指令的一生
取指令这一步在整本书里会以完全相同的形式出现三次(本节、5.2.1、5.2.2),寄存器传送级的写法是:
三阶段:
| 阶段 | 做什么 | 用到的部件 |
|---|---|---|
| 取指令 | 按 PC 取出指令送入 IR,PC+1 | PC、MAR、主存、MDR、IR |
| 分析指令(译码) | CU 解释 IR 中的操作码,确定要做什么 | IR、CU |
| 执行指令 | 取操作数、运算、写回结果 | ALU、ACC、MQ、X、主存 |
三个边界:
① PC+1 里的”1”不一定是 1。 它是一条指令的长度——按字节编址、指令 4 字节的机器上,实际是 PC+4。教材写 (PC)+1 是抽象写法。 这一点在 4.1.2 落实。
② 取指阶段对所有指令都相同,执行阶段才因指令而异。 这正是流水线能成立的前提——如果每条指令的取指都不一样,就没法把它们排成整齐的流水段。
③ “分析指令”看的是操作码,不是整条指令。 操作码决定做什么,地址码决定对谁做。
边界
| 说法 | 判断 | 理由 |
|---|---|---|
| ”存储程序意味着程序永远顺序执行” | ❌ | 转移、调用、中断都改写 PC |
| ”PC+1 中的 1 表示 1 个字节” | ❌ | 表示一条指令的长度 |
| ”取指令阶段各条指令不同” | ❌ | 完全相同,这是流水线的前提 |
| ”编译之后就可以执行了” | ❌ | 还要汇编、链接、装入 |
| ”链接由操作系统完成” | ❌ | 链接是工具链,装入才归 OS |
| ”预处理阶段会用到 ISA” | ❌ | 纯文本替换 |
| ”IR 中存放的是指令的地址” | ❌ | 存放指令本身 |
| ”分析指令时要看地址码” | ⚠️ | 译码看操作码;地址码在取操作数时才用 |
| ”一条指令的执行必须访存” | ❌ | 寄存器型指令(如 ADD R1,R2)执行阶段不访存 |
对照速查
取指的四步(背下来,第 5 章原样再用):
五步链条:
| 步 | 归谁 |
|---|---|
| 预处理 / 编译 / 汇编 / 链接 | 工具链 |
| 装入 | 操作系统 |
| 三阶段 | 关键部件 |
|---|---|
| 取指 | PC → MAR → 主存 → MDR → IR |
| 译码 | IR 的操作码 → CU |
| 执行 | ALU / 访存 / 写回 |
考点
- 取指的寄存器传送序列——第 5 章大题的第一步。
- 五步链条中每一步的归属,尤其链接与装入的分界。
- 存储程序的含义及”自动”的来源(PC 自动加 1)。
- 取指阶段对所有指令相同。
链接
- 🏠 返回总览:计算机组成原理第 1 章:计算机系统概述总览
- ⬅️ 上一节:1.2.4 计算机系统的层次结构
- ➡️ 下一节:1.3.1 计算机的主要性能指标
- 🔑 编译链接的完整机制:4.1.1 指令系统的基本概念
- 🔑 指令周期的完整数据流:5.2.1 指令周期 5.2.2 指令周期的数据流
- 🔗 PC+1 中的”1”:4.1.2 指令格式
- 🔗 为什么取指必须整齐:5.6.1 指令流水线的基本概念
- 📖 名词库:第 1 章名词库