指令集体系结构

前三章都在硬件内部打转:数据怎么编码(第 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:

  1. 预处理(.c → .i):纯文本替换——展开宏、插入头文件、处理条件编译。完全看不见 ISA,这一步的输出还是 C 代码。
  2. 编译(.i → .s):ISA 第一次进场。编译器必须知道目标机器有哪些指令、有几个寄存器、支持哪些寻址方式,才能生成合法的汇编。换 ISA 就要换这一步的后端,这是 ISA 的主要”客户”。
  3. 汇编(.s → .o):把助记符逐条查表翻译成二进制。这一步用的就是 ISA 手册里的编码表。 输出的 .o 里,本模块内部的地址已经算好了,但引用其他模块的符号只能先填 0,同时留下一条重定位记录说明”这里以后要填什么”。
  4. 链接(.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,由链接器按重定位记录补齐

链接