计算机系统的工作原理

这一节是全书的缩影。 它把一件事从头讲到尾:你写的一行 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+1PC、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)。
  • 取指阶段对所有指令相同。

链接