指令集体系结构
前三章都在硬件内部打转:数据怎么编码(第 2 章)、运算器怎么算(第 2 章)、存储器怎么组织(第 3 章)。从这一章起,问题换了一边——软件怎么指挥硬件。
指令集体系结构(Instruction Set Architecture,ISA)就是这一边和那一边之间唯一的正式约定。它是一份契约文本,不是一个部件:你在主板上找不到”ISA”这个芯片,正如你在公司里找不到”合同”这台机器。
ISA 的全部价值在于它把两侧解耦:只要契约不变,硬件可以从 8086 换成酷睿而老程序照跑;软件可以从汇编换成 C 而硬件不用改。凡是”换了之后老程序还能跑”的改动,都在 ISA 之下;凡是”换了之后老程序跑不了”的改动,都动了 ISA。 这句话是本节所有判断题的判据。
机制
ISA 规定什么
王道把 ISA 的内容列成一张清单,这张清单要能默写,因为选择题的问法就是”下列哪一项(不)属于 ISA”:
| ISA 规定 | 具体是什么 | 本书在哪展开 |
|---|---|---|
| 指令格式 | 操作码多少位、地址码几个、指令字长 | 4.1.2~4.1.4 |
| 指令类型 | 有哪些操作、每条指令干什么 | 4.1.5 |
| 寻址方式 | 支持哪几种,各自怎么算有效地址 | 4.2.2 |
| 数据类型与格式 | 支持定点/浮点、位宽、大小端 | 2.3.4、2.1.2 |
| 寄存器组织 | 有几个通用寄存器、编号、哪些有专用含义、PSW 各位含义 | 4.1.2、5.1 CPU 的寄存器(本地资料) |
| 存储模型 | 编址单位、地址空间大小、是否要求对齐 | 3.3.2 |
| I/O 方式 | 是独立编址还是统一编址、有没有专用 I/O 指令 | 7.2.4 I/O 端口及其编址 |
| 异常与中断机制 | 有哪些异常、中断如何响应、特权级怎么切换 | 5.6 异常和中断(本地资料) |
| 机器状态与特权级 | 用户态/内核态的划分、哪些是特权指令 | OS 1.3.1 处理器运行模式 |
这张表的每一行都能单独出一道题,而这一章接下来就是把前四行逐节展开。
ISA 不规定什么:三层要分清
真正的失分点不在”ISA 规定什么”,而在它下面还有一层、旁边还有一层:
graph TD A["应用程序<br/>(C / Java / Python)"] --> B B["ABI 应用程序二进制接口<br/>调用约定 · 栈帧布局 · 目标文件格式 · 系统调用编号"] B --> C C["ISA 指令集体系结构<br/>指令格式 · 寻址方式 · 寄存器 · 数据类型 · 中断机制"] C --> D D["微体系结构 Microarchitecture<br/>流水线深度 · Cache 大小 · 分支预测 · 译码方式 · 乱序执行"] D --> E["逻辑电路与器件"] classDef isa fill:#ffe6cc,stroke:#d79b00,stroke-width:3px classDef other fill:#e1f5fe,stroke:#0288d1 class C isa class A,B,D,E other
微体系结构(ISA 的下面):同一份 ISA 可以有无数种硬件实现。流水线多少级、Cache 多大、几路组相联、用硬布线还是微程序控制、要不要乱序执行——全部不属于 ISA。 判据仍然是那一句:这些改了,老程序照样跑(只是快慢不同)。
ABI(ISA 的上面):参数用哪几个寄存器传、多了怎么压栈、返回值放在哪、栈指针要不要 16 字节对齐、哪些寄存器由调用者保存、系统调用号是多少——这些 ISA 一个字都不管。它们由操作系统和编译器共同约定,叫应用程序二进制接口。所以同一颗 x86-64 处理器上,Windows 和 Linux 的调用约定不同,编译好的 .o 不能互换,但两边的机器指令编码是完全一样的。这一层会在 4.3.4 过程调用兑现。
从源程序到机器指令:谁在哪一层看见 ISA
graph LR S["hello.c<br/>源程序"] -->|"预处理 cpp<br/>宏展开·插头文件"| I["hello.i"] I -->|"编译 cc1<br/>★第一次接触 ISA"| A["hello.s<br/>汇编代码"] A -->|"汇编 as<br/>★查编码表变二进制"| O["hello.o<br/>可重定位目标文件"] O -->|"链接 ld<br/>符号解析 + 重定位"| X["hello<br/>可执行文件"] X -->|"装入 loader<br/>★这一步归 OS"| M["内存映像<br/>进程"] classDef isaStep fill:#ffe6cc,stroke:#d79b00,stroke-width:2px classDef osStep fill:#f8cecc,stroke:#b85450 class A,O isaStep class M osStep
四步各自在干什么,以及各自看不看得见 ISA:
- 预处理(
.c→.i):纯文本替换——展开宏、插入头文件、处理条件编译。完全看不见 ISA,这一步的输出还是 C 代码。 - 编译(
.i→.s):ISA 第一次进场。编译器必须知道目标机器有哪些指令、有几个寄存器、支持哪些寻址方式,才能生成合法的汇编。换 ISA 就要换这一步的后端,这是 ISA 的主要”客户”。 - 汇编(
.s→.o):把助记符逐条查表翻译成二进制。这一步用的就是 ISA 手册里的编码表。 输出的.o里,本模块内部的地址已经算好了,但引用其他模块的符号只能先填 0,同时留下一条重定位记录说明”这里以后要填什么”。 - 链接(
.o+ 库 → 可执行文件):符号解析(把”用到的printf”和”定义printf的那个.o”接上)+ 重定位(按最终布局把那些填 0 的地方补上真地址)。链接器基本不看 ISA 的指令语义,它只认符号表和重定位类型。
疑问点:ISA 的边界到底在哪,以及"看着硬件手册把汇编翻译成二进制"是哪一步
那位博主做的是第 3 步(汇编)的手工版,他查的那份”硬件手册”就是 ISA 手册的编码表章节。 这件事之所以能手工做,恰恰因为 ISA 是一份完全公开、逐位定义的契约——它必须公开,否则编译器没法写。
顺着这条链把”边界”落实成一句可操作的判据:改了之后老程序还能不能跑。
改动 老程序还能跑吗 所以它属于 流水线从 5 级加到 14 级 ✅ 能(只是变快) 微体系结构 Cache 从 2 路改成 8 路组相联 ✅ 能 微体系结构 控制器从微程序换成硬布线 ✅ 能 微体系结构 新增一条指令 ✅ 老的能,新的在老机器上不能 ISA(向后兼容地扩展) 删掉一条指令 / 改一条指令的编码 ❌ 不能 ISA 通用寄存器从 8 个改成 16 个 ❌ 不能(寄存器编号位数变了) ISA 参数传递从”压栈”改成”用寄存器” ❌ 不能(但机器码合法) ABI,不是 ISA 目标文件从 ELF 换成 PE ❌ 不能(机器码可能一模一样) ABI,不是 ISA 最后两行是这张表的价值所在:它们同样”跑不了”,但原因不在指令本身——机器认得每一条指令,只是双方对”约定俗成的用法”理解不同。这就是为什么必须在 ISA 之上再分出一层 ABI。
边界
ISA 是”机器语言级”的接口,不是”汇编语言级”的。 同一份 ISA 可以有不同的汇编语法:x86 的 Intel 语法写 mov eax, ebx,AT&T 语法写 movl %ebx, %eax,操作数顺序都是反的,但汇编出来的机器码一个字节不差。汇编语法属于汇编器的约定,不属于 ISA。 这一点在 4.3.1会再遇到。
“指令系统”和”指令集体系结构”在王道里当同义词用,但严格讲后者更宽。 指令系统强调”有哪些指令”,ISA 还包括寄存器组织、存储模型、中断机制这些不是指令但同样必须约定的东西。做题时按 ISA 的宽定义答,不会错。
ISA 不是”越靠近硬件越底层”的那种层次,它是横切的一刀。 层次结构里 ISA 位于传统机器语言层(见 1.2.4 计算机系统的层次结构),它上面是操作系统层和汇编语言层,下面是微程序(或直接是硬件)。注意”微程序”在这里是实现手段,不是层次义务——硬布线控制器根本没有微程序层,但同样实现这份 ISA。
同一 ISA 的不同实现之间,性能可以差几十倍,但结果必须完全一致。 这句是 ISA 存在的全部意义,也是判断题的常见正确项。
特权指令属于 ISA,但”哪些操作需要特权”是 ISA 和 OS 共同划定的。 ISA 提供机制(用户态/内核态、特权指令、陷入指令),OS 决定策略。这条边界在 OS 1.3.1里从另一侧再讲一遍。
对照速查
| 概念 | 一句话 | 谁定的 | 改了老程序还能跑吗 |
|---|---|---|---|
| ISA | 软硬件之间的机器语言级契约 | 处理器厂商 | ❌(除非只是新增) |
| 微体系结构 | 这份契约的一种硬件实现 | 处理器厂商 | ✅ |
| ABI | 同一 ISA 上二进制模块的互操作约定 | OS + 编译器 | ❌(但机器码合法) |
| 汇编语法 | 助记符的书写规则 | 汇编器 | ✅(机器码不变) |
| 项目 | 属于 ISA? |
|---|---|
| 指令格式、操作码位数 | ✅ |
| 通用寄存器的个数与编号 | ✅ |
| 支持哪些寻址方式 | ✅ |
| 中断响应的硬件流程与向量表机制 | ✅ |
| 是否统一编址、有无专用 I/O 指令 | ✅ |
| Cache 容量、块大小、映射方式 | ❌ 微体系结构 |
| 流水线级数、是否乱序 | ❌ 微体系结构 |
| 控制器是硬布线还是微程序 | ❌ 微体系结构 |
| 主频、制程 | ❌ 微体系结构/工艺 |
| 参数传递用哪几个寄存器 | ❌ ABI |
| 栈帧布局、返回值位置 | ❌ ABI |
| 系统调用号 | ❌ ABI/OS |
汇编写 mov a,b 还是 mov b,a | ❌ 汇编器语法 |
| 编译链接四步 | 输入 → 输出 | 干了什么 | 见 ISA? |
|---|---|---|---|
| 预处理 | .c → .i | 宏展开、插头文件、条件编译 | ❌ |
| 编译 | .i → .s | 生成目标机器的汇编代码 | ✅ 主要客户 |
| 汇编 | .s → .o | 查编码表逐条译成二进制,留重定位记录 | ✅ 编码表 |
| 链接 | .o → 可执行文件 | 符号解析 + 重定位 | 基本不用 |
| (装入) | 可执行文件 → 进程 | 建立内存映像,归 OS | ❌ |
考点
- ISA 是软硬件的分界面,是机器语言级的契约;判据是”改了老程序还能不能跑”
- 微体系结构在 ISA 之下:Cache、流水线、控制器实现方式、主频都不属于 ISA
- ABI 在 ISA 之上:调用约定、栈帧布局、目标文件格式、系统调用号都不属于 ISA
- ISA 的内容清单:指令格式、指令类型、寻址方式、数据类型、寄存器组织、存储模型、I/O 方式、异常中断机制、特权级
- 汇编语法不属于 ISA(Intel 与 AT&T 语法机器码相同)
- 编译链接四步:预处理 → 编译(第一次用 ISA)→ 汇编(查编码表)→ 链接(符号解析 + 重定位)
.o里跨模块的地址先填 0,由链接器按重定位记录补齐
链接
- 🏠 返回总览:计算机组成原理第 4 章:指令系统总览
- ➡️ 下一节:4.1.2 指令的基本格式
- 🔗 1.2.4 计算机系统的层次结构(ISA 在层次图上的位置)
- 🔗 4.3.4 过程调用的机器级表示(ABI 在这里兑现)
- 🔗 4.4.3 CISC 和 RISC 的比较(两种 ISA 设计哲学)
- 🔗 OS 1.3.1 处理器运行模式(特权级的另一侧)
- 📖 名词库:第 4 章名词库