1 为什么软件最终要落到「指令」
H6 把计算机看成数据通路与控制的骨架:寄存器、ALU、存储器与总线在时钟节拍下搬运和变换数据。本课追问更细的一层:程序员与编译器到底向机器下达什么命令?这些命令的集合、编码与语义,合称指令集体系结构(ISA)。ISA 是软件与硬件之间的契约:同一份机器码,只要实现遵循同一 ISA,就应表现出约定的结果,尽管流水线深度、缓存大小可以不同。
没有稳定的 ISA,操作系统、编译器与应用无法独立演进。云上的虚拟机与容器,表面上是镜像与编排,底层仍假设某类 ISA(常见为 x86-64 或 AArch64)。本课不背诵某厂商全部操作码,而练 5 项能力:区分 ISA 与微架构;解释指令的操作与操作数;对比寄存器、立即数、存储器寻址;读懂简单 R 型/I 型编码字段;说明为何对齐、字节序与特权级会出现在契约里。
与前后课分工:H5/H6 提供时序与整机直觉;H8 进入汇编与机器码书写;本课夹在中间,专门把「指令」从黑盒变成可讨论的对象。禁止在本课展开乱序执行与分支预测细节——那些是 H19 之后的微架构专题。
把「兼容」说清楚:新 CPU 可以实现旧 ISA 的超集,软件仍可跑;反过来,软件用了新指令,旧 CPU 会陷入未定义操作码异常。云镜像选对架构标签,本质是选对 ISA 族。
课堂开场可以做一次「契约听力」:一人只讲语义,另一人只讲实现(流水线级数、缓存容量),听众判断哪些话属于 ISA、哪些属于微架构。能稳定分开,才说明边界清楚。再追问:若只改缓存大小而不改指令语义,已编译的二进制要不要重编?答案应是「不必」,因为那是微架构变化。
云工程场景再落一层:编排系统调度到不同实例类型时,镜像架构字段若与节点 ISA 不一致,容器会在启动阶段失败,而不是在业务逻辑里「算错」。把这类失败归因到 ISA 契约,而不是「云不稳定」,是本课要养成的排障习惯。

若用一句话向非专业同事解释:ISA 像电源插头标准,微架构像插座内部怎么接线;插头不变,电器才能通用。云多架构发布,就是为不同插头各做一批电器。把这句比喻写进对内培训可以,写进对外 SLA 时要改回精确术语:目标指令集族与版本。
2 指令长什么样:操作、操作数、结果
一条指令至少回答:做什么运算或控制(操作码);数据从哪里来(源操作数);结果写到哪里(目的);是否还隐含影响标志或程序计数器。算术例:ADD rd, rs1, rs2 表示把两个寄存器相加写入目的寄存器。访存例:LOAD rd, offset(rs) 表示以寄存器加偏移形成地址,从存储器读入。控制例:BEQ rs1, rs2, label 表示相等则分支。
操作数来源通常有 3 类:寄存器、立即数(指令中嵌的常量)、存储器。不同 ISA 对「运算是否允许存储器操作数」态度不同。教学上先掌握「运算在寄存器、访存用显式 load/store」的 RISC 风格,再对比 CISC 中复杂寻址的存在理由。立即数有位宽限制,过大常量常要多条指令拼装——这是编码与语义交界的现实。
副作用也要进契约:这条指令是否改条件码?是否可能缺页?是否特权?云上排障时,「这条指令在用户态非法」往往比「ALU 算错」更常见。
教室练习:用自然语言写 4 条最小指令语义(加、加载、存储、相等分支),不写二进制。同伴互查是否漏了目的或地址计算方式。
把语义写成「前置条件 → 动作 → 后置条件」更稳。例如 LOAD:前置是有效虚拟地址且权限允许;动作是按端序读出宽度约定的字节;后置是目的寄存器更新,且可能触发缺页或对齐异常。写清这 3 段,后面读手册才不会只盯着助记符。
再练一次「缺字段」互查:故意隐去 BEQ 的比较对象或目标标签,让同伴指出契约不完整。养成习惯后,读厂商伪代码时会主动找:是否更新标志、是否改 PC、是否定义对溢出的行为。H8 写汇编时,这些检查会直接变成少踩坑。
再区分「结果写入」与「结果可见」。有的算术指令把结果写入通用寄存器;有的还会更新条件码。若条件码属于可见状态,后续条件分支可不另发比较指令。若 ISA 选择「比较结果进通用寄存器」,则条件码可能不存在或仅特权可见。两种设计都能工作,软件必须按选定 ISA 书写,不能混用假设。把这一段写进笔记:我的目标 ISA 条件码在不在用户态契约里?
结果宽度与溢出行为也属语义:饱和、环绕、还是置位标志。教学示例统一用环绕加法,并注明「不代表目标 ISA」。小组各选 1 条访存指令,写出缺页时软件看到的异常类名(可用「缺页」占位),避免只写「失败」。
3 寄存器、PC 与程序员可见状态
程序员可见状态是 ISA 的一部分:通用寄存器、程序计数器(PC)、状态寄存器/标志、以及特权 ISA 扩展出的控制寄存器。微架构内部的流水线缓冲、重命名寄存器可以更多,但那不在基础 ISA 契约里——对软件不可见。
PC 指出下一条指令地址。顺序执行时 PC 递增指令长度;分支与跳转写 PC。理解 PC 是读汇编与调试的前提:断点、单步都在操纵「下一条去哪」。
调用约定(哪些寄存器调用者保存)通常属于 ABI,比纯 ISA 更高一层,但依赖 ISA 的寄存器集合。本课点到:写函数接口时,ABI 与 ISA 一起构成软件契约;细节留给系统课。
画一张两栏表:左栏「软件可读写的状态」,右栏「实现可自由改变的缓冲」。把重命名寄存器、保留站、线缓冲全部放进右栏。若有人把「二级缓存行数」写进左栏,当场纠正。这张表可以贴进实验报告首页,作为本课的最小交付物之一。
调试器里看寄存器窗口时,问自己:我看到的是 ABI 命名(如返回值寄存器别名),还是原始编号?别名属于工具链约定,编号属于 ISA。混淆二者会在内联汇编里写错约束。云上 core dump 跨机器对比时,也要先确认同一 ISA 与同一 ABI,再比寄存器快照。

状态寄存器里常见的中断使能、模式位等,属于特权或半特权切片。应用课先假设「我碰不到」;驱动课再打开。教学演示可用只读方式展示模拟器里的特权寄存器名,强调「看见名字≠可以写」。若课程实验提供用户态模拟器,关闭特权寄存器写入,故意触发一次失败,记下异常类别,作为第 7 节的伏笔。
PC 的宽度通常与地址宽度一致。教学示意可用 32 位地址空间,但提醒真实云主机多为 64 位用户地址的子集约定。讨论「高位指针标记」一类扩展时,只点名:它们改的是可见指针表示,属于 ISA/ABI 边界上的扩展,细节不在本课。
4 寻址方式与对齐
寻址方式描述如何从指令字段得到有效地址或操作数。常见:寄存器直接、立即数、基址+偏移、PC 相对(便于位置无关代码)。load/store 型 ISA 把复杂寻址收束在访存指令;运算指令只看寄存器。
对齐:许多 ISA 要求对自然对齐的访问更快或必须对齐,未对齐可能陷阱或拆成多次访问。字节序(小端/大端)决定多字节整数在存储器中的排列。网络协议常规定大端,主机可能是小端,于是有显式转换。云镜像与跨架构迁移时,字节序与对齐假设写进规格,能减少「同值不同布局」的隐蔽缺陷。
手算:基址寄存器=100,偏移=8,数据字长 4 字节,小端。写出地址 100..103 存放数值 0x12345678 时的字节序。再问:若 ISA 要求对齐,地址 102 的字访问是否合法——按本课约定标「风险/非法」并写一句理由。
补充手算变体:把基址改成 0x1000,偏移改成 -4,讨论有符号偏移如何参与有效地址。再假设结构体字段按 8 字节对齐打包,计算两个字段的起始地址差,体会「语言层对齐」与「ISA 层对齐」如何叠在一起。跨平台序列化应使用显式布局(如 protobuf)或规定端序的二进制协议,而不是直接 dump 内存中的 C 结构体。
PC 相对寻址再落一例:共享库被装载到不同基址时,调用点到目标的字节距离若编码在指令里且相对 PC,则无需对每条调用做绝对地址重写。理解这一点,有助于读懂位置无关代码与动态链接的基本前提,细节仍留给系统课,本课只要求能口述「相对谁」。
对齐失败的两种处理要会复述:严格陷阱,或硬件拆成多次总线事务并可能更慢。规格书会写清;软件若假设「总会拆分且沉默成功」,在换架构时可能突然收到总线错误。把「未对齐策略」列入跨架构检查表第 1 行。网络大端与主机小端的转换点应画在协议边界,而不是散落在业务函数里。
基址+偏移的溢出与包裹规则以手册为准。示意手算若出现负数偏移,先按有符号加法算出有效地址,再谈是否合法映射。把「算出的地址」与「是否可访问」分成两步,避免混成一步。
5 指令编码:字段、定长与变长
编码把语义压成比特串。定长编码(如许多 RISC 的 32 位)便于取指与对齐;变长编码(如经典 x86)密度高但取指与译码更复杂。字段通常包括:操作码、寄存器编号、立即数、函数码等。R 型(寄存器-寄存器)与 I 型(带立即数)是教学最小集合。
例(示意,非某一真实 ISA 的官方图):
| 字段 | 位数示意 | 含义 |
|---|---|---|
| opcode | 7 | 操作大类 |
| rd | 5 | 目的寄存器 |
| rs1 | 5 | 源 1 |
| rs2 | 5 | 源 2 |
| funct | 若干 | 细分类 |

练习:给定 32 位定长、寄存器窗口 32 个(编号 5 比特),计算立即数字段最多能占多少比特若 opcode+rd+rs1 已用 17 比特——用手算理解「常量太大要拆指令」。不要背某一厂商手册页码。
把同一语义用「短立即数一条」与「高位加载+低位加」两条路径对比:指令条数增加,但可表示的常量空间变大。问学生:若热点循环里每次都要拼常量,编译器会不会把常量放到文字池再 LOAD?这是编码宽度逼出来的代码形态,不是程序员「喜欢多写」。阅读反汇编时,看到成对的高位/低位装载,应联想到立即数字段不够,而不是怀疑工具链出错。
定长与变长的取指代价也用一句话钉住:定长可用对齐的取指窗口;变长可能要从任意字节开始截指令,译码器更重。当代 x86 对外保持变长兼容,对内译成微操作,说明「对外契约」与「对内实现」可以分层——这恰好呼应第 1 节的 ISA/微架构划分。
操作码空间也会耗尽或预留。保留编码与未定义编码不同:保留可能留给未来标准;未定义则软件不得依赖。写兼容层时,遇到未定义操作码应视为硬错误,而不是「当 NOP」。教学示意表里的 funct 字段,只为说明「同大类下再细分」,数值不得抄进生产解码器。
练习扩展:若 opcode 改为 8 比特,寄存器仍 5 比特×3,问立即数还剩多少;再问若改为 2 操作数指令(目的兼源),寄存器字段如何减少。每次只改 1 个假设,观察编码交换。
6 控制流:分支、跳转与调用
控制流指令改写 PC。条件分支根据比较结果选择是否转移;无条件跳转直接写 PC;调用还要保存返回地址(寄存器或栈)。ISA 要定义:比较在独立指令还是融在分支里;偏移是绝对还是 PC 相对;延迟槽是否存在(现代教学默认无延迟槽,除非讲历史)。
条件码风格与「比较结果进寄存器再分支」风格都有。本课要求能读状态表式描述:若 equal 则 PC=PC+offset 否则 PC=PC+4(示意)。云上安全与沙箱还会限制某些跳转目标,那是更高层策略,但依赖 ISA 能否表达间接跳转。

反例:把高级语言 if 想像成硬件自动结构,而不写清楚比较与分支两条语义。编译器正是把 if 降到这些指令;你读反汇编时要对得上。
对照练习:给出一段伪代码 if (a==b) x=1; else x=2;,要求写成「比较 / 分支 / 两条赋值 / 汇合」的指令级提纲,不写二进制。再改写成「比较结果写入寄存器,再用该寄存器条件分支」的形式,指出可见状态里多了什么。最后讨论间接跳转:函数指针或虚调用需要「寄存器里的目标地址 → 写入 PC」,沙箱若要限制目标集合,必须能在更高层拦截这类指令语义。
返回地址保存位置也要说清:链寄存器风格把返回地址放进约定寄存器;栈风格由调用指令或配套序列压栈。ABI 会规定谁负责平衡栈。本课不展开调用帧布局,但要求能区分「ISA 提供的调用/返回原语」与「ABI 规定的寄存器职责」。
延迟槽是历史包袱:分支后下一条仍执行。现代课程默认无延迟槽,但读旧架构资料时要能识别。若在反汇编里看到分支后紧跟似乎「不该执行」的指令,先查该 ISA 是否定义延迟槽,再谈编译器错误。本课作业禁止默认使用延迟槽语义。
调用与返回要成对理解:返回指令把保存的返回地址写回 PC。若返回地址被破坏,控制流劫持就发生在 ISA 语义层之上的内存安全问题——本课点到为止,安全课再展开。
7 特权、陷阱与系统调用入口
ISA 区分用户态与特权态:某些指令、控制寄存器访问仅特权允许。违反时触发陷阱。系统调用是受控的陷入特权的路径:用户态放好参数与调用号,执行陷入指令,内核处理后再返回。缺页、非法操作码、断点也走陷阱/中断机制的不同入口。本课只建立「契约里有特权与陷阱」的图景;具体向量表与中断控制器留给 H13。
教室讨论:为什么 HALT 或直接写页表的指令不能给普通应用?用「隔离」与「稳定契约」各写 1 句。

把「非法操作码」「特权违反」「缺页」写成 3 行预期行为表:谁可以触发、默认是否可恢复、用户态能否直接处理。不必背向量号,但要能说:系统调用是主动陷入;缺页常常是可恢复的被动陷入;特权违反通常意味着程序错误或攻击面。云上若看到反复特权异常,先查是否二进制架构不匹配或错误启用了仅内核可用的扩展,而不是先怀疑 ALU。
最小序列图作业:用户态准备调用号 → 陷入指令 → 特权态分发 → 返回用户态。图中标注「参数在哪些寄存器/栈槽」留给 ABI 课,本课只要求框出陷入边界。这张图是进 H8/H13 的桥。
系统调用号与陷入指令的关系:调用号是软件约定(ABI/OS),陷入指令是 ISA 提供的门。换操作系统可能改调用号,但不改陷入指令形态;换 ISA 则两者都可能变。云函数或容器若用到冷门调用,要确认客户机内核是否实现。把「调用号表版本」与「ISA 陷入助记符」分两行记录,避免混成一项。
断点与单步往往也走陷阱:调试器写入陷阱指令或配置硬件断点寄存器。用户态能否设置硬件断点,取决于特权与 OS 接口。理解「调试也是契约上的控制流」,有助于读懂调试器与内核的分工。
8 CISC/RISC 对照与选型直觉
RISC 倾向:定长、load/store、简单寻址、让微架构做流水。CISC 倾向:复杂指令、多种寻址、更高编码密度。当代现实是两者融合:x86 对外 CISC,对内译成微操作;ARM 也有复杂扩展。选型直觉:看软件生态、能效与云厂商镜像支持,而不是只看「指令条数少」。
把「指令数少」与「周期数少」分开:复杂指令可能 1 条顶多条简单指令,但可能更难流水。性能结论要落到测量,本课只要求能解释交换。
选型讨论用 3 列白板:生态(编译器、发行版、现成镜像)、约束(功耗、授权、安全扩展)、迁移成本(重编、汇编重写、端序)。禁止只写「RISC 一定更快」。补充阅读任务:找同一开源项目的 amd64 与 arm64 发布说明,各摘 1 句与 ISA 相关的构建要求,体会「多架构」不是换个文件名。
再强调扩展指令:向量、加密、原子操作往往以可选扩展存在。镜像基线若声明「仅基础整数 ISA」,却在代码里用了扩展而无运行时探测,会在旧内核或精简实例上崩溃。契约意识包括:默认基线是什么、扩展如何探测、失败如何降级。
能效与生态往往压过「指令优美」。边缘设备可能选更小的地址宽度或更受限的扩展集;数据中心可能为了现成二进制生态坚持某族 ISA。方案陈述应写清约束来源:是工具链成熟度、是镜像仓库现状,还是功耗信封。禁止用「CISC 过时」一类空话代替证据。
课堂辩论限时 5 分钟:一方主张「为密度选变长」,一方主张「为译码简单选定长」。裁判只准打分「论证是否提到生态与测量」,不准宣布哪种绝对正确。
9 设计流程、验收与和 H8 的接缝
学 ISA 的验收不靠背操作码表,而靠:能解释 1 条算术、1 条访存、1 条分支的语义;能指出程序员可见状态;能做 1 次基址+偏移与字节序手算;能读懂示意编码字段;能说明特权指令为何存在。
反例 1:把缓存命中率当成 ISA 字段。反例 2:把某一 CPU 的流水线深度写进「必须兼容」的软件假设。反例 3:忽略对齐,在跨平台数据文件里直接 memcmp 结构体。反例 4:在文档写「类似汇编」却不声明具体 ISA 族,导致云主机选错架构。
与 H8 接缝:本课契约 → 下一课用汇编助记符与机器码把契约写出来。与 H6 接缝:数据通路执行的正是这些指令的语义。
扩展阅读边界:向量扩展、原子指令、内存模型(弱序)点到名称为止,不在本课展开公式。
把课内 4 次手算(语义描写、基址偏移、立即数位宽、分支 PC)收成个人检查表,作为进入 H8 的门票。
验收口述模板:不提缓存,只讲语义、编码、异常 3 词;能默写 ADD/LOAD/BEQ 的源与目的;能划掉流水线缓冲只留可见状态;能完成小端 0x12345678 字节表;能在 32 位定长示意下算剩余立即数比特;能用 PC 相对解释共享库;能说明用户态执行特权指令的预期陷阱;能用生态与能效而非「指令条数」做选型陈述。
风险与工具备忘:文档写「CPU 要求」却未写 ISA 族,部署必踩架构坑;反汇编只为观察字段,不把示意编码当官方;镜像架构标签与工具链三元组必须同族;调用约定依赖寄存器集合但不等同于微架构;间接跳转是沙箱关心的能力;过大常量拆多指令属于编码宽度交换;跨平台结构体不要直接 memcmp;页表写入属特权;延迟槽仅作读旧资料标记;复杂指令快慢必须测量;内存模型与原子指令点名即可。
寄存器耗尽时编译器溢出到栈槽,是 ABI 与 ISA 共同结果,不是 ALU 故障。云脆弱性通报里若出现「某扩展指令侧信道」,先判断扩展是否在你的镜像基线内。收尾自问:不谈缓存能否讲清 ISA?能,才算过关。
设计流程建议按「场景 → 契约表 → 手算 → 反例 → 冻结」5 步走。场景写清:教学模拟器、云镜像、还是嵌入式固件。契约表列出 ISA 族、端序、对齐、扩展。手算 4 项过关。反例至少收集 4 条并对应到检查表行号。冻结时同时冻结手册版本与示例编码免责声明。与 H8 交接清单:助记符教材章节、可用模拟器、禁止使用的扩展列表。与 H13 预告:向量表与中断控制器不在本课范围,但特权/陷阱词汇表要带走。
小组互评时只听 3 分钟:对方能否在不提缓存的前提下讲完 ISA?计时结束仍在讲流水线,则未过关。通过者把检查表拍照或提交文本,作为 H8 选课或选章的门票。教师可抽查其中 1 次手算细节。
提交物清单:契约表 1 份、手算纸 4 张、反例列表 1 份、口述录音或课堂通过标记。缺一则不计本课完成。H8 开课第一分钟抽查契约表上的 ISA 族字段是否非空。
补充讨论轮次2:用自己的话重述 ISA 与微架构边界,并写 1 条与云镜像相关的验收句,要求出现「架构标签」。再写 1 条与编码相关的验收句,要求出现「立即数」。两句都不得出现缓存字样。把两句贴进检查表备注栏。轮次编号仅用于区分讨论,不改变契约内容。
放进方案里
在方案「处理器与镜像」节增加 ISA 约束表:目标 ISA 族与版本、端序、对齐要求、禁用扩展清单、特权指令使用策略。每条自研汇编或内联片段写明依赖的指令扩展。验收至少含:1 条算术语义对照、1 次 load 地址手算、1 次分支 PC 推演、1 次非法特权访问的预期陷阱说明。写明禁止项:把微架构细节写进对外兼容承诺、未声明架构就分发裸机二进制、跨端序共享内存映像而不转换。把工具链目标三元组与云镜像架构标签写入检查表末行。责任人与冻结日期并列。若临时启用实验性扩展,记录回退到基线 ISA 的条件。发布前再读:文档中的「CPU」是否其实该写「ISA」;示例编码是否误当成保证的官方格式。证据附手算纸或仿真反汇编片段编号,不附密钥与账号。接口表登记用户态禁止的控制寄存器访问。向量集包含未对齐访问的预期行为(陷阱或拆分)并与规格一致。附录写明示意编码仅用于教学,生产以官方手册为准。联调前冻结 ABI 与 ISA 文档版本号,避免口头改寄存器约定。板上或云主机验收时核对手册版本号与 -march 旗标一致。若仅教学示意编码,禁止写入对外兼容矩阵。方案评审抽问:示意 opcode 宽度若误写入对外 SDK,如何发现并回滚;示例反汇编是否与官方手册章节号交叉引用;错误架构二进制的失败信息模板是否写入运维手册;冻结包是否附架构兼容矩阵版本哈希。
联调记录禁止粘贴密钥与账号,只保留架构标签、手册版本与 -march 值。若发现示例编码进入对外 SDK,立即回滚并改为「参见官方手册章节」。运维手册登记错误架构二进制的失败信息模板。冻结包附带架构兼容矩阵的版本哈希。接口评审确认用户态禁止的控制寄存器列表与陷阱预期一致。
10 小结:下一课用汇编把契约写出来
本课建立了 ISA 作为软硬件契约的图景:语义、可见状态、寻址与端序、编码字段、控制流与特权。下一课 H8 将进入汇编助记符与机器码,把这些字段写成可运行的文本与比特。
下一课转向 H8 汇编与机器码:把本章验收过的结论带进后续主题,写成可检查的方案约束。