编程语言研究书库

计算机组成原理(基于 x86-64 架构) — 全书拆解

计算机组成原理(基于 x86-64 架构) — 全书拆解

30 秒导读: 这是一本写给程序员的组成原理教材,回答一个问题: 你在高级语言里写的一行代码,在机器层到底发生了什么? 它的讲法是一条完整的因果链:开关 → 位 → 数 → 算术 → 逻辑 → 门 → 电路 → 存储器 → CPU → 指令 → 汇编 → 函数 → 数据结构 → 浮点 → I/O → 中断。 全程不用假想的教学处理器,只用真实的 x86-64 指令和 GNU 工具链——每章都能在自己 电脑上复现。

本组文档是我们重写的完整拆解,二十一章。读完不必看原书。

1. 先交代清楚:这是谁在什么时候写的

作者罗伯特·G.普兰茨(Robert G. Plantz)——曾为双子星座号飞船和阿波罗登月舱设计电子设备,伯克利电气工程博士,做过 8 年软件工程师,后任教于索诺玛州立大学 20 余年1
中文版门佳、朱西方 译,人民邮电出版社 2025 年
内容组织作者自述分三部分:数学和逻辑 → 硬件 → 软件;所有编程在 Ubuntu 的 GNU 环境(gcc 9.3 / as 2.34)下完成
写作立场前言里明说:目标不是培养汇编专家,而是让你成为更优秀的程序员——书里的汇编是理解工具,不是生产工具1

两件事读之前必须知道:

第一,全部示例用 -O0 编译。 作者在前言里交代,为的是「学概念,不是生成最高效的代码」。 这意味着你看到的汇编是"最忠实于 C 语义"的版本,而不是真实世界的优化输出—— 两者差多少,各章的「边界与局限」里都有提示。

第二,这是一个「教学子集」。 x86-64 约 1500 条指令,作者只选了讲清概念必需的一小撮, 理由同样写在前言:别的书靠"理想化指令集"回避指令太多的问题,这本书宁可用真指令的真子集1。

2. 全书一条主线(读完这一节,你就懂了这本书)

这本书是一条从头贯到尾的推理链:每一层都是被上一层逼出来的。 只读这一节,你也能把这本书讲给别人听。

① 计算机只有三种东西

所有计算机硬件 = CPU + 内存 + 输入/输出,三根总线相连(01)。 程序 = 存在内存里的一串指令;CPU 的工作就是取一条、执行一条,再问下一条在哪。

② 但"存在内存里"之前,得先回答:用什么记?

内存里只有双态开关。用位记开关状态,8 位一字节,十六进制是人看的速记(02)。 开关记数字没有算术,所以先立规矩:固定位数,溢出和进位天生存在(03); 开关要表达"条件",所以引入布尔代数——真和假能算,还能化简(04)。

③ 代数要有身体:门与电路

布尔函数怎么变成物理?晶体管造逻辑门(05),门组成电路: 没有记忆的(加法器、译码器)叫组合逻辑(06), 把输出喂回输入就能"记住"的(锁存器、触发器)叫时序逻辑——存储就是这么来的(07)。 锁存器阵列 → 寄存器、缓存、内存,越近 CPU 越快越贵,组成金字塔(08)。

④ 现在,看一颗真正的 CPU

子系统齐备:指令指针、指令寄存器、控制单元、ALU、寄存器堆(09)。 CPU 一生只做一件事:取指 → 译码 → 执行 → 再取指。

⑤ 从 C 到机器:一条流水线四个岗

gcc 四步:预处理 → 编译(生成汇编)→ 汇编(机器码)→ 链接(填地址)(10)。 你写的高级语言,到这一步已经全部变成字节。从此每章都在拆这些字节。

⑥ 函数:程序的真正组织单位

参数经寄存器传(前 6 个),局部变量住栈帧,返回地址压在栈上,规矩叫 System V ABI(11)。 指令本身也只是一串字节:操作码 + ModR/M + SIB + 偏移量,CPU 靠字段分解找回操作数(12)。 循环和分支到机器层只剩「改 rip」:跳回去是循环,跳过去是分支(13)。 函数间的数据流动只有四条路:全局、按值、按指针/引用、返回值; 第 7 个起参数走栈,正偏移读参数、负偏移用变量——17 条规则收拢全部协议(14)。 递归没有新机制,只是一串栈帧自己套自己(15 前半)。

⑦ 算得动,才算得好

位掩码改写单个位,移位乘除 2 的幂,imul/div 的隐式寄存器约定是 x86 的传统艺能(16)。 数组的索引、结构体的字段名,机器层全是「基址 + 偏移量」(17); 对象 = 记录 + 一个藏起来的 this 参数 + 改名后的函数(18)。

⑧ 数与外界的麻烦

小数有两条路:定点(换范围)与 IEEE 754 浮点(换精度),后者不是实数, 舍入/吸收/抵消三种误差各配了 gdb 实录(19)。 I/O 设备比内存慢几个数量级且脾气各异,于是有了轮询、中断、DMA, 以及端口映射与内存映射两种够到设备的方式(20)。

⑨ 收尾:操作系统凭什么管得住一切

靠特权级(0 级内核 / 3 级程序,内存访问按级别把关)和中断/异常机制 (保存现场 → 查门描述符表 → 升级 → 跳转)。 你平时调的每个 C 库 I/O 函数,底层都是一次从 3 级到 0 级的系统调用—— 书的最后一个程序,就是不用任何库、直接 syscall 的 Hello, World!(21)。

⑩ 一句话压缩

高级语言的每一行,都是开关序列的一层糖衣;这本书做的, 就是把每一层糖衣剥给你看,并让你看到糖衣之所以这么包,是被下一层的硬约束逼的。

3. 二十一章地图

这张表不用记任何术语——每章名字后面写的是「读完你能回答什么」。

章读完你就能回答
01 预备知识一台计算机最少由哪几块组成?程序从磁盘到执行走了哪几步?
02 数据存储格式开关怎么记数字和字母?「123abc」在内存里到底长什么样?
03 计算机算术为什么整数会"溢出"?补码是什么,为什么非用它不可?
04 布尔代数"真和假"怎么变成能化简的代数?卡诺图凭什么能化简电路?
05 逻辑门晶体管怎么造出与/或/非门?为什么说 NAND 是"万能门"?
06 组合逻辑电路加法器怎么一位一位进位?译码器和复用器是干什么的?
07 时序逻辑电路电路怎么"记住"一个位?寄存器和状态机是怎么造出来的?
08 存储器为什么既要有内存又要有缓存?寄存器到云,速度和价格怎么排?
09 中央处理单元CPU 内部有哪几个部门?"取指-执行"循环具体怎么转?
10 汇编语言编程一个 .c 文件怎么变成可执行文件?每一步的产物长什么样?
11 深入 main 函数参数怎么进寄存器?局部变量住在哪?return 怎么找到回家的路?
12 剖析指令一行汇编怎么编码成字节?CPU 怎么从字节里找回操作数在哪?
13 控制流结构while/if/switch 在机器层各长什么样?「高于」和「大于」为什么必须分清?
14 剖析函数第 7 个参数起去哪了?「画栈帧图」为什么是调试函数的第一动作?
15 函数的特殊用法递归在机器层有新机制吗?C 读不到溢出标志,汇编怎么读?
16 位、乘除0.1+0.2 的整数版问题是什么?div 之前为什么必须先清 rdx?
17 数据结构a[i] 和 x.field 在机器层是同一种东西吗?结构体的"填充"从哪来?
18 面向对象"对象"在汇编层是什么?this 指针藏在哪?编译器删了你的构造函数会怎样?
19 小数为什么 0.1 存不精确?浮点误差的三种长相各是什么?
20 输入/输出为什么 I/O 比内存难得多?轮询、中断、DMA 各解决什么?
21 中断与异常操作系统凭什么拦得住程序?一个不用 C 库的 Hello World 怎么写?

推荐顺序: 01 → … → 21,这也是原书的推理顺序,不建议跳。 硬件部分(01–09)和软件部分(10–21)可以分开读,但 09(CPU)与 10(编译流水线)之间不要断。

读汇编清单的通用姿势

给一套读任何编译器汇编产出的通用顺序——后面十九章都适用:

  1. 先找序言和结语:push rbp / mov rbp, rsp 在哪、结语 mirror 在哪, 函数边界就清楚了;
  2. 再看常量区:.rodata 里的字符串和数字,是程序的"原料清单";
  3. 然后找调用:call 指令标出了程序的结构骨架——每个 call 就是一次"求人办事";
  4. 最后看控制流跳转:jmp/jcc 画出的箭头,是程序的实际走法。 C 源码里的结构(if/循环)到这一层全部变成这些箭头。

这个顺序反过来也成立:拿到一段陌生汇编,先找 call 和 jmp,大致结构就出来了—— 比从头逐行读快得多。本章的所有清单,都可以用这个姿势再过一遍,当作练习。

4. 这本书覆盖什么、不覆盖什么

覆盖(而且讲得比多数教材清楚)——读完你能回答:

  • 一行 C 从源代码到机器码经过哪四步,每步产物(.i/.s/.o)怎么查看;
  • 函数调用的完整协议:寄存器传参、栈帧、红区、第 7 个参数、17 条规则;
  • 递归、栈金丝雀、名称重整、this 指针——从 gdb 实录里看清,而不是背定义;
  • 整数乘除的隐式寄存器约定(div 前清 rdx)、有符号除法的 C/Python 规则差异;
  • IEEE 754 的格式与三个真实误差实验(舍入、吸收、抵消、不结合性);
  • 中断/异常/系统调用的机制轮廓,以及不依赖 C 运行时的最小程序。

不覆盖(别指望在这本里找):

缺什么说明
多核与现代微架构乱序执行、分支预测、流水线细节、缓存一致性——本书是单核视角
虚拟内存的完整故事只讲了页表/MMU 的概念轮廓,分页机制、TLB、置换算法都没展开
ARM/RISC-V 等其他指令集全书只基于 x86-64
性能工程提到缓存重要,但没有体系化的分析与优化方法
并发与网络多线程、锁、协议栈不在范围
C++ 的深度虚函数、继承、多态、异常(被 -fno-exceptions 关掉了)都没讲

5. 我们的判断

判断(我们的,不是书里的): 这本书今天的价值排序是—— ① 函数协议与栈帧教学(11–14 章,至今少有对手)> ② 用真实工具链教概念的方法论 > ③ 硬件部分(01–09,合格但传统的教法)> ④ 浮点与 I/O 两章(方向对、深度浅)。 如果你的目标是「读懂编译器输出、调得了段错误」,这本比经典的 CS:APP 更薄、更直接; 如果要性能优化与系统编程,它是地基,不是楼房。 如果错,会错在: CS:APP 的实验体系(炸弹实验、Attack Lab)在动手深度上其实更强, 我们只是以「读得懂」为判据加权;换「练得会」为判据,排序可能反转。

判断(我们的,不是书里的): 全书最值得带走的是一种读法: 每遇到一个抽象,问三个问题——「它的机器层形状是什么?」「它被哪个硬约束逼成这样?」 「编译器/工具链能怎么验证它?」(本书三件套:gcc -S 看输出、gdb 看寄存器内存、 as -al 看机器码)。这套三问对任何新语言、新框架都通用。 如果错,会错在: 解释型语言和托管运行时(虚拟机、GC)把「机器层」隔得更远, 三问的第一问有时答不出来——但它依然有效,只是答案从"汇编"变成了"字节码/运行时语义"。

判断(我们的,不是书里的): 注意作者的边界感——几乎每个深坑 (ModR/M 全表、链接器细节、中断处理程序、浮点 fenv)他都只走到「能查、能读」就停, 并且明说不讲。这不是偷懒,是教材最难的部分:范围的纪律。 对照组是试图什么都讲的教材,读者往往什么都没留下。 如果错,会错在: 有些读者需要的恰恰是其中某个深坑的全部细节—— 对那批人,这本书的正确用法是「地图 + 手册指针」,而它给的手册指针 (Levine、Tanenbaum、Goldberg、fenv)恰好够用。

6. 许诺兑现表

许诺在哪应兑现在哪核过了吗
本页 §2 ⑤「从此每章都在拆这些字节」12(指令字节)、16(乘除编码)✓
本页 §2 ⑥「17 条规则收拢全部协议」14 §5 末的协议总表✓
本页 §2 ⑨「最后一个程序」21 §3 主线例子✓

各章内部的「第 N 章讲」均在当章或目标章的原文地图中可回核,不再逐条登记。


拆解依据 2025 年中文版(人民邮电出版社)。原始书籍文件不入库,本组文档是我们自己的重写; 每条引用都可用 node scripts/book-verify.mjs ji-suan-ji-zu-cheng-yuan-li 回核。

Footnotes

  1. 作者背景与写作立场见原书前言(text/02-fm.txt:13,搜「目标读者」)与封底作者介绍(text/27-ch21.txt:368,搜「双子星座号」)。「1500 条指令」「-O0 选项」的口径见前言第 43–46 段(text/02-fm.txt:43,搜「1500 条指令」)与第 133–137 段(text/02-fm.txt:1,搜「前 言 1」)。 ↩ ↩2 ↩3

On this page