计组 × 操作系统 融合精讲408 · 零基础到真题
408 · 计算机组成原理(45 分) × 操作系统(35 分)

计组与操作系统融合精讲

写给没系统学过计算机的高三毕业生:从“0 和 1 是什么”讲起,到能独立做 2009—2026 年的 408 真题。

计组和操作系统在 408 里一共 80 分,占整张卷子一半以上。两门课其实在讲同一台计算机:计组讲硬件“怎么做到”,操作系统讲软件“怎么安排”。很多真题(尤其是综合大题)专门考两者交界的地方,比如“一次访存要经过 TLB、页表、Cache、主存,缺页了操作系统怎么接手”。所以本讲义不按两本教材各讲各的,而是把它们拼成一条主线。

怎么用这份讲义
  1. 按顺序读。第 0 篇是零基础补课,后面每篇都假设你读过前面的篇。
  2. 每遇到 真题卡 先自己做,再点「展开解析」。解析会写出每一步为什么这样想,而不只给答案。
  3. 每个小节标题下面有一条“真题标签”:★★★ 高频 表示这个知识点在 2009—2026 年真题里出现得多(★★ 常考、★ 考过、“基础铺垫”是没直接考但后面要用的);后面的 09#26 这样的小按钮就是“2009 年第 26 题”,点一下在页面里弹出题目,直接作答、判对错、看讲解。正文里出现的“2019-14”这类题号也都能点。读完一个小节就把它下面的题做掉——这就是“看完能做真题”的检验。
  4. 每篇末尾有“本篇真题闯关”,把这一篇涉及的真题按小节排好;第 12 篇 12.8 可以按年份刷完 2009—2026 年全部 451 道计组、操作系统真题(选择 379 道、综合 72 道,每道都有讲解,综合题是分步完整解答,其中 22 道附原卷参考答案的评分标注)。做对做错会记在本机,题号随之变绿/变红。
  5. 每个考过选择题的小节(共 72 个),末尾都有「选择题变式」(虚线框,共 205 道,考得越多的小节题越多;点选项立即判对错):每道都由一道真题改动一两个条件而来,解析后面的“变化点”说明改了什么、答案为什么跟着变。先做真题,再做变式,用来检验是不是真正懂了,而不是记住了答案。
  6. 标 融合 的地方是两门课的交界,是综合大题的高发区,读两遍。
  7. 页面里带 ⚙ 的是可以动手的小工具(进制转换、Cache 模拟、页面置换、调度甘特图、磁盘调度、银行家算法……),改数字就能看到结果,用来验证你手算的答案。
  8. 第 11 篇「大题满分训练营」把 72 道计组、操作系统综合题分成 13 类,每类讲“这类题长什么样 → 必须会的几件事 → 解题模板 → 完整示范 → 自编模拟大题 → 举一反三变式表 → 丢分点”,最后 11.15 有两套按真实考试格式出的整卷模拟(附评分细则),每道大题都有“答题卡这样写”的满分解答。学完对应的知识篇就去练对应的题型。
  9. 每篇末尾有「自测」和「本篇学完」勾选框;第 12 篇是考前速查:公式 + 易错清单 + 真题索引 + 按年份刷题。
  10. 第 13 篇「满分冲刺」给已经学完一遍的人用:先看两张“年份 × 章节”热力图和 2027 预测,再做冷考点专项、概念辨析(Ⅰ/Ⅱ/Ⅲ 组合题)、计算题专练、融合综合题,最后用三套80 分全真模拟卷(21 道选择 + 4 道综合,限时 100 分钟,可只做计组或只做操作系统,交卷自动分科计分)检验;13.8 的知识点细目表可以逐条勾选,查漏补缺。

一张图看懂:两门课为什么要合在一起学

用户程序用户态 操作系统内核态 硬件计组 源程序 hello.c第 3 篇 编译·汇编·链接得到可执行文件 运行中的进程代码·数据·堆·栈 scanf / printf / read要请操作系统帮忙 进程·线程·调度第 6 篇 同步互斥·死锁第 7 篇 内存管理页表·置换 第 8 篇 文件系统inode·目录 第 10 篇 I/O 管理·驱动缓冲·SPOOLing 第 9 篇 中断/异常处理 · 系统调用入口 CPU 进入内核的唯一大门(第 5 篇) CPU 取指·执行数据通路·流水线 第 4 篇 ALU 运算补码·浮点 第 2 篇 MMU·TLB·Cache地址翻译 第 8 篇 主存 DRAM芯片·交叉 第 8 篇 I/O 接口·DMA键盘·磁盘 第 9/10 篇 系统总线:地址线 · 数据线 · 控制线(第 9 篇) ① 系统调用(陷入) ② 中断 ③ 缺页异常 ④ 页表 ⑤ 进程切换
图 0-1 全书地图。上层是你写的程序,中层是操作系统,下层是硬件。橙色箭头是两门课“握手”的地方:① 程序请操作系统办事(系统调用);② 设备通知 CPU(中断);③ 访存时页不在内存(缺页异常);④ 页表由操作系统填写、由硬件 MMU 每次访存时读取;⑤ 进程切换时操作系统要改写 CPU 里的寄存器。驱动程序(第 9 篇)通过读写 I/O 接口里的寄存器指挥设备。408 综合大题几乎都落在这几条箭头上。
篇内容两门课的交界
第 0 篇零基础装备:二进制、十六进制、2 的幂、单位、地址、位运算所有计算题的地基
第 1 篇一台计算机的全景:五大部件、程序怎么执行、操作系统是什么、性能指标硬件与 OS 的分工
第 2 篇数据怎么表示和运算:补码、C 语言类型转换、标志位、IEEE 754、大小端计组独有
第 3 篇指令系统与“程序的一生”:寻址方式、汇编代码、编译链接装入、进程地址空间融合 编译链接装入
第 4 篇CPU 内部:数据通路、控制信号、控制器、流水线、多核计组为主
第 5 篇中断、异常、系统调用、用户态与内核态融合 核心交界
第 6 篇进程、线程、CPU 调度、上下文切换OS 为主
第 7 篇同步互斥(PV 大题)、死锁、银行家算法融合 硬件原子指令
第 8 篇存储系统全景:主存芯片、Cache、分页分段、TLB、虚拟内存、页面置换融合 最大交界
第 9 篇总线与 I/O:查询/中断/DMA、驱动、缓冲、一次 scanf 的全过程融合
第 10 篇磁盘与文件系统:访问时间、调度算法、inode、目录、链接融合
第 11 篇大题满分训练营:13 类综合题,每类有模板、示范和满分答案综合题专项
第 12 篇考场作战手册:题型速查、公式速查、易错清单、真题索引、按年份刷题考前冲刺
第 13 篇满分冲刺:考情热力图与 2027 预测、冷考点专项、概念辨析与计算专练、融合综合题、三套 80 分全真模拟卷、知识点细目表查漏补缺、限时实战
关于“高频”标记

小节标题下的星级按“这一小节挂了多少道真题”自动算(选择题算 1、综合题算 3:≥9 为三星,≥4 为两星)。每道题挂在哪个小节、讲解怎么写,都是对照题目逐道确定的;题干和答案来自本机题库(2009—2026 年计组 235 题、操作系统 216 题),缺图的题干按原卷重绘。考过多次说明重要,但不代表明年一定考;没挂题的知识点也在大纲里,同样要会。

第 0 篇 · 零基础补课

出发前的装备:0 和 1 的世界

后面所有计算题——地址有几位、Cache 分几组、页表多大、磁盘多少秒——用到的数学只有四样:二进制、十六进制、2 的幂、单位换算。这一篇把它们一次补齐,花 1~2 小时,后面会省很多时间。

对应:王道计组 2.1.1、贯穿全书难度:★☆☆真题直接考:2018-12(为什么用二进制)、2021-14(哪个小数不能精确表示)

0.1计算机只认识“开”和“关”

★ 考过选择题

计算机里最小的零件是晶体管,你可以把它想成一个电灯开关:只有“通电”和“不通电”两种状态。我们把“通电”记作 1,“不通电”记作 0。

  • 一个开关能记住 1 个 0 或 1,这叫 1 位(bit,简写 b)。
  • 8 个开关排成一排,叫 1 字节(Byte,简写 B)。8 个开关一共有 2×2×…×2 = 28 = 256 种开关组合,所以 1 字节能区分 256 种东西。
  • n 位能表示 2n 种不同的组合——这是全书用得最多的一句话。
1 字节 = 8 位(8 个开关) 0 1 0 0 0 0 0 1 2⁷=1282⁶=642⁵=322⁴=16 2³=82²=42¹=22⁰=1 = 64+1= 65 最左边叫“最高位”,最右边叫“最低位”;每一位的“权重”从右往左依次是 1、2、4、8、16……
图 0-2 一个字节。亮着的开关是 1。把亮着的位的权重加起来就是它表示的数:0100 0001 = 64 + 1 = 65。至于这 65 是一个整数、字母 “A” 还是一条指令的一部分,取决于谁、怎么去解释它——这是第 2 篇的主题。
2018-12计组为什么用二进制
冯·诺依曼结构计算机中数据采用二进制编码表示,其主要原因是( )。Ⅰ.二进制的运算规则简单 Ⅱ.制造两个稳态的物理器件较容易 Ⅲ.便于用逻辑门电路实现算术运算
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅰ、Ⅲ
  • C. 仅Ⅱ、Ⅲ
  • D. Ⅰ、Ⅱ和Ⅲ
展开解析

考点0.1 计算机只认识“开”和“关”

D三条都是计算机采用二进制的理由,分别从“运算”“器件”“电路实现”三个角度说的。

  • Ⅰ 对二进制加法只有 0+0、0+1、1+0、1+1 四种情况,乘法也只有 0、1 相乘,运算规则最简单。
  • Ⅱ 对开 / 关、高 / 低电平这种只有两个稳定状态的器件最容易造,也最不容易出错。
  • Ⅲ 对0 / 1 正好对应逻辑上的假 / 真,用与门、或门、异或门就能搭出加法器,算术运算可以用逻辑电路实现。

干扰项从哪来:A 是漏了Ⅲ,以为“用逻辑门实现运算”只是结果而不是原因;B 是漏了Ⅱ,忽视了物理器件容易制造这一最根本的原因;C 是漏了Ⅰ,没注意到二进制运算规则简单本身就是优点

采用二进制的原因:器件容易造(两个稳态)、运算规则简单、能用逻辑电路实现运算,可靠性高。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2018-12 改
下列关于计算机中使用二进制的叙述中,错误的是( )。
  • A. 二进制只有 0 和 1 两个数字,用有两个稳定状态的器件就能表示
  • B. 二进制的算术运算可以用逻辑门电路实现
  • C. 任何十进制小数都能用有限位二进制数精确表示
  • D. 十六进制只是二进制的简写,计算机内部存的仍是二进制
展开解析

C0.1、0.3 这类十进制小数化成二进制是无限循环小数,有限位只能近似表示。其余三项都是采用二进制的理由或事实。

变化点:2018-12 问采用二进制的原因(器件容易做、运算规则简单、便于用逻辑门实现)。这里反过来找一个错误说法。

0.2十进制、二进制、十六进制

★ 考过选择题

我们平时用的十进制,本质是“逢十进一”,每一位的权重是 1、10、100……。二进制就是“逢二进一”,权重是 1、2、4、8……。这几种进制之间怎么互相换算,就是教材里的进制转换。

十进制 347 = 3×10² + 4×10¹ + 7×10⁰ 二进制 1011₂ = 1×2³ + 0×2² + 1×2¹ + 1×2⁰ = 8 + 0 + 2 + 1 = 11

二进制太长,人看着眼花,于是有了十六进制:逢十六进一,用 0~9 和 A(10)、B(11)、C(12)、D(13)、E(14)、F(15) 共 16 个符号。408 里十六进制数常在末尾写 H(如 2000H),C 语言里写 0x 前缀(如 0x2000),两种写法意思一样。

最重要的技巧:1 个十六进制位 = 4 个二进制位

因为 16 = 24,所以十六进制和二进制可以四位四位直接对照,不用经过十进制。把下面这张表背熟(其实只要会数数):

十六进制0123456789ABCDEF
二进制0000000100100011010001010110011110001001101010111100110111101111
十进制0123456789101112131415

例:2F3AH → 2=0010,F=1111,3=0011,A=1010 → 0010 1111 0011 1010。反过来,二进制从右往左每 4 位一组(最左边不够 4 位就在前面补 0),每组换成一个十六进制位。

001021111F001131010A
考场上最常见的坑

地址切分题里,字段位数往往不是 4 的倍数。比如 12 位页内偏移正好是 3 个十六进制位(很舒服),但 5 位块内地址、3 位组号就会“劈开”一个十六进制位。稳妥做法:先把十六进制全部展开成二进制,再按位数切。不要试图直接在十六进制上切。

八进制,以及“任意进制 → 十进制”

八进制逢八进一,只用 0~7 八个符号。因为 8 = 23,1 个八进制位 = 3 个二进制位,道理和十六进制完全一样,只是 3 位一组。C 语言里以 0 开头的整数是八进制(017 是十进制的 15,不是 17),教材里常在末尾写 O 或 Q(17O、17Q)。408 里八进制很少出现,会换就行。

任意 r 进制 → 十进制:按权展开。小数点左边第 0、1、2 位的权是 r0、r1、r2……,小数点右边第 1、2、3 位的权是 r−1、r−2、r−3……每一位“数码 × 权”再加起来:

(2A.8)₁₆ = 2×16¹ + 10×16⁰ + 8×16⁻¹ = 32 + 10 + 0.5 = 42.5 (57.4)₈ = 5×8¹ + 7×8⁰ + 4×8⁻¹ = 40 + 7 + 0.5 = 47.5 (101.011)₂ = 4 + 0 + 1 + 0.25 + 0.125 = 5.375

带小数点的二进制换成十六进制(八进制):从小数点出发,往两边分组。整数部分往左每 4 位(3 位)一组,最左边不够在左边补 0;小数部分往右每 4 位(3 位)一组,最右边不够在右边补 0。小数部分如果把 0 补在左边,就把 0.1 变成了 0.0001,值全变了。

二进制 1 0110 1101.101分组(补的 0 用红色)结果
4 位一组 → 十六进制0001 0110 1101 . 101016D.AH
3 位一组 → 八进制101 101 101 . 101555.5₈
验算(按权展开)16D.AH = 256 + 96 + 13 + 10/16 = 365.625;555.5₈ = 320 + 40 + 5 + 5/8 = 365.625 ✓

十进制 → 二进制

  • 整数部分:除 2 取余,倒着写。例:13 → 13÷2=6 余 1,6÷2=3 余 0,3÷2=1 余 1,1÷2=0 余 1 → 倒读 1101。更快的办法是“凑 2 的幂”:13 = 8+4+1 = 1101。
  • 小数部分:乘 2 取整,正着写。例:0.25 → ×2=0.5 取 0,×2=1.0 取 1 → 0.01₂。
  • 有的小数乘不尽:0.1 → 0.2(0)→0.4(0)→0.8(0)→1.6(1)→1.2(1)→0.4(0)… 无限循环 0.000110011…₂。所以0.1、0.2、1.2 这样的小数计算机存不精确;而 0.5、0.25、0.125、1.25 这类“分母是 2 的幂”的小数可以精确存。
  • 换成八进制、十六进制也是同一套办法:整数部分“除 8(16)取余、倒着写”,小数部分“乘 8(16)取整、正着写”。但考场上更快、更不容易错的永远是先变成二进制,再 3 位(4 位)一组。
为什么“乘 2 取整”是正着写、“除 2 取余”是倒着写

除 2 取余时,第一次得到的余数是“这个数是奇数还是偶数”,也就是最低位(20 位),所以先得到的写在最右边;乘 2 取整时,第一次乘 2 溢出到整数部分的是“这个小数有没有 1/2”,也就是小数点后第 1 位(2−1 位),所以先得到的写在最左边。两个方向都是“离小数点最近的位先出来”。

2021-14计组
下列数值中,不能用 IEEE 754 浮点格式精确表示的是( )。
  • A. 1.2
  • B. 1.25
  • C. 2.0
  • D. 2.5
展开解析

考点2.6 浮点数的精度0.2 小数转二进制

A一个小数能被 IEEE 754 精确表示的前提是:它的二进制小数部分是有限位(能写成若干个 1/2、1/4、1/8 …之和),且有效位不超过 24(float)。

  • A 对1.2 = 1.0011 0011…₂,0.2 乘 2 取整永远循环(0.2 → 0.4 → 0.8 → 1.6 → 1.2 → 0.4…),存不下,只能舍入——不能精确表示,本题选它。
  • B 错1.25 = 1.01₂,能精确表示。
  • C 错2.0 = 1.0 × 21,能精确表示。
  • D 错2.5 = 10.1₂ = 1.01 × 21,能精确表示。

十进制的 0.1、0.2 在二进制里是无限循环小数,float、double 都不能精确表示。

0.32 的幂与单位:408 计算题的“九九乘法表”

基础铺垫
2n20=121=222=423=824=1625=3226=6427=12828=25629=512
名字210 = 1024 = 1K220 = 1M230 = 1G240 = 1T250=1P

于是任何“2 的幂”都能口算:4KB = 22×210 B = 212 B;64MB = 26×220 = 226 B;4GB = 232 B。反过来,“2n 个东西需要 n 位编号”,记作 n = log2(个数)。

一句话记住

“有多少个东西” ⇄ “需要几位编号”:个数 = 2位数,位数 = log2个数。后面 Cache 的组号位数、页号位数、寄存器编号位数、磁盘扇区号位数……全是这一句。如果个数不是 2 的幂(比如 500 个扇区),位数要向上取整:⌈log2500⌉ = 9 位(29=512 ≥ 500)。

为什么是 2n?每多一位,就是在原来所有编号前面分别写一个 0 和一个 1,编号的个数翻一倍:

每多 1 位,编号个数 × 2 1 位 0 1 2 种 2 位 00 01 10 11 4 种 3 位 000 001 010 011 100 101 110 111 8 种 蓝色 = 原来的编号前面加 0,橙色 = 原来的编号前面加 1 n 位 → 2ⁿ 种;反过来 2ⁿ 个东西 → 需要 n 位才能各有一个编号 408 里天天用 16 个通用寄存器 → 编号 4 位 Cache 有 512 组 → 组号 9 位 页面 4KB = 2¹² B → 页内偏移 12 位 操作码 6 位 → 最多 64 种指令 一道 500 个扇区 → 扇区号 9 位 (500 不是 2 的幂,向上取到 512)
图 0-3 位数和个数。每道计算题拿到手,先问一句“这里有多少个东西?”,再换成“需要几位”。

两种“K”:存储用 210,速度用 103

容量(存储器、文件、页面)
K = 210,M = 220,G = 230
例:4KB 页 = 4096 B
速率与频率(主频、带宽、传输率)
k = 103,M = 106,G = 109
例:主频 1GHz = 每秒 109 个时钟周期

真题里偶尔两者混在一个式子里(比如 “0.5MB/s 的设备,每次传 4B”)。408 的评分答案一般直接按题目数字算(0.5MB/4B = 125000 次/秒,把 M 当 106),除非题目明确给了 220。遇到页面、块这种“容量”,一律按 2 的幂。

b 和 B、时间单位

  • 小写 b = bit(位),大写 B = Byte(字节),1B = 8b。网络、总线的“Mbps”是兆位每秒;“MB/s”是兆字节每秒,差 8 倍。
  • 时间:1 s = 103 ms(毫秒)= 106 μs(微秒)= 109 ns(纳秒)= 1012 ps(皮秒)。
  • 频率和周期互为倒数:主频 500MHz → 时钟周期 = 1/(500×106) s = 2 ns。

0.4地址:内存就是一排带门牌号的格子

★ 考过选择题

主存(内存)可以想成一条很长的街,每个格子是一个存储单元,门牌号就是地址。CPU 要读写内存,必须先说出门牌号。

主存:按字节编址,每个格子放 1 字节 41H 6CH 0AH FFH 00H …… 37H 012342ⁿ−1 ←内容←地址 CPU 给出地址 2 地址线有 n 根 → 能说出 2ⁿ 个不同门牌号 → 最多 2ⁿ 个单元 容量 = 单元个数 × 每个单元多大  最大地址 = 2ⁿ − 1
图 0-4 地址和内容是两回事。地址 2 这个格子里装的内容是 0AH。“地址有几位”决定能访问多少个格子;“每个格子多宽”由编址方式决定。

按字节编址 vs 按字编址

按字节编址:每个地址对应 1 个字节(现在几乎都是这样,408 大题也基本是)。按字编址:每个地址对应 1 个“字”(字长 16 位就是 2 字节一个地址)。同样 16 位地址,按字节编址能访问 216 B = 64KB,按 16 位字编址能访问 216×2B = 128KB。

容量 = 2地址位数 × 编址单位大小 地址位数 = log2(容量 ÷ 编址单位大小) 一段区域的最后一个地址 = 起始地址 + 大小 − 1
2011-15计组地址空间 ≠ 实际装了多少内存
某计算机存储器按字节编址,主存地址空间大小为 64MB,现用 4M×8 位的 RAM 芯片组成 32MB 的主存储器,则存储器地址寄存器 MAR 的位数至少是( )。
  • A. 22 位
  • B. 23 位
  • C. 25 位
  • D. 26 位
展开解析

考点1.2 MAR 的位数8.2 主存

DMAR 要能给出整个主存地址空间中的任何一个地址(以后可以插满内存),所以看地址空间 64MB,而不是现在装了多少。64MB = 226B,按字节编址 → 26 位。

  • A 错22 位对应 4M,是单个芯片的单元数。
  • B 错23 位只能编址 223B = 8MB,连实际装的 32MB 都覆盖不了,更不用说 64MB 的地址空间。
  • C 错25 位对应实际装的 32MB,这是干扰条件。
  • D 对26 位。

MAR 位数由地址空间决定;实际容量只影响用几片芯片。

练习:20 位地址、按字节编址 → 地址空间 220 B = 1MB,地址范围 00000H ~ FFFFFH(20 位 = 5 个十六进制位,全 1 就是 FFFFF)。一块 8KB 的区域从 0800H 开始,最后一个地址是 0800H + 2000H − 1 = 27FFH(8K = 213 = 2000H;十六进制加法 0800H + 2000H = 2800H,再减 1)。

0.5逻辑运算与位运算

基础铺垫
AB与 AND(A·B)
都为 1 才 1
或 OR(A+B)
有 1 就 1
异或 XOR(A⊕B)
不同才 1
非 NOT(Ā)
00000Ā:0→1
01011
100111→0
11110

对一串位逐位做这些运算,就是“位运算”。它在 408 里主要有三个用途:

  1. 取出某几位(掩码):想要 x 的低 8 位,就算 x AND 0xFF(0xFF = 1111 1111,与 1 保留、与 0 清零)。
  2. 移位 = 乘除 2 的幂:左移 1 位相当于 ×2,右移 1 位相当于 ÷2(取整)。所以“地址右移 12 位”就是“去掉低 12 位的页内偏移,只剩页号”。
  3. 组合起来切字段:2013-46 让写二级页表的页目录号:(LA >> 22) & 0x3FF——先右移 22 位把低位扔掉,再和 10 个 1 做与运算只留 10 位。
打个比方

十进制里“把 3847 右移 2 位”就是 38(去掉最后两位 = 除以 100 取整),“3847 mod 100”就是 47(留下最后两位)。二进制里完全一样:右移 k 位 = 除以 2k 取整 = 去掉低 k 位;mod 2k = 只留低 k 位。所以“块号 mod 组数”在组数是 2 的幂时,就是“取块号的低几位”。这一点第 8 篇会反复用。

下面这张图把“切字段”完整做一遍。它就是第 8 篇分页、Cache 题的第一步,先在这里练熟:

例:32 位虚拟地址 0040 1025H,页面 4KB(页内偏移 12 位) ① 十六进制 00401 025 ② 每位换 4 位 0000 0000 0100 0000 0001 0000 0010 0101 高 20 位 = 页号 00401H 低 12 位 = 页内偏移 025H ③ 用位运算写 页号 = VA >> 12 右移 12 位:低 12 位被挤掉 偏移 = VA & 0xFFF 与 12 个 1:只留低 12 位 捷径:12 位正好是 3 个十六进制位,所以直接把最后 3 个十六进制位切下来就行(00401 | 025)。 位数不是 4 的倍数时(比如偏移 13 位、组号 5 位)不能这样切,一定要先展开成二进制(上面的 ②)。
图 0-5 切字段。页号、Cache 标记/组号/块内地址、磁盘柱面/磁头/扇区……所有“地址切分”都是这一个动作:写成二进制,从右往左按位数切。

试试:输入 −9 选 16 位看它的补码 FFF7H;输入 134 选 8 位看它当有符号数是多少;输入 FFFFFFDFH 选 32 位。第 2 篇会解释这些结果为什么是这样。

0.6本讲义的记号约定

基础铺垫
写法意思例
2000H、0x2000十六进制数2000H = 8192
1011B、1011₂二进制数1011B = 11
(R1) 或 R[r1]寄存器 R1 里面的内容(R1) = 1234H
((R1)) 或 M[R[r1]]以 R1 的内容为地址,去内存里取出的内容先得 1234H,再取内存 1234H 单元
A → B 或 B ← A把 A 送到 B 里(PC) → MAR
[x]补x 的补码(机器里存的那串位)[−9]补 = FFF7H(16 位)
⌈x⌉、⌊x⌋向上取整、向下取整⌈log2500⌉ = 9
计组 操作系统 融合知识点所属;“融合”是两门课交界
第 0 篇自测
  1. 把 3A7H 写成二进制,再写成十进制。
    答案
    0011 1010 0111B;3×256 + 10×16 + 7 = 768+160+7 = 935。
  2. 某 Cache 有 512 组,组号要几位?一个磁道有 500 个扇区,扇区号至少几位?
    答案
    512 = 29 → 9 位;⌈log2500⌉ = 9 位。
  3. 页面大小 4KB、按字节编址,页内偏移几位?32 位虚拟地址有多少个页?
    答案
    4KB = 212B → 12 位;页数 = 232/212 = 220 = 1M 个。
  4. 主频 2.5GHz 的时钟周期是多少 ns?
    答案
    1/(2.5×109) s = 0.4 ns。
  5. 32 位地址 0040 1025H 右移 12 位是多少?它的低 12 位是多少?
    答案
    右移 12 位 = 去掉最后 3 个十六进制位 → 00401H;低 12 位 = 最后 3 个十六进制位 → 025H。(这就是 2019-46 的“页号”和“页内地址”。)

0.✎本篇真题闯关(1 题)

本篇各小节挂的真题全部在这里,按小节排好。可以展开后一道道做,也可以用弹窗模式连续做。做题记录(对/错)只保存在你这台电脑的浏览器里,小节标题下的题号会随之变绿或变红。

第 1 篇 · 计组第 1 章 + 操作系统第 1 章

一台计算机的全景:硬件、操作系统与性能

先建立一张“整机地图”:计算机由哪几块组成、一条指令怎么被执行、操作系统夹在硬件和程序之间干什么、怎样衡量一台机器快不快。这一篇的题基本都是选择题,但它是后面所有篇章的坐标系。

对应:王道计组第 1 章;王道操作系统第 1 章(1.3 的用户态/内核态、中断、系统调用放在第 5 篇)难度:★★☆高频:CPU 执行时间计算(2012、2013、2014、2017、2022、2023)、操作系统特征与多道程序(2009、2016~2018、2022)

1.1冯·诺依曼结构:五大部件 + “存储程序”

★ 考过选择题

1945 年冯·诺依曼提出的设计思想,今天所有电脑、手机都还在用。核心就两句话:

一句话记住

① 存储程序:程序(一条条指令)和数据先一起存进存储器,然后机器自己一条接一条地取出来执行,不需要人干预。
② 五大部件:运算器、控制器、存储器、输入设备、输出设备。指令和数据都用二进制,在存储器里长得一模一样,没有标签说明“我是指令”。

CPU = 运算器 + 控制器 主机 = CPU + 主存 输入设备 存储器指令和数据都在这 运算器 控制器 输出设备 数据/程序 数据 指令 结果 虚线 = 控制器发出的控制信号(指挥所有部件)
图 1-1 冯·诺依曼机。实线是数据和指令的流动,虚线是控制信号。早期机器以运算器为中心(所有数据都要过运算器),现代计算机以存储器为中心,让 I/O 设备可以直接和存储器交换数据(这就是第 9 篇的 DMA)。
2009-11计组经典
冯·诺依曼计算机中指令和数据均以二进制形式存放在存储器中,CPU 区分它们的依据是( )。
  • A. 指令操作码的译码结果
  • B. 指令和数据的寻址方式
  • C. 指令周期的不同阶段
  • D. 指令和数据所在的存储单元
展开解析

考点1.1 冯·诺依曼结构:存储程序

C指令和数据在存储器里长得一模一样,都是二进制位串;CPU 只能靠“什么时候、按什么地址取出来”来区分。

  1. 取指阶段:按 PC 的值访存,取出来的一定是指令,送进 IR。
  2. 执行阶段:按指令地址码给出的有效地址访存,取出来的是数据,送进运算器(或寄存器)。
  • A 错因果倒了:只有先知道“这是指令”才会把它送进 IR 去译码;数据根本不会被送去译码,所以不可能靠译码结果来区分。
  • B 错寻址方式只规定“操作数的地址怎么算”,它本身写在指令里;取指令时用的是 PC,不经过寻址方式,所以它区分不了指令和数据。
  • C 对取指周期取到的是指令,执行周期取到的是数据——这就是冯·诺依曼机的区分办法。
  • D 错同一个存储单元既可以放指令也可以放数据(自修改程序就是例子),存储器没有划分“指令区 / 数据区”的硬件标记。

冯·诺依曼机靠“指令周期的阶段”区分指令和数据;哈佛结构才把指令和数据放进两个存储器。

冯·诺依曼机的特点,选择题常逐条考(2019-12):① 由运算器、控制器、存储器、输入、输出五大部件组成;② 指令和数据以同等地位存放在存储器中,按地址访问;③ 指令和数据都用二进制表示,形式上没有区别;④ 指令由操作码(做什么)和地址码(对谁做)组成;⑤ 指令在存储器中按顺序存放,通常顺序执行,遇到转移指令才改变顺序;⑥ 早期以运算器为中心。注意:数据一般放在存储器里,指令里给的是它的地址,只有立即数才直接写在指令里。

2019-12计组
下列关于冯·诺依曼结构计算机基本思想的叙述中,错误的是( )。
  • A. 程序的功能都通过中央处理器执行指令实现
  • B. 指令和数据都用二进制表示,形式上无差别
  • C. 指令按地址访问,数据都在指令中直接给出
  • D. 程序执行前,指令和数据需预先存放在存储器中
展开解析

考点1.1 冯·诺依曼结构3.2 寻址方式

C冯·诺依曼机“按地址访问”:指令和数据都存放在存储器中,指令里一般给出的是数据的地址。

  • A 对程序的功能都由 CPU 执行指令来完成。叙述正确。
  • B 对指令和数据都用二进制表示,形式上没有区别(靠取出的阶段区分,见 2009-11)。叙述正确。
  • C 错“数据都在指令中直接给出”错:只有立即寻址才把数据写在指令里,一般数据放在存储器中。本题选它。
  • D 对存储程序:执行前指令和数据要预先存入存储器。叙述正确。

冯·诺依曼机的核心是“存储程序 + 按地址访问 + 二进制 + 五大部件”。

五大部件各管什么:主机和外设怎么分

五大部件是“功能上”的划分。真正拿起一台电脑,教材还有一种“物理上”的划分:主机和外部设备。这两种划分放在一张图里看:

计算机硬件 主机 = CPU + 主存 CPU = 运算器 + 控制器 运算器ALUACC MQ XPSW… 控制器CUIR PC 主存储器(内存) 存储体 + MAR + MDR CPU 能直接访问 断电内容丢失(RAM) 外部设备(外设)= I/O 设备 + 辅存 输入设备键盘 鼠标 扫描仪 输出设备显示器 打印机 辅存(外存):硬盘 SSD U 盘 光盘CPU 不能直接访问,要先调入主存 主机和外设之间隔着 I/O 接口与总线(第 9 篇);硬盘、U 盘虽然“存东西”,但它们是外设,不属于主机
图 1-2 主机与外设。功能划分看“五大部件”,物理划分看“主机 + 外设”。辅存(硬盘等)属于外部设备,从操作系统的角度看它就是一种 I/O 设备(第 9、10 篇的磁盘管理就在“设备管理”里)。
部件干什么由什么组成选择题常考
运算器做算术运算(加减乘除)和逻辑运算(与或非、移位)核心是 ALU;外加 ACC、MQ、X、通用寄存器、程序状态字 PSWALU 宽度 = 机器字长(1.2)
控制器取指令、分析指令、按节拍发出控制信号,指挥所有部件CU(控制单元)、IR、PC“计算机的指挥中心”;第 4 篇
主存存放正在运行的程序和数据存储体 + MAR + MDR(1.2)CPU 能直接访问的只有主存(和寄存器、Cache)
辅存长期保存程序和数据,断电不丢磁盘、固态硬盘、光盘、U 盘属于外设;程序要先装入主存才能执行
输入 / 输出设备把外界信息送进来 / 把结果送出去键盘、鼠标 / 显示器、打印机经 I/O 接口挂在总线上(第 9 篇)

运算器里的三个专用寄存器。教材的模型机运算器里除了 ALU,还有三个寄存器:ACC(累加器)、MQ(乘商寄存器)、X(操作数寄存器)。做不同运算时它们分工不同,选择题偶尔会问“做乘法时乘数放在哪”:

运算ACC(累加器)MQ(乘商寄存器)X(操作数寄存器)
加法被加数 → 算完变成和不用加数
减法被减数 → 算完变成差不用减数
乘法乘积的高位乘数 → 算完变成乘积的低位被乘数
除法被除数 → 算完变成余数商除数

为什么乘法的积要占 ACC 和 MQ 两个寄存器?n 位 × n 位的积最长有 2n 位,一个寄存器装不下。2.5 节的乘法器电路里,“部分积 P”就是 ACC,“乘数 Y”就是 MQ,“被乘数 X”就是 X——到那里再对照着看会很清楚。

计算机的发展与分类(大纲写了“发展与分类”,了解即可)

代大致年代逻辑元件这一代的标志
第一代1946—1957电子管1946 年的 ENIAC 是第一台电子数字计算机,但它还不是存储程序式的,改程序要重新插线;1945 年冯·诺依曼在 EDVAC 方案里提出“存储程序”。只能用机器语言、汇编语言编程
第二代1958—1964晶体管出现 FORTRAN 等高级语言和编译程序,出现批处理的监督程序(操作系统的雏形)
第三代1965—1971中小规模集成电路高级语言迅速发展;操作系统走向成熟,出现多道程序、分时系统
第四代1972—现在大规模、超大规模集成电路出现微处理器和微型计算机;并行、流水线、Cache、虚拟存储器等技术普遍使用

各本教材划分的年份略有出入,记“元件”就够了。摩尔定律:集成电路上能容纳的晶体管数目,大约每 18~24 个月翻一番(性能也大致随之翻倍)。微处理器按字长经历了 4 位、8 位、16 位、32 位到 64 位。

按什么分分成
处理的信息形式电子数字计算机(处理离散的 0/1,今天的计算机都是);电子模拟计算机(处理连续变化的电压等物理量)
用途通用计算机;专用计算机(为某类问题专门设计,如工业控制)
规模、速度、价格巨型机(超级计算机)、大型机、中型机、小型机、微型机、单片机
指令流和数据流SISD、SIMD、MISD、MIMD(Flynn 分类,第 4 篇 4.5)

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2009-11、2019-12 改
下列关于冯·诺依曼计算机“存储程序”思想的叙述中,正确的是( )。
  • A. 指令和数据分别存放在两个独立的存储器中
  • B. CPU 在取指阶段取出的是指令,在执行阶段按地址取出的是数据
  • C. 指令中不能包含操作数
  • D. 程序执行时不必预先存放在存储器中
展开解析

B指令和数据放在同一个存储器里,形式上没有区别,CPU 靠“在哪个阶段取的”来区分(2009-11)。A 说的是哈佛结构;C 错:立即数就在指令中;D 错:程序要先存入存储器才能执行。

变化点:2009-11 问区分指令和数据的依据,2019-12 找错误叙述。这里把两道题的考点合在一起,问正确的一项。

1.2走进 CPU 和主存:几个必须认识的寄存器

★★ 常考选择题综合题

寄存器是 CPU 里的“小抽屉”,速度最快、数量很少。下面这张模型机图里的每个名字,后面都会反复出现,第一次见先认个脸:

CPU PC IR CU控制单元 ALU PSW 标志 通用寄存器R0 R1R2 R3… CPU 内部总线 MAR MDR 控制信号 主存储器 地址内容 0000H指令 1 0004H指令 2 … 2000H数据 a 2004H数据 b 地址总线 数据总线 控制总线(读/写命令) ① (PC)→MAR③ MDR→IR ② 读
图 1-3 模型机。取一条指令的过程:① PC 里的地址经内部总线送进 MAR;② MAR 的地址放上地址总线,控制总线发“读”,主存把内容经数据总线送进 MDR;③ MDR 的内容送进 IR;同时 PC 加上“一条指令的长度”指向下一条;④ IR 里的操作码送 CU,CU 分析后发出一串控制信号(虚线)指挥各部件完成这条指令。
名字干什么要记住的性质
PC 程序计数器存下一条要执行的指令的地址;取完一条指令会自动加“1”(一条指令的长度)程序员可见(跳转指令能改它)
IR 指令寄存器存当前正在执行的指令程序员不可见;位数 = 指令字长
CU 控制单元分析 IR 里的指令,发出控制信号第 4 篇详讲
ALU 算术逻辑单元做加减、与或非、移位宽度 = 机器字长
通用寄存器暂存操作数和结果(R0、R1…;x86 里叫 eax、ebx…)程序员可见;宽度 = 机器字长
PSW 程序状态字/标志寄存器记录运算结果的特征(有无进位、溢出、是否为零、正负),以及 CPU 状态(第 5 篇的“用户态/内核态”、中断允许位)程序员可见
MAR 存储器地址寄存器存要访问的内存地址位数决定能寻址多少单元;不可见
MDR 存储器数据寄存器存从内存读出的/要写入内存的数据位数一般 = 存储字长;不可见

打开主存看一看:存储体、存储单元、MAR 和 MDR

第 0 篇把主存比作“一排带门牌号的格子”。教材对这排格子有一套专门的名词,选择题和大题的第一问经常用到,下面这张图把它们一次对上号:

CPU MAR(n 位) 放“要访问哪个单元” 放“读出/要写的内容” MDR(m 位) 地址 地址译码器 0 1 2 选中 3 2ⁿ−1 存储体:2ⁿ 个存储单元 01000001 ⋮ 数据:一次读出 / 写入一整个存储单元(m 位) 存储元:存 1 位的器件 存储单元:一行,共用一个地址 存储字长 = 一行有几位(m) 存储字 = 这一行存的那串 0/1 单元个数 = 2ⁿ(MAR 定) 容量 = 2ⁿ × m 位 CPU 把地址放进 MAR → 译码器在 2ⁿ 行里选中 1 行 → 这一行的 m 位一起读出(或写入)
图 1-4 主存的基本组成。图中选中的是 2 号单元,里面存的存储字是 0100 0001(41H)。一个存储单元是“一起读、一起写”的最小单位,它的位数叫存储字长;选中哪个单元由 MAR 里的地址决定。
名词意思和谁有关
存储元存 1 位二进制的物理器件(一个小电容或一个触发器),存储器里最小的零件——
存储单元若干个存储元组成,共用一个地址,一次一起读写个数 = 2MAR 位数
存储字 / 存储字长存储字 = 一个存储单元里存的那串二进制代码;存储字长 = 一个存储单元有多少位(8、16、32 位……)MDR 位数一般 = 存储字长
存储体全部存储单元的集合容量 = 单元个数 × 存储字长
主存容量 = 2MAR 位数 × 存储字长(MDR 位数) (按字编址时;按字节编址见本节末“四种宽度”) 例:MAR 16 位、MDR 32 位、按字编址 → 216 × 32 位 = 64K × 32 位 = 64K × 4B = 256KB 例:主存 4GB、按字节编址、每次读写 1 个字节 → 232 个单元 → MAR 至少 32 位,MDR 8 位
“字”和“字节”

字节永远是 8 位;“字”的长度随机器而定——说“字长 16 位的机器”,1 字就是 16 位;说“字长 32 位”,1 字就是 32 位。题目写“按字编址、字长 32 位”,意思是每个地址对应 4 个字节。(x86 汇编里的 word 专指 16 位、dword 指 32 位,是历史遗留叫法,看到时按题目语境理解。)
PC 和 MAR 的位数:一般都等于地址位数;但如果指令都按字对齐,PC 的最低几位永远是 0,可以省掉不存(下面 2016-18)。

三种“字长”别搞混

机器字长:CPU 一次整数运算能处理的位数 = ALU 宽度 = 通用寄存器宽度(“32 位机”说的就是它)。指令字长:一条指令有多少位(决定 IR 位数)。存储字长:一个存储单元有多少位(决定 MDR 位数)。三者可以不相等。
“汇编程序员可见”的寄存器:PC、通用寄存器、PSW/标志寄存器、基址/变址寄存器;不可见:IR、MAR、MDR、微指令寄存器(2010-18、2015-43、2021-17 都考过)。

2020-12计组
下列给出的部件中,其位数(宽度)一定与机器字长相同的是( )。Ⅰ. ALU Ⅱ. 指令寄存器 Ⅲ. 通用寄存器 Ⅳ. 浮点寄存器
  • A. Ⅰ、Ⅱ
  • B. Ⅰ、Ⅲ
  • C. Ⅱ、Ⅲ
  • D. Ⅱ、Ⅲ、Ⅳ
展开解析

考点1.2 三种字长

B机器字长 = CPU 一次能处理的整数的位数 = ALU 宽度 = 通用寄存器宽度。

  • Ⅰ 对ALU 的宽度就是机器字长的定义之一。
  • Ⅱ 错指令寄存器的位数 = 指令字长,可以比机器字长短或长(如变长指令)。
  • Ⅲ 对通用寄存器存放整数操作数,宽度 = 机器字长。
  • Ⅳ 错浮点寄存器的位数由浮点格式决定(如 64 位 double),不一定等于机器字长。

三种字长:机器字长(ALU、通用寄存器)、指令字长(IR)、存储字长(MDR),可以各不相同。

2016-18计组
某计算机主存空间为 4GB,字长为 32 位,按字节编址,采用 32 位定长指令字格式。若指令按字边界对齐存放,则程序计数器(PC)和指令寄存器(IR)的位数至少分别是( )。
  • A. 30、30
  • B. 30、32
  • C. 32、30
  • D. 32、32
展开解析

考点1.2 PC 与 IR 的位数2.7 边界对齐

BIR 要装下一整条指令;PC 要能表示所有可能的指令地址。

  1. IR:指令 32 位定长 → IR 至少 32 位。
  2. PC:主存 4GB 按字节编址,地址本来 32 位;但指令 4 字节且按字边界对齐,每条指令的地址最低 2 位都是 00,不必存——能放指令的位置只有 4GB ÷ 4B = 230 个 → PC 至少 30 位。
  • A 错IR 只有 30 位装不下 32 位的指令;按字对齐能省的是 PC 的低 2 位,IR 要存整条指令,一位都不能省。
  • B 对PC 30 位、IR 32 位。
  • C 错PC 32 位、IR 30 位正好把两者弄反:IR 要装整条 32 位指令,PC 才能因按字对齐省掉最低 2 位。
  • D 错PC 32 位够用但不是“至少”:指令按字边界对齐,地址最低 2 位恒为 00,PC 只需 30 位。

“至少多少位”题:看有没有对齐——对齐到 2k 字节,地址最低 k 位恒为 0,可以省去。

MAR、MDR 装在哪?哪些寄存器程序员“看得见”

问题引入:图 1-3 把 MAR、MDR 画在 CPU 里,上面“打开主存看一看”的图却说主存由存储体、MAR、MDR 组成——它们到底归谁?再看 2010-18:四个选项都是 CPU 里的寄存器,为什么只有 PC 算“可见”?这两个问题其实在问同一件事:从哪个角度看。

打个比方

MAR、MDR 像银行柜台的“取号单”和“递物托盘”:按业务算,它们属于“柜台窗口”(主存系统);按摆放位置,现代银行把托盘装在了客户这一侧(CPU 芯片里),离客户更近、递得更快。不像的地方:托盘客户摸得到,MAR、MDR 程序员却摸不到——没有哪条指令能点名读写它们,它们只在每次访存时被硬件自动使用。

教材口径:冯·诺依曼机的教材模型里,主存储器由存储体、MAR、MDR 组成(另有地址译码器和读写控制电路);现代计算机中,MAR 和 MDR 通常集成在 CPU 芯片内,而地址译码器在存储器(内存芯片)一侧。题目问“主存的组成”时按教材模型答;问“现代计算机中 MAR、MDR 在哪个部件里”时答 CPU。

教材模型:按“功能”划分 CPU 运算器ALU、ACC… 控制器CU、PC、IR 主存储器 MAR MDR 地址译码器 存储体2ⁿ 个单元 地址 数据 现代计算机:按“芯片”位置 CPU 芯片 运算器ALU、寄存器 控制器CU、PC、IR 总线接口 MAR MDR 内存条 地址译码器 存储体+ 读写电路 地址总线 数据总线 逻辑上:MAR、MDR 是 CPU 与主存之间的“窗口”,教材把它们算作主存的组成部分 物理上:现代 CPU 把它们做在芯片内部;地址译码器始终在存储器一侧
图 1-5 MAR、MDR 的“逻辑归属”和“物理位置”。两种画法都对,只是角度不同。真题若按冯·诺依曼教材模型问主存由哪几部分组成,答“存储体、MAR、MDR”;若问现代计算机,答“MAR、MDR 在 CPU 中,译码器在主存中”。

“透明”与“可见”:说某个部件对某人透明,意思是“他看不见、也不用管”(和日常用语相反,1.4)。判断一个寄存器对汇编程序员是否可见,只问一句:有没有机器指令能读它、写它,或者把它的值当作执行的依据?有就可见。

寄存器存什么可见?理由(能被哪类指令用到)真题
PC下一条指令的地址可见转移、调用、返回指令就是在改写 PC;相对寻址以 PC 为基准2010-18
通用寄存器(R0…、eax…)操作数、结果可见指令里直接写出它的编号或名字2015-43
PSW / 标志寄存器ZF、CF、OF、SF 和状态位可见比较、算术指令设置标志,条件转移指令读标志2021-17 Ⅳ
基址、变址寄存器基地址、下标可见基址 / 变址寻址要在指令里指定用哪一个2021-17 Ⅲ
栈指针 SP栈顶地址可见push、pop、call、ret 都隐含地使用它,也能用 mov 改它第 3 篇
IR当前正在执行的指令不可见取指时由硬件自动装入,没有指令能读写它2021-17 Ⅰ
MAR、MDR访存地址 / 访存数据不可见每次访存由硬件自动使用2010-18
暂存器(ALU 输入端 Y、输出端 Z 等)中间结果不可见数据通路内部用的,第 4 篇单总线大题里常见2015-43
微指令寄存器 μIR、微地址寄存器当前微指令及其地址不可见在控制器内部,比机器指令还低一层(图 1-8 的 M0)2021-17 Ⅱ
Cache、TLB主存 / 页表内容的副本不可见由硬件自动管理(第 8 篇)—

真题怎么考:2010-18 四选一(只有 PC 可见);2021-17 组合题(基址寄存器、标志寄存器可见,IR、微指令寄存器不可见);2015-43(1)在单总线结构图里问“哪些寄存器程序员可见”,答通用寄存器 R0~R3 和 PC,暂存器 T、MAR、MDR、IR 都不可见。秒杀法:IR、MAR、MDR、各种暂存器、带“微”字的都不可见;指令里能写出名字的(Rx、SP、基址、变址)和被转移类指令读写的(PC、标志)都可见。口诀失效的情况:题目换了“对谁透明”的对象。对高级语言程序员,连通用寄存器、PC 也是透明的(变量放哪个寄存器由编译器决定);对操作系统内核,PSW 里的特权位(用户态 / 内核态、中断允许位)可以用特权指令改写(第 5 篇)。

易错

① PC 可见,但不一定能直接赋值:x86 没有 mov eip, … 这样的指令,PC“可见”是因为 jmp、call、ret 等指令就是在改它。② MAR、MDR 在 CPU 里,不等于它们可见:位置和可见性是两回事。③ 地址译码器在存储器里,不在 CPU 里。

自测
  1. 某 CPU 有 R0~R7、SP、PC、IR、MAR、MDR、暂存器 Y 和 Z、PSW、微地址寄存器。哪些对汇编程序员可见?
    答案
    R0~R7、SP、PC、PSW。其余(IR、MAR、MDR、Y、Z、微地址寄存器)都由硬件自动使用,不可见。
  2. 判断:“地址译码器在 CPU 中”“现代计算机的 MAR 在 CPU 中”。
    答案
    前者错,译码器在存储器一侧;后者对。
  3. 对 C 语言程序员来说,通用寄存器是透明的吗?
    答案
    是透明的:C 程序员写的是变量,变量放在哪个寄存器、什么时候放回内存,由编译器决定。

四种“宽度”:机器字长、指令字长、存储字长、数据总线宽度

问题引入:“32 位机”到底什么是 32 位?地址?指令?一次读内存的位数?其实这几个数在同一台机器上可以各不相同,真题就爱在这里设陷阱:2016-18 问 PC 和 IR 的位数,2020-12 问哪个部件的位数“一定”等于机器字长。

大白话:把 CPU 想成一个厨师。机器字长是他一刀能切多宽(ALU 一次算几位);指令字长是菜谱上一句话有多长;存储字长是仓库里一个格子有多大;数据总线宽度是传菜窗口一次能递出多宽的托盘。四样东西由不同的设计决定,所以不必相等。(不像的地方:厨师可以多切几刀;机器字长说的是一条整数运算指令一次处理的位数,更宽的数要拆成几条指令去算。)

名称教材定义决定谁的位数补充
机器字长CPU 一次整数运算所能处理的二进制位数ALU、通用寄存器“n 位机”说的就是它(2020-12)
指令字长一条指令包含的二进制位数IR(至少等于最长指令)可以定长或变长;指令字长 = 1 个机器字长叫单字长指令,2 个叫双字长指令(第 3 篇)
存储字长一个存储单元存放的二进制位数MDR按字编址时,一个地址对应一个存储字
数据总线宽度数据总线的根数,即一次能并行传送的位数一次总线传送的数据量也叫数据通路带宽(1.8);可以是存储字长的整数倍(第 8 篇交叉存储一次读多个字)
附:地址位数主存地址的二进制位数MAR、地址总线根数,PC 一般也等于它可寻址单元数 = 2地址位数
同一台机器上的几种“宽度”(下面例 2 的机器,条的长度 ∝ 位数) 机器字长 32 位 ALU、R0…R15 一条整数运算指令一次算 32 位 指令字长 16 / 32 位 短指令长指令 IR 至少 32 位(要装得下最长的指令) MAR 30 位 地址 最多 2³⁰ 个单元;按字节编址 → 1GB 存储字长 = MDR 64 位 一次访存读写 8 个字节 一个存储字 = 地址为 8 的倍数开头的 8 字节 数据总线 64 根 一个总线周期并行传 64 位 题目问哪个位数,就去找决定它的那个条件:问 IR 找指令字长,问 MDR 找存储字长,问 MAR 找地址空间。
图 1-6 四种“宽度”可以各不相同。这台机器是“32 位机”(机器字长 32),但一次访存却读 64 位、地址只有 30 位。只有 ALU 和通用寄存器的宽度一定等于机器字长(2020-12)。
可寻址单元数 = 2MAR 位数  主存容量 = 单元数 × 编址单位 按字编址:编址单位 = 存储字长 = MDR 位数 → 容量 = 2MAR × 存储字长 按字节编址:编址单位 = 1B → 容量 = 2MAR B;MDR 仍等于“一次访存读写的位数”,可以是 16、32、64 位

跟我做一遍(数字均已用 verify/08/p01_calc.py 验算):

例 1计组同样大的主存,换个编址方式
主存 512K × 32 位,按字编址。MAR、MDR 各几位?容量是多少字节?若容量不变、改为按字节编址,MAR 要几位?
展开解析
  1. 单元数:“512K × 32 位”的前半是单元个数 512K = 29 × 210 = 219 → MAR 19 位(依据:MAR 为 n 位时单元数 = 2n)。
  2. MDR:后半 32 位是每个单元的位数,即存储字长 → MDR 32 位。
  3. 容量:219 × 4B = 221 B = 2MB。
  4. 改按字节编址:每个字节都要一个地址,单元数变成 221 → MAR 21 位;容量还是 2MB,每次访存仍读写 32 位,MDR 仍是 32 位。
例 2计组四种宽度一起出现
某计算机机器字长 32 位,指令有 16 位和 32 位两种长度;主存按字节编址,地址线 30 根;数据总线 64 根,每次访存读写 8 字节。求:① 通用寄存器几位;② IR 至少几位;③ MAR 几位、主存最大容量;④ MDR 几位;⑤ 读一个 8 字节的 double,起始地址为 1000H 和 1004H 时各要几次总线传送?
展开解析
  1. 通用寄存器 = 机器字长 = 32 位。
  2. IR 要装下最长的指令 → 32 位(只看最长的,不看最短的)。
  3. MAR = 地址线根数 = 30 位;按字节编址,最大容量 230 B = 1GB。
  4. MDR = 一次访存读写的位数 = 64 位(比机器字长还宽,这是允许的)。
  5. 存储器一次读出的是“起始地址为 8 的倍数”的 8 个字节。1000H 是 8 的倍数 → 1 次;从 1004H 开始的 8 字节是 1004H~100BH,跨了 1000H~1007H 和 1008H~100FH 两个存储字 → 2 次。这就是第 2 篇要求“边界对齐”的原因。
例 3计组读题翻译:2015-43 的题干
“某 16 位计算机的主存按字节编址,存取单位为 16 位;采用 16 位定长指令字格式。”这句话给出了哪几个“宽度”?取完一条指令后 PC 加几?
展开解析
  1. “16 位计算机” → 机器字长 16 位(ALU、通用寄存器 16 位)。
  2. “按字节编址” → 编址单位 1B;“存取单位为 16 位” → 存储字长 16 位,MDR 16 位。
  3. “16 位定长指令字” → 指令字长 16 位,IR 16 位。
  4. 一条指令占 2 字节,按字节编址 → 取指后 PC + 2(不是加 1)。

真题怎么考:2016-18(PC 跟地址走,指令按 4 字节对齐时可去掉最低 2 位,所以 30 位;IR 跟指令字长走,32 位);2020-12(只有 ALU、通用寄存器“一定”等于机器字长);2011-15(MAR 位数看地址空间 64MB,不看实际装了 32MB)。方法失效的情况:“IR 位数 = 指令字长”对变长指令要取最长的;“PC 位数 = 地址位数”只是一般情况,题目问“至少”且指令对齐时,PC 可以去掉恒为 0 的低位。

自测
  1. 某机按字节编址,主存 256MB,存储字长 32 位。MAR、MDR 各几位?若改为按字(32 位)编址,MAR 几位?
    答案
    256MB = 228 B → 按字节编址 MAR 28 位;MDR 32 位。按字编址:228 ÷ 4 = 226 个字 → MAR 26 位。
  2. “64 位 CPU 的地址一定是 64 位”对吗?
    答案
    不对。机器字长和地址位数是两回事。例如 x86-64 处理器机器字长 64 位,但目前的虚拟地址一般只用 48 位,物理地址线也远少于 64 根。
  3. 机器字长 32 位、数据总线 16 位的机器,读一个 32 位整数至少要几次总线传送?
    答案
    2 次:每次只能并行传 16 位。早期的 Intel 8088 就是这种设计(内部 16 位、外部数据总线 8 位)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2016-18 改
某计算机主存空间为 1GB,按字节编址,采用 16 位定长指令字,指令按 2 字节边界对齐存放。程序计数器 PC 和指令寄存器 IR 的位数至少分别是( )。
  • A. 29、16
  • B. 30、16
  • C. 29、32
  • D. 30、32
展开解析

A1GB = 230B,地址 30 位。指令按 2 字节对齐,指令地址的最低位总是 0,不用存,PC 至少 29 位。IR 要放下一条完整的指令,16 位。

变化点:2016-18 是 4GB、32 位指令、按 4 字节对齐(PC 30 位、IR 32 位)。PC 位数 = 地址位数 − log2(对齐字节数),IR 位数 = 指令字长。

变式计组由 2010-18、2021-17 改
下列寄存器中,汇编语言程序员不可见的是( )。
Ⅰ. 程序计数器 PC Ⅱ. 存储器地址寄存器 MAR Ⅲ. 通用寄存器 Ⅳ. ALU 输入端的暂存器 Ⅴ. 程序状态字寄存器 PSW
  • A. 仅Ⅱ
  • B. 仅Ⅱ、Ⅳ
  • C. 仅Ⅰ、Ⅱ、Ⅳ
  • D. 仅Ⅱ、Ⅳ、Ⅴ
展开解析

BMAR、MDR、IR、暂存器是 CPU 内部自己用的,程序员看不见。PC(转移指令会改它)、通用寄存器、PSW(条件转移要用标志位)都是可见的。

变化点:两道真题问“可见的”,这里问“不可见的”,还加入了暂存器。判断标准:写机器语言程序时能不能直接用到或影响它。

1.3一条指令是怎么被执行的

★ 考过选择题

CPU 一辈子只做一件事,周而复始:

① 取指令按 PC 取到 IR;PC+“1” ② 译码CU 分析操作码 ③ 取操作数寄存器或内存 ④ 执行·写回ALU 算,结果存好 ⑤ 查中断请求有就去处理(第 5 篇) 下一条指令(PC 已经指向它;若是跳转指令,PC 已被改成目标地址) 一个“指令周期” = 取出并执行一条指令的全部时间,由若干个“时钟周期”组成
图 1-7 指令周期。注意 ⑤:CPU 在每条指令执行完之后才检查有没有中断请求。这一个小框,就是第 5 篇(中断)和第 9 篇(I/O)的起点。

用一个例子走一遍:C 语言 c = a + b; 编译后大约是三条指令:

0000H: LOAD  R1, [2000H]   ; 把内存 2000H 单元的数(a)读进 R1
0004H: ADD   R1, [2004H]   ; R1 加上内存 2004H 单元的数(b)
0008H: STORE R1, [2008H]   ; 把 R1 写回内存 2008H 单元(c)

开始时 PC = 0000H。取指:按 0000H 取出 LOAD 指令放进 IR,PC 变成 0004H(这台机器一条指令 4 字节、按字节编址,所以“加 1 条”就是加 4);译码发现是“读内存”;按 2000H 读出 a 放进 R1。然后按 PC=0004H 取下一条……一直这样下去。

把每一小步里各个寄存器装的是什么列成一张表(假设内存里 a = 5、b = 3)。第一次学一定要跟着表一行一行看,后面第 4 篇的“数据通路”大题就是把这张表写成控制信号:

阶段这一步做什么PCMARMDRIRR1
取指 1PC 的值送 MAR0000H0000H——?
按 MAR 读内存,读出的指令进 MDR;PC + 40004H0000HLOAD 指令—?
MDR 送 IR,交给 CU 译码0004H0000HLOAD 指令LOAD R1,[2000H]?
执行 1指令里的地址 2000H 送 MAR,读内存,数据 5 进 MDR0004H2000H5LOAD…?
MDR 送 R10004H2000H5LOAD…5
取指 2同上:MAR←0004H,读出 ADD 指令进 MDR 再进 IR,PC + 40008H0004HADD 指令ADD R1,[2004H]5
执行 2MAR←2004H,读出 3 进 MDR;ALU 算 R1 + MDR = 8,结果写回 R10008H2004H3ADD…8
取指 3MAR←0008H,取出 STORE 指令,PC + 4000CH0008HSTORE 指令STORE R1,[2008H]8
执行 3MAR←2008H,R1 的 8 送 MDR,发“写”命令,8 写进内存 2008H000CH2008H8STORE…8

从这张表可以直接读出几个考点:

  • MDR 在取指阶段装的是指令,在执行阶段装的是数据——同一个寄存器、同一种 0/1 串,身份只由“当时在哪个阶段”决定(这就是 2009-11 的答案 C)。
  • PC 在取指一结束就已经指向下一条,所以执行阶段看到的 PC 是“下一条指令的地址”。以后算相对转移(第 3 篇)、中断断点(第 5 篇)都要用这一点。
  • 每条指令至少访存一次(取指令本身);LOAD/ADD/STORE 这种带内存操作数的还要再访存一次,所以它们的指令周期比只用寄存器的指令长(2011-19)。

用教材的记号再写一遍(大题就这样作答)

上面的表是“看状态”,教材和真题答案更常用一种“看动作”的写法:每一步写成“谁 → 送到哪”。先认记号,一共就这几个:

记号读法
(PC) → MAR把 PC 的内容送进 MAR(圆括号 = “里面的内容”,第 0 篇 0.6)
M(MAR) → MDR按 MAR 里的地址读主存,读出的内容送进 MDR(M = 主存)
(MDR) → M(MAR)把 MDR 的内容写进 MAR 所指的主存单元(这是“写”)
(MDR) → IR取出的指令送进指令寄存器
OP(IR) → CUIR 里的操作码字段送控制单元译码
Ad(IR) → MARIR 里的地址码字段(形式地址)送 MAR
(PC) + 1 → PCPC 指向下一条指令(“+1”是加一条指令的长度,按字节编址、指令 4 字节时就是 +4)

教材的模型机是单累加器结构:指令里只写一个内存地址 X,另一个操作数默认在 ACC 里。三条最基本的指令从头到尾是这样执行的:

取数 LOAD X(ACC ← [X])
取指:(PC)→MAR;M(MAR)→MDR;(MDR)→IR;(PC)+1→PC
分析:OP(IR)→CU
执行:Ad(IR)→MAR;M(MAR)→MDR;(MDR)→ACC
存数 STORE X([X] ← ACC)
取指、分析:同左
执行:Ad(IR)→MAR;(ACC)→MDR;(MDR)→M(MAR)(发“写”命令)
加法 ADD X(ACC ← ACC + [X])
取指、分析:同左
执行:Ad(IR)→MAR;M(MAR)→MDR;(MDR)→X;(ACC)+(X)→ACC

c = a + b 在这台机器上就是 LOAD a;ADD b;STORE c——和前面表格里的三条指令一一对应(那里用的是通用寄存器 R1,这里用 ACC,道理一样)。

从这几行能读出的规律

① 取指阶段所有指令都一样,一定是“PC → MAR → 读 → MDR → IR”这四个动作,PC 自增可以和“读主存”同时做。② 读主存一定是“地址进 MAR、结果出现在 MDR”;写主存一定是“地址进 MAR、数据先进 MDR、再发写命令”——MAR、MDR 是 CPU 和主存之间唯一的“窗口”。③ 指令的操作码去 CU(决定做什么),地址码去 MAR(决定对谁做)。第 4 篇写控制信号的大题,就是把这里的每一个箭头换成对应的 Xout、Yin 信号。

计算机的工作过程(教材的三句话):① 用编译、链接等工具把源程序变成可执行文件;② 操作系统把程序和数据装入主存;③ CPU 从程序的首地址开始,周而复始地“取指令 → 分析 → 执行”,直到程序结束。①②在第 3 篇 3.5 细讲,③就是本节。

2011-19计组
假定不采用 Cache 和指令预取技术,且机器处于“开中断”状态,则在下列有关指令执行的叙述中,错误的是( )。
  • A. 每个指令周期中 CPU 都至少访问内存一次
  • B. 每个指令周期一定大于或等于一个 CPU 时钟周期
  • C. 空操作指令的指令周期中任何寄存器的内容都不会被改变
  • D. 当前程序在每条指令执行结束时都可能被外部中断打断
展开解析

考点1.3 指令的执行过程5.4 中断的检查时机

C哪怕是“什么都不做”的空操作指令,也要经过取指:取指后 PC 自动加上指令长度指向下一条——PC 是寄存器,它的内容变了。

  • A 对没有 Cache 和指令预取,每条指令至少要访存一次把指令本身取出来。叙述正确。
  • B 对一个指令周期由若干个时钟周期组成,至少一个。叙述正确。
  • C 错空操作指令执行后 PC 改变了(还有 IR 也装入了这条指令),所以“任何寄存器都不变”错误——本题选它。
  • D 对开中断时,CPU 在每条指令执行结束时检查中断请求,所以每条指令结束时都可能被打断。叙述正确。

取指一定会改变 PC 和 IR。“选错误的是”这类题,绿色底的那一项就是要选的错误叙述。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2011-19 改
在不采用 Cache 和指令预取的情况下,下列关于指令周期的叙述中,正确的是( )。
  • A. 所有指令的指令周期都相同
  • B. 每个指令周期的取指阶段都要访问主存
  • C. 每条指令都有间址周期
  • D. 每条指令执行完后一定会进入中断周期
展开解析

B取指令要按 PC 访问主存,所以每个指令周期至少访存一次。不同指令的指令周期长短不同;只有间接寻址的指令才有间址周期;只有检测到中断请求(且允许响应)时才进入中断周期。

变化点:2011-19 找错误叙述(空操作指令也会改变 PC)。这里把指令周期的几个组成部分放进选项,问正确的一项。

1.4从 C 程序到电路:计算机系统的层次

★★ 常考选择题
应用程序(微信、浏览器、你写的程序) 高级语言(C、Java) 汇编语言(mov、add、jmp 的助记符) 操作系统(Windows、Linux) 指令集体系结构 ISA / 机器语言软件和硬件的分界面 微体系结构(计算机组成)数据通路、控制器、流水线、Cache 逻辑电路与器件 ↓ 编译器翻译↓ 汇编器翻译 操作系统课 计组课(从 ISA 往下) 机器指令:CPU 唯一能直接执行的 微程序控制器里还有“微指令”(第 4 篇)
图 1-8 层次结构。每一层只需要懂下一层给它的“接口”。ISA(指令集体系结构)规定了指令有哪些、什么格式、有几个通用寄存器、多少位、怎么寻址——它是程序员(和编译器)能“看见”的机器。同一个 ISA 可以有不同的实现(微架构):用几级流水线、Cache 多大、乘法器怎么搭,都不归 ISA 管。
  • 编译程序:把高级语言一次性翻译成汇编/机器语言,生成目标文件;解释程序:翻一句执行一句,不生成目标文件;汇编程序:把汇编语言翻译成机器语言。
  • C 程序变成可执行文件要经过 预处理 → 编译 → 汇编 → 链接 四步(2022-20),第 3 篇详讲。
  • “透明”在计算机里的意思是看不见(与日常用法相反):对高级语言程序员,指令格式是透明的。
  • 软件和硬件在逻辑功能上等价:同一个功能既可以用硬件做,也可以用软件做。比如乘法,可以造一个乘法器(硬件,快、贵),也可以写一段“移位 + 加法”的程序(软件,慢、便宜,2020-43 就考这个)。把常用的软件固化在 ROM 里,叫固件(如 BIOS)。

教材的“五级”层次模型:实际机器与虚拟机器

图 1-8 是按“谁在用”画的。教材还有一种按“机器”画的五级模型:把每一层都看成一台能执行本层语言的“机器”,上层机器的程序由下层机器翻译或解释后执行。

M4 高级语言机器程序员用 C、Java 写程序 M3 汇编语言机器助记符 mov、add M2 操作系统机器机器指令 + 广义指令(系统调用) M1 传统机器(机器语言机器)执行二进制机器指令 M0 微程序机器硬件直接执行微指令 虚拟机器由软件实现 实际机器由硬件实现 编译程序把高级语言程序翻译成汇编 / 机器语言 汇编程序把汇编语言翻译成机器语言 广义指令由操作系统的机器语言程序实现 每条机器指令由一段微程序解释执行 (硬布线控制的机器没有 M0,机器指令直接由电路执行) 下层是上层的基础,上层是下层的扩展;每一层的用户只需要懂本层的“机器语言”
图 1-9 五级层次结构。M0、M1 是真实存在的硬件(实际机器);M2~M4 是靠软件“变”出来的虚拟机器。“翻译”是先把整个上层程序转换成下层程序再执行(编译、汇编);“解释”是下层一边逐条分析、一边执行上层的语句(微程序解释机器指令、解释程序执行脚本),不生成新的程序。

软件的分类、三种语言和三种“翻译程序”

计算机系统 = 硬件 + 软件。软件按用途分两类:

系统软件
保证计算机系统高效、正确运行的基础软件,一般由厂商提供:操作系统、数据库管理系统(DBMS)、语言处理程序(编译程序、汇编程序、解释程序)、链接程序、分布式软件系统、网络软件系统、标准程序库、服务性程序(诊断、调试程序)。
应用软件
为解决某个应用领域的问题而编写的程序:办公软件、浏览器、科学计算程序、游戏、你自己写的程序……
易错:DBMS 是系统软件;而“学生成绩管理系统”这种用 DBMS 做出来的程序是应用软件。
语言长什么样机器能直接执行吗可移植性
机器语言二进制代码能,唯一能直接执行的(2015-12)和具体机器绑定
汇编语言用助记符代替操作码(add、mov),用符号代替地址不能,要汇编和具体机器绑定(与机器语言一一对应)
高级语言C、Java、Python,接近自然语言和数学公式不能,要编译或解释与机器无关,可移植
程序把什么变成什么工作方式特点
汇编程序(汇编器)汇编语言源程序 → 机器语言目标程序整体翻译,生成目标程序与机器语言一一对应,翻译简单
编译程序(编译器)高级语言源程序 → 汇编语言或机器语言目标程序(2016-12)一次全部翻译完,生成目标程序,之后直接运行目标程序翻译一次、执行多次,执行快
解释程序(解释器)高级语言源程序 → 边翻译边执行翻译一句、执行一句,不生成目标程序每次运行都要重新翻译,执行慢

三者都是“语言处理程序”(王道把编译、解释都叫“翻译程序”,汇编程序也是一种翻译程序),区别在于是否生成目标程序:编译、汇编生成,解释不生成。硬件描述语言(Verilog 等)描述的是电路设计,不是交给 CPU 执行的程序(2015-12 Ⅲ)。

计算机体系结构 vs 计算机组成:ISA 管“有什么”,组成管“怎么做”

计算机体系结构(ISA)计算机组成(微架构)
定义机器语言程序员(和编译器)所看到的计算机的属性:概念性结构与功能特性如何实现体系结构所规定的属性,对程序员透明
包括指令系统(有哪些指令、指令格式、是否定长)、数据类型与格式、寻址方式、通用寄存器的个数和位数、存储器编址方式、I/O 指令、中断机制(如是否用向量中断)、虚拟存储管理方式数据通路结构(单总线还是专用通路)、控制器用微程序还是硬布线、流水线级数(是否超流水线)、Cache 大小、乘法器是阵列乘法器还是用加法器移位实现、加法器的进位方式、时钟周期
经典例子“这台机器有没有乘法指令”“乘法指令是用专用乘法器实现,还是用加法器多次相加实现”

判别口诀(下面 2022-18 解析里也有):写汇编程序、做编译器时需要知道 → ISA;不需要知道 → 组成。真题:2022-18(指令格式、通用寄存器个数属于 ISA)、2025-16(是否定长指令字属于 ISA;阵列乘法器、微程序控制器、单总线数据通路都属于组成)、2026-16(I/O 指令、向量中断、虚拟存储管理方式由 ISA 规定,“是否用超流水线”不是)、2026-12(微架构是 ISA 的逻辑实现,说它是“物理实现层”是错的;再往下的芯片工艺才是物理实现)。

2022-18计组
下列选项中,属于指令集体系结构(ISA)规定的内容是( )。Ⅰ. 指令字格式和指令类型 Ⅱ. CPU 的时钟周期 Ⅲ. 通用寄存器个数和位数 Ⅳ. 加法器的进位方式
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅰ、Ⅲ
  • C. 仅Ⅱ、Ⅳ
  • D. 仅Ⅰ、Ⅲ、Ⅳ
展开解析

考点1.4 ISA 与微架构

B判断方法:写汇编程序或编译器时需不需要知道它?需要 → 属于 ISA;只影响“怎么实现、实现得多快” → 属于计算机组成(微架构)。

  • Ⅰ 对指令字格式和指令类型:写汇编必须知道 → ISA。
  • Ⅱ 错CPU 的时钟周期:同一 ISA 的不同芯片主频可以不同 → 实现层面。
  • Ⅲ 对通用寄存器的个数和位数:程序要用寄存器 → ISA。
  • Ⅳ 错加法器的进位方式(串行/先行进位):程序员看不见 → 实现层面。

ISA = 程序员看得见的机器:指令、格式、寄存器、寻址方式、数据类型、I/O 与中断机制、虚存管理方式。

2024-13计组
将汇编语言程序中实现特定功能的指令序列定义成一条伪指令。下列选项中,CPU 能理解并直接执行的是( )。Ⅰ. 伪指令 Ⅱ. 微指令 Ⅲ. 机器指令 Ⅳ. 汇编指令
  • A. 仅Ⅰ和Ⅳ
  • B. 仅Ⅱ和Ⅲ
  • C. 仅Ⅲ和Ⅳ
  • D. 仅Ⅰ、Ⅲ和Ⅳ
展开解析

考点1.4 层次结构4.3 微程序控制

B“CPU 能理解并直接执行”的,是硬件能直接识别的东西:机器指令(由 CPU 执行)和微指令(由控制器内部的硬件执行)。

  • Ⅰ 错伪指令:只是给汇编器看的“宏”,要由汇编器展开、翻译成机器指令。
  • Ⅱ 对微指令:微程序控制器把一条机器指令拆成若干条微指令,由控制存储器中取出直接执行。
  • Ⅲ 对机器指令:CPU 直接取出、译码、执行。
  • Ⅳ 错汇编指令:要经汇编器翻译成机器指令后才能执行。

硬件只认 0/1 形式的机器指令(及控制器内部的微指令);汇编、伪指令、高级语言都要先翻译。

同一个 ISA,多种实现:系列机与兼容

问题引入:同一个 Windows 安装包,装在 Intel 的 CPU 上能跑,装在 AMD 的 CPU 上也能跑;可苹果电脑从 Intel 芯片换成自家的 M 系列芯片后,老软件却要经过一个叫 Rosetta 2 的程序“翻译”才能运行。区别就在于:前者 ISA 相同,后者 ISA 变了。

打个比方

ISA 像餐馆的菜单,微架构像后厨。两家连锁店用同一份菜单,后厨可以完全不同(一家用燃气灶、一家用电磁炉,一家 5 个厨师、一家 10 个),顾客照着菜单点菜都能吃到同样的菜;换了一份菜单,老顾客点的菜就没有了,得有人帮他“翻译”成新菜单上的菜。不像的地方:菜做得快慢顾客能直接感觉到;而 CPU 的微架构差异只会体现为“同一个程序跑得快慢不同”,运行结果必须完全一样。

教材说法:系列机是同一厂家生产的、具有相同的体系结构(ISA)、但组成和实现不同的一系列机器(经典例子是 IBM System/360 系列)。新机型要能运行旧机型上的软件,叫软件兼容(其中“新机器能运行老机器上的软件”叫向后兼容)。兼容的前提就是 ISA 相同或在原 ISA 上只增不改。

同一份 x86-64 机器码(Windows、各种软件) x86-64 指令集体系结构指令、寄存器、寻址方式都一样 Intel 酷睿流水线、Cache、乘法器各有设计 AMD 锐龙另一套微架构(计算机组成不同) 同一份 ARM(AArch64)机器码(手机 App 等) ARMv8 / ARMv9 指令集体系结构另一份“菜单” 苹果 M 系列微架构 1 高通骁龙微架构 2 华为鲲鹏微架构 3 ✗ 左右两边:ISA 相同 → 同一份机器码直接运行(只是快慢不同) 跨过红线(ISA 不同):要么重新编译,要么用模拟器 / 二进制翻译把一种 ISA 的指令变成另一种(2025-24 D)
图 1-10 ISA 是“合同”,微架构是“履约方式”。指令集相同,机器码就通用;微架构(流水线级数、Cache 大小、乘法器怎么做)只影响速度和成本,对程序员透明(1.4 上表)。
发生了什么变化例子旧程序还能直接运行吗
只换微架构,ISA 不变新一代 CPU 加深流水线、加大 Cache、改用更快的乘法器能,而且通常更快——这正是“组成对程序员透明”的好处
ISA 扩展(只增不改)x86 先后加入 MMX、SSE、AVX 等新指令旧程序能在新 CPU 上运行;用了新指令的新程序在旧 CPU 上不能
换 ISAx86 → ARM不能直接运行,要重新编译,或用模拟器 / 二进制翻译(性能有损失)

真题怎么考:本节的 2022-18、2025-16、2026-16、2026-12 都在考“某项属于 ISA 还是属于组成(微架构)”。判别法仍是“写机器语言程序时需不需要知道”。容易判反的一对:“有没有乘法指令”是 ISA;“乘法指令用阵列乘法器还是用加法器加移位实现”是组成。

自测
  1. 同一厂家先后推出的 A、B 两款 CPU 属于同一系列机。下列哪项一定相同:主频、Cache 容量、指令系统、流水线段数?
    答案
    指令系统(ISA)。系列机的定义就是体系结构相同、组成与实现不同;主频、Cache 容量、流水线段数都属于组成或实现。
  2. 为什么在 x86 电脑上编译出的 .exe 文件,不能直接拷到 ARM 手机上运行?
    答案
    .exe 里是 x86 的机器指令,ARM 处理器的 ISA 不同,看不懂这些指令的编码。要在 ARM 上运行,必须重新编译,或者用模拟器 / 二进制翻译软件把 x86 指令转换成 ARM 指令。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2022-18、2025-16 改
下列选项中,属于指令集体系结构(ISA)规定的内容是( )。
Ⅰ. 程序员可以使用的通用寄存器个数 Ⅱ. 主存按字节编址还是按字编址
Ⅲ. Cache 采用几路组相联 Ⅳ. 是否采用 5 段指令流水线
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅰ、Ⅲ
  • C. 仅Ⅱ、Ⅳ
  • D. 仅Ⅰ、Ⅱ、Ⅲ
展开解析

AISA 规定的是机器语言程序员看得见的东西:指令格式和种类、寄存器、寻址方式、编址方式、数据类型、I/O 与中断机制等,所以Ⅰ、Ⅱ属于 ISA。Cache 的组织方式和流水线的段数属于计算机组成(微架构),换一种实现,程序照样能运行。

变化点:换了考法:真题问“指令格式、时钟周期、加法器进位方式”,这里问“编址方式、Cache、流水线”。判断标准不变:程序员写机器语言程序时是否必须知道。

变式计组由 2015-12、2024-13 改
下列关于翻译程序的叙述中,错误的是( )。
  • A. 编译程序把高级语言源程序翻译成目标程序
  • B. 汇编程序把汇编语言程序翻译成机器语言程序
  • C. 解释程序逐条翻译并执行源程序,不生成目标程序
  • D. 汇编语言程序可以被 CPU 直接执行
展开解析

DCPU 能直接执行的只有机器指令(微程序控制的机器还要执行微指令)。汇编语言程序要先由汇编程序翻译成机器语言,所以 D 错。

变化点:真题问“哪些能被直接执行”,这里反过来问翻译程序的分工:编译、汇编生成目标程序,解释程序不生成目标程序。

1.5为什么需要操作系统:管家、接口、魔术师

★ 考过选择题

先想象一台没有操作系统的电脑。你写了一个程序,想把 “hello” 存进磁盘里的一个文件,你得自己搞清楚:这块硬盘的控制器有哪些寄存器、往哪个寄存器写什么命令能让磁头移到第几号磁道、哪些扇区是空的、写完了怎么知道写好了……每个程序都要把这些重写一遍。更糟的是,如果同时跑两个程序,它们都认为“整块内存、整个磁盘都是我的”,互相覆盖对方的数据,谁都跑不对。

操作系统(OS)就是为了解决这两件事而装在硬件之上的一层管理软件:把难用的硬件包装成好用的服务,让多个程序安全地共用一台机器。从三个角度看它:

① 资源的管理者
管 CPU(进程调度)、内存(分配回收)、文件、设备。对应第 6~10 篇。
② 用户和硬件之间的接口
命令接口(敲命令、点图标)和程序接口——即系统调用,程序通过它请 OS 办事。
③ 扩充机器(虚拟机器)
把难用的裸机包装成好用的“虚拟机器”:程序以为自己独占 CPU、独占大内存。
定义(选择题照着它判断)

操作系统是指控制和管理整个计算机系统的硬件与软件资源,合理地组织、调度计算机的工作与资源的分配,进而为用户和其他软件提供方便的接口与环境的程序集合。它是计算机系统中最基本的系统软件。
没有任何软件支持的计算机叫裸机;装上操作系统后,用户看到的是一台功能更强、更好用的“扩充机器”(虚拟机器)。

作为资源管理者:四大功能分别管什么

功能具体要做的事本讲义
处理机管理多道程序环境下,处理机的分配和运行都以进程(或线程)为基本单位,所以处理机管理也叫进程管理:进程控制(创建、撤销、状态转换)、进程同步、进程通信、死锁处理、处理机调度第 6、7 篇
存储器管理内存分配与回收、地址映射(逻辑地址 → 物理地址)、内存保护与共享、内存扩充(覆盖、交换、虚拟内存)第 8 篇
文件管理文件存储空间管理(空闲块)、目录管理(实现“按名存取”)、文件读写管理和保护、文件共享第 10 篇
设备管理缓冲管理、设备分配、设备处理(驱动程序)、虚拟设备(SPOOLing)、设备独立性(程序用逻辑设备名,不依赖具体的物理设备);目标是完成用户的 I/O 请求、提高设备利用率第 9 篇

操作系统的目标

方便性
提供命令接口、程序接口,用户不必了解硬件细节就能用计算机。
有效性
提高 CPU、内存、I/O 设备等资源的利用率,提高系统吞吐量。
可扩充性
方便增加新功能、支持新硬件:这是 OS 结构从无结构 → 模块化 → 分层 → 微内核演变的动力(1.7)。
开放性
遵循国际标准,不同厂家的硬件和软件能彼此兼容、互相连接。

其中方便性和有效性是设计操作系统时最重要的两个目标。

给“人”用:命令接口 联机命令敲一句执行一句 脱机命令批处理:一次交一张命令清单 图形界面点图标、菜单(最终也调系统调用) 给“程序”用:程序接口 = 系统调用 你的程序printf("hi"); C 库函数 printf格式化字符串……最后调用 write() 用户态 ↑内核态 ↓(第 5 篇) 系统调用(陷入) 命令解释程序代为调用 操作系统内核 进程管理(第 6、7 篇) 内存管理(第 8 篇) 设备管理(第 9 篇) 文件管理(第 10 篇)
图 1-11 操作系统的两类接口。人通过命令接口用它(联机命令接口 = 交互式,脱机命令接口 = 批处理用的“作业说明书”);程序通过程序接口用它,程序接口就是一组系统调用。库函数不是操作系统的接口:printf 是用户态的一段普通代码,它做完字符串格式化以后,才通过 write 这个系统调用真正请操作系统把字符送到屏幕(2010-23)。
2010-23操作系统
下列选项中,操作系统提供给应用程序的接口是( )。
  • A. 系统调用
  • B. 中断
  • C. 库函数
  • D. 原语
展开解析

考点1.5 操作系统的接口5.6 系统调用

A操作系统给程序用的接口就是系统调用(程序接口);给人用的是命令接口和图形界面。

  • A 对系统调用:程序通过陷入指令请操作系统办事,例如读文件、创建进程。
  • B 错中断:是 CPU 和外设之间的一种硬件机制,不是提供给应用程序调用的接口。
  • C 错库函数:属于用户态的程序代码(如 printf),它内部再去调用系统调用;库函数由编程语言的库提供,不是操作系统的接口。
  • D 错原语:操作系统内部执行时不可被打断的小程序,应用程序不能直接调用。

OS 的接口 = 命令接口(给人)+ 程序接口(系统调用,给程序)。

四种接口放在一起对比(选择题常问“哪个是给程序用的”):

接口怎么用例子
联机命令接口
交互式命令接口
用户在终端上敲一条命令,命令解释程序执行完返回结果,再等下一条——“说一句,做一句”Linux shell 里的 ls、cd;Windows 的 cmd
脱机命令接口
批处理命令接口
用户事先用作业控制语言写好一份“作业说明书”,和作业一起交给系统;系统按说明书逐条执行,用户中途不能干预早期批处理的作业控制语言;今天的 .bat、shell 脚本
程序接口
= 系统调用(广义指令)
只能在程序里使用:程序执行陷入指令进入内核,请求操作系统服务(第 5 篇 5.6)read、write、fork、open
图形用户界面 GUI用窗口、图标、菜单、鼠标操作;最终也是通过调用系统调用来实现功能Windows 桌面

操作系统的四个特征

先把最容易混的一对词分清楚:并发是“一段时间里都在推进”,并行是“同一时刻真的都在做”。

① 单核 CPU:并发(宏观上同时推进,微观上交替执行) CPU A B C A B C 任一时刻只有一个在跑,但 1 秒内三个都有进展 ② 双核 CPU:并行(同一时刻真的同时执行) 核 0核 1 A C B 这一刻 A、B 同时在执行 ③ 单核也有并行:CPU 和 I/O 设备各干各的 CPU磁盘 CPU 在执行 A 的指令 同一段时间里,磁盘在为 B 读数据 设备有自己的控制电路,不需要 CPU 一直盯着
图 1-12 并发与并行。单核上的进程与进程之间只能并发;CPU 与设备、设备与设备之间可以并行(2009-23)。“多任务操作系统具有并发和并行的特点、需要保护共享资源,但不一定要多 CPU”(2018-23)。
特征意思要注意(常考)
并发两个或多个事件在同一时间间隔内发生。单 CPU 上宏观同时、微观交替OS 的并发是靠进程实现的——引入进程的目的就是让程序能并发执行(第 6 篇)。对比并行:同一时刻同时发生,需要多套硬件
共享系统中的资源可供内存中多个并发执行的进程共同使用互斥共享:一段时间内只允许一个进程访问,如打印机、磁带机、某些共享变量——这类资源叫临界资源(第 7 篇);同时共享:允许一段时间内多个进程“同时”访问,如磁盘上的可重入代码、文件(微观上仍可能交替)
虚拟把一个物理上的实体变成若干个逻辑上的对应物时分复用:虚拟处理器(多道 + 分时,一个 CPU 当 n 个用)、虚拟设备(SPOOLing,一台打印机当多台用,第 9 篇);空分复用:虚拟存储器(第 8 篇)。若一台物理 CPU 被时分复用成 n 台虚拟处理器,每台虚拟处理器的平均速度 ≤ 物理 CPU 的 1/n(虚拟设备同理)
异步多道程序环境下,进程以不可预知的速度向前推进,走走停停但只要运行环境相同,同一个作业多次运行的结果相同(由同步互斥机制保证,第 7 篇)

四个特征的关系:并发和共享是最基本的两个特征,而且互为存在条件——没有并发,就只有一道程序在用资源,谈不上共享;反过来,若不能对共享资源进行有效管理,并发的程序就会互相干扰,并发也无法实现。虚拟以并发和共享为前提,异步是并发的必然结果。(有的教材只说“并发是最基本的特征”,单选题问“最基本的特征”时选并发。)

2009-23操作系统
单处理机系统中,可并行的是( )。Ⅰ. 进程与进程 Ⅱ. 处理机与设备 Ⅲ. 处理机与通道 Ⅳ. 设备与设备
  • A. Ⅰ、Ⅱ、Ⅲ
  • B. Ⅰ、Ⅱ、Ⅳ
  • C. Ⅰ、Ⅲ、Ⅳ
  • D. Ⅱ、Ⅲ、Ⅳ
展开解析

考点1.5 并发与并行1.6 多道程序

D“并行”是同一时刻真的同时在做,需要多套硬件各干各的;单处理机只有一个 CPU,同一时刻只能运行一个进程。

  • Ⅰ 错进程与进程:都要用唯一的 CPU,只能轮流(并发),不能并行。
  • Ⅱ 对处理机与设备:CPU 算的时候,磁盘、打印机靠自己的控制电路同时工作。
  • Ⅲ 对处理机与通道:通道是专管 I/O 的处理器,和 CPU 同时工作。
  • Ⅳ 对设备与设备:打印机打印时磁盘也可以同时读写。

单 CPU:进程之间只能并发;CPU 与 I/O、I/O 与 I/O 之间可以并行。

共享的两种方式、虚拟的两种技术:配例子看懂

问题引入:上表说共享分“互斥共享”和“同时访问”,虚拟靠“时分复用”和“空分复用”。同样是很多进程一起用,为什么打印机只能互斥共享,磁盘却可以“同时”访问?虚拟内存和虚拟打印机又为什么是两种不同的技术?

打个比方

互斥共享像复印店的一台复印机:一个人的一整份材料印完,下一个人才能接着印,中途插队两份材料就混在一起了。同时访问像食堂打菜窗口:大家排队一勺一勺地打,一段时间里每个人都打到了菜,感觉是“一起在用”。时分复用像一位老师在三个班轮流上课(按时间切开);空分复用像把一块地划成三块分给三户人家(按空间切开)。不像的地方:食堂窗口每次服务的“一勺”很小,磁盘每次服务的是“一次读写请求”,也很小——同时访问能成立,恰恰因为交替的粒度小、交替了也不会出错。

教材定义:互斥共享方式——一段时间内只允许一个进程访问该资源(这样的资源叫临界资源,如打印机、磁带机,第 7 篇);同时访问方式——允许在一段时间内由多个进程“同时”访问(宏观上同时,微观上可能交替),如磁盘、用可重入代码编写的程序。时分复用技术——利用资源在时间上的空闲为多个用户服务,如虚拟处理器、虚拟设备(SPOOLing);空分复用技术——利用存储器的空闲空间分区域存放多道程序,如虚拟存储器、虚拟磁盘。

① 互斥共享:打印机 P1P2 P1 打印整份文件 等待(阻塞) P2 打印 按“一整份作业”轮流,中途不能插入(临界资源) ② 同时访问:磁盘 磁盘 P1 P2 P1 P2 P2 P1 每个小块 = 一次读写请求,P1、P2 的请求交替被服务 一段时间里两个进程都在用:宏观同时、微观交替 ③ 时分复用:虚拟处理器、虚拟设备 CPU A B C A B C 1 个物理 CPU 按时间片轮流给 A、B、C → 3 台“虚拟处理器” 每台的平均速度 ≤ 物理 CPU 的 1/3(还要扣掉切换开销) SPOOLing 把 1 台打印机“变成”多台,也是时分复用(第 9 篇) ④ 空分复用:虚拟存储器、虚拟磁盘 内存 OS A B C 磁盘 C 盘 D 盘 E 盘 按空间切成几份、各用各的:内存分给多个进程, 一块硬盘分成几个逻辑盘;虚拟存储器借磁盘空间扩充内存
图 1-13 共享的两种方式(上)与虚拟的两种技术(下)。上面两格的区别在交替的粒度:打印机必须按“整份作业”轮流,磁盘可以按“一次请求”交替。下面两格的区别在切什么:时分复用切时间,空分复用切空间。
说法意思例子详见
互斥共享一段时间内只允许一个进程用,用完才轮到下一个打印机、磁带机、多个进程都要修改的共享变量(临界资源)第 7 篇
同时访问一段时间内允许多个进程交替访问,宏观上同时磁盘、可重入代码(多个进程共用一份只读的程序代码)第 9、10 篇
时分复用同一个资源按时间轮流给多个用户用虚拟处理器(分时)、虚拟设备(SPOOLing)第 6、9 篇
空分复用同一个资源按空间分成几份分别给多个用户用内存划分给多个进程、虚拟存储器、虚拟磁盘第 8 篇

真题怎么考:408 还没有单独考过这四个词,但“操作系统特征”一类组合题(2009-23、2018-23)的选项随时可能换成它们。判别法:能不能中途插进别的进程?不能 → 互斥共享;能 → 同时访问。分的是时间还是空间?时间 → 时分复用;空间 → 空分复用。失效的情况:同一个资源换一种用法,方式就会变——一个文件被多个进程只读时是同时访问,有进程要写它时就要互斥。

易错

① “同时访问”不是物理上的同一瞬间:磁头一次只能读一个地方,只是请求交替得很快。② 互斥共享也是共享:多个进程都能用,只是要轮流、而且每人一次用完一整段。③ 时分复用得到的 n 台虚拟设备,每台都不比原来的物理设备快。

自测
  1. 打印机、磁盘、多个进程共用的只读库代码、多个进程都要加 1 的全局计数器,各属于哪种共享方式?
    答案
    打印机:互斥共享;磁盘:同时访问;只读库代码:同时访问;要被修改的全局计数器:互斥共享(第 7 篇的临界资源)。
  2. 虚拟存储器、SPOOLing、分时系统的“每人一台虚拟机”各用了哪种复用技术?
    答案
    虚拟存储器:空分复用;SPOOLing:时分复用;分时系统的虚拟处理器:时分复用。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2009-23 改
单处理机系统中,下列可以并行(同一时刻同时进行)的是( )。
Ⅰ. 两个进程的计算 Ⅱ. CPU 计算与打印机打印 Ⅲ. 磁盘的 DMA 传送与 CPU 计算 Ⅳ. 两个进程分别使用两台不同的设备做 I/O
  • A. 仅Ⅱ、Ⅲ
  • B. 仅Ⅱ、Ⅲ、Ⅳ
  • C. 仅Ⅰ、Ⅱ、Ⅲ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

B只有一个 CPU,两个进程的计算只能交替进行(并发),不能并行。CPU 与设备、设备与设备可以同时工作。

变化点:2009-23 的选项是抽象的“进程与进程、处理机与设备……”,这里换成具体场景。核心仍是:单 CPU 上进程只能并发,硬件部件之间可以并行。

变式操作系统由 2010-23、2018-23 改
下列关于操作系统接口和特征的叙述中,正确的是( )。
  • A. 库函数是操作系统提供给应用程序的接口
  • B. 命令接口和系统调用都属于操作系统提供的用户接口
  • C. 单 CPU 的多任务系统中,多个进程可以并行执行
  • D. 原语是应用程序可以直接调用的接口
展开解析

B操作系统向用户提供命令接口(给用户直接用)和程序接口(系统调用,给程序用)。A 错:库函数属于语言或库,不是操作系统接口(2010-23)。C 错:单 CPU 只能并发。D 错:原语是内核内部使用的,应用程序不能直接调用。

变化点:把 2010-23(接口)和 2018-23(并发与并行)的考点放在同一道题里。

变式操作系统考纲补充(尚无真题)
下列关于操作系统共享方式和虚拟技术的叙述中,正确的是( )。
  • A. 磁盘只能以互斥共享方式被多个进程使用
  • B. 虚拟存储器采用的是时分复用技术
  • C. SPOOLing 技术利用时分复用,把一台独占设备变成多台逻辑设备
  • D. 采用时分复用后,每台虚拟处理器的速度都高于物理处理器
展开解析

CSPOOLing 让多个进程轮流使用同一台打印机(借助磁盘上的输出井),每个进程都以为自己有一台打印机,这是按时间轮流的时分复用。A 错:磁盘允许多个进程的请求交替访问,是同时访问方式。B 错:虚拟存储器借用磁盘空间、把内存按空间分给多个进程,是空分复用。D 错:n 台虚拟处理器分一台物理 CPU,每台平均速度不超过物理 CPU 的 1/n。

考点:共享的两种方式、虚拟的两种技术。判别时问两句:能不能中途插进别的进程?分的是时间还是空间?

1.6操作系统怎么一步步发展出来

★ 考过选择题

整个发展史就是一个问题:CPU 太快、I/O 太慢,怎么别让 CPU 闲着?

单道批处理:内存里只有一道程序 CPUI/O A 计算 A 输入输出 CPU 空等 A 计算 B 计算 B 输入输出 CPU 空等 多道批处理:内存里放多道程序,A 去做 I/O 时 CPU 立刻跑 B CPUI/O A 计算 A 输入输出 B 计算 A 计算 B 输入输出 全部完成(单道要到右上虚线处)
图 1-14 多道程序设计。A 发起 I/O 后就让出 CPU,B 趁机计算;A 的 I/O 完成时设备发一个中断通知 CPU。CPU 和 I/O 设备并行工作,CPU 利用率、I/O 设备利用率、系统吞吐量都提高,代价是系统要管理多道程序,开销变大(2017-28)。
阶段怎么工作特点(选择题原词)解决了什么 / 留下什么问题
手工操作程序员把穿孔纸带装上机器,亲自操作用户独占全机;CPU 等待人工操作人机矛盾:人慢机器快,CPU 大量时间在等人
单道批处理一批作业先存到磁带上,监督程序把它们一个接一个自动调入内存运行自动性、顺序性、单道性(内存中始终只有一道作业)缓解了人机矛盾;但作业做 I/O 时 CPU 仍在空等
多道批处理内存同时放多道作业,一道去做 I/O 就让 CPU 运行另一道(靠中断技术)多道;宏观上并行,微观上串行资源利用率高、系统吞吐量大;但用户响应时间长、没有人机交互(作业提交后无法干预)
分时系统CPU 时间切成很短的时间片,轮流为多个终端用户服务同时性(多路性)、交互性、独立性、及时性解决了人机交互;但不能优先处理紧急任务
实时系统在严格的时间限制(截止时间)内完成对外部事件的响应和处理及时性、可靠性硬实时必须绝对按时(导弹控制、工业控制);软实时偶尔超时可以接受(视频播放、网上订票)
网络操作系统把网络中各台计算机有机结合起来,提供统一、经济而有效的使用方式网络中资源的共享和各种通信各台计算机仍各自完成自己的任务,只是能共享资源、互相通信
分布式操作系统多台计算机组成一个整体系统,一个任务可以分布到多台机器上协同完成分布性和并行性;各台计算机地位平等、无主从与网络 OS 的本质区别:多台计算机协同完成同一个任务,对用户呈现为“一台机器”
个人计算机 OSWindows、Linux、macOS单用户多任务,界面友好

分时和实时都“响应快”,区别在于:分时追求让每个用户都及时得到响应(秒级),交互性强;实时追求在截止时间内一定完成(毫秒甚至微秒级),可靠性要求更高、交互性弱。

多道批处理让 CPU 忙起来了,但坐在终端前的人很难受:提交一个作业,要等它排队、跑完才看到结果,中间想改一个参数都不行。分时系统换了个思路:不等程序自己去做 I/O 才换人,而是按时间强行轮换——

分时:CPU 时间切成很短的“时间片”(例如 20ms),终端 A、B、C 轮流用 A B C A B C A B C CPU 1 个时间片 A 等一轮 = 60ms,人根本感觉不到 …… 时间片到了,由时钟中断把 CPU 从当前程序手里“拿回来”交给下一个——这是第 5 篇中断、第 6 篇时间片轮转调度的原型。 四个特点:同时性(多个用户同时上机)、交互性、独立性(彼此不干扰)、及时性(很快得到响应)。
图 1-15 分时系统。和多道批处理的区别:批处理是“程序自己去做 I/O 才让出 CPU”,目标是吞吐量;分时是“时间一到就强制换人”,目标是响应快、能交互。实时系统又不一样,它追求的是“在截止时间之前一定完成”,比如导弹控制晚 1 毫秒都不行(硬实时),视频偶尔卡一帧可以接受(软实时)。

多道程序能成立要靠两样硬件:中断(I/O 做完了设备主动通知 CPU,CPU 不用守着等)和通道/DMA(数据搬运不用 CPU 一个字一个字搬)。这两样都在第 9 篇。

多道到底省了多少:动手算一次

图 1-14 只是示意。考研题会给出具体的时间让你算“总共用多久、CPU 利用率多少”,方法就是画时间轴(甘特图)。先看一个例子:

例操作系统单道 vs 多道
单 CPU 系统中有两道程序,A 先开始:A 计算 10ms → I/O 40ms → 计算 10ms;B 计算 30ms → I/O 20ms → 计算 20ms。A、B 使用不同的 I/O 设备,不计切换开销。分别求单道和多道(A 做 I/O 时 CPU 立刻运行 B)两种方式下,两道程序全部完成所需的时间和 CPU 利用率。
展开解析
单道:A 全部做完再做 B CPUI/O A CPU 空闲 A B 空闲 B A 的 I/O B 的 I/O 010506090110130 多道:A 去做 I/O,CPU 马上运行 B CPU设备 1设备 2 A B 空 A B A 的 I/O B 的 I/O 01040506080 全部完成,比单道早 50ms 时间轴单位 ms。多道时 CPU 只在 40~50ms 空闲(B 在做 I/O,A 的 I/O 还没完)。
  1. 单道:A 用 10+40+10 = 60ms,B 用 30+20+20 = 70ms,总共 130ms;CPU 真正在算的时间 = 10+10+30+20 = 70ms,利用率 = 70/130 ≈ 53.8%。
  2. 多道:0~10 A 算;10 时 A 去 I/O(到 50),CPU 运行 B 到 40;40 时 B 去 I/O(到 60),CPU 没有就绪的程序,空闲到 50;50 时 A 回来算到 60,A 结束;60 时 B 的 I/O 完成,算到 80。总共 80ms,CPU 利用率 = 70/80 = 87.5%。
  3. 结论:CPU 要做的工作量(70ms)一点没少,省下的是“空等”的时间,所以多道提高的是资源利用率和系统吞吐量。对单个作业来说,它自己要用的 CPU 时间、I/O 时间一点没变,还可能因为要等别人让出 CPU 而完成得更晚(比如 A 的 I/O 在 50ms 结束时,如果 B 还占着 CPU,A 就得排队)——这就是多道批处理“平均周转时间长”的原因。
CPU 利用率 = CPU 忙的时间 ÷ 总时间  吞吐量 = 完成的作业数 ÷ 总时间

若两道程序用同一台 I/O 设备,I/O 也要排队(设备同一时刻只能服务一个),画图时给设备也单独画一行,按先来先服务排。真题 2012-29 就是这种题(第 6 篇 6.7 有完整甘特图,答案 260ms)。

2016-23操作系统
下列关于批处理系统的叙述中,正确的是( )。Ⅰ. 批处理系统允许多个用户与计算机直接交互 Ⅱ. 批处理系统分为单道批处理系统和多道批处理系统 Ⅲ. 中断技术使得多道批处理系统的 I/O 设备可与 CPU 并行工作
  • A. 仅Ⅱ、Ⅲ
  • B. 仅Ⅱ
  • C. 仅Ⅰ、Ⅱ
  • D. 仅Ⅰ、Ⅲ
展开解析

考点1.6 批处理系统

A批处理:用户把作业交上去,系统成批自动运行,用户中途不能干预。

  • Ⅰ 错批处理最大的缺点就是缺少交互性;允许多个用户与计算机直接交互的是分时系统。
  • Ⅱ 对批处理系统分为单道批处理和多道批处理。
  • Ⅲ 对有了中断,I/O 完成时设备主动通知 CPU,CPU 不必守着等,才能在 I/O 期间去运行其他程序——多道批处理中 CPU 与 I/O 设备并行工作。

批处理 → 提高吞吐量、无交互;分时 → 交互、及时响应;实时 → 截止时间内完成。

2022-23操作系统
下列关于多道程序系统的叙述中,不正确的是( )。
  • A. 支持进程的并发执行
  • B. 不必支持虚拟存储管理
  • C. 需要实现对共享资源的管理
  • D. 进程数越多 CPU 利用率越高
展开解析

考点1.6 多道程序8.7 抖动

D进程并不是越多越好:内存有限,进程太多每个分到的页框就少,频繁缺页甚至“抖动”,CPU 忙着等 I/O,利用率反而下降。

  • A 对多道程序系统的目的就是让多个进程并发执行。叙述正确。
  • B 对早期多道系统把整个程序装入内存也能多道,不一定要有虚拟存储。叙述正确。
  • C 对多个进程共享 CPU、内存、设备,必须管理共享资源。叙述正确。
  • D 错“进程数越多 CPU 利用率越高”错误——本题选它。

多道度要适中:太少 CPU 闲,太多会抖动。

分时与实时、网络 OS 与分布式 OS:两组最容易混的分类

问题引入:分时和实时都说“响应快”;网络操作系统和分布式操作系统都是“很多台计算机连在一起”。选择题最爱把这两组的说法互换,考你分不分得清。

打个比方

分时像餐馆服务员在几桌客人之间来回照应,让每桌都觉得“马上有人理我”;实时像急救中心,电话一来必须在规定时间内出车,晚到就是事故。网络 OS 像几户邻居,各过各的日子,但能互相借东西、打电话;分布式 OS 像几户人家合成一个大家庭,一件大事分给几个人一起干,外人看来就是“一户”。不像的地方:实时系统不一定比分时系统“快”,它的本质是保证在截止时间前完成——截止时间是 1 秒,它就保证 1 秒内完成。

分时系统实时系统
追求什么每个用户都能及时得到响应、能交互在截止时间之前一定完成对外部事件的处理
响应时间由谁定人能接受的等待时间(通常秒级)被控制的对象(常常是毫秒甚至微秒级)
交互性强,用户随时可以输入命令弱,主要响应外部事件,只提供有限的交互
可靠性要求一般很高,常采用冗余、容错
典型调度时间片轮转按优先级抢占、按截止时间调度(第 6 篇)
例子多个终端共享的主机硬实时:导弹控制、汽车安全气囊,错过截止时间就是失败;软实时:视频播放、网上订票,偶尔超时可以接受
网络操作系统:各干各的,可以互相借用 计算机 1自己的 OS自己的任务 计算机 2自己的 OS自己的任务 计算机 3自己的 OS打印机、文件 网络 1 号机借用 3 号机的打印机 用户知道资源在哪台机器上;一个任务在一台机器上完成 分布式操作系统:合成一台,分工协作 对用户来说:像一台计算机 机器 1子任务 T1 机器 2子任务 T2 机器 3子任务 T3 任务 T 拆成 T1、T2、T3 并行执行,结果再合起来 各台机器地位平等、没有主从;资源在哪台机器上对用户透明
图 1-16 网络 OS 与分布式 OS。两者都要靠网络把计算机连起来,本质区别是:分布式 OS 让多台计算机协同完成同一个任务,并把“任务和资源在哪台机器上”对用户隐藏起来(透明性);网络 OS 只提供资源共享和通信,每台机器仍各自完成自己的任务。
网络操作系统分布式操作系统
各台计算机各自独立,有自己的操作系统地位平等、无主从,由一个统一的分布式 OS 管理
一个任务在一台机器上完成可以分到多台机器上并行完成
用户看到知道资源在哪台机器上,要指明去哪取像使用一台计算机,资源位置透明
主要特征资源共享、网络通信分布性、并行性、透明性

真题怎么考:这两组概念近年选择题没有单独出过,但都在考纲里,常作为“操作系统发展与分类”组合题的选项(同 2016-23、2022-23 的考法)。判别法:看到“截止时间、可靠性”想实时,看到“交互、时间片、多终端”想分时;看到“协同完成同一任务、像一台计算机、无主从”想分布式,看到“各自独立、共享资源与通信”想网络 OS。失效的情况:“实时系统响应时间一定比分时短”不能当判据,软实时系统的截止时间可以是秒级,关键在“保证”二字。

自测
  1. 汽车的防抱死刹车控制系统、网上购票系统各属于硬实时还是软实时?
    答案
    防抱死刹车:硬实时(错过截止时间可能车毁人亡);网上购票:软实时(偶尔慢一点只是体验差)。
  2. “各台计算机地位平等,一个大任务被拆开分给多台计算机并行处理,用户看起来像在用一台机器”描述的是哪种操作系统?
    答案
    分布式操作系统。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2016-23、2022-23 改
下列关于分时系统的叙述中,正确的是( )。
  • A. 用户可以通过终端与计算机交互
  • B. 分时系统的首要目标是提高系统吞吐量
  • C. 分时系统必须采用抢占式优先级调度
  • D. 时间片越长,用户的响应越快
展开解析

A分时系统把 CPU 时间分成时间片轮流分给各用户,每个用户都像独占计算机,可以交互。B 是批处理系统的目标。C 错:分时系统一般用时间片轮转。D 错:时间片越长,轮一圈越慢,响应越慢。

变化点:2016-23 考批处理系统(不能交互)。这里换成分时系统,两者的目标正好不同:批处理要吞吐量,分时要响应时间。

变式操作系统由 2017-28 改
某系统中每个进程都有 80% 的时间在等待 I/O。若内存中同时有 3 个进程(各自独立,忽略切换开销),则 CPU 利用率约为( )。
  • A. 20%
  • B. 48.8%
  • C. 60%
  • D. 80%
展开解析

BCPU 空闲当且仅当 3 个进程都在等 I/O,概率 = 0.83 = 0.512。CPU 利用率 = 1 − 0.512 = 48.8%。

变化点:2017-28 只问多道程序的优点(CPU 利用率高、吞吐量大)。这里定量算一下:单道时只有 20%,3 道时提高到 48.8%。C 是把 3 个 20% 直接相加。

变式操作系统考纲补充(尚无真题)
下列关于实时操作系统和分布式操作系统的叙述中,错误的是( )。
  • A. 硬实时系统必须保证在截止时间之前完成任务
  • B. 实时系统对可靠性的要求通常比分时系统高
  • C. 分布式系统可以把一个任务分到多台计算机上协同完成
  • D. 网络操作系统中各台计算机地位平等,对用户呈现为一台计算机
展开解析

D“地位平等、无主从、对用户像一台计算机”是分布式操作系统的特征;网络操作系统中各台计算机各自独立,用户知道资源在哪台机器上,D 把两者说反了。A、B 是实时系统的定义与特点;C 是分布式系统的本质(协同完成同一任务)。

考点:分时与实时、网络 OS 与分布式 OS。

1.7操作系统的结构、开机引导与虚拟机

★★ 常考选择题综合题

内核的几种组织方式

宏内核(Linux、Windows 主体) 应用程序(用户态) 内核(内核态) 进程管理 · 内存管理 · 文件系统设备驱动 · 网络 · 中断处理 …… 硬件 微内核(鸿蒙、QNX) 应用程序 文件服务器 内存服务器 设备服务器 微内核(内核态)进程通信·调度机制·地址变换·中断捕获 硬件
图 1-17 宏内核 vs 微内核。宏内核所有功能都在内核态、互相直接调用,性能好;微内核只把最核心的“机制”留在内核,其他功能做成用户态的服务进程,靠消息通信,所以可靠性、安全性、可扩展性、可移植性好(一个服务崩了不影响整个系统),代价是频繁在用户态和内核态之间切换,性能差(2023-23)。

内核怎么“盖楼”,教材总结了五种做法。先记住一个比方:操作系统像一家公司,宏内核是所有部门挤在一间大办公室里,喊一声就能配合(快),但一个人把咖啡洒了全办公室遭殃;微内核是只有总经理办公室在大楼核心区,其他部门都搬到外面的小楼,靠发邮件沟通(慢一点),但哪个小楼着火都不影响核心。

结构怎么组织优点缺点
分层分成若干层,每层只能调用紧挨着的下一层(最底层是硬件)便于自底向上逐层调试和验证;易扩充和维护层次难以合理划分;一个请求要穿过很多层,效率低
模块化按功能划分成模块,模块之间通过接口调用多个模块可以并行开发,设计效率高模块之间牵连多,正确性难保证。衡量标准:高内聚、低耦合
宏内核(大内核)进程、内存、文件、设备管理等主要功能都放在内核态各功能直接互相调用,性能高(Linux、传统 UNIX)内核庞大,一处出错可能导致整个系统崩溃,维护难
微内核内核只保留最基本的功能:与硬件紧密相关的处理(中断、陷入、时钟)、进程(线程)管理、低级存储管理、进程通信;文件系统、设备驱动等作为用户态的服务进程可靠性、安全性、可扩展性、可移植性好,适合分布式系统;“机制”在内核、“策略”在用户态服务之间靠消息通信,频繁在用户态和内核态之间切换,性能差
外核拓展内核只负责为应用分配和保护物理资源(如直接分给某个应用一批磁盘块),资源怎么用由应用自己的“库操作系统”决定应用可按需定制,减少虚拟化层次,效率高应用的负担重,一致性难保证
分层 用户程序 第 3 层 第 2 层 第 1 层 硬件(第 0 层) 只能调用紧挨着的下一层易调试;效率低 模块化 进程 内存 文件 设备 模块通过接口互相调用并行开发;牵连多 宏内核 应用(用户态) 内核态进程 内存 文件驱动 网络 … 硬件 全在内核态,直接调用性能高;一处崩全崩 微内核 应用 文件服务 驱动服务 微内核 硬件 服务在用户态,靠消息可靠易扩展;切换多 外核 (拓展) 应用+库 OS 应用+库 OS 外核:只分配、保护 磁盘块 内存页 资源怎么用由应用定高效;应用负担重 左两种说的是“内核内部代码怎么组织”,右三种说的是“哪些功能放在内核态”——两个维度,可以组合(如 Linux:宏内核 + 模块化)
图 1-18 五种内核结构一张图。微内核图中橙色箭头表示:应用请求文件服务时,消息先进入微内核,再由微内核转给用户态的文件服务进程——一次请求至少两次进出内核态,这就是它“性能差”的来源(2023-23)。
两个维度别混

分层 / 模块化回答“内核内部的代码怎么组织”;宏内核 / 微内核回答“哪些功能放在内核态”。所以它们可以组合:Linux 是宏内核,但内部按模块组织,设备驱动还能做成“可加载内核模块”在运行时装入——装入后仍在内核态运行,所以 Linux 仍是宏内核。判断宏 / 微只看一件事:文件系统、设备驱动这些服务在内核态还是用户态。

自测
  1. “分层结构的操作系统中,第 3 层可以直接调用第 1 层的功能”对吗?
    答案
    不对。严格的分层结构里每层只能调用紧挨着的下一层,第 3 层要经过第 2 层。这也是分层结构效率低的原因。
  2. “Linux 支持动态加载驱动模块,所以它是微内核”对吗?
    答案
    不对。可加载模块装入后在内核态运行,Linux 仍是宏内核;“能动态加载”说明它模块化,与宏 / 微无关。

内核里到底放了什么

内核是计算机上配置的最底层软件,是操作系统最基本、最核心的部分,运行在内核态(第 5 篇)。不管宏内核还是微内核,下面前三项“与硬件关联较紧密”的功能都一定留在内核里:

内核功能做什么联系
时钟管理计时,向用户提供系统时间;通过时钟中断管理时间片,实现进程切换;实时系统里按截止时间控制运行分时系统的基础(1.6);第 6 篇时间片轮转
中断机制现代操作系统靠中断驱动:键盘输入、进程管理和调度、系统调用、设备驱动、文件访问……都依赖中断。内核里只放其中与硬件关系最紧的一小部分——保护和恢复中断现场、把控制权转到相应的处理程序,以缩短中断处理时间第 5 篇
原语处在操作系统最底层、最接近硬件的小程序;运行具有原子性(一气呵成,不可被打断,常用关中断实现);运行时间短、调用频繁。设备驱动、CPU 切换、进程通信里的部分操作都可以定义为原语第 6 篇进程控制原语、第 7 篇 P/V 原语
系统控制的数据结构及处理进程管理:进程状态管理、进程调度和分派、创建与撤销 PCB;存储器管理:空间分配和回收、内存信息保护、代码对换;设备管理:缓冲区管理、设备分配和回收这部分在微内核里多半移到了用户态服务进程

开机时发生了什么:操作系统引导

① ROM 中的引导程序(BIOS) ② 硬件自检建中断向量表 ③ 磁盘第 1 扇区MBR:含分区表 ④ 活动分区第 1 扇区PBR 分区引导记录 ⑤ 启动管理器选择操作系统 ⑥ OS 初始化程序装入 RAM 通电后 CPU 只能先执行 ROM 里固化的程序;操作系统本身存在磁盘上,要一级一级“接力”才能被装进内存(RAM)运行
图 1-19 引导过程。2021-46 考过顺序:ROM 中的引导程序 → 磁盘引导程序(MBR)→ 分区引导程序(PBR)→ 操作系统的初始化程序。2013-29:操作系统最终被加载到 RAM(不是 ROM、BIOS)。磁盘分区、格式化的顺序见第 10 篇。

为什么要这么多级“接力”?ROM 里的程序断电不丢,但容量小、不方便更新,只能放一个很小的引导程序;操作系统又大又要经常升级,只能放在磁盘上。于是开机后先跑 ROM 里的小程序,由它去磁盘上读一段稍大的引导程序,再由那段程序去装入操作系统。逐步展开如下:

步做什么具体过程
①激活 CPU加电后 CPU 从一个固定地址开始执行,这个地址落在 ROM 里——执行的第一条指令就是 BIOS(ROM 中的引导程序)的第一条指令
②硬件自检BIOS 检查内存、显卡、键盘等硬件是否正常,出错就报警停机
③找到启动盘BIOS 按设置好的启动顺序(硬盘、U 盘、光盘……)找到能启动的那块盘,把它的第一个扇区读进内存,把控制权交给它
④执行主引导记录 MBRMBR 在磁盘的第一个扇区(0 柱面 0 磁头 1 扇区),包含磁盘引导程序和分区表。它的作用是告诉 CPU 去哪个分区找操作系统
⑤执行分区引导记录 PBR磁盘引导程序扫描分区表,找到活动分区(装了操作系统、被标记为可引导的分区),读入该分区的第一个扇区——分区引导记录 PBR,由它找到分区里的启动管理器
⑥加载操作系统启动管理器把操作系统内核和初始化程序装入 RAM 并执行,操作系统完成初始化,开始工作

2021-46(大题)考的就是这条链:“ROM 中的引导程序 → 磁盘引导程序 → 分区引导程序 → 操作系统的初始化程序”的执行顺序,以及把硬盘做成启动盘的准备顺序“物理格式化(划分扇区)→ 分区 → 逻辑格式化(建立文件系统和根目录)→ 安装操作系统”(第 10 篇 10.3)。

BIOS、CMOS、POST:开机第一秒的三个缩写

问题引入:开机时屏幕上闪过“按 Del 进入设置”,有人叫它“进 BIOS”,有人叫它“进 CMOS”;主板上的纽扣电池没电后,每次开机日期都回到很多年前……这三个缩写各是什么,彼此什么关系?

打个比方

把开机想成一台洗衣机启动:BIOS 是出厂时烧在芯片里的控制程序;CMOS 是记住你上次选的“水温、转速”的那块小存储器,要靠电池维持;POST 是洗衣机通电后先自检“门关好没有、有没有水”。不像的地方:洗衣机的设置断电就丢,而 CMOS 有纽扣电池,主机拔掉电源设置也还在。

名称是什么存在哪、断电后
BIOS
基本输入输出系统
固化在主板上的一组程序:加电自检、设置程序(Setup)、引导程序、最基本的 I/O 驱动例程。CPU 加电后执行的第一条指令就在 BIOS 里ROM(现在多为可改写的闪存);断电不丢
CMOS本是一种芯片工艺的名字,这里指主板上一小块用这种工艺做的 RAM,保存 BIOS 用到的设置参数:日期时间、启动顺序、硬件配置RAM,靠主板纽扣电池供电;电池没电或“清 CMOS”后恢复默认值
POST
加电自检
BIOS 开机后做的第一件事:检查 CPU、内存、显卡、键盘等是否正常,出错用蜂鸣声或屏幕提示报警是 BIOS 里的一段程序
BIOS 设置
俗称“进 CMOS”
开机时按 Del、F2 等键进入的设置界面,用来修改启动顺序等参数程序在 BIOS 里,改的是 CMOS 里的数据
BIOS 程序(ROM / 闪存,断电不丢) ① POST:加电自检 ② Setup:设置程序(按 Del 进入) ③ 引导:找启动盘、读第 1 个扇区 CMOS RAM(存设置数据) 日期时间 · 启动顺序硬件参数 电池 写设置 读启动顺序 启动盘 0 号扇区:MBR 各分区……(后续接力见上表 ④⑤⑥) 把 MBR 读进内存,交出控制权
图 1-20 BIOS 是程序,CMOS 是数据,POST 是 BIOS 做的第一件事。开机顺序:加电 → CPU 从固定地址执行 BIOS → POST 自检 → 按 CMOS 里的启动顺序找启动盘 → 读入 MBR,进入上面引导表的第 ④ 步。

真题怎么考:2013-29 问“操作系统最终被加载到哪里”(RAM,不是 BIOS、ROM),2021-46 考引导各步的先后。CMOS、POST 本身还没有单独出题,但“BIOS 存在 CMOS 里”“POST 在读 MBR 之后”这类错误说法很适合做选项。

易错

① BIOS 不在 CMOS 里:BIOS 程序在 ROM / 闪存里,CMOS 存的只是设置数据。② 清 CMOS 不会删掉 BIOS,只是把设置恢复成出厂默认值。③ POST 在读 MBR 之前:硬件都没检查完,不会去读磁盘。

自测
  1. 主板电池没电后,每次开机日期都变回出厂时的日期,坏掉的是什么?
    答案
    CMOS RAM 失去供电,保存的日期时间等设置丢失;BIOS 程序本身没坏,电脑仍能开机。
  2. 把下列步骤排序:读入 MBR、POST、执行 BIOS 第一条指令、按 CMOS 设置确定启动盘。
    答案
    执行 BIOS 第一条指令 → POST → 按 CMOS 设置确定启动盘 → 读入 MBR。

UEFI + GPT:今天的电脑怎么开机 拓展

问题引入:上面的“BIOS + MBR”是 408 的考法(2021-46)。可你今天买的电脑,设置界面多半写着“UEFI”,硬盘分区表是“GPT”。为什么要换?两套流程哪里一样、哪里不同?

大白话:MBR 是一张只能写 4 行的小卡片:最多记 4 个主分区,而且分区地址只有 32 位,大硬盘记不下;BIOS 也很“笨”,只会执行第一个扇区里那几百字节的小程序。GPT 是一本大目录,UEFI 是一个“聪明”的固件,能直接看懂 FAT32 文件系统,去专门的分区里找启动程序文件来执行。

传统:BIOS + MBR BIOS(POST) 0 号扇区 MBR 活动分区的 PBR 启动管理器 内核装入 RAM MBR 这 512 字节: 引导代码 446B 分区表 64B 55AAH 分区表 = 4 项 × 16B(示意,未按比例) 现代:UEFI + GPT UEFI 固件(POST) 读 GPT,找到 EFI 系统分区 执行分区里的 .efi 启动程序文件 内核装入 RAM 不变的主线:固件(在 ROM / 闪存)→ 外存上的引导程序 → 把内核装入 RAM。变的只是“中间怎么找到引导程序”
图 1-21 两代引导流程。UEFI 能直接读懂 EFI 系统分区(FAT32 格式)里的文件,所以不再需要 MBR 里那 446 字节的引导代码和分区里的 PBR 这两级“接力”。
BIOS + MBRUEFI + GPT
分区表MBR 中的 64 字节,4 个分区项 → 最多 4 个主分区(更多要靠扩展分区)GPT,通常预留 128 个分区项
单盘容量上限扇区号 32 位:232 × 512B = 2TiB(扇区为 4KB 时是 16TiB)扇区号 64 位,理论上 264 × 512B = 8ZiB,实际用不完
引导代码放在MBR 的 446 字节 + 活动分区的 PBREFI 系统分区里的 .efi 文件
可靠性分区表只有一份磁盘开头和末尾各存一份 GPT,带 CRC 校验
安全不检查引导程序是否被篡改支持“安全启动”:只执行有合法签名的启动程序

GPT 磁盘的 0 号扇区仍保留一个“保护性 MBR”,让只认识 MBR 的老工具把整块盘看成一个已占用的分区,不会误当成空盘去格式化。上面的数字已用 verify/08/p01_calc.py 验算。考试按题目给的口径答:题目说 BIOS、MBR、PBR,就按传统流程作答。

自测
  1. 扇区为 512B 时,MBR 分区方式最多能管理多大的磁盘?为什么是这个数?
    答案
    MBR 的分区项用 32 位记录扇区号,最多 232 个扇区,232 × 29 B = 241 B = 2TiB。
  2. UEFI 启动时还需要执行 MBR 里的引导代码吗?
    答案
    不需要。UEFI 固件读 GPT 找到 EFI 系统分区,直接执行其中的 .efi 启动程序文件。

虚拟机

把一台物理机器变成多台“看起来完整”的机器,每台都能装自己的操作系统——像一栋楼隔成好几套房子,每套都有自己的厨房卫生间。负责“隔房子”的软件叫虚拟机管理程序(VMM,又叫 Hypervisor),它有两种装法:

第一类 VMM(裸机型) 硬件 VMM(Hypervisor)唯一运行在最高特权级(真正的内核态) 客户 OS 1“虚拟内核态” 客户 OS 2“虚拟内核态” 客户 OS 3… 应用 应用 应用 第二类 VMM(寄居型) 硬件 宿主操作系统(Windows、Linux…) 普通应用 VMM像一个普通应用程序运行 客户 OS+ 应用 客户 OS+ 应用
图 1-22 两类虚拟机管理程序。第一类(如 VMware ESXi、Xen)直接跑在硬件上,VMM 的特权级最高,客户操作系统实际运行在用户态,它以为自己在执行特权指令,其实会“陷入”VMM,由 VMM 代为完成;第二类(如个人电脑上装的 VirtualBox、VMware Workstation)装在宿主操作系统里,像普通软件一样运行。

要记住的几点:① 一台主机可以同时跑多个虚拟机,每个虚拟机里都能装操作系统;② “VMM 与操作系统特权级相同”是错的(2025-24):第一类 VMM 的特权级比客户操作系统高;③ 虚拟机技术还可以用软件模拟另一种指令集(在 x86 电脑上模拟 ARM),即一台主机模拟多种 ISA;④ 第一类的性能一般比第二类好(少隔一层宿主操作系统)。

第一类 VMM(裸机型)第二类 VMM(寄居型,也叫宿主型)
装在哪直接装在硬件上,本身就像一个精简的操作系统装在宿主操作系统上,像一个普通应用程序
物理资源VMM 直接控制全部硬件,直接分给各虚拟机VMM 向宿主 OS 申请,由宿主 OS 分配,再转给虚拟机
特权级VMM 运行在最高特权级(真正的内核态);客户 OS 实际运行在用户态(“虚拟内核态”),执行特权指令时陷入 VMM,由 VMM 代为完成VMM 主体作为宿主机上的进程在用户态运行,借助宿主内核里的驱动 / 模块完成底层操作
性能高(少了宿主 OS 这一层)较低(请求要经过宿主 OS 转发)
能支持的虚拟机数多较少,受宿主机限制
典型产品VMware ESXi、Xen、Hyper-VVirtualBox、VMware Workstation

记一句:第一类“自己当房东”,第二类“租宿主 OS 的房子再转租”。2025-24 的陷阱“VMM 与操作系统特权级相同”就是在考第一类 VMM 的特权级比客户 OS 高。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2023-23 改
与宏内核相比,微内核操作系统的主要缺点是( )。
  • A. 可靠性差
  • B. 扩展性差
  • C. 需要在用户态与内核态之间频繁切换,性能较差
  • D. 内核代码量大
展开解析

C微内核只把最基本的功能(进程通信、低级调度、中断处理等)留在内核里,文件系统、设备驱动等作为用户态的服务进程。服务之间要通过消息传递和多次模式切换,开销大、性能较差。可靠性、安全性、扩展性反而更好(2023-23)。

变化点:2023-23 问微内核的优点,这里问缺点。

变式操作系统由 2013-29、2021-46 改
下列关于计算机开机引导的叙述中,正确的是( )。
  • A. 操作系统在开机时直接从 ROM 中执行
  • B. 先执行 ROM 中的引导程序,由它把磁盘上的引导程序读入内存
  • C. 分区引导程序存放在主引导记录(MBR)中
  • D. 操作系统最终被加载到磁盘缓存中运行
展开解析

B开机后 CPU 先执行 ROM 中的引导程序(BIOS),它读入 MBR 中的磁盘引导程序;磁盘引导程序根据分区表找到活动分区,读入该分区的分区引导程序;最后把操作系统装入内存(RAM)(2013-29)。MBR 中放的是磁盘引导程序和分区表,分区引导程序在各分区的第一个扇区中。

变化点:2013-29 只问操作系统最终加载到哪里(RAM)。这里把整条引导链放进选项。

变式操作系统考纲补充(尚无真题)
下列关于计算机开机过程的叙述中,正确的是( )。
  • A. 加电自检(POST)由 BIOS 执行,发生在读入主引导记录(MBR)之前
  • B. BIOS 程序存放在由纽扣电池供电的 CMOS RAM 中
  • C. 清除 CMOS 设置会删除 BIOS 程序,使计算机无法启动
  • D. 操作系统的初始化程序存放在主引导记录(MBR)中
展开解析

A加电后 CPU 先执行 BIOS,BIOS 做的第一件事就是 POST;硬件检查通过后,才按 CMOS 里的启动顺序找到启动盘、读入 MBR。B 错:BIOS 是程序,在 ROM / 闪存里;CMOS 存的是设置数据。C 错:清 CMOS 只是把设置恢复为默认值。D 错:MBR 中是磁盘引导程序和分区表,操作系统的初始化程序在活动分区里,要经 PBR、启动管理器一级级接力才装入内存(2021-46)。

考点:BIOS、CMOS、POST、操作系统引导。

1.8性能指标:怎样算“一台机器有多快”

★★★ 高频选择题

最核心的公式只有一个,所有性能题都从它变形:

CPU 执行时间 = 指令条数 × CPI × 时钟周期 = 指令条数 × CPI ÷ 主频 时钟周期 = 1 ÷ 主频   CPI = 平均每条指令用几个时钟周期 平均 CPI = Σ(某类指令所占比例 × 该类指令的 CPI) MIPS(每秒百万条指令)= 主频 ÷ CPI ÷ 106  IPS = 主频 ÷ CPI
时钟:CPU 里的“节拍器” 时钟 1 个时钟周期 ADD:4 个周期(CPI = 4) LOAD:5 个周期(要多访一次存) 主频 = 每秒有多少个时钟周期;时钟周期 = 1 / 主频。1GHz → 每秒 10⁹ 个周期 → 每个 1ns。 一条指令要占好几个时钟周期(取指、译码、执行……各占一些),不同指令占的个数不同 → 所以才有“平均 CPI”。
图 1-23 时钟周期、主频和 CPI。“每来一个时钟脉冲就开始执行一条新指令”是错的(2019-16):一个时钟周期只完成一条指令里的一个小步骤。时钟周期多长,要看相邻两组寄存器之间那段电路最慢要多久才能算出稳定结果——周期太短,结果还没算出来就被下一拍锁存,就错了。
打个比方

你抄一本书:指令条数 = 要抄多少个字;CPI = 每个字平均写几笔;时钟周期 = 每一笔花多少秒。总时间 = 字数 × 每字笔数 × 每笔秒数。想快可以:少写字(编译优化)、每个字少几笔(改进指令/结构)、每笔写快点(提高主频)。但它们互相牵制:主频提高可能让 CPI 变大,所以只看主频判断快慢是错的。

CPI 不是一个固定常数

① 不同指令 CPI 不同(加法 4 拍、访存 5 拍),所以要按比例求平均。② 程序运行时的 CPI 还受 Cache 缺失影响:缺失一次就要多等几十上百个周期去主存取数,平均 CPI 随之变大(2025-18 说“与 Cache 缺失率无关”是错的)。③ 两种极端 CPU(第 4 篇):单周期 CPU 每条指令 1 个周期(CPI = 1),但时钟周期要按最慢的那条指令定,很长;流水线 CPU 的时钟周期按最慢的那一段定。

性能指标逐个过(选择题的概念题都在这张表里)

指标含义要注意
机器字长计算机进行一次整数(定点)运算所能处理的二进制位数= ALU 宽度 = 通用寄存器宽度(2020-12);字长越长,数的表示范围越大、精度越高
数据通路带宽数据总线一次所能并行传送信息的位数指外部数据总线的宽度,可以和 CPU 内部的机器字长不同
主存容量主存能存放的最大信息量,写成“字数 × 字长”(如 512K × 16 位)或字节数MAR 位数 → 单元个数,MDR 位数 → 存储字长。如 MAR 16 位、MDR 32 位 → 64K × 32 位
吞吐量系统在单位时间内处理请求(任务)的数量它取决于信息多快能进出主存、CPU 多快能取到指令和数据……几乎每一步都和主存有关,所以教材的结论是:系统吞吐量主要取决于主存的存取周期
响应时间从用户发出一个请求,到系统作出响应并得到结果所等待的时间= CPU 时间 + 等待时间(磁盘访问、存储器访问、I/O 操作、操作系统开销、排队等)
主频 / 时钟周期主频 = 每秒多少个时钟周期;时钟周期 = 1 / 主频,是 CPU 中最小的时间单位1GHz ↔ 1ns,500MHz ↔ 2ns
CPI执行一条指令平均需要多少个时钟周期与指令种类、具体程序、Cache 缺失率都有关(上面的易错框)
IPS / MIPS每秒执行多少条 / 多少百万条指令,IPS = 主频 ÷ CPIISA 不同的机器不能用 MIPS 比快慢(下面的例子)
MFLOPS、GFLOPS、TFLOPS、PFLOPS、EFLOPS、ZFLOPS每秒浮点运算次数:106、109、1012、1015、1018、1021衡量浮点运算(科学计算、向量机)性能(2011-12、2021-12)
CPU 执行时间运行一个程序所花的 CPU 时间 = CPU 时钟周期数 × 时钟周期分用户 CPU 时间(执行程序本身的指令)和系统 CPU 时间(为这个程序执行的操作系统代码);题目说“CPU 执行时间”“用户 CPU 时间”时一般只算前者,不含等 I/O 的时间
基准程序专门用来测试性能的一组程序(如 SPEC)在同一组程序上比执行时间,比 MIPS 更能反映实际性能
“快几倍”到底怎么说

性能 = 1 ÷ 执行时间。“M1 的性能(速度)是 M2 的 n 倍” ⇔ TM2 ÷ TM1 = n。
“速度提高 50%” = 速度变成 1.5 倍 = 时间变成原来的 1/1.5(2012-12),不是“时间减少 50%”;“时间减少 50%” = 速度变成 2 倍。
加速比 = 改进前的时间 ÷ 改进后的时间 = 改进后的性能 ÷ 改进前的性能。

MIPS 和 MFLOPS:一个数“指令”,一个数“浮点运算”

问题引入:2011-12 问“描述浮点数操作速度的指标”,2021-12 用 PFLOPS 考超级计算机。MIPS 也是“速度”,为什么超级计算机排行榜用 FLOPS 而不用 MIPS?

打个比方

评比两家搬家公司:MIPS 像数“跑了几趟”,MFLOPS 像数“搬了多少箱”。一辆大卡车一趟能搬 4 箱,趟数少了,箱数一点没少。不像的地方:搬家的箱子大小差不多;而浮点运算里加法和除法的代价差很多,所以 MFLOPS 也不是完美的尺子。

MIPS = 指令条数 ÷ (执行时间 × 106) = 主频 ÷ (CPI × 106) MFLOPS = 浮点运算次数 ÷ (执行时间 × 106)  GFLOPS、TFLOPS、PFLOPS 依次把 106 换成 109、1012、1015
标量指令每条做 1 次 指令 ①:× 指令 ②:× 指令 ③:× 指令 ④:× 4 条指令,4 次浮点运算 SIMD 指令每条做 4 次 × × × × 1 条指令,4 次浮点运算 MIPS 数的是蓝色(指令条数:4 vs 1),MFLOPS 数的是橙色(浮点运算次数:4 vs 4)——活儿一样多,MIPS 却差了 4 倍
图 1-24 同样 4 次浮点乘法,两种打包方式。SIMD(单指令流多数据流,第 4 篇 4.5)一条指令同时处理多个数据。用 MIPS 衡量会觉得 SIMD 版本“指令少、没干活”,用 MFLOPS 衡量两者干的活一样多。
MIPSMFLOPS
数什么每秒执行多少百万条指令每秒完成多少百万次浮点运算
与 ISA 的关系大:ISA 不同,同一程序的指令条数不同,MIPS 不能直接比小:同一个计算问题要做的浮点运算次数基本固定
适合衡量同一 ISA 的机器之间比较科学计算、图形、AI 等浮点密集的程序(超级计算机、GPU)
不适合跨 ISA 比较(下面“MIPS 为什么会骗人”的例子)几乎不做浮点运算的程序(编译、数据库),这时 MFLOPS 很低也不说明机器慢
例计组MIPS 不变,MFLOPS 变了
主频 2GHz 的机器运行程序 P,共执行 4×109 条指令,其中 25% 是浮点指令(每条完成 1 次浮点运算),平均 CPI = 2。① 求执行时间、MIPS、MFLOPS。② 改用 4 路 SIMD 指令(1 条做 4 次浮点运算),浮点指令条数变为原来的 1/4,其余指令不变,平均 CPI 仍为 2,再求三者。
展开解析
  1. ① 时间:T = 4×109 × 2 ÷ (2×109) = 4 s。MIPS = 2000 ÷ 2 = 1000。浮点运算次数 = 0.25 × 4×109 = 109 次 → MFLOPS = 109 ÷ 4 ÷ 106 = 250。
  2. ② 指令数:浮点指令 109 条 → 2.5×108 条;其余 3×109 条不变;共 3.25×109 条。
  3. ② 时间:T = 3.25×109 × 2 ÷ (2×109) = 3.25 s;MIPS = 2000 ÷ 2 = 1000(没变);浮点运算还是 109 次 → MFLOPS = 109 ÷ 3.25 ÷ 106 ≈ 307.7。
  4. 结论:程序真的快了(加速比 4 ÷ 3.25 ≈ 1.23),MIPS 却纹丝不动——因为 CPI 和主频都没变;MFLOPS 跟着真实性能一起提高了。数字已用 verify/08/p01_calc.py 验算。

峰值 FLOPS 拓展:厂商宣传的“××TFLOPS”一般是理论峰值 = 处理器核数 × 主频 × 每核每周期最多完成的浮点运算次数。例:8 核、3GHz、每核每周期 16 次 → 8 × 3×109 × 16 = 3.84×1011 = 384 GFLOPS。真实程序要等内存、要做分支判断,通常达不到峰值。

真题怎么考:2011-12 考概念(浮点运算速度用 MFLOPS;CPI、IPC、MIPS 都不是);2021-12 考单位(P = 1015,换成中文是“千万亿”,再乘 93 得“亿亿”量级)。失效的情况:MFLOPS 也不能跨运算种类公平比较(一次除法比一次加法慢得多);真正公平的比较仍是同一基准程序的执行时间(上表最后一行)。

自测
  1. 某程序运行 2 s,共完成 6×109 次浮点运算,它的浮点性能是多少 GFLOPS?
    答案
    6×109 ÷ 2 ÷ 109 = 3 GFLOPS。
  2. “MIPS 高的机器,MFLOPS 一定也高”对吗?
    答案
    不对。MIPS 数指令、MFLOPS 数浮点运算;指令很简单(一条做不了多少浮点运算)或程序里浮点指令很少时,MIPS 高而 MFLOPS 低。

MIPS 为什么会“骗人”

MIPS 只数“每秒执行了多少条指令”,不管每条指令干了多少活。不同指令集里,一条指令能做的事差别很大(CISC 一条复杂指令可能顶 RISC 好几条),所以 MIPS 高的机器不一定快:

例计组MIPS 与执行时间
M1、M2 主频都是 2GHz,指令集不同。同一个 C 程序在 M1 上编译得到 2×109 条指令,平均 CPI = 2;在 M2 上编译得到 6×109 条指令(M2 的指令更简单,要更多条),平均 CPI = 1。哪台机器 MIPS 高?哪台跑得快?
展开解析
  1. M1:MIPS = 2000 ÷ 2 = 1000;时间 = 2×109 × 2 ÷ (2×109) = 2 s。
  2. M2:MIPS = 2000 ÷ 1 = 2000;时间 = 6×109 × 1 ÷ (2×109) = 3 s。
  3. M2 的 MIPS 是 M1 的两倍,跑同一个程序却慢了 50%。比较性能只能比“同一个程序的执行时间”;MIPS 只在 ISA 相同(指令条数相同)时才有比较意义——所以 2017-12 题干特意说明“具有相同的 ISA”。

只加速一部分,整体快多少:Amdahl 定律 拓展

2012-12 那种“CPU 快了、I/O 没变”的题,本质是一条通用规律:整体的加速受限于没被加速的那部分。设原来的执行时间里,能被改进的部分占比例 f(按时间算),这部分被加速到 s 倍,则:

改进后时间 = 原时间 × [(1 − f) + f ÷ s] 整体加速比 S = 1 ÷ [(1 − f) + f ÷ s]  s 再大,S 也不会超过 1 ÷ (1 − f)

套 2012-12:f = 90/100 = 0.9,s = 1.5 → 新时间 = 100 × (0.1 + 0.9/1.5) = 100 × 0.7 = 70 s,加速比 = 1/0.7 ≈ 1.43;就算 CPU 无限快,最多也只能加速到 1/0.1 = 10 倍,因为那 10 s 的 I/O 一秒都没少。

例计组f 要按时间算
主频 1GHz 的机器运行程序 P,共 108 条指令:40% 的指令 CPI = 1,60% 的指令 CPI = 3。现改进硬件,把后一类指令的 CPI 降到 2,主频不变。求改进前后的 CPU 时间和加速比。
展开解析
  1. 改进前:平均 CPI = 0.4×1 + 0.6×3 = 2.2,时间 = 108 × 2.2 ÷ 109 = 0.22 s。
  2. 改进后:平均 CPI = 0.4×1 + 0.6×2 = 1.6,时间 = 0.16 s,加速比 = 0.22 ÷ 0.16 = 1.375。
  3. 用 Amdahl 验算:后一类指令占的时间比例 f = (0.6×3) ÷ 2.2 = 1.8/2.2 ≈ 0.818(不是 60%!),s = 3/2 = 1.5 → S = 1 ÷ (0.182 + 0.818/1.5) = 1 ÷ 0.727 ≈ 1.375 ✓。
    如果误把 f 当成指令条数比例 0.6,会算成 1 ÷ (0.4 + 0.4) = 1.25 ✗。
2013-12计组必会计算
某计算机主频为 1.2GHz,其指令分为 4 类,它们在基准程序中所占比例及 CPI 如下:A 类 50%、CPI=2;B 类 20%、CPI=3;C 类 10%、CPI=4;D 类 20%、CPI=5。该机的 MIPS 数是( )。
  • A. 100
  • B. 200
  • C. 400
  • D. 600
展开解析

考点1.8 平均 CPI 与 MIPS

C先按比例求平均 CPI,再用 MIPS = 主频 ÷ CPI ÷ 106。

  1. 平均 CPI = 50%×2 + 20%×3 + 10%×4 + 20%×5 = 1 + 0.6 + 0.4 + 1 = 3。
  2. 每秒指令数 = 1.2 × 109 ÷ 3 = 4 × 108 条 = 400 MIPS。
  • A 错100 MIPS 对应平均 CPI = 1.2G ÷ 100M = 12,比四类指令中最大的 CPI(5)还大,加权平均不可能这样。
  • B 错200 MIPS 对应平均 CPI = 6,也超过了最大的 5;加权平均一定落在最小 2 和最大 5 之间。
  • C 对平均 CPI = 3,1.2GHz ÷ 3 = 400 MIPS。
  • D 错600 MIPS 对应 CPI = 2,即只用了占比最大的 A 类指令的 CPI,没有加权平均。

平均 CPI 一定要按比例加权,不能直接取算术平均或取占比最大的那一类。

2014-12计组
程序 P 在机器 M 上的执行时间是 20 秒,编译优化后,P 执行的指令数减少到原来的 70%,而 CPI 增加到原来的 1.2 倍,则 P 在 M 上的执行时间是( )。
  • A. 8.4 秒
  • B. 11.7 秒
  • C. 14 秒
  • D. 16.8 秒
展开解析

考点1.8 CPU 时间公式

D同一台机器,时钟周期不变,执行时间 ∝ 指令条数 × CPI。

  1. 新时间 = 20s × 0.7 × 1.2 = 16.8s。
  • A 错8.4s 比“只算指令数减少”的 14s 还短;可 CPI 是增加的(× 1.2),时间只会比 14s 更长。
  • B 错11.7s = 20 × 0.7 ÷ 1.2:CPI 增加到 1.2 倍,却当成除以 1.2。
  • C 错14s = 20 × 0.7:只考虑了指令数减少,忘了 CPI 增加到 1.2 倍。
  • D 对20 × 0.7 × 1.2 = 16.8s。

比例题不必求出具体数值,把变化倍数连乘即可。

2017-12计组
假定计算机 M1 和 M2 具有相同的指令集体系结构(ISA),主频分别为 1.5GHz 和 1.2GHz。在 M1 和 M2 上运行某基准程序 P,平均 CPI 分别为 2 和 1,则程序 P 在 M1 和 M2 上运行时间的比值是( )。
  • A. 0.4
  • B. 0.625
  • C. 1.6
  • D. 2.5
展开解析

考点1.8 CPU 时间公式1.4 ISA

CISA 相同 → 同一程序编译出的指令条数相同,时间只和 CPI ÷ 主频有关。

  1. M1:每条指令时间 = 2 ÷ 1.5GHz ≈ 1.333ns;M2:1 ÷ 1.2GHz ≈ 0.833ns。
  2. 时间比 M1 : M2 = 1.333 ÷ 0.833 = 1.6。
  • A 错0.4 = (1/2) × (1.2/1.5):CPI 之比用反了,应为 M1 : M2 = 2 : 1。
  • B 错0.625 = 1 ÷ 1.6:比值写反了,算成了 M2 : M1。
  • C 对(2 ÷ 1.5) ÷ (1 ÷ 1.2) = 1.6,主频高的 M1 反而更慢。
  • D 错2.5 = (2/1) × (1.5/1.2):主频之比用反了,主频高的 M1 每个周期更短,应乘 1.2/1.5。

只看主频判断快慢是错的:主频高的 M1 因为 CPI 大,反而更慢。

2012-12计组
假定基准程序 A 在某计算机上的运行时间为 100 秒,其中 90 秒为 CPU 时间,其余为 I/O 时间。若 CPU 速度提高 50%,I/O 速度不变,则运行基准程序 A 所耗费的时间是( )。
  • A. 55s
  • B. 60s
  • C. 65s
  • D. 70s
展开解析

考点1.8 性能计算

D“CPU 速度提高 50%”是速度变成原来的 1.5 倍,所以 CPU 时间变成原来的 1/1.5,而不是“减少 50%”。

  1. 原来:CPU 时间 90s,I/O 时间 10s。
  2. CPU 时间变为 90 ÷ 1.5 = 60s,I/O 时间不变仍为 10s。
  3. 总时间 = 60 + 10 = 70s。
  • A 错55s:把“速度提高 50%”当成“时间减半”(90 ÷ 2 + 10)。
  • B 错60s:只算了 CPU 时间,忘了加上不变的 I/O 时间 10s。
  • C 错65s = 60 + 5,相当于把 I/O 时间也减了一半;题目说 I/O 速度不变,I/O 仍要 10s。
  • D 对70s = 90 ÷ 1.5 + 10。

速度 × 时间 = 工作量不变:速度变 k 倍,时间变 1/k。

2023-12计组
若机器 M 的主频为 1.5GHz,在 M 上执行程序 P 的指令条数为 5×105,P 的平均 CPI 为 1.2,则 P 在 M 上的指令执行速度和用户 CPU 时间分别为( )。
  • A. 0.8GIPS、0.4ms
  • B. 0.8GIPS、0.4μs
  • C. 1.25GIPS、0.4ms
  • D. 1.25GIPS、0.4μs
展开解析

考点1.8 MIPS1.8 CPU 时间

C指令执行速度 = 主频 ÷ CPI;CPU 时间 = 指令条数 × CPI ÷ 主频。单位分开换算。

  1. 速度 = 1.5 × 109 ÷ 1.2 = 1.25 × 109 条/秒 = 1.25 GIPS。
  2. 时钟周期数 = 5 × 105 × 1.2 = 6 × 105。
  3. CPU 时间 = 6 × 105 ÷ (1.5 × 109) = 4 × 10−4 s。
  4. 单位换算:10−3 s = 1ms,所以 4 × 10−4 s = 0.4ms。
  • A 错0.8 = 1.2 ÷ 1.5,是把 CPI ÷ 主频当成了速度(单位其实是“纳秒/条”);速度应是主频 ÷ CPI = 1.25 GIPS。
  • B 错速度 0.8 是 CPI ÷ 主频(1.2 ÷ 1.5),应为主频 ÷ CPI = 1.25 GIPS;时间单位也错,4 × 10−4 s 是 0.4ms,0.4μs 只有 4 × 10−7 s。
  • C 对1.25 GIPS、0.4ms。
  • D 错速度对了,但 4 × 10−4 s = 0.4 × 10−3 s = 0.4ms;0.4μs = 4 × 10−7 s,差了 1000 倍。

ms = 10−3 s,μs = 10−6 s,ns = 10−9 s。同类:2022-12(CPI = 0.8 × 1 + 0.2 × 10 = 2.8,10000 × 2.8 ÷ 109 = 28μs)。

2010-12计组
下列选项中,能缩短程序执行时间的措施是( )。Ⅰ. 提高 CPU 时钟频率 Ⅱ. 优化数据通路结构 Ⅲ. 对程序进行编译优化
  • A. 仅Ⅰ和Ⅱ
  • B. 仅Ⅰ和Ⅲ
  • C. 仅Ⅱ和Ⅲ
  • D. Ⅰ、Ⅱ和Ⅲ
展开解析

考点1.8 CPU 执行时间公式

D执行时间 = 指令条数 × CPI × 时钟周期,三个因子任何一个变小都能缩短时间。

  • Ⅰ 对提高时钟频率 → 时钟周期变短。
  • Ⅱ 对优化数据通路 → 可以减少每条指令的周期数(CPI)或缩短关键路径(时钟周期)。
  • Ⅲ 对编译优化 → 生成的指令条数变少(或用更快的指令)。

性能题先写公式“时间 = 条数 × CPI × 周期”,再看选项改的是哪个因子。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2012-12 改
某基准程序在计算机上运行 100s,其中 CPU 时间 80s,其余为 I/O 时间。若 CPU 速度提高 300%,I/O 速度提高 1 倍,则运行该程序需要( )。
  • A. 25s
  • B. 30s
  • C. 35s
  • D. 40s
展开解析

B“速度提高 300%”是变为原来的 4 倍,CPU 时间 = 80 ÷ 4 = 20s;“提高 1 倍”是变为 2 倍,I/O 时间 = 20 ÷ 2 = 10s。共 30s。

变化点:2012-12 只提高 CPU 速度(提高 50%,即 90 ÷ 1.5 = 60s)。这里两部分都加速,而且要先把“提高 x%”换成“变为原来的 (1 + x%) 倍”,再用它去除原来的时间。

变式计组由 2013-12 改
某计算机主频为 2GHz,基准程序中 A、B、C 三类指令所占比例分别为 50%、30%、20%,CPI 分别为 1、2、7。该机的 MIPS 数是( )。
  • A. 600
  • B. 800
  • C. 1000
  • D. 2000
展开解析

B平均 CPI = 0.5 × 1 + 0.3 × 2 + 0.2 × 7 = 2.5,MIPS = 主频 ÷ CPI ÷ 106 = 2000 ÷ 2.5 = 800。

变化点:CPI 要按比例加权平均。若把三类的 CPI 直接平均:(1 + 2 + 7) ÷ 3 ≈ 3.33,就会错选 600。

变式计组由 2014-12、2017-12 改
程序 P 在主频 2GHz 的机器 M1 上执行需 10s。编译优化后,P 执行的指令数减少 20%,平均 CPI 增加 10%,并改在与 M1 指令集相同、主频为 2.5GHz 的 M2 上运行,则执行时间为( )。
  • A. 5.63s
  • B. 7.04s
  • C. 8.8s
  • D. 11s
展开解析

BCPU 时间 = 指令数 × CPI ÷ 主频。三个因子分别变为 0.8 倍、1.1 倍、1/1.25 倍:10 × 0.8 × 1.1 ÷ 1.25 = 7.04s。

变化点:2014-12 只改指令数和 CPI(答案 16.8s),2017-12 只比较两台机器。这里三个因子一起变:公式里的每个量各乘上自己的变化倍数即可。忘了换机器会错选 8.8s。

变式计组由 2021-12 改
某 GPU 的单精度浮点峰值性能为 20TFLOPS,表示它每秒最多完成的浮点运算次数为( )。
  • A. 2 万亿次
  • B. 20 万亿次
  • C. 2 千万亿次
  • D. 20 亿亿次
展开解析

BT = 1012,20TFLOPS = 2 × 1013 次/秒。1 万亿 = 1012,所以是 20 万亿次。

变化点:2021-12 是 PFLOPS(1015)。记住 M、G、T、P、E 依次是 106、109、1012、1015、1018;换成中文时,1 万亿 = 1012,1 亿亿 = 1016。

变式计组由 2011-12 改(考纲补充:MFLOPS 计算,尚无真题)
某计算机主频为 1.5GHz,程序 P 共执行 3×109 条指令,其中 1/3 是浮点指令(每条完成 1 次浮点运算),平均 CPI 为 1.5。P 运行时的浮点运算速度约为( )。
  • A. 1000 MFLOPS
  • B. 333 MFLOPS
  • C. 500 MFLOPS
  • D. 1500 MFLOPS
展开解析

B执行时间 T = 3×109 × 1.5 ÷ (1.5×109) = 3 s;浮点运算次数 = 109 次;MFLOPS = 109 ÷ 3 ÷ 106 ≈ 333。A 是 MIPS(所有指令都数了);C 是忘了乘 CPI(把 T 算成 2 s);D 是直接拿主频当答案。

变化点:2011-12 只问“哪个指标描述浮点速度”,这里要真算一次 MFLOPS:分子只数浮点运算,分母是整个程序的执行时间(MIPS 和 MFLOPS)。

第 1 篇自测
  1. MAR 为 20 位、MDR 为 16 位、按字编址,主存容量是多少字节?
    答案
    220 个单元 × 16 位 = 1M × 2B = 2MB。
  2. “并发”和“并行”哪个是 OS 最基本的特征?单核 CPU 上两个进程能并行吗?
    答案
    并发;单核上两个进程只能并发(轮流),不能并行。但 CPU 和 I/O 设备可以并行。
  3. M 主频 2GHz,程序 P 运行 10 s。改进后主频提升,但 CPI 变为原来 1.5 倍,要使 P 只跑 6 s,主频至少多少?
    答案
    原时钟周期数 = 10×2G = 2×1010;新 = 1.5×2×1010 = 3×1010;主频 = 3×1010/6 = 5 GHz(王道例 1.1)。
  4. 微内核为什么性能比宏内核差?
    答案
    很多服务在用户态,服务之间、服务与内核之间要靠消息传递,频繁在用户态和内核态之间切换,开销大。
  5. 硬盘属于主机吗?CPU 能直接执行存在硬盘上的程序吗?
    答案
    不属于,主机 = CPU + 主存,硬盘是辅存、属于外设。CPU 不能直接执行,程序要先由操作系统装入主存。
  6. 用教材记号写出单累加器机器上 STORE X 的全部过程。
    答案
    取指:(PC)→MAR;M(MAR)→MDR;(MDR)→IR;(PC)+1→PC。分析:OP(IR)→CU。执行:Ad(IR)→MAR;(ACC)→MDR;(MDR)→M(MAR)。
  7. 下列哪些是系统软件:操作系统、DBMS、编译程序、办公软件、学生成绩管理系统?
    答案
    操作系统、DBMS、编译程序是系统软件;办公软件、学生成绩管理系统是应用软件。
  8. 某程序 80% 的执行时间在做浮点运算。把浮点部件的速度提高到原来的 4 倍,整体加速比是多少?浮点部件再快,最多能加速多少?
    答案
    S = 1 ÷ (0.2 + 0.8/4) = 1 ÷ 0.4 = 2.5;上限 1 ÷ 0.2 = 5。
  9. 单道批处理、多道批处理、分时系统的特点各是哪几个词?
    答案
    单道:自动性、顺序性、单道性;多道:多道、宏观上并行、微观上串行;分时:同时性(多路性)、交互性、独立性、及时性。

1.✎本篇真题闯关(34 题)

本篇各小节挂的真题全部在这里,按小节排好。可以展开后一道道做,也可以用弹窗模式连续做。做题记录(对/错)只保存在你这台电脑的浏览器里,小节标题下的题号会随之变绿或变红。

第 2 篇 · 计组第 2 章

数据的表示与运算:补码、类型转换、浮点数

一串 0 和 1 可以是正数、负数、小数、字符。这一篇回答三个问题:负数怎么存(补码)、C 语言里不同类型互相赋值时位串怎么变、小数怎么存(IEEE 754)。它是计组选择题里分值最密的一章,而且每年综合题都会拿 C 程序来考它。

对应:王道计组第 2 章难度:★★★本机题库计数(2009—2026):补码 13 题、IEEE 754 11 题、类型转换 8 题、大小端 5 题、标志位 3 题

2.1无符号数和补码:用“时钟”理解负数

★★★ 高频选择题

先认几个名词:真值、机器数、定点数

真值是带“+、−”号的数本身(如 −13、+0.625);机器数是它在机器里的 0/1 编码,连正负号也用 0(正)、1(负)来表示。机器里还要约定小数点在哪:小数点位置固定不变的叫定点数,小数点位置随“阶码”浮动的叫浮点数(2.6 节)。定点数分两种:

符号位数值位 定点整数 0 0 0 0 0 1 0 1 小数点隐含在最低位之后 → 表示整数 0 0000101 = +5 定点小数 0 1 0 1 0 0 0 0 小数点隐含在符号位之后 → 表示纯小数(|x| < 1) 0.1010000 = +(0.5 + 0.125) = +0.625 红点是约定好的小数点位置,它不占任何一位
图 2-1 定点整数与定点小数。同样是 8 位、同样“1 位符号 + 7 位数值”,只是约定的小数点位置不同。C 语言的 int、short 都是定点整数;定点小数主要用在浮点数的尾数上(2.6 节)。

无符号数最简单:n 位二进制直接按权重相加,范围 0 ~ 2n−1。8 位是 0~255,16 位是 0~65535。

负数怎么办?最直观的想法是拿最高位当符号(0 正 1 负),后面放绝对值,这叫原码。但原码有两个 0(+0 和 −0),而且加法电路要先比较符号,很麻烦。计算机真正用来存整数的是补码,它的想法来自钟表:

000000 000111 001022 001133 010044 010155 011066 011177 10008−8 10019−7 101010−6 101111−5 110012−4 110113−3 111014−2 111115−1 顺时针 = +1逆时针 = −1 读图方法 外圈:机器里存的 4 位二进制(机器数) 中圈:当成无符号数读 → 0 ~ 15 里圈:当成补码读 → −8 ~ 7(最高位为 1 的是负数) 4 位只有 16 个格子,走满一圈回到原处:运算都是“模 16”的 −3 在哪?从 0 逆时针走 3 格 → 1101,恰好 = 16 − 3 = 13 蓝线(顶部 1111→0000):15+1 变 0,无符号数“进位”了   对补码只是 −1+1 = 0,完全正确 红线(底部 0111→1000):7+1 变成 −8,补码“溢出”了   对无符号数只是 7+1 = 8,完全正确 同一次加法,结果对不对,取决于你把它当什么数
图 2-2 4 位补码时钟。补码就是把 0~15 这 16 个格子里的后一半(1000~1111)“挪用”来表示 −8~−1。一个负数 x 的补码 = 2n + x(绕一圈回来)。加法器根本不关心你存的是有符号还是无符号,它只会“顺时针走几格”,所以同一个加法器可以做有符号和无符号加减(2011-43 第 3 问)。
n 位补码范围:−2n−1 ~ 2n−1−1(8 位 −128~127,16 位 −32768~32767,32 位 −231~231−1) n 位无符号范围:0 ~ 2n−1 负数 x 的补码 = 2n + x = |x| 的二进制“按位取反,末位加 1” 从补码读真值:最高位的权重是 −2n−1,其余位照常。例:1101 = −8+4+1 = −3
考场速算

求 −x 的 n 位补码:先写出 x,再“取反加 1”;或者直接算 2n−x。例:16 位 −9 → 216−9 = 65527 = FFF7H;或 9 = 0009H,取反 FFF6H,加 1 → FFF7H。
十六进制补码读真值:最高位 ≥ 8 就是负数,真值 = 该数 − 2n。例:8 位 F2H = 242 − 256 = −14;32 位 FFFFFFDFH = −(100000000H − FFFFFFDFH) = −21H = −33。
特殊的那个数:1000…0 是最小负数 −2n−1,对它取反加 1 还是它自己(−(−128) 在 8 位里溢出)。

四种编码放在一起比一比

补码是主角,另外三种选择题会考“范围、0 有几个、怎么互相转换”。以 8 位的 −5 为例:

编码正数负数怎么得−5 的 8 位编码0 有几种8 位范围用在哪
原码符号位 0 + 数值符号位 1 + 绝对值1000 01012(+0、−0)−127 ~ 127浮点数的尾数(“符号 + 绝对值”)
反码绝对值的各位取反1111 10102−127 ~ 127过渡用,基本不单独用
补码反码末位加 1(= 2n + x)1111 10111−128 ~ 127整数
移码真值 + 偏置(8 位常用偏置 128)0111 10111−128 ~ 127浮点数的阶码

记三句话:① 正数的原、反、补码都一样;② 偏置为 2n−1 的移码 = 补码把符号位取反(−5:补码 1111 1011 → 移码 0111 1011);③ 移码的好处是比大小可以直接按无符号比,所以浮点数的阶码用它。注意 IEEE 754 的偏置是 127 而不是 128(2.6 节),所以它的阶码不是“标准”移码。

定点小数的编码和四种编码的表示范围

定点小数的编码规则和整数一模一样,只是数值位的权重变成 2−1、2−2……。例:x = −0.1011₂(= −0.6875),用 1 位符号 + 4 位数值:原码 1.1011;反码 1.0100;补码 1.0101(= 2 + x,“绕一圈”的圈长从 2n 变成了 2)。补码的符号位权重是 −1:1.0101 = −1 + 0.0101₂ = −1 + 0.3125 = −0.6875 ✓。

编码共 n+1 位(1 位符号 + n 位数值)例:8 位(n = 7)
定点整数定点小数整数小数
原码−(2n−1) ~ 2n−1−(1−2−n) ~ 1−2−n−127 ~ 127−127/128 ~ 127/128
反码同原码(都有 +0、−0 两个 0)−127 ~ 127−127/128 ~ 127/128
补码−2n ~ 2n−1−1 ~ 1−2−n−128 ~ 127−1 ~ 127/128
移码−2n ~ 2n−1不用(移码只用来表示整数,即浮点数的阶码)−128 ~ 127—
补码比原码“多一个数”,多的是谁

原码和反码里 0 有两种写法(+0、−0),浪费了一个编码;补码只有一个 0,省下来的那个编码 1000…0 用来表示最小的那个负数:在定点整数里它是 −2n(8 位就是 −128),在定点小数里它是 −1([−1]补 = 1.0000000)。这两个数只有补码(和移码),没有原码、反码——“−128 的原码是多少”这种问法本身就不成立。

上表里 n 是数值位个数(总位数 n+1)。前面公式“n 位补码范围 −2n−1 ~ 2n−1−1”里的 n 是总位数,两种写法说的是同一件事,做题时先看清题目给的是总位数还是数值位数。

四种编码互相转换的速算法

要做什么方法例(8 位)
原码 → 补码(负数)符号位不变,数值位取反,末位加 1−13:1000 1101 → 1111 0010 → 1111 0011
↑ 的快捷法符号位不变;数值位从右往左找第一个 1,这个 1 和它右边的 0 都不变,它左边的数值位全部取反−40:1010 1000 → 最右的 1 在第 3 位,右边 000 不变,左边数值位 010 取反为 101 → 1101 1000
补码 → 原码(负数)完全同上(再做一次“数值位取反、末位加 1”,或用同一个快捷法)1111 0011 → 1000 1100 → 1000 1101
[x]补 → [−x]补连同符号位一起取反,末位加 1[−13]补 1111 0011 → 0000 1100 → 0000 1101 = [+13]补
补码 ↔ 移码偏置为 2n−1(8 位偏置 128)时:符号位取反,其余不变[−13]补 1111 0011 ↔ [−13]移 0111 0011
负数的 反码= 原码数值位取反 = 补码 − 1[−13]反 = 1111 0010
最容易混的两个“取反加 1”

求补码(或由补码求原码):符号位不动,只动数值位——这是在同一个数的两种编码之间换。
由 [x]补 求 [−x]补:符号位也要一起取反——这是在求另一个数(相反数)的补码,减法 A − B 就是这样把 −B 做出来的(2.4 节)。
还有一个常用的:由 [x]补 求 [x/2]补 = 算术右移 1 位,求 [2x]补 = 算术左移 1 位(2.3 节,2013-14)。

2023-13计组
若 short 型变量 x = −8190,则 x 的机器数为( )。
  • A. E002H
  • B. E001H
  • C. 9FFFH
  • D. 9FFEH
展开解析

考点2.1 求负数的补码

A两种算法任选:① 216 − 8190 = 65536 − 8190 = 57346 = E002H;② 8190 = 1FFEH,取反得 E001H,再加 1 得 E002H。

  • A 对E002H。
  • B 错E001H 是反码(取反后忘了加 1)。
  • C 错9FFFH 既不是 −8190 的原码(9FFEH),也不是补码。
  • D 错9FFEH 是 −8190 的原码(符号位 1 + 绝对值 1FFEH)。

负数补码 = 2n − |x| = 绝对值取反加 1;别和原码(符号位置 1)、反码(只取反)混。

2015-13计组
由 3 个“1”和 5 个“0”组成的 8 位二进制补码,能表示的最小整数是( )。
  • A. −126
  • B. −125
  • C. −32
  • D. −3
展开解析

考点2.1 补码的权重

B8 位补码的最高位权重是 −128,其余位是 +64 … +1。要最小:最高位必须是 1(先拿到 −128),剩下两个 1 放在权重最小的两位,加回来的正数最小。

  1. 1000 0011 = −128 + 2 + 1 = −125。
  • A 错−126 = 1000 0010,只有 2 个 1。
  • B 对1000 0011,恰好 3 个 1。
  • C 错−32 = 1110 0000,虽然是 3 个 1,但不是最小的。
  • D 错−3 = 1111 1101,有 7 个 1。

补码读值:最高位权重 −2n−1,其他位照常相加。

2021-13计组
已知带符号整数用补码表示,变量 x、y、z 的机器数分别为 FFFDH、FFDFH、7FFCH,下列结论中,正确的是( )。
  • A. 若 x、y 和 z 为无符号整数,则 z < x < y
  • B. 若 x、y 和 z 为无符号整数,则 x < y < z
  • C. 若 x、y 和 z 为带符号整数,则 x < y < z
  • D. 若 x、y 和 z 为带符号整数,则 y < x < z
展开解析

考点2.1 无符号数和补码

D同一串位,当无符号数就直接比十六进制大小;当补码就先看最高位定正负,再算真值。

  1. 当带符号(补码):FFFDH = −3,FFDFH = −33,7FFCH = 32764 → y < x < z。
  2. 当无符号:直接比十六进制,7FFCH < FFDFH < FFFDH → z < y < x。
  • A 错无符号时 z 最小没错,但 x 与 y 比反了:FFFDH 与 FFDFH 前两位相同,第三位 F > D,所以 x > y,应为 z < y < x。
  • B 错无符号时 7FFCH 最高位是 0,是三个里最小的,不可能最大;x < y < z 的顺序完全反了。
  • C 错带符号时 x = −3、y = −33,−3 > −33,所以 x > y,不是 x < y。
  • D 对带符号:y = −33 < x = −3 < z = 32764。

无符号比位串;补码先看符号位:负数的补码位串越大,真值越大(越接近 0)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2015-13 改
由 2 个“1”和 6 个“0”组成的 8 位二进制补码,能表示的最大整数是( )。
  • A. 96
  • B. 3
  • C. −127
  • D. 127
展开解析

A要最大,最高位(符号位)必须是 0,两个 1 放在其余位中权值最大的两位:0110 0000B = 96。

变化点:2015-13 问最小值:符号位放 1,其余的 1 放在最低位。求最大值时正好反过来。

变式计组由 2021-13 改
16 位变量 x、y、z 的机器数分别为 8001H、7FFFH、FFFFH,下列结论中正确的是( )。
  • A. 若为带符号整数,则 x < z < y
  • B. 若为带符号整数,则 z < x < y
  • C. 若为无符号整数,则 x < y < z
  • D. 若为无符号整数,则 z < x < y
展开解析

A带符号:x = 8001H − 10000H = −32767,y = 32767,z = −1,所以 x < z < y。无符号:x = 32769,y = 32767,z = 65535,所以 y < x < z,C、D 都不对。

变化点:同样是“两种读法比大小”。最高位为 1 的数:按无符号读很大,按有符号读是负数,排序会整个翻过来。

变式计组由 2023-13 改
若 short 型变量 x = −300,则 x 的机器数为( )。
  • A. FED4H
  • B. FED3H
  • C. 812CH
  • D. 012CH
展开解析

A216 − 300 = 65236 = FED4H。也可以写出 300 = 012CH,取反得 FED3H,再加 1 得 FED4H。

变化点:只换了数。B 是忘了加 1(反码),C 是原码。

2.2C 语言里的类型转换:位串到底变没变

★★★ 高频选择题综合题

这是真题最爱考的地方(2009、2012、2016、2019、2024、2025 都有选择题,2011-43、2017-43 是大题)。只要记住三条规则:

情况位串怎么变例子(short 16 位,int 32 位)
① 等长,有符号 ↔ 无符号位串一点不变,只是换个读法short −32767 = 8001H → 当 unsigned short 读 = 32769(2016-13)
② 短 → 长(扩展)看原来的类型:原来有符号 → 符号扩展(高位补符号位);原来无符号 → 零扩展(高位补 0)short −9 = FFF7H → int FFFFFFF7H
unsigned short 65530 = FFFAH → unsigned int 0000FFFAH(2012-13)
③ 长 → 短(截断)直接砍掉高位,只留低位,值可能完全变了int 32777 = 00008009H → short 8009H = −32759(2024-12)
符号扩展:short −9(FFF7H)→ int 1111 1111 1111 0111 1111 1111 1111 1111 1111 1111 1111 0111 = FFFFFFF7H,仍是 −9 ✓补 16 个符号位 1 零扩展:unsigned short 65530(FFFAH)→ unsigned int 0000 0000 0000 0000 1111 1111 1111 1010 = 0000FFFAH,仍是 65530 ✓补 16 个 0 截断:int 32777(00008009H)→ short 0000 0000 0000 0000 1000 0000 0000 1001 = 8009H,当 short 读是 −32759 ✗
图 2-3 三种变化。扩展永远保持数值不变(前提是按原类型选对扩展方式);截断只在原值恰好落在短类型范围内时才保持数值。
两个易错点

① 短有符号 → 长无符号(如 short → unsigned int):C 规定先按“原来是有符号”做符号扩展,再按无符号读。short −32767 = 8001H → FFFF8001H → 当无符号 = 232 − 32767 = 232 − 215 + 1(2025-12 选 D)。
② 有符号和无符号混在一起运算/比较时,C 会把有符号的那个转成无符号。所以 unsigned n = 0; n - 1 得到的是 FFFFFFFFH = 4294967295,而不是 −1(2017-43 第 1 问的死循环就是这么来的)。

2009-12计组
一个 C 语言程序在一台 32 位机器上运行。程序中定义了三个变量 x、y 和 z,其中 x 和 z 是 int 型,y 为 short 型。当 x = 127,y = −9 时,执行赋值语句 z = x + y 后,x、y 和 z 的值分别是( )。
  • A. x=0000007FH,y=FFF9H,z=00000076H
  • B. x=0000007FH,y=FFF9H,z=FFFF0076H
  • C. x=0000007FH,y=FFF7H,z=FFFF0076H
  • D. x=0000007FH,y=FFF7H,z=00000076H
展开解析

考点2.1 补码2.2 类型转换:符号扩展

D先用“数值”排除最快:y 的补码看正负和大小,z = 127 − 9 = 118 是正数;再算机器数验证。

  1. x = 127 = 0000007FH(四个选项相同)。
  2. y = −9 的 16 位补码:9 = 0009H,取反 FFF6H,加 1 → FFF7H。验算:FFF7H + 0009H = 1 0000H,低 16 位为 0 ✓。
  3. z = x + y:y 先符号扩展成 FFFFFFF7H,0000007FH + FFFFFFF7H = 1 00000076H,最高位进位丢掉 → 00000076H = 118 ✓。
  • A 错y 写成了 FFF9H。FFF9H 是 −7 的补码(FFF9H + 0007H = 1 0000H),常见错法是“前面补 F、后面照抄 9”,没有做取反加一。
  • B 错y 同样错成了 FFF9H(那是 −7);z = FFFF0076H 最高位为 1 是个负数(−65418),而 127 − 9 = 118 是正数。
  • C 错y 对了,但 z = FFFF0076H 是负数。错法是把 y 符号扩展出来的高 16 位 FFFF 直接抄进结果,忘了相加时低位来的进位会把高位 FFFF 全变成 0(进位丢掉)。
  • D 对y = FFF7H,z = 00000076H = 118,与真值一致。

short 转 int 做符号扩展;补码加法的进位丢掉。选择题先比“正负和大小”,再动手算机器数。

2024-12计组
C 语言代码如下:int i = 32777; short si = i; int j = si; 执行上述代码段后,j 的值为( )。
  • A. −32777
  • B. −32759
  • C. 32759
  • D. 32777
展开解析

考点2.2 截断与符号扩展

B两步转换:int → short 是截断(砍掉高 16 位);short → int 是符号扩展(数值不变)。

  1. 32777 = 32768 + 9 = 0000 8009H。
  2. 截断成 short:8009H,最高位是 1,按补码读 = 8009H − 10000H = 32777 − 65536 = −32759。
  3. si 赋给 int:符号扩展为 FFFF 8009H,值仍是 −32759。
  • A 错−32777 是把原值直接加了负号;而 −32777 小于 short 的最小值 −32768,根本存不进 short,j 不可能是它。
  • B 对−32759。
  • C 错32759 符号弄反了(8009H 最高位是 1,是负数)。
  • D 错32777 超出 short 范围(最大 32767),截断后不可能保持原值。

长 → 短:只有原值在短类型范围内才保持不变;短 → 长:按原类型扩展,数值一定不变。

2019-13计组
unsigned short usi = 65535; short si = usi; 执行后 si 的值是( )。
  • A. −1
  • B. −32767
  • C. −32768
  • D. −65535
展开解析

考点2.2 等长转换

Aunsigned short → short 等长,位串不变:65535 = FFFFH,按补码读,16 个 1 就是 −1。

  • A 对FFFFH 的补码真值 = 65535 − 65536 = −1。
  • B 错−32767 = 8001H。
  • C 错−32768 = 8000H,是 16 位补码能表示的最小数。
  • D 错−65535 超出 short 的范围(−32768 ~ 32767)。

全 1 的位串:无符号是最大数 2n − 1,有符号是 −1。

C 语言各类型占多少位(408 默认 32 位机器)

类型charshortintlonglong long指针floatdouble
位数8163232
64 位 Linux 为 64
6432
64 位系统为 64
3264
表示补码(有符号)/ 无符号二进制;unsigned 版本位数相同地址IEEE 754

题目若另有规定(如 2011-43 的“8 位字长计算机”里 int 是 8 位),以题目为准。

整数和浮点数之间怎么转

转换会发生什么例
int → float不会溢出(float 范围大得多),但 float 只有 24 位有效位,|x| 超过 224 时末尾的位要舍入16777217(224+1)→ 16777216.0
int → double精确:double 有 53 位有效位,装得下任何 int
float → double精确:位数只多不少2010-14 Ⅲ
double → float可能溢出(超过 float 最大值变成 ±∞),也可能舍入(有效位从 53 降到 24)(float)1e40 = +∞
float / double → int小数部分直接截断(向 0 取整,不是四舍五入);超出 int 范围时结果不确定(int)3.7 = 3,(int)−3.7 = −3;2010-14 Ⅱ

有符号数与无符号数混用:比较结果会“反常”

C 规定:一个运算(包括比较)的两边一个是 int、一个是 unsigned int 时,先把 int 按位串不变转成 unsigned 再算。于是负数一下变成很大的正数:

表达式(32 位)结果原因
-1 < 0真两边都是 int,正常比较
-1 < 0U假0U 是 unsigned,−1 被当成 FFFFFFFFH = 4294967295
2147483647 > -2147483647-1真两边都是 int:最大值 > 最小值
2147483647U > -2147483647-1假右边的 INT_MIN(80000000H)被当成 2147483648
(unsigned)-1 > -2真4294967295 > 4294967294
unsigned n = 0; n - 14294967295无符号减法“绕一圈”,所以 for (i = 0; i <= n - 1; i++) 在 n = 0 时死循环(2017-43)

以上结果都用 gcc 实测过。char、short 参与运算时先被提升成 int(“整型提升”),所以两个 short 相加不会在 16 位上溢出,而是先变成 int 再加。

跟我判:混合类型的表达式到底按什么类型算

问题引入:上面两张表分别讲了“整数 ↔ 浮点”和“有符号 ↔ 无符号”。真题常把它们揉进一个表达式:-1 > 1u 为什么是真?两个 short 比较为什么又不会“反常”?关键是先判断这个运算最终按哪种类型做。

大白话:C 语言做二元运算前,先把两边“拉到同一个类型”,而且总是往“能装下更多东西”的方向拉,像两个人对暗号前先统一用哪种语言。不像的地方:往 unsigned 拉的时候并不保证“装得下”负数——−1 会被硬读成 4294967295,这正是坑所在。

教材 / C 标准的说法(408 默认 32 位,int 与 long 都是 32 位):① 整型提升:char、short(不论有无符号)参加运算前先变成 int;② 寻常算术转换:两边类型不同时,向下图中靠右的类型转换;③ 赋值时转换成左边变量的类型(可能截断、可能舍入)。

char、short(含 unsigned) int unsigned int long long float double 整型提升 int 遇到 unsigned:位串不变,按无符号读(负数变成很大的正数) 两边类型不同 → 向右边的类型转换;整数遇到浮点数 → 转成浮点数(可能舍入)
图 2-4 混合运算的转换方向。先做“整型提升”(char、short 一律先变 int),再看两边:谁更靠右就按谁算。最危险的一步是红框——int 和 unsigned int 相遇。

跟我判(32 位,结果全部 gcc 实测,程序见 verify/08/p02_mix.c):

表达式结果判断过程
-1 > 1u1(真)int 遇 unsigned → −1 按无符号读 = 4294967295 > 1
short s=-1; unsigned short us=1;
s > us
0(假)两个都先整型提升成 int(−1 和 1),按 int 比较,−1 < 1。“反常”只发生在 int 与 unsigned int 之间
unsigned u=10; int i=-20;
u + i > 0
1(真)u + i 按无符号算:10 − 20 绕一圈 = 4294967286 > 0;若赋给 int 再看,(int)(u+i) = −10
float f=16777217;
(int)f == 16777217
0(假)224+1 存进 float 要舍入(24 位有效位),f = 16777216.0,转回 int 是 16777216
0.1f == 0.10(假)float 转成 double 比较;0.1 在两种精度下舍入到的值不同
3 / 2 * 2.02从左往右:3/2 两边都是 int,先做整数除法得 1,再 1 × 2.0 = 2.0。对比 3 / 2.0 * 2 = 3

真题怎么考:2009-12(short 与 int 相加,y 先符号扩展)、2016-13 / 2019-13(等长换读法)、2017-43(无符号 n − 1 绕圈导致死循环)、2010-14(int、float、double 来回转)。判别法:先找出运算的最终类型,再决定是“位串不变换读法”“扩展”“截断”还是“舍入”。失效的情况:题目换了机器(如 2011-43 的 8 位机里 int 只有 8 位),类型位数要按题目改。

自测
  1. 32 位机上,unsigned char a = 200, b = 100; 则 a + b 的值和类型是什么?
    答案
    两者先整型提升为 int:200 + 100 = 300,类型 int,不会在 8 位上溢出变成 44。只有把结果再赋给 unsigned char 时才截断成 44。
  2. int i = -1; unsigned int u = 0; 判断 i < u。
    答案
    假。i 转成 unsigned = 4294967295,不小于 0。

大题精讲:2011-43(8 位机上的有符号/无符号运算)

2011-43计组综合题 11 分
假定在一个 8 位字长的计算机中运行如下 C 程序段:
unsigned int x = 134;  unsigned int y = 246;
int m = x;  int n = y;
unsigned int z1 = x - y;  unsigned int z2 = x + y;
int k1 = m - n;  int k2 = m + n;
编译器将 8 个 8 位寄存器 R1~R8 分别分配给 x、y、m、n、z1、z2、k1、k2。(1)R1、R5、R6 的内容(十六进制)?(2)m 和 k1 的值(十进制)?(3)这四种运算能否用同一个加法器及辅助电路实现?(4)计算机内部如何判断带符号加减是否溢出?哪些带符号运算语句会溢出?
展开解析
  1. 写机器数。134 = 128+6 = 1000 0110 = 86H → R1 = 86H。246 = 256−10 = 1111 0110 = F6H。
  2. z1 = x − y:减法 = 加上 y 的补数:86H + (100H − F6H) = 86H + 0AH = 90H → R5 = 90H(无符号意义下是 134−246+256 = 144,发生了借位)。
    z2 = x + y = 86H + F6H = 17CH,8 位只留 7CH → R6 = 7CH(最高位进位丢了)。
  3. m 的位串就是 86H,当有符号读:86H − 100H = 134 − 256 = −122。n = F6H = −10。k1 = m − n 的位串和 z1 一样是 90H,当有符号读 = 144 − 256 = −112(验算:−122 − (−10) = −112 ✓)。
  4. (3)能。n 位加法器做的就是“模 2n 的加法”(图 2-2 顺时针走格子)。无符号和补码的加法都是模 2n 相加,减法都是“加上减数的补数”(取反加 1),所以一个加法器 + 取反电路 + 进位输入就够了,区别只在于怎么解读结果和标志。
  5. (4)溢出判断:两个同号数相加,结果符号却与它们不同 → 溢出;或者:最高位的进位 ≠ 次高位的进位 → 溢出。
    k1 = −112 在 −128~127 内,不溢出;k2 = m + n = −122 + (−10) = −132 < −128,溢出:1000 0110 + 1111 0110 = (1)0111 1100,两个负数加出了正数 7CH。

答案:R1 = 86H,R5 = 90H,R6 = 7CH;m = −122,k1 = −112;能;只有 k2 = m + n 溢出。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2025-12、2016-13 改
在 32 位计算机上执行 C 语言代码 short si = -2; unsigned int ui = si;,则 ui 的真值为( )。
  • A. 216 − 2
  • B. 231 − 2
  • C. 232 − 2
  • D. 2
展开解析

Csi 是有符号数,短变长做符号扩展:FFFEH → FFFF FFFEH。再按无符号数读:232 − 2。

变化点:2025-12 是 −32767(答案 232 − 215 + 1)。规律:负数 −k 先符号扩展成 32 位,再按无符号读,就是 232 − k。若误做零扩展,就会得到 A。

变式计组由 2024-12 改
C 语言代码 int i = 98305; short si = i; int j = si; 执行后,j 的值为( )。
  • A. 98305
  • B. 32769
  • C. −32767
  • D. −32769
展开解析

C98305 = 1 8001H。赋给 short 时截去高位,得 8001H,最高位为 1,是负数:8001H − 10000H = −32767。再赋给 int 做符号扩展,值不变。

变化点:2024-12 是 32777(= 8009H,答案 −32759)。这里的数超过了 216,截断时连第 17 位也砍掉了。方法一样:只看低 16 位,最高位为 1 就减 65536。

变式计组由 2012-13、2019-13 改
执行 unsigned short x = 65530; int y = x; short z = x; 后,y 和 z 的值分别为( )。
  • A. y = 65530,z = −6
  • B. y = −6,z = −6
  • C. y = 65530,z = 65530
  • D. y = −6,z = 65530
展开解析

Ax = FFFAH。y = x:x 是无符号数,零扩展为 0000 FFFAH,y = 65530。z = x:等长赋值,位串不变,按有符号数读,FFFAH = −6。

变化点:扩展方式看原来的类型(这里是 unsigned,补 0),读法看目标的类型。2012-13 只问扩展,2019-13 只问等长换读法,这里两个一起考。

变式计组由 2016-13、2017-43 改
在 32 位计算机上执行:short a = -1; unsigned short b = 1; int x = (a > b); unsigned int c = 1; int y = (-1 > c); 则 x、y 的值分别为( )。
  • A. 1、1
  • B. 0、0
  • C. 1、0
  • D. 0、1
展开解析

Da、b 都是 16 位,参加比较前先整型提升成 int:−1 > 1 为假,x = 0。−1 与 unsigned int 比较时,−1 按位串不变转成无符号数 4294967295,大于 1,y = 1(gcc 实测)。

变化点:同样是“有符号与无符号比较”,只有 int 遇到 unsigned int 才会“反常”;short、unsigned short 会先变成 int 再比(跟我判:混合类型的表达式)。

2.3移位:逻辑移位与算术移位

★★★ 高频选择题综合题
逻辑移位(给无符号数用)
左移、右移,空出来的位都补 0。
1101 1000 逻辑右移 1 → 0110 1100
算术移位(给补码用)
左移低位补 0;右移高位补符号位(保持正负不变)。
1101 1000 算术右移 1 → 1110 1100
原数(补码 −40) 1 1 0 1 1 0 0 0 逻辑左移 1 位 1 0 1 1 0 0 0 0 最高位 1 被移出,最低位补 0 逻辑右移 1 位 0 1 1 0 1 1 0 0 最高位补 0(当无符号数:216 → 108) 算术右移 1 位 1 1 1 0 1 1 0 0 最高位补符号位 1(补码:−40 → −20) 循环左移 1 位 1 0 1 1 0 0 0 1 移出的最高位 1 回到最低位
图 2-5 四种移位。橙色格是最高位(补码的符号位),红色数字是新补进来的位。逻辑移位不管符号,空位一律补 0;算术右移补的是原来的符号位,所以负数右移后还是负数;循环移位把移出去的位从另一头送回来(2018-16 考过逻辑右移)。
  • 左移 1 位 = ×2,右移 1 位 = ÷2。算术右移对负数是向下取整:−513 算术右移 1 位 = −257(不是 −256)。16 位 −513 = FDFFH → FEFFH(2012-44 第 1 问)。
  • 算术左移时,如果移出去的位和新的符号位不同,就溢出了(值超出范围)。
  • 编译器常把“乘以常数”优化成移位和加法:x*2 → 左移 1 位;a[i] 的地址 = 首地址 + i×4 → 用 sll R4, R2, 2(左移 2 位)算 i×4(2014-44、2024-44)。
2026-13计组
对机器数 1010 0110B 先执行算术右移 3 位,再执行算术左移 2 位,最终结果是( )。
  • A. 1101 0000B
  • B. 1101 0011B
  • C. 0101 0000B
  • D. 0101 0011B
展开解析

考点2.3 算术移位2.3 补码怎么算术移位

A补码算术右移:高位补符号位;算术左移:低位补 0。

  1. 算术右移 3 位:符号位是 1,高位补 3 个 1,低 3 位 110 被移出 → 1111 0100。
  2. 算术左移 2 位:低位补 0,高 2 位 11 被移出 → 1101 0000。
  • A 对1010 0110 → 1111 0100 → 1101 0000。
  • B 错低 2 位是 11:算术左移时把“补符号位”的规则也用到了低位上。补符号位只用于右移的高位,左移低位一律补 0。
  • C 错右移时高位补了 0(做成了逻辑右移):1010 0110 → 0001 0100,再左移得 0101 0000。补码算术右移必须补符号位 1。
  • D 错两处都错:右移高位补 0(逻辑右移),左移低位又补了 1。

算术右移补符号位、逻辑右移补 0;左移(算术、逻辑)低位都补 0。同类:2018-16(1101 1000 逻辑右移 0110 1100、算术右移 1110 1100,选 B);2013-14(x = 1111 0100,y = 1011 0000,2x + y/2 = 1110 1000 + 1101 1000 = 1100 0000,选 A)。

原码、反码、补码各自怎么算术移位

上面讲的“算术移位”是对补码而言的。教材还要求知道原码、反码的算术移位:规则是符号位不动,只移数值位,空出来的位补什么,取决于“补什么才能让值恰好 ×2 或 ÷2”:

码制正数(左移、右移)负数左移,低位补负数右移,高位补为什么
原码都补 000负数原码的数值位就是绝对值,和正数一样补 0
反码11负数反码的数值位是绝对值取反,原来该补 0 的地方取反后变 1
补码01补码从右往左第一个 1 的右边和原码相同(低位补 0),左边和反码相同(高位补 1)
−26(8 位)机器数左移 1 位(−52)右移 1 位(−13)
原码1 001 10101 011 01001 000 1101
反码1 110 01011 100 10111 111 0010
补码1 110 01101 100 11001 111 0011

红色是新补进来的位。三种码移位后分别是 −52、−13 的原码、反码、补码(已验算)。什么时候出错:左移时如果移出去的数值位不是“本该补进来的那个值”(正数移出 1;原码负数移出 1;反码、补码负数移出 0),结果就超出范围了(溢出);右移时移出 1 只会丢精度(相当于舍入)。

右移 ≠ C 语言的除法

算术右移对负数是向下取整(往 −∞ 方向),C 语言的整数除法 / 是向 0 取整:−7 >> 1 = −4,而 −7 / 2 = −3(gcc 实测)。正数两者一样。另外 C 语言里 >> 作用于有符号数时(常见编译器)是算术右移,作用于 unsigned 时是逻辑右移:-8 >> 1 = -4,(unsigned)-8 >> 1 = 2147483644。

循环移位:不带进位(小循环)与带进位(大循环)

问题引入:逻辑移位、算术移位都会把移出去的那一位“扔掉”。可如果想把一个 16 位数的高低字节对调,或者在 8 位机上把一个 16 位数整体左移一位,扔掉的那一位就必须“找回来”——这就要用循环移位。

打个比方

把 8 个位排成一圈转一格。不带进位(小循环)像 8 个人围着一张桌子换座位,从一头挤出去的人从另一头坐回来,顺便在门口登记一下(抄进进位标志 CF);带进位(大循环)是把门口登记的那个人(CF)也拉进圈里,9 个人一起转。不像的地方:位永远不会丢,小循环转 8 次、大循环转 9 次就回到原样。

名称移出去的位去哪空出来的位补什么典型用途
循环左移(不带进位,ROL)最高位移到最低位,同时送进 CF原来的最高位交换高低字节、逐位检查
循环右移(不带进位,ROR)最低位移到最高位,同时送进 CF原来的最低位同上
带进位循环左移(RCL)最高位送进 CF原来的 CF多字长数的移位
带进位循环右移(RCR)最低位送进 CF原来的 CF多字长数的移位
不带进位循环左移(小循环) CF 10010111 最高位绕回最低位 同时抄进 CF 1001 0111,CF = 0 → 0010 1111,CF = 1 循环右移同理:→ 1100 1011,CF = 1 带进位循环左移(大循环) CF原 0 10010111 最高位进 CF 原来的 CF 补进最低位(CF 也在圈里) 1001 0111,CF = 0 → 0010 1110,CF = 1 带进位循环右移同理:→ 0100 1011,CF = 1
图 2-6 两种循环左移。同一个数 1001 0111、CF = 0,只有最低位不同:小循环补的是移出去的最高位 1,大循环补的是原来的 CF(0)。两者移完后 CF 都是 1。

跟我做一遍(已用 verify/08/p02_calc.py 验算):

  1. 交换高低字节:16 位 1234H 不带进位循环左移 8 位 → 3412H。高字节 12H 整个转到了低位——2.7 节大小端互换时,硬件或库函数就是这样做的。
  2. 多字长移位:8 位机上把 16 位数 0001 0010 | 1011 0100 左移 1 位。先对低字节逻辑左移:1011 0100 → 0110 1000,移出的最高位 1 进入 CF;再对高字节做带进位循环左移:0001 0010 → 0010 0101(CF 的 1 补进最低位)。拼起来 0010 0101 0110 1000,和直接 16 位左移的结果一样。

真题怎么考:408 选择题考的多是逻辑移位和算术移位(2013-14、2018-16、2026-13),循环移位一般作为“移位类型”的概念出现。判别只看两件事:移出去的位去哪、空出来的位补什么。失效的情况:带进位循环移位的结果取决于原来的 CF,题目没给 CF 的值就不能确定最低(高)位。

自测
  1. 8 位数 0110 1001,CF = 1,带进位循环右移 1 位后,结果和 CF 各是什么?
    答案
    原 CF = 1 补进最高位,最低位 1 送进 CF:1011 0100,CF = 1。
  2. 8 位数不带进位循环左移 8 次后是什么?带进位循环左移几次才回到原样?
    答案
    不带进位转 8 次回到原数;带进位时 CF 也在圈里,共 9 位,要转 9 次才回到原样(数和 CF 都复原)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2013-14 改
某字长为 8 位的计算机中,整型变量 x、y 的机器数分别为 [x]补 = 1111 1000,[y]补 = 0110 0000。若 z = 2y + x/2(乘除用算术移位实现),则 z 的机器数为( )。
  • A. 1011 1100
  • B. 0011 1100
  • C. 1100 0000
  • D. 溢出
展开解析

Dx = −8,y = 96。2y = 192 超出 8 位补码范围(−128 ~ 127),溢出。从移位看:0110 0000 左移一位,最高两位 0、1 不同,符号位由 0 变 1,所以溢出。

变化点:2013-14 的 2x + y/2 不溢出(答案 1100 0000)。这里只把 x、y 的位置对调,左移的对象变成了 96,就溢出了。A 是忽略溢出、把 2y 当成 −64 算出的结果。

变式计组由 2018-16、2026-13 改
对机器数 1001 0110B 先逻辑左移 1 位,再算术右移 2 位,结果是( )。
  • A. 0000 1011B
  • B. 1110 0101B
  • C. 1100 1011B
  • D. 0010 1100B
展开解析

A逻辑左移 1 位:0010 1100B(最高位的 1 被移出)。再算术右移 2 位,高位补符号位 0:0000 1011B。

变化点:2026-13 是先算术右移再算术左移。这里第一步换成逻辑左移,符号位被移掉了,第二步补的是新的符号位 0,不是原来的 1。

变式计组考纲补充(尚无真题)
8 位寄存器的内容为 1100 1010,进位标志 CF = 1。执行一次带进位循环右移后,寄存器内容和 CF 分别为( )。
  • A. 0110 0101,0
  • B. 1110 0101,0
  • C. 0110 0101,1
  • D. 1110 0101,1
展开解析

B带进位循环右移时 CF 也在“圈”里:原来的 CF(1)补进最高位,原来的最低位(0)送进 CF。1100 1010 → 1110 0101,CF = 0。A 是把它当成逻辑右移(最高位补 0);C、D 把 CF 弄错了。

考点:循环移位。看清两件事:移出去的位去哪、空出来的位补什么。

2.4加减法电路与四个标志位

★★★ 高频选择题综合题

CPU 里只有一个加法器,减法 A − B 是这样做的:把 B 按位取反,再让最低位进位输入为 1,于是算的是 A + (~B) + 1 = A + [−B]补。运算完,电路顺手生成四个标志位存进标志寄存器(PSW),后面的条件跳转指令就靠它们判断大小。

A B 减法时按位取反 n 位加法器 进位输入 Cin减法 = 1 结果 F(n 位) 最高位进位 Cout 标志位(存进 PSW) ZF零标志:F 全为 0 则为 1 SF符号标志:= F 的最高位 CF进/借位标志(给无符号数看)加法 CF = Cout;减法 CF = Cout ⊕ 1 OF溢出标志(给有符号数看)= 最高位进位 ⊕ 次高位进位= 两同号数相加,结果符号变了
图 2-7 加法器与标志位。硬件不管你是有符号还是无符号,四个标志全都算出来;程序员(编译器)按数据类型挑着看:无符号数看 CF,有符号数看 OF。减法时 Cout = 1 表示“够减、没借位”,所以借位标志 CF 要取反。
判标志的笨办法最可靠

不用硬背电路公式,直接用“数值”判断:
CF:把两个数当无符号数,做加法结果 ≥ 2n(装不下)→ CF=1;做减法被减数 < 减数(不够减)→ CF=1。
OF:把两个数当补码,真实结果超出 −2n−1~2n−1−1 → OF=1。
ZF:结果为 0 → 1。SF:结果最高位。

比较 A 和 B(先做 A − B)无符号数(看 CF、ZF)有符号数(看 SF、OF、ZF)
A = BZF = 1
A < BCF = 1(借位了)SF ⊕ OF = 1
A > BCF = 0 且 ZF = 0SF ⊕ OF = 0 且 ZF = 0
A ≤ BCF = 1 或 ZF = 1(SF ⊕ OF) = 1 或 ZF = 1
2018-19计组
减法指令 sub R1, R2, R3 的功能为 (R1) − (R2) → R3,该指令执行后将生成进位/借位标志 CF 和溢出标志 OF。若 (R1) = FFFFFFFFH,(R2) = FFFFFFF0H,则该减法指令执行后,CF 与 OF 分别为( )。
  • A. CF=0,OF=0
  • B. CF=1,OF=0
  • C. CF=0,OF=1
  • D. CF=1,OF=1
展开解析

考点2.4 CF 与 OF

A硬件同时算出 CF 和 OF:CF 按无符号判断是否借位,OF 按有符号判断是否溢出。

  • A 对无符号:FFFF FFFFH ≥ FFFF FFF0H,够减 → CF = 0;有符号:(−1) − (−16) = 15,在范围内 → OF = 0。
  • B 错CF = 1 错:按无符号比,被减数 FFFF FFFFH ≥ 减数 FFFF FFF0H,够减,不借位,CF = 0。
  • C 错OF = 1 错:按有符号算 (−1) − (−16) = 15,远在 int 范围内,不溢出,OF = 0。
  • D 错两个标志都判错:无符号够减(CF = 0),有符号结果 15 不溢出(OF = 0)。

判 CF:当无符号,“不够减”才借位;判 OF:当补码,算真值看是否越界。

2023-16计组
已知 x、y 为 int 类型,当 x=100、y=200 时,执行 x−y 指令得到的溢出标志 OF 和借位标志 CF 分别为 0、1,那么当 x=10、y=−20 时,执行该指令得到的 OF 和 CF 分别是( )。
  • A. 0, 0
  • B. 0, 1
  • C. 1, 0
  • D. 1, 1
展开解析

考点2.4 四个标志位2.4 溢出判断

B同一次减法,OF 按有符号结果判(真值是否超范围),CF 按无符号判(被减数是否不够减)。第一句告诉你 CF 是“借位”口径:100 − 200 不够减,CF = 1。

  1. OF:有符号 10 − (−20) = 30,在 int 范围内 → OF = 0。
  2. CF:−20 的位串是 FFFF FFECH,当无符号数约 4.29 × 109;10 比它小,不够减,要借位 → CF = 1。
  • A 错OF = 0 对;CF 错:不能看有符号结果 30 是正数就说不借位,CF 要把两个数都当无符号比,10 < FFFF FFECH,必须借位。
  • B 对OF = 0、CF = 1。
  • C 错两个都反了:30 在 int 范围内,OF 应为 0;无符号 10 < FFFF FFECH 不够减,CF 应为 1。
  • D 错CF = 1 对;OF 错:异号相减(正 − 负)可能溢出,但这里真值只有 30,远没超出 int 范围。

OF 管有符号、CF 管无符号,同一条指令两个标志各算各的。减法时 CF = 1 表示“无符号不够减”(借位口径)。

2011-17计组
某机器有一个标志寄存器,其中有进位/借位标志 CF、零标志 ZF、符号标志 SF 和溢出标志 OF,条件转移指令 bgt(无符号整数比较大于时转移)的转移条件是( )。
  • A. CF + OF = 1
  • B. SF + ZF = 1
  • C. CF + ZF = 1
  • D. CF + SF = 1
展开解析

考点2.4 标志位与比较

C比较 A 和 B 就是做 A − B。无符号数只看 CF 和 ZF:A > B ⇔ 不借位(CF = 0)且结果不为 0(ZF = 0)⇔ CF + ZF = 0 ⇔ CF + ZF = 1。

  • A 错OF 是有符号数的溢出标志,无符号比较不看它。
  • B 错SF 是有符号数的符号,无符号比较不看它。
  • C 对CF、ZF 都为 0 时转移,正是“无符号大于”。
  • D 错含 SF,而且没考虑结果为 0(相等)的情况。

无符号:小于看 CF = 1,等于看 ZF = 1,大于看 CF = 0 且 ZF = 0;有符号:小于看 SF ⊕ OF = 1。

2025-14计组
8 位字长的计算机中,两个带符号整数 x 和 y 的补码分别为 [x]补 = A3H,[y]补 = 75H,则通过补码加减运算器得到的 x−y 的值及 OF 标志分别为( )。
  • A. 24,0
  • B. 24,1
  • C. 46,0
  • D. 46,1
展开解析

考点2.4 补码加减法与溢出

D运算器做 x − y = [x]补 + [−y]补,结果只留 8 位;OF 看真值是否超出 −128 ~ 127。

  1. 真值:x = A3H − 100H = −93,y = 75H = 117;x − y = −210 < −128 → OF = 1。
  2. [−y]补 = 8BH;A3H + 8BH = 12EH,只留 8 位 → 2EH = 46(负数减正数得正数,典型溢出)。
  • A 错24 = A3H + 75H 的低 8 位 18H,是把减法做成了加法(忘了先求 [−y]补);x + y = −93 + 117 = 24 确实不溢出,所以 OF = 0 也是按加法判的。
  • B 错24 是把减法做成了加法的结果(A3H + 75H → 18H);而且就算按加法,x + y = 24 也不溢出,OF = 1 自相矛盾。正确的减法结果是 46。
  • C 错46 对,OF 错:不能看运算器得到的 46 在范围内就说不溢出,要看真值 −210 已超出 −128 ~ 127。
  • D 对结果 2EH = 46,OF = 1。

溢出时运算器给出的结果是“错的”,判 OF 必须看真值(或看“负 − 正 = 正”这种符号矛盾)。

2022-43(1)计组大题小问
ALU 的输入端 A、B 及输出端 F 的最高位分别为 A15、B15 及 F15,FR 中的符号标志和溢出标志分别为 SF 和 OF,写出 SF 的逻辑表达式,以及 A 加 B、A 减 B 时 OF 的逻辑表达式。
展开解析

SF = F15。
加法溢出 = 两负得正 或 两正得负:OF = A15·B15·F15 + A15·B15·F15。
减法 A − B 溢出 = 负减正得正 或 正减负得负:OF = A15·B15·F15 + A15·B15·F15。

补码加减法的公式与溢出判断的三种方法

补码加减法的全部规则就两条公式(都是“模 2n”,最高位的进位直接丢掉):

[A + B]补 = [A]补 + [B]补   [A − B]补 = [A]补 + [−B]补 [−B]补 = [B]补 连同符号位一起取反,末位加 1(2.1 节)

符号位和数值位一起参加运算,不用单独处理符号——这就是计算机用补码的原因。(原码加减要先比较两数符号:同号相加、异号用大绝对值减小绝对值,结果取大者的符号,电路麻烦得多,所以只作了解。)

溢出:结果超出了 n 位补码的表示范围。只有同号相加(或异号相减)才可能溢出;异号相加的结果绝对值不会变大,永远不溢出。判断方法有三种,考研要求三种都会:

方法怎么判逻辑表达式
① 一位符号位:看符号两个加数符号相同,而结果符号与它们不同 → 溢出(正 + 正得负、负 + 负得正)。减法先变成加法再看V = AsBsS̄s + ĀsB̄sSs
② 一位符号位:看进位符号位产生的进位 Cs 与最高数值位向符号位的进位 C1 不同 → 溢出(硬件最常用,也就是 OF 的算法)V = Cs ⊕ C1
③ 双符号位(变形补码、模 4 补码)运算时每个数用两个符号位(正数 00、负数 11),结果的两个符号位:00 正、11 负、01 正溢出、10 负溢出。最高符号位永远代表真正的符号V = Ss1 ⊕ Ss2
① 3 + 2 = 5 00 011 + 00 010 00 101 符号位 00:正数,未溢出 ② 5 + 4 = 9 00 101 + 00 100 01 001 符号位 01:正溢出 (9 > 7,超出了 4 位补码的范围) ③ (−5) + (−4) = −9 11 011 + 11 100 1 10 111 符号位 10:负溢出 (−9 < −8;最高位进位丢掉) ④ (−3) + (−2) = −5 11 101 + 11 110 1 11 011 符号位 11:负数,未溢出 (11 011 = −5 ✓) 4 位补码(1 位符号 + 3 位数值,范围 −8 ~ 7)扩成双符号位来算:两个符号位不同就是溢出,最高符号位是真正的正负
图 2-8 双符号位(变形补码)判溢出。双符号位只在运算器里临时使用;存进寄存器、主存时还是一位符号位(正常情况下两个符号位本来就相同)。
2014-13计组三种方法各判一次
若 x = 103,y = −25,则下列表达式采用 8 位定点补码运算实现时,会发生溢出的是( )。
  • A. x+y
  • B. −x+y
  • C. x−y
  • D. −x−y
展开解析

考点2.4 溢出判断的三种方法2.1 补码的表示范围

C先用数值筛最快:8 位补码范围是 −128 ~ 127,逐个算真实结果。

  • A 错x + y = 103 + (−25) = 78。异号相加,结果的绝对值不会变大,永远不溢出。
  • B 错−x + y = −103 + (−25) = −128,恰好是 8 位补码能表示的最小值,不溢出——本题的陷阱。
  • C 对x − y = 103 + 25 = 128 > 127,溢出。三种方法验证:① 看符号:0110 0111 + 0001 1001 = 1000 0000,正 + 正得“负”;② 看进位:最高数值位向符号位的进位 C1 = 1,符号位向外的进位 Cs = 0,Cs ⊕ C1 = 1;③ 双符号位:00 110 0111 + 00 001 1001 = 01 000 0000,符号位 01 → 正溢出。
  • D 错−x − y = −103 + 25 = −78。

补码的负数比正数多一个:8 位能表示 −128,不能表示 +128。选择题判溢出,先算真值最快。

无符号数的“溢出”看什么

以上三种方法都是针对有符号(补码)运算的,对应 OF。无符号数加法溢出看最高位有没有进位,减法看有没有借位,对应 CF(上面“笨办法”框)。同一次运算,OF 和 CF 各算各的,互不相干(2011-43 第 4 问、2018-19)。

加法器是怎么搭起来的

大纲要求知道“加法器、ALU 的基本结构”。从最小的积木说起:一位全加器(FA)把两个加数的第 i 位 Ai、Bi 和低位送来的进位 Ci 相加,得到本位和 Si 和向高位的进位 Ci+1。n 个全加器连起来就是 n 位加法器。

一位全加器 Ai Bi Ci(低位来的进位) Ci+1 Si S = A ⊕ B ⊕ C C(i+1) = AB + (A ⊕ B)·C 4 位串行进位加法器(进位像多米诺骨牌一样逐级传) FA3 FA2 FA1 FA0 C0 C1 C2 C3 C4 S3S2S1S0 串行进位:C4 要等 C3,C3 要等 C2……n 位就要等 n 级,慢 先行(并行)进位:令 G = AB(本位产生进位),P = A ⊕ B(本位传递进位) C1 = G0 + P0·C0,C2 = G1 + P1·G0 + P1·P0·C0,…… 每个进位都只由 G、P 和 C0 直接算出,所有进位同时得到,快
图 2-9 从一位全加器到 n 位加法器。ALU 就是在加法器旁边再加上与、或、非、移位等逻辑电路,用控制信号 ALUop 选择这一次输出哪一种结果。加法器前面加一个受“减法信号”控制的取反电路、最低位进位接“减法信号”,就能做补码减法(上面图 2-7)。

串行加法器、并行加法器和 ALU

结构怎么做速度
串行加法器只有一个全加器,加上移位寄存器和一个进位触发器:每个时钟周期加一位,结果移入寄存器,进位留给下一拍n 位要 n 拍,最省器件、最慢
并行加法器 · 串行进位n 个全加器同时工作,但进位从低位一级一级传上去(行波进位,图中上半部分)进位链有 n 级,位数越多越慢
并行加法器 · 并行进位(先行进位)用 Gi = AiBi、Pi = Ai ⊕ Bi(或 Ai+Bi)把每一位的进位都直接写成 C0 和各位 G、P 的表达式,所有进位同时算出进位延迟与位数无关,最快;但位数多了公式太长、电路太大
分组先行进位 拓展折中:每 4 位一组。组内并行、组间串行(单级先行进位,如 74181 芯片);组内并行、组间也并行(多级先行进位,再加 74182 先行进位部件)介于两者之间

结论记一句:并行加法器的速度主要取决于进位的传递速度;提高速度的关键是改进进位方式。“加法器的进位方式”属于计算机组成,不属于 ISA(2022-18 Ⅳ)。

ALU组合逻辑电路(没有记忆) A(n 位) B(n 位) ALUop(k 位)选做哪种运算:最多 2ᵏ 种 Cin(最低位进位) F(n 位结果) Cout 和 ZF、SF、CF、OF → 标志寄存器 要运算的数得先放在寄存器里,ALU 算出来的结果也要马上打进寄存器——ALU 本身不能存数(第 4 篇数据通路)
图 2-10 ALU 的外部特性。ALU 的核心是加法器,旁边并联着与、或、非、异或、移位等逻辑电路,控制信号 ALUop 选出其中一路作为输出 F。例如 ALU 要支持 7 种操作,ALUop 至少 ⌈log27⌉ = 3 位(2015-43)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2014-13 改
若 x = 100,y = −28,下列表达式采用 8 位定点补码运算时,会发生溢出的是( )。
  • A. x + y
  • B. −x + y
  • C. x − y
  • D. −x − y
展开解析

C逐个算真值:x + y = 72,−x + y = −128,x − y = 128,−x − y = −72。8 位补码的范围是 −128 ~ 127,只有 x − y = 128 超出,溢出。

变化点:2014-13 的 y = −25。这里故意让 −x + y 恰好等于 −128:它在范围内,不溢出。补码的负数比正数多一个,边界要记准。

变式计组由 2023-16、2018-19 改
int 型变量 x = −2147483647,y = 2,执行 x − y 后,溢出标志 OF 和借位标志 CF 分别为( )。
  • A. 0,0
  • B. 0,1
  • C. 1,0
  • D. 1,1
展开解析

C有符号:−2147483647 − 2 = −2147483649 < −231,溢出,OF = 1。无符号:x 的机器数 8000 0001H,当作无符号数是 231 + 1,减 2 够减,不借位,CF = 0。

变化点:2023-16 的答案是 OF = 0、CF = 1,这里恰好反过来。两个标志各看各的:OF 按有符号的真值判断,CF 按无符号的真值判断。

变式计组由 2011-17 改
标志寄存器中有 CF、ZF、SF、OF。条件转移指令“有符号整数比较小于时转移”的转移条件是( )。
  • A. CF = 1
  • B. SF = 1
  • C. SF ⊕ OF = 1
  • D. CF + ZF = 1
展开解析

C做 A − B:没溢出时,结果为负(SF = 1)就是 A < B;溢出时符号是反的,所以要用 SF ⊕ OF = 1 判断。

变化点:2011-17 是无符号数“大于”(CF + ZF = 0)。记住:无符号看 CF、ZF;有符号看 SF ⊕ OF、ZF。A 是无符号的“小于”,D 是无符号的“小于等于”。

2.5乘法和除法:原理和电路结构

★★★ 高频选择题综合题

大纲对乘除法只要求两件事:基本原理和电路的基本结构。真题怎么考?2020-43 问“没有乘法指令怎么算乘法、控制逻辑干什么、三种实现谁快”,2024-15 问“乘法能不能一个周期完成”,2025-44 直接画了一个除法器问寄存器初值和计数器在哪。所以不用背复杂的算法,把“竖式”搬到电路上就够了。

乘法 = 移位 + 加法(和小学竖式一模一样)

先用十进制回忆:123 × 45,就是“123 × 5”加上“123 × 4 再左移一位(×10)”。二进制更简单,因为乘数的每一位只可能是 0 或 1:这一位是 1,就把被乘数(左移到对应位置)加上;是 0,就什么都不加。

二进制竖式:1101(13)× 1011(11) 1101被乘数 X × 1011乘数 Y 1101Y 的第 0 位 = 1 → 写 X 1101 第 1 位 = 1 → 写 X,左移 1 位 0000 第 2 位 = 0 → 写 0 1101 第 3 位 = 1 → 写 X,左移 3 位 10001111= 143 = 13 × 11 ✓(8 位 = 两个 4 位数之积要 2n 位)
图 2-11 二进制竖式乘法。n 位 × n 位,最多要加 n 次、积最长 2n 位。计算机里不会一下子把 n 行都写出来再相加,而是“加一行、移一位”,循环 n 次——下面的电路就是这么做的。

乘法器的基本结构:ALU + 移位 + 寄存器 + 控制逻辑

被乘数寄存器 X n 位 ALU(加法) 部分积 P(积的高 n 位) 乘数 Y(最后变成积的低 n 位) ←—— 2n 位,每步整体右移 1 位 ——→ 控制逻辑 计数器 Cn:数到 n 次停止 看 Y 最低位 y₀: 1 → 先加 X 再右移;0 → 只右移 y₀ ALUop(加 / 不加) 右移、写使能 一次乘法要循环 n 次,每次一个或几个时钟周期 → 这种乘法指令不能在一个时钟周期内完成(2024-15 B 对)。
图 2-12 “ALU + 移位器 + 寄存器 + 控制逻辑”实现的乘法器。控制逻辑的作用(2020-43 第 2 问的标准答案):控制循环次数(计数器),并根据乘数的当前位决定这一步做加法还是只移位。

用图里的电路把 13 × 11 跑一遍(C 是加法产生的进位,右移时一起移进 P 的最高位):

第几步y₀做什么CPY
初始000001011
11P ← P + X011011011
C、P、Y 一起右移001101101
21P ← P + X100111101
右移010011110
30只右移001001111
41P ← P + X100011111
右移,结束010001111

最后 P、Y 拼起来 1000 1111 = 143。乘数 Y 的位被一位一位“挤出去”,腾出的位置正好装积的低位——所以一个寄存器身兼两职。

三种实现方式谁快(2020-43 第 3 问、2024-15)

实现方式怎么做速度
① 没有乘法指令编译器把 x*y 翻译成一个循环程序:判断、加法、移位、计数……每一轮都是好几条指令,每条指令都要取指、译码、执行最慢
② ALU + 移位器实现的乘法指令一条指令,但硬件内部循环 n 次(上面的图),要多个时钟周期中等
③ 阵列乘法器把竖式里的 n 行“同时”用 n 排加法器加起来,纯组合电路,一个时钟周期出结果最快(硬件最多)

另外两条常考结论:变量乘以常数,编译器会优化成移位和加减(x*9 = (x<<3) + x,a[i] 的地址 i*4 = i<<2);两个变量相乘也能编译成“移位+加法”的循环(就是 ①),所以 2024-15 的 D 说“无法”是错的。

带符号的数怎么乘:原码一位乘法、补码一位乘法

上面的电路算的是两个无符号数(或绝对值)。带符号数有两种办法:

原码一位乘法
符号和数值分开算:积的符号 = 两数符号异或(同号为正、异号为负);积的数值 = 两个绝对值相乘,用的就是上面“看乘数最低位、加或不加、右移”的方法,循环 n 次。
例:x = −1101、y = +1011 → 符号 1 ⊕ 0 = 1,数值 1101 × 1011 = 1000 1111 → 积 = −1000 1111(−143)。
补码一位乘法(Booth 算法)拓展
符号位直接参加运算,不用分开处理。在乘数最低位后面补一个附加位 y−1 = 0,每一步看最低两位 y0y−1:00、11 → 只右移;10 → 部分积 − 被乘数(加 [−x]补)再右移;01 → 部分积 + 被乘数再右移。右移是算术右移(补符号位)。
Booth 例:x = 0101(+5),y = 1101(−3),4 位补码,积 8 位。A = 部分积,Q = 乘数,Q−1 = 附加位
步看 Q0Q−1操作AQQ−1
初始000011010
110A − x:0000 + 1011101111010
A、Q、Q−1 一起算术右移110111101
201A + x:1101 + 0101001011101
算术右移000101110
310A − x:0001 + 1011110001110
算术右移111000111
411只算术右移111100011

积 = A、Q 拼起来 1111 0001 = −15 = 5 × (−3) ✓(已用程序验算)。为什么这样做是对的:乘数里一串连续的 1(如 0111 1000)等于“高一位的权 − 最低那个 1 的权”(1000 0000 − 0000 1000),所以只需在一串 1 的开始处减一次、结束处加一次——“10”正是一串 1 的开始,“01”正是结束。王道教材用的是定点小数写法(部分积用双符号位,共做 n+1 次加法、最后一次不移位),原理相同。

乘法溢出:积要 2n 位,只留低 n 位时怎么判断

不溢出的条件(看 2n 位完整乘积)例:n = 32,x = 231−1,y = 2,积 = 0000 0000 FFFF FFFEH
无符号高 n 位全为 0高 32 位全 0 → umul 不溢出(232−2 放得进 unsigned)
有符号高 n 位每一位都等于低 n 位的最高位(即高 n+1 位全 0 或全 1)低 32 位最高位是 1,高 32 位却全 0 → imul 溢出(232−2 > int 最大值)

直觉:有符号数的低 n 位最高位是“符号位”,如果高 n 位只是它的符号扩展,说明真值没超过 n 位补码能表示的范围。2019-45 第 5 问、2020-43 第 4 问都这样判。

高 8 位低 8 位(最高位标红) (−3) × 5 = −15 1111 1111 1111 0001 高 8 位全是 1 = 符号位的复制 → 不溢出 20 × 7 = 140 0000 0000 1000 1100 符号位是 1,高 8 位却全 0 → 有符号溢出当无符号:高 8 位全 0 → 不溢出(140 < 256) (−20) × 7 = −140 1111 1111 0111 0100 符号位是 0,高 8 位却全 1 → 溢出 判断只看“高 n 位是不是低 n 位最高位的复制”,不用把 2n 位积换算成十进制
图 2-13 用完整乘积判断乘法溢出(n = 8)。机器做 n 位 × n 位乘法时,乘法器先得到 2n 位的完整积;只把低 n 位存进目标寄存器时,就用这条规则判断有没有丢失有效位(已用 verify/08/p02_calc.py 验算)。

真题怎么考:选择题(2010-13)直接给 8 位补码让你判断哪个乘法溢出——最快的办法是换成真值相乘、看是否超出 −128 ~ 127;大题(2019-45 第 5 问、2020-43 第 4 问)要你说出机器“怎么判断”,就答上面的规则。失效的情况:无符号乘法和有符号乘法的规则不同,同一个 2n 位积(如 008CH)当无符号不溢出、当有符号却溢出,先看清题目是哪种。

自测
  1. 8 位补码做 (−16) × 8 和 16 × 8,各自的 16 位积是多少?哪个溢出?
    答案
    (−16) × 8 = −128 → FF80H,低 8 位最高位 1、高 8 位全 1 → 不溢出(−128 正好能表示);16 × 8 = 128 → 0080H,低 8 位最高位 1、高 8 位全 0 → 溢出。

除法 = 移位 + 试减

除法同样照搬竖式:从高位开始,每次把余数左移一位、拉下一位被除数,然后试着减去除数——够减,商上 1、保留差;不够减,商上 0(再把减掉的加回来,或者下一步改做加法补偿)。

1101(13)÷ 11(3) 0100← 商 = 4 11 )1101 11 < 11,商 0 1111 ≥ 11,商 1,减 −11 000拉下 0:不够,商 0 0001拉下 1:不够,商 0 余数 = 1(13 = 3×4 + 1)
除数寄存器 Y ALU(加 / 减) 余数 R 被除数 / 商 Q R、Q 连成 2n 位,每步左移 控制逻辑·计数器

除法器(2025-44 的图就是它):余数寄存器 R 和被除数/商寄存器 Q 连成 2n 位。开始时 2n 位被除数放在 R、Q 里(32 位有符号被除数要先符号扩展成 64 位:高 32 位进 R,低 32 位进 Q),除数放 Y。每一步 R、Q 一起左移一位,ALU 算 R − Y 或 R + Y,根据结果的正负在 Q 的最低位上商 0 或 1。循环 n 次后,Q 里是商,R 里是余数。所以 ALU 只要会加法和减法两种运算,计数器在控制逻辑里。

恢复余数法与加减交替法(不恢复余数法)

“试减”试错了怎么办?有两种处理方式:

恢复余数法
每步:左移,减除数。差 ≥ 0 → 商 1;差 < 0 → 商 0,并把除数加回去恢复原来的余数。缺点:减错一次就要多做一次加法,步数不固定,控制复杂。
加减交替法(不恢复余数法)
规则:余数为正 → 商 1,下一步左移后减除数;余数为负 → 商 0,下一步左移后加除数。不恢复,下一步直接用“加”来补偿(因为 2(r+Y) − Y = 2r + Y)。最后一步余数若为负,才恢复一次。每步都是一次加或减,节奏固定,计算机实际用的是它。
加减交替法例:1101(13)÷ 0011(3),n = 4。余数寄存器 A 取 5 位(多一位放符号),Q 放被除数,最后变成商
步上一步余数左移后 A、Q操作A上商Q
初始000001101
1正00001 101_A − 311110负 → 01010
2负11101 010_A + 300000正 → 10101
3正00000 101_A − 311101负 → 01010
4负11011 010_A + 311110负 → 00100
收尾最后余数为负,恢复一次:A + 3000010100

商 Q = 0100 = 4,余数 A = 1,13 = 3 × 4 + 1 ✓(已用程序验算,恢复余数法得到同样结果)。带符号的原码除法同乘法:商的符号 = 两数符号异或,数值部分用绝对值相除;补码除法 拓展 规则类似,只是“商 1 还是商 0”要比较余数和除数的符号是否相同。

除法会触发异常的两种情况

① 除数为 0(被除数任意)。② 商溢出:n 位补码里只有一种情况——最小负数除以 −1。32 位时 −231 ÷ (−1) = 231,比 int 最大值 231−1 还大 1。对应机器数:被除数 8000 0000H、除数 FFFF FFFFH(2025-44 第 2 问)。
乘法溢出不会触发异常(只是设置标志或直接截断),而除法这两种情况 CPU 会产生除法异常(属于“故障/异常”类,第 5 篇)。
教材里的“商溢出”条件:定点小数除法要求 |被除数| < |除数|,否则商的绝对值 ≥ 1,定点小数放不下;2n 位 ÷ n 位的无符号除法要求被除数的高 n 位 < 除数,否则商超过 n 位。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2010-13 改
4 个整数用 8 位补码表示为 a = 0FH,b = F8H,c = 10H,d = F0H,运算结果存放在 8 位寄存器中。下列运算中会发生溢出的是( )。
  • A. a × b
  • B. b × c
  • C. a × c
  • D. b × b
展开解析

Ca = 15,b = −8,c = 16。a × b = −120,b × c = −128,a × c = 240,b × b = 64。只有 240 超出 −128 ~ 127,溢出。

变化点:故意让 b × c 恰好等于 −128:它在 8 位补码范围内,不溢出。判断乘法溢出,先算真值再看范围最稳。

2.6浮点数 IEEE 754:计算机里的“科学计数法”

★★★ 高频选择题综合题

十进制科学计数法:−1234.5 = −1.2345 × 103。二进制也一样:−8.25 = −1000.01₂ = −1.00001₂ × 23。规格化后小数点前总是 1,所以这个 1 不用存(“隐藏位”),只存小数点后面的部分。

先看教材的一般格式:阶码 + 尾数

在讲 IEEE 754 之前,教材先讲一种“通用”的浮点格式,选择题(2009-13)和概念题都会用到。一个浮点数写成

N = rE × M  r:基数(通常 2);E:阶码;M:尾数
Jf阶符阶码数值 J1…Jm定点整数,常用移码或补码Sf数符尾数数值 S1…Sn定点小数,常用原码或补码

阶码(含阶符)是定点整数,它的位数决定数的表示范围;尾数(含数符)是定点小数,它的位数决定精度;数符决定整个数的正负。

把能表示的浮点数画在数轴上(阶码决定能到多远,尾数决定格子多密) 负上溢 可表示的负数 负下溢 正下溢 可表示的正数 正上溢 0 −最大−最小 +最小+最大 上溢:绝对值比最大的数还大(阶码 > 最大阶码)→ 真正的溢出,要进行溢出处理(中断) 下溢:绝对值比最小的数还小(阶码 < 最小阶码)→ 一般直接当作 0(机器零),不算出错
图 2-14 浮点数的表示范围。和定点数“一条线段”不同,浮点数的可表示范围是两段,中间 0 附近有个“空洞”(下溢区),两端有上溢区。所以浮点数的溢出看阶码:阶码上溢才是溢出。

规格化:让尾数的最高位“有效”

同一个数有很多种写法:0.1×23 = 0.01×24 = 0.001×25……后面几种白白在尾数最前面浪费了几个 0,精度变差。规格化就是规定尾数的最高数值位必须是有效数字,这样表示唯一、精度最高:

尾数用什么码规格化的标志正数负数尾数范围
原码最高数值位为 10.1××…×1.1××…×1/2 ≤ |M| < 1
补码符号位与最高数值位不同0.1××…×1.0××…×1/2 ≤ M < 1 或 −1 ≤ M < −1/2
补码规格化的两个特例

−1/2 的补码是 1.100…0,符号位和最高数值位相同 → 不是规格化数(要左规成 1.000…0 × 2−1);−1 的补码是 1.000…0 → 是规格化数。
左规:尾数左移 1 位、阶码减 1(可能要做好几次);右规:尾数右移 1 位、阶码加 1(加减运算后尾数溢出时,最多一次)。基数 r = 4 时规格化要求尾数最高 2 位不全为 0,r = 8 时最高 3 位不全为 0 拓展——基数越大,范围越大,但精度越低。

例:阶码 8 位(含 1 位阶符,补码),尾数 24 位(含 1 位数符,补码),基数 2,规格化
尾数阶码真值
最大正数0.111…1(23 个 1)= 1 − 2−230111 1111 = 127(1 − 2−23) × 2127
最小正数(规格化)0.100…0 = 2−11000 0000 = −1282−1 × 2−128 = 2−129
最大负数(绝对值最小,规格化)1.011…1 = −(2−1 + 2−23)1000 0000 = −128−(2−1 + 2−23) × 2−128
最小负数(绝对值最大)1.000…0 = −10111 1111 = 127−2127

最大负数的尾数不能取 1.100…0(−1/2),因为那不是规格化数;往下一格是 1.011…1。

IEEE 754:今天真正在用的格式

IEEE 754 和上面的一般格式相比有三处不同:尾数用原码且规格化形式是 1.M(隐藏最前面的 1,多出一位精度);阶码用移码但偏置是 127(不是 128);阶码全 0、全 1 留作特殊用途。

S1 位 符号阶码 E8 位,移码,偏置 127尾数 M23 位,只存小数点后面

float(单精度,32 位)。double(双精度,64 位):1 + 11(偏置 1023)+ 52。

规格化数(1 ≤ E ≤ 254):真值 = (−1)S × 1.M × 2E−127 E 是“阶码”的无符号值;E − 127 才是真正的指数

编码:十进制 → 机器数(四步走)

  1. 定符号:正数 S = 0,负数 S = 1,后面只处理绝对值。
  2. 化成二进制:整数部分除 2 取余,小数部分乘 2 取整(0.25 = 0.01₂,0.75 = 0.11₂,0.1 化不尽,只能近似)。
  3. 规格化:把小数点移到第一个 1 的后面,写成 1.xxx × 2k;阶码 E = k + 127,写成 8 位二进制。
  4. 拼接:S | E(8 位)| 小数点后的 xxx(补 0 到 23 位,最前面的 1 不存),32 位每 4 位一组换十六进制。
−8.25 = −1000.01₂ = −1.00001₂ × 2³ 1 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 C 1 0 4 0 0 0 0 S=1 阶码 E(8 位) 尾数 M(23 位,只存小数点后面) ① 符号:负数 → S = 1 ② 规格化:1.00001 × 2³,指数 3 → 阶码 E = 3 + 127 = 130 = 1000 0010 ③ 尾数:小数点后面的 00001,后面补 0 凑满 23 位(最前面的 1 是“隐藏位”,不存) ④ 32 位从左往右每 4 位一组换成十六进制:C104 0000H
图 2-15 把 −8.25 编成 float 的机器数(就是下面 2011-13 的答案)。三个字段的边界(第 31 位、第 23 位)都不落在 4 位一组的边界上,所以一定要先写满 32 位二进制,再从左往右 4 位一组换十六进制。解码反过来做:十六进制 → 32 位二进制 → 按 1 | 8 | 23 切开。
2011-13计组必会
float 型数据通常用 IEEE 754 单精度浮点数格式表示。若编译器将 float 型变量 x 分配在一个 32 位浮点寄存器 FR1 中,且 x = −8.25,则 FR1 的内容是( )。
  • A. C1040000H
  • B. C2420000H
  • C. C1840000H
  • D. C1C20000H
展开解析

考点2.6 IEEE 7542.6 规格化

AIEEE 754 单精度:1 位符号 S、8 位阶码 E(偏置 127)、23 位尾数 M(隐藏了规格化后小数点前的 1)。

  1. 符号:负数,S = 1。
  2. 化二进制:8.25 = 8 + 0.25 = 1000.012。
  3. 规格化:1000.01 = 1.00001 × 23;阶码 E = 3 + 127 = 130 = 1000 0010;尾数 M = 00001 后面补 0 到 23 位。
  4. 拼起来四位一组:1100 0001 0000 0100 0000 … = C1040000H。
  • A 对S = 1、E = 130、M = 000 01 → C1040000H。
  • B 错解码:E = 1000 0100 = 132 → 指数 5,尾数 1.100001,值为 −48.5。它是把 8.25 写成 0.100001 × 24(把小数点前的 1 也存进尾数),阶码又按 4 + 128 算出来的,两处都错。
  • C 错解码:E = 1000 0011 = 131 → 指数 4,值为 −1.00001 × 24 = −16.5。错在偏置用了 128(3 + 128 = 131),把移码的偏置 128 和 IEEE 754 的 127 混了。
  • D 错解码:E = 131、尾数 100001,值为 −1.100001 × 24 = −24.25。它是按老式“0.1xxx”规格化成 0.100001 × 24、阶码 4 + 127,并把小数点后的 100001 全存进尾数——没用 IEEE 754 的“1.xxx + 隐藏位”。

IEEE 754 规格化成 1.xxx,小数点前的 1 不存;阶码偏置单精度 127、双精度 1023。做完把选项“解码回真值”验算,最不容易错。

解码:机器数 → 十进制

2013-13计组
某数采用 IEEE 754 单精度浮点数格式表示为 C6400000H,则该数的值是( )。
  • A. −1.5×213
  • B. −1.5×212
  • C. −0.5×213
  • D. −0.5×212
展开解析

考点2.6 IEEE 754

A十六进制先展开成二进制,再按 1 | 8 | 23 切开。注意阶码跨过了十六进制的分组。

  1. C6400000H = 1100 0110 0100 0000 … → S = 1;E = 1000 1100 = 140;M = 100 0000 …。
  2. 指数 = 140 − 127 = 13;尾数加上隐藏位 1:1.12 = 1.5。
  3. 值 = −1.5 × 213。
  • A 对S = 1、指数 13、尾数 1.5。
  • B 错指数 12 是用 140 − 128 算的,把 IEEE 754 的偏置 127 记成了移码的 128。
  • C 错尾数 0.5 是只取了存储的 0.12,忘了加上隐藏的 1(规格化数尾数是 1.M)。
  • D 错两处都错:偏置用 128 算出指数 12,尾数又漏了隐藏位写成 0.5。

IEEE 754 解码:值 = (−1)S × 1.M × 2E − 127。同类:2025-13(47300000H = 1.375 × 215);2022-14(−0.4375 = −1.112 × 2−2,E = 125 → BEE00000H);2026-14(12.1 就近舍入 → 4141999AH)。

特殊值:阶码全 0 和全 1

阶码 E尾数 M表示真值
全 0全 0±00
全 0非 0非规格化数(极小的数)(−1)S × 0.M × 2−126(隐藏位变 0,指数固定 −126)
1~254任意规格化数(−1)S × 1.M × 2E−127
全 1(255)全 0±∞7F800000H = +∞
全 1(255)非 0NaN(非数,如 0/0)
float 最大正数 = 1.11…1 × 2127 = (2 − 2−23) × 2127 = 2128 − 2104(2012-14) float 最小规格化正数 = 1.0 × 2−126(2018-14);最小非规格化正数 = 2−23 × 2−126 = 2−149 有效位:float 24 位(23 + 隐藏的 1),double 53 位
IEEE 754 一览float(单精度,32 位)double(双精度,64 位)
符号 / 阶码 / 尾数1 / 8 / 231 / 11 / 52
偏置127(7FH)1023(3FFH)
规格化数的阶码 E1 ~ 254,指数 −126 ~ +1271 ~ 2046,指数 −1022 ~ +1023
有效位(含隐藏位)24 位53 位
最大正数(2 − 2−23) × 2127 ≈ 3.4 × 1038(2 − 2−52) × 21023 ≈ 1.8 × 10308
最小规格化正数2−126 ≈ 1.18 × 10−382−1022 ≈ 2.2 × 10−308
最小非规格化正数2−149 ≈ 1.4 × 10−452−1074 ≈ 4.9 × 10−324

非规格化数的指数固定是 −126(不是 0 − 127 = −127),这样最大的非规格化数 0.11…1 × 2−126 和最小的规格化数 1.0 × 2−126 正好首尾相接,0 附近没有“空洞”(叫渐进下溢)。

2023-14计组
已知 float 型变量用 IEEE 754 单精度浮点数格式表示。若 float 型变量 x 的机器数为 8020 0000H,则 x 的值为( )。
  • A. −2−128
  • B. −1.01×2−127
  • C. −1.01×2−126
  • D. 非数(NaN)
展开解析

考点2.6 非规格化数2.6 特殊值

A先切开:阶码全 0 且尾数不为 0 → 非规格化数,值 = (−1)S × 0.M × 2−126(隐藏位是 0,指数固定 −126)。

  1. 8020 0000H = 1 | 0000 0000 | 010 0000 …:S = 1,E = 0,M = 01000…≠ 0 → 非规格化数。
  2. 值 = −0.012 × 2−126 = −2−2 × 2−126 = −2−128。
  • A 对−0.012 × 2−126 = −2−128。
  • B 错当成了规格化数:隐藏位按 1 补成 1.01,指数按 0 − 127 = −127 算。阶码全 0 时隐藏位是 0、指数固定为 −126。
  • C 错指数 −126 对了,但隐藏位仍按 1 算成 1.01;非规格化数的隐藏位是 0,应为 0.012。
  • D 错NaN 要求阶码全 1(E = 255)且尾数不为 0;本题阶码全 0,是非规格化数。

阶码全 0:尾数 0 → ±0,尾数非 0 → 非规格化(0.M × 2−126);阶码全 1:尾数 0 → ±∞,尾数非 0 → NaN。

非规格化数:为什么阶码全 0 时隐藏位变成 0

问题引入:规格化数的尾数前面总有一个看不见的 1,所以最小的正规格化数是 1.0 × 2−126。那么 0 怎么表示?比 2−126 还小的数(科学计算里很常见)只能一律当成 0 吗?

打个比方

浮点数像一把刻度不均匀的尺子:越靠近 0 刻度越密,但在 0 和第一个刻度之间留着一大段空白。非规格化数就是在这段空白里补上等间距的小刻度。不像的地方:补上的这些数有效位变少了(前面的 0 不算有效位),精度更低,不是“白送”的。

教材定义:阶码 E 全 0、尾数 M ≠ 0 时是非规格化数,值 = (−1)S × 0.M × 2−126;E 全 0、M 全 0 时是 ±0(+0 与 −0 位串不同,比较时相等)。指数取 −126(= 1 − 127)而不是 0 − 127 = −127,是为了让最大的非规格化数 0.11…1 × 2−126 和最小的规格化数 1.00…0 × 2−126 首尾相接,这种设计叫渐进下溢。

玩具格式:1 位符号 + 3 位阶码(偏置 3)+ 2 位尾数,0 ~ 1 之间能表示的全部正数 ① 有非规格化数(IEEE 754 的做法) 0 1/4 1/2 1 非规格化 0.mm × 2⁻²:间距 1/16 E = 1:间距 1/16 E = 2:间距 1/8 ② 没有非规格化数(E = 0 只用来表示 0) 0 1/4 1/2 1 空洞:0 之后直接跳到 1/4
图 2-16 非规格化数填上 0 附近的空洞。把 float 缩小成 6 位的“玩具格式”画出来(偏置 3,规格化数指数 −2 ~ +3):橙色刻度是 E = 0 的非规格化数 1/16、1/8、3/16,间距和 E = 1 那一段一样,所以从 0 到 1/2 是均匀衔接的。若没有非规格化数,0 后面直接跳到 1/4。float 的情况完全一样,只是把 2−2 换成 2−126。

跟我做一遍:解码表(先看 E 是全 0、全 1 还是中间值,再决定隐藏位;数值已用 verify/08/p02_calc.py 验算)

机器数SEM类型值
0000 0001H00000…001非规格化2−23 × 2−126 = 2−149(最小正数)
0040 0000H00100…000非规格化0.1₂ × 2−126 = 2−127
007F FFFFH00111…111非规格化(1 − 2−23) × 2−126(最大非规格化数)
0080 0000H01000…000规格化1.0 × 2−126(最小规格化正数,2018-14)
8000 0000H10000…000−0−0(与 +0 比较相等)
7F80 0000H0255000…000无穷+∞
7FC0 0000H0255100…000NaN非数(如 0/0、√−1 的结果)

真题怎么考:2023-14(8020 0000H:E = 0、M ≠ 0,按非规格化算得 −2−128;当成规格化数就会错选 B、C)、2018-14(最小规格化正数 2−126)、2012-14(最大正数,E 只能取到 254)。秒杀法:拿到机器数先看 E 那 8 位——全 0 → 隐藏位 0、指数 −126;全 1 → ∞ 或 NaN;其他 → 隐藏位 1、指数 E − 127。失效的情况:题目用的是教材“一般格式”(如 2009-13 阶码、尾数都用补码)时,没有隐藏位和非规格化数的规定,要按题目给的格式算。

自测
  1. 8000 0001H 表示的 float 值是多少?
    答案
    S = 1,E = 0,M = 0…01 → 非规格化:−2−23 × 2−126 = −2−149(绝对值最小的负数)。
  2. “IEEE 754 中阶码全 0 的数都等于 0”对吗?
    答案
    不对。只有尾数也全 0 时才是 ±0;尾数不为 0 时是非规格化数,是非常小但不为 0 的数。

精度:为什么 int 转 float 会变

float 只有 24 位有效位。整数只要不超过 224 就能精确存进 float;超过了,末尾的位就要舍入。double 有 53 位有效位,任何 int 都能精确转成 double。

2010-14计组
i、f、d 分别为 int、float、double,i = 785,f = 1.5678e3,d = 1.5e100。在 32 位机器中结果为“真”的是( )。Ⅰ. i == (int)(float)i Ⅱ. f == (float)(int)f Ⅲ. f == (float)(double)f Ⅳ. (d+f)−d == f
  • A. 仅Ⅰ和Ⅱ
  • B. 仅Ⅰ和Ⅲ
  • C. 仅Ⅱ和Ⅲ
  • D. 仅Ⅲ和Ⅳ
展开解析

考点2.6 IEEE 7542.6 大数吃小数2.2 类型转换

B逐个判断“转过去再转回来会不会丢信息”:float 有 24 位有效位,double 有 53 位;转 int 会截掉小数。

  • Ⅰ 对785 < 224,float 能精确表示,转回 int 还是 785,为真。
  • Ⅱ 错f = 1567.8,转 int 截掉小数变成 1567,再转回 float 是 1567.0 ≠ 1567.8,为假。
  • Ⅲ 对float → double 是变宽,精确无损;double 再转回 float 恰好还原原值,为真。
  • Ⅳ 错d = 1.5×10100 比 f 大约 1096 倍,对阶时 f 的尾数被右移出 53 位以外,d + f = d,所以 (d + f) − d = 0 ≠ f,为假。

干扰项从哪来:A 是把Ⅱ当成真(忘了转 int 会截掉小数),又漏了Ⅲ;C 是把Ⅱ当成真,又以为Ⅰ中 int 转 float 会丢精度(785 很小,不会);D 是把Ⅳ当成真(以为浮点加减满足结合律),又漏了Ⅰ

int → float 只在 |i| ≥ 224 时才可能丢精度;float → double 永远精确;转 int 截断小数;大数加小数,小数可能被“吃掉”。

2014-14计组
float 型变量 x 和 y 分别存放在 32 位寄存器 f1 和 f2 中,(f1) = CC90 0000H,(f2) = B0C0 0000H,则 x 和 y 之间的关系为( )。
  • A. x<y 且符号相同
  • B. x<y 且符号不同
  • C. x>y 且符号相同
  • D. x>y 且符号不同
展开解析

考点2.6 IEEE 754

A先看符号位,再比绝对值。同号的 IEEE 754 数,去掉符号位后按无符号整数比位串,就是按绝对值比(阶码在高位且用移码)。

  1. CC90 0000H = 1100 1100 1001 … → S = 1,E = 1001 1001 = 153,指数 26,x = −1.0012 × 226。
  2. B0C0 0000H = 1011 0000 1100 … → S = 1,E = 0110 0001 = 97,指数 −30,y = −1.12 × 2−30。
  3. 两个都是负数(符号相同),|x| 远大于 |y|;负数绝对值越大值越小 → x < y。
  • A 对x < y 且两数都为负,符号相同。
  • B 错“符号不同”错:B0C0 0000H 的最高位是 B = 1011 的第一位 1,y 也是负数。
  • C 错只比了绝对值 |x| > |y| 就下结论,忘了两个负数比较时绝对值大的反而小。
  • D 错两处都错:y 的符号位是 1,两数同号;而且负数绝对值大的更小,应是 x < y。

比较两个 IEEE 754 数:先比符号;同为正数按位串大小比;同为负数位串大的反而小。

浮点加减:对阶 → 尾数加减 → 规格化 → 舍入 → 判溢出

  1. 对阶:小阶向大阶看齐(阶码小的那个尾数右移),因为右移只丢最低位,误差小。
  2. 尾数加减。
  3. 规格化:结果不是 1.xxx 的形式就调整:尾数溢出(如 10.xx)要右规(尾数右移、阶码 +1,可能阶码上溢);太小(0.0xx)要左规(尾数左移、阶码 −1,可能阶码下溢)。
  4. 舍入:IEEE 754 默认“就近舍入,一半时取偶数”,舍入进位也可能导致阶码 +1。
  5. 判溢出:看阶码是否超出范围。尾数溢出不等于结果溢出(右规一下就好)。
例:X = 1.1010 × 2³(= 13),Y = 1.0111 × 2²(= 5.75),尾数保留 4 位小数,就近舍入 ① 对阶 阶差 3 − 2 = 1,小阶的 Y 尾数右移 1 位、阶码 +1:Y = 1.0111 × 2² = 0.10111 × 2³ (移出的 1 先留着,叫保护位) ② 尾数求和 1.1010 0+ 0.1011 110.0101 1 小数点前出现了 10:尾数溢出(还不是结果溢出) ③ 规格化 右规:尾数右移 1 位、阶码 +110.01011 × 2³ = 1.001011 × 2⁴ ④ 舍入 1.0010 | 11 → 舍去部分 11 大于一半 → 进 1 → 1.0011 × 2⁴ ⑤ 判溢出 看阶码:4 没超出范围 → 结果 1.0011 × 2⁴ = 19(精确值 18.75,舍入误差 0.25)
图 2-17 浮点加法五步走一遍。第 ② 步“尾数求和”就是两个定点小数相加;它得到 10.xxx 并不是错误,第 ③ 步右规一下就好。真正判断结果是否溢出要等到第 ⑤ 步看阶码(2015-14 Ⅳ)。数值已用 verify/08/p02_calc.py 验算。

对阶时为什么是“小阶向大阶看齐”?小阶对大阶要把尾数右移,丢掉的是最低位,误差很小;反过来大阶对小阶要把尾数左移,丢掉的是最高位,结果全错。下面用 2009-13 把五步完整走一遍,教材习惯用双符号位补码表示阶码和尾数,溢出一眼能看出来:

2009-13计组五步全过程
浮点数加、减运算过程一般包括对阶、尾数运算、规格化、舍入和判溢出等步骤。设浮点数的阶码和尾数均采用补码表示,且位数分别为 5 位和 7 位(均含 2 位符号位)。若有两个数 X = 27×29/32,Y = 25×5/8,则用浮点加法计算 X+Y 的最终结果是( )。
  • A. 00111 1100010
  • B. 00111 0100010
  • C. 01000 0010001
  • D. 发生溢出
展开解析

考点2.6 浮点加减的五个步骤2.4 双符号位判溢出

D阶码、尾数都是双符号位补码:阶码 5 位 = 2 位符号 + 3 位数值(阶的范围 −8 ~ +7),尾数 7 位 = 2 位符号 + 5 位数值。先写机器数:X 的阶码 00 111、尾数 00.11101(29/32 = 0.11101₂);Y 的阶码 00 101、尾数 00.10100(5/8 = 0.101₂)。

步骤阶码尾数说明
① 对阶00 111Y:00.10100 → 00.00101阶差 7 − 5 = 2,小阶的 Y 向大阶看齐,尾数右移 2 位(移出的是 00,没有损失)
② 尾数相加00 11100.11101 + 00.00101 = 01.0001029/32 + 5/32 = 34/32 ≥ 1,尾数双符号位 01:尾数溢出(不等于结果溢出)
③ 右规00 111 + 1 = 01 00000.10001尾数右移 1 位、阶码加 1;移出的位是 0,不用舍入
④ 判溢出01 000阶码双符号位 01 → 阶码上溢 → 结果溢出(真实结果 28 × 17/32,阶 8 超过最大阶 7)
  • A 错尾数 11.00010 是个负数,两个正数相加不可能得负数。
  • B 错尾数 01.00010 是没有右规的中间结果,双符号位不同,不是合法的规格化数。
  • C 错阶码 01 000、尾数 00.10001 正是右规后的样子,但阶码已经上溢,不能当结果——这是忘了第 ④ 步的陷阱。
  • D 对发生溢出。

尾数溢出(双符号位 01、10)右规一下就好;结果是否溢出,只看阶码。

舍入:移出去的位怎么处理

方法规则用在哪
0 舍 1 入右移时被移掉的最高位是 1,就在尾数末位加 1,是 0 就直接舍去(类似四舍五入)。加 1 后可能又溢出,还要再右规一次教材一般格式
恒置 1不管移掉的是什么,尾数末位一律置 1教材一般格式(误差大,但简单)
就近舍入(到偶数)舍入到最近的可表示值;正好在两者中间时,选末位为 0(偶数)的那个IEEE 754 默认
朝 +∞ / 朝 −∞ / 朝 0分别总是往大的方向、往小的方向、往 0 的方向(截断)取IEEE 754 的另外三种模式

例:12.1 = 1100.0001 1001 1001…₂ = 1.1000 0011 0011 0011 0011 001 | 1001…₂ × 23,竖线后被舍掉的部分大于一半,就近舍入要进 1,尾数末段 …001 变成 …010,得到 4141 999AH(2026-14)。又如 int 16777217 = 224 + 1 转 float:被舍掉的恰好是“一半”,按“取偶”舍去,得到 16777216.0;而 16777219 会进位成 16777220.0(gcc 实测)。

四种舍入放在同一组数上比一比(保留 4 位小数,竖线右边是要舍去的位;已用程序验算):

舍入前0 舍 1 入恒置 1就近舍入(偶)截断
1.0110 | 011.01101.01111.01101.0110
1.0110 | 111.01111.01111.01111.0110
1.0110 | 10
正好一半
1.01111.01111.0110
末位已是 0
1.0110
1.0101 | 10
正好一半
1.01101.0101
末位本来就是 1
1.0110
进位后末位为 0
1.0101

怎么记:0 舍 1 入只看被舍去的最高位;恒置 1 根本不看舍去的位,保留部分末位一律变 1;就近舍入把舍去的部分和“一半”比——大于进、小于舍、正好一半就选末位为 0 的那个;截断直接扔掉(对原码表示的数就是“朝 0 舍入”)。前三种的差别只在“正好一半”和“末位情况”上,题目里遇到这两种情况要格外小心。

2015-14计组
下列有关浮点数加减运算的叙述中,正确的是( )。Ⅰ. 对阶操作不会引起阶码上溢或下溢 Ⅱ. 右规和尾数舍入都可能引起阶码上溢 Ⅲ. 左规时可能引起阶码下溢 Ⅳ. 尾数溢出时结果不一定溢出
  • A. 仅Ⅱ、Ⅲ
  • B. 仅Ⅰ、Ⅱ、Ⅳ
  • C. 仅Ⅰ、Ⅲ、Ⅳ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

考点2.6 浮点加减五步

D按浮点加减的五步(对阶 → 尾数加减 → 规格化 → 舍入 → 判溢出)逐句看阶码会不会越界。

  • Ⅰ 对对阶是小阶向大阶看齐,大阶本来就是一个合法的阶码,所以不会上溢也不会下溢。
  • Ⅱ 对右规时阶码 +1,可能超过最大阶码而上溢;舍入(如 0 舍 1 入)可能让尾数变成 10.00…,又要右规,同样可能上溢。
  • Ⅲ 对左规时尾数左移、阶码 −1,阶码可能小于最小值而下溢。
  • Ⅳ 对尾数溢出(如变成 01.xx 或 10.xx)可以右规修正,只要右规后阶码不上溢,结果就不溢出。

干扰项从哪来:A 是以为对阶会越界、又把“尾数溢出”等同于“结果溢出”;B 是以为左规只动尾数、不会改阶码;C 是以为舍入只影响尾数最低位、不会引起阶码变化

浮点运算真正的溢出只看阶码:阶码上溢才是溢出,阶码下溢按机器零处理。例:2009-13 中 X = 27 × 29/32,Y = 25 × 5/8,阶码最大 7;X + Y = 27 × 34/32,右规成 28 × 17/32,阶码 8 > 7 → 溢出。

2024-14计组
整型参数 α 和 β 的取值范围分别为 −220~220、−240~240,为保证表数精度并提高运算速度,α 和 β 最适宜采用的数据表示方法分别是( )。
  • A. 32 位整数、32 位整数
  • B. 单精度浮点数、单精度浮点数
  • C. 32 位整数、双精度浮点数
  • D. 单精度浮点数、双精度浮点数
展开解析

考点2.6 float、double 的有效位2.1 整数范围

C既要“精度”(整数不能被舍入)又要“速度”(整数运算比浮点快):能用整数就用整数;整数放不下,就选有效位足够的浮点数。

  • A 错β 到 ±240,超出 32 位整数范围(±231)。
  • B 错α 用 float:220 以内虽然能精确,但浮点运算比整数慢;β 用 float 只有 24 位有效位,240 附近的整数会被舍入。
  • C 对α 在 32 位整数范围内,整数运算最快;β 用 double,53 位有效位足以精确表示 240 以内的整数。
  • D 错α 用单精度浮点数没必要,速度不如整数。

float 能精确表示的整数到 224,double 到 253;32 位 int 到 231 − 1。

浮点运算的“反直觉”:结合律不成立、大数吃小数

先看一个怪现象。数学里 (a + b) + c 一定等于 a + (b + c)。可在 C 程序里令 float a = 16777216, b = 1, c = 1;(a = 224),(a + b) + c 算出来是 16777216,a + (b + c) 却是 16777218——同样三个数,只是换了加法的先后,结果就不一样了。2010-14 的 Ⅳ“(d + f) − d == f”为假,也是这个原因。

打个比方float 像一台只能显示 24 位二进制有效数字的计算器:每算完一步,都要把结果“凑整”到这 24 位上。数越大,它能显示的最小变化(相邻两个 float 之间的间距)也越大:224 附近间距是 2,108 附近间距是 8。往一个大数上加一个不到“半个间距”的数,加完一凑整,等于没加——这就叫大数吃小数。哪里不像:计算器是十进制四舍五入,float 是二进制,而且“正好一半”时取末位为 0 的那个(就近舍入到偶数,见 舍入)。

教材结论:浮点数的加法和乘法都满足交换律,但不满足结合律,乘法对加法也不满足分配律。原因是每一步运算后都要舍入,中间结果还可能溢出(如 (1020 × 1020) × 10−20 在 float 里先上溢成 +∞,而 1020 × (1020 × 10−20) = 1020)。对比整数:补码加法即使溢出也是按模 2n 计算,(u1 + u2) + u3 与 u1 + (u2 + u3) 的位串一定相同——整数加法的结合律总成立,浮点加法不一定。

224 附近的 float:只有偶数能表示(间距 2) 1677721616777217167772181677721916777220 尾数末位 0尾数末位 1尾数末位 0 正好一半 → 取偶 正好一半 → 取偶 (a + b) + c a + b = 16777217 → 舍入成 16777216 再 + c:又是 16777217 → 又被舍掉 → 16777216 a + (b + c) b + c = 2(小数相加,精确) a + 2 = 16777218,正好能表示 → 16777218
图 2-18 同样三个数,加的顺序不同,结果不同。实心点是 float 能精确表示的值,空心点不能。两个“1”分开加,每次都只有半个间距,各被舍掉一次;先合成 2 再加,正好一个间距,就留下来了。数值由 verify/08/p13/a_assoc.c(gcc 实测)得到。

跟我做一遍:float 计算 108 + 1,结果是多少?(gcc 实测)

  1. 写成规格化形式:108 = 101 1111 0101 1110 0001 0000 0000B(27 位)= 1.0111 1101 0111 1000 0100 0000 00B × 226,小数点后的有效部分能放进 23 位尾数,所以 108 本身能精确表示(机器数 4CBE BC20H)。
  2. 算间距:指数是 26,尾数最低位的权是 226 − 23 = 23 = 8——在 226 ~ 227 这一段,相邻两个 float 相差 8。一般地,间距 = 2指数 − 23(双精度把 23 换成 52)。
  3. 对阶:1 = 1.0 × 20,阶差 26,1 的尾数要右移 26 位,那个 1 落在小数点后第 26 位——已经在 23 位尾数之外,只能当作舍入时要看的位。
  4. 尾数相加 + 舍入:要舍去的部分是 1(单位是 20),而半个间距是 4,1 < 4 → 舍去。结果仍是 108,即 108 + 1 == 108。
  5. 换几个数检验:+4 正好是半个间距,108 的尾数末位是 0(偶),取偶 → 仍是 108;+5 超过一半 → 进到 100000008;+12 落在 100000008 和 100000016 正中间,100000008 的尾数末位是 1 → 取偶到 100000016。

同一个道理还有两个常见现象(都已 gcc 实测):① 用 float 把 1.0 累加 2000 万次,结果停在 16777216 = 224——加到这里以后,每次 +1 都只有半个间距,全被舍掉;② 1000 个 1 和一个 108 相加,先加大数再一个个加 1,得 100000000;先把 1000 个 1 加完再加 108,得 100001000(精确)。所以要求和时,先加绝对值小的更准。

真题怎么考
  • 2010-14 Ⅳ:d = 1.5 × 10100,f = 1567.8,(d + f) − d:对阶时 f 被右移出尾数,d + f 就是 d,减去 d 得 0 ≠ f → 假。
  • 精度类(2024-14、2021-14、2026-14):float 只有 24 位有效位,大于 224 的整数和 0.1 这类无限循环二进制小数都不能精确表示。
  • 可能的新问法:给 Ⅰ “x + y == y + x”、Ⅱ “(x + y) + z == x + (y + z)”、Ⅲ “(x + y) − y == x” 问哪些恒为真(不考虑 NaN)——只有 Ⅰ。Ⅱ 用上面的 224、1、1 就能举反例,Ⅲ 取 x = 1、y = 108:(1 + 108) − 108 = 0。
  • 秒杀法:判断一个浮点等式“是否恒成立”,先拿一个大数 + 一个 1 试反例。失效条件:题目若限定所有数和中间结果都是绝对值小于 224 的整数,float 加减是精确的,此时等式可能成立——所以要看清题目问的是“一定”还是“可能”。
自测
  1. float x = 225(= 33554432),x + 3 的结果是多少?x + 2 呢?
    答案
    225 附近间距 = 225 − 23 = 4,能表示的是 …432、…436、…440。+3 超过半个间距(2)→ 进到 33554436;+2 正好一半,225 的尾数末位为 0 → 取偶,仍是 33554432(gcc 实测)。
  2. double d = 1016,判断 d + 1 == d 的真假。
    答案
    真。253 ≈ 9.007 × 1015 ≤ 1016 < 254,指数 53,间距 253 − 52 = 2;+1 正好半个间距,1016 的尾数末位为 0 → 取偶,仍是 1016(gcc 实测 d + 1 == d 为 1)。
  3. “浮点加法满足交换律,所以 a + b + c 不管怎么加都一样”,错在哪?
    答案
    交换律只保证两个数相加与顺序无关;三个数时还涉及“先算哪两个”,那是结合律,浮点不满足(图中的 224、1、1)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2011-13、2022-14 改
float 型变量 x = 10.75,用 IEEE 754 单精度格式表示,其机器数为( )。
  • A. 40AC 0000H
  • B. 412C 0000H
  • C. 4156 0000H
  • D. C12C 0000H
展开解析

B10.75 = 1010.11B = 1.01011B × 23。S = 0,E = 3 + 127 = 130 = 1000 0010B,M = 0101 1000…。拼起来是 0 10000010 01011000…0 = 0100 0001 0010 1100 0…B = 412C 0000H。

变化点:真题是负数(−8.25、−0.4375),这里是正数。三个错误选项分别对应三种常见错误:A 是阶码只加了 126,C 是把隐藏的 1 也写进了尾数,D 是符号位写成了 1。

变式计组由 2013-13、2025-13 改
某 float 型变量的机器数为 C1A8 0000H,其值为( )。
  • A. −21
  • B. −10.5
  • C. −42
  • D. −5.25
展开解析

A1100 0001 1010 1000 0…B:S = 1;E = 1000 0011B = 131,指数 = 131 − 127 = 4;M = 0101 0…,值 = −1.0101B × 24 = −10101B = −21。

变化点:真题的选项写成“1.5 × 213”这种形式,这里要求算出十进制数。步骤不变:拆出 S、E、M,指数 = E − 127,尾数前面补上隐藏的 1。

变式计组由 2021-14、2026-14 改
下列数值中,不能用 IEEE 754 单精度浮点格式精确表示的是( )。
  • A. 0.375
  • B. 3.125
  • C. 0.3
  • D. 1024.5
展开解析

C0.375 = 0.011B,3.125 = 11.001B,1024.5 = 10000000000.1B,都是有限位二进制小数,而且有效位不超过 24 位。0.3 化成二进制是无限循环小数(0.0100110011…),只能舍入,所以不能精确表示。

变化点:判断方法:小数部分能否写成 k / 2n。0.3 = 3/10,分母含 5,不行;1.2(2021-14)、12.1(2026-14)也是这个原因。

变式计组由 2018-14、2023-14 改
IEEE 754 单精度浮点格式能表示的最大的非规格化正数是( )。
  • A. (1 − 2−23) × 2−126
  • B. (1 − 2−23) × 2−127
  • C. 2−149
  • D. (2 − 2−23) × 2−127
展开解析

A非规格化数:E = 0,没有隐藏的 1,值 = 0.M × 2−126(指数固定为 −126,不是 −127)。M 全为 1 时最大:(1 − 2−23) × 2−126。它比最小的规格化正数 1.0 × 2−126(2018-14)只小一点点。

变化点:2018-14 问最小的规格化正数,2023-14 考 E = 0 的机器数。这里把两者合在一起:E = 0 时指数按 −126 算,尾数前面是 0 而不是 1。最小的非规格化正数是 C(2−149)。

变式计组由 2026-14 改(考纲补充:教材的舍入方法)
某浮点格式的尾数保留 4 位小数。对尾数 1.0101 | 10(竖线右边是要舍去的位)分别采用“0 舍 1 入”法和“恒置 1”法,结果分别为( )。
  • A. 1.0110、1.0110
  • B. 1.0101、1.0101
  • C. 1.0110、1.0101
  • D. 1.0101、1.0110
展开解析

C0 舍 1 入:被舍去的最高位是 1 → 末位加 1,1.0101 + 0.0001 = 1.0110。恒置 1:不看舍去的位,保留部分末位置 1,原来末位已是 1 → 仍是 1.0101。若用 IEEE 754 的就近舍入(偶),舍去部分正好一半、末位为 1,要进位成 1.0110。

变化点:2026-14 用就近舍入;这里换成教材一般格式的两种方法(四种舍入对照表)。

2.7大端、小端与边界对齐

★★★ 高频选择题综合题

一个 int 占 4 个字节、4 个地址。问题来了:先放高位字节还是先放低位字节?

int x = 12345678H,存放在地址 0800H 开始的 4 个单元 地址 → 0800H0801H0802H0803H 大端高位在前 12H 34H 56H 78H 小端低位在前 x86 78H 56H 34H 12H 像写字:从高位写起“倒着放”:最低字节在最低地址
图 2-19 大端和小端。变量的地址永远是它最低的那个地址(0800H)。区别只在于这个最低地址里放的是最高字节(大端)还是最低字节(小端)。字节内部的位顺序不会颠倒。
怎么从机器码判断大小端

看指令里的立即数或偏移量:2019-45 中 call 指令的偏移量应为 FFFFFFD6H,而机器码里写的是 D6 FF FF FF(最低字节 D6 在前)→ 小端。2018-15:int i = −64 的指令机器码末尾 4 字节是 −64 的补码 FFFFFFC0H 按小端放 → C0 FF FF FF。

边界对齐:为了一次访存就能取到一个数据,编译器让每个变量的地址是它自身大小的整数倍(short 放在 2 的倍数地址,int 放在 4 的倍数地址,double 放 8 的倍数),中间不够就空着(填充);结构体的总大小还要是其最大成员大小的整数倍(这样结构体数组的每个元素也对齐)。

struct { int a; char b; short c; } record; 首地址 C008H,小端,a = 273 = 00000111H 11H 01H 00H 00H b 填充 c 低 c 高 C008C009C00AC00BC00CC00DC00EC00F a:4 字节,地址是 4 的倍数b:1 字节c:short 要放在 2 的倍数地址
图 2-20 2012-15 的内存布局。地址 C008H 里放的是 a 的最低字节 11H;c 的地址是 C00EH,结构体共 8 字节。答案 D(0x11、0xC00E)。

为什么要对齐:不对齐会多访存一次

主存和 CPU 之间一次传一个存储字(32 位机器一次 4 字节,而且只能从 4 的倍数地址开始取一整行)。一个 int 如果从 4 的倍数地址开始放,一次就能取到;如果“骑”在两行之间,就要取两次再拼起来:

对齐:int 从地址 4 开始 048 4567 正好占满一个存储字 → 访存 1 次 不对齐:int 从地址 6 开始 048 6789 跨了两个存储字 → 访存 2 次,还要拼接
图 2-21 边界对齐的好处。每行是一个 32 位存储字(4 个字节),CPU 一次只能读写一整行。不按边界对齐(紧凑存放)能省空间,但可能要多访存;按边界对齐浪费一点空间,换来每个数据一次访存就能取到。
数据按边界对齐的要求(按字节编址)地址的特征
字节(char)任意地址——
半字(short,2B)地址是 2 的倍数最低 1 位为 0
字(int、float,4B)地址是 4 的倍数最低 2 位为 00
双字(double,8B)地址是 8 的倍数最低 3 位为 000
结构体每个成员按自己的大小对齐;整个结构体的大小是最大成员大小的整数倍(末尾补齐),这样结构体数组的每个元素也对齐

成员顺序会影响结构体大小(gcc 实测):struct { char a; int b; char c; } 占 12 字节(a 占 0,1~3 填充,b 占 4~7,c 占 8,9~11 填充凑成 4 的倍数);把两个 char 放在一起 struct { char a; char c; int b; } 只占 8 字节(a、c 占 0、1,2~3 填充,b 占 4~7)。2020-14 的 struct { short x1; int x2; } 也是 8 字节:x1 占 0~1,2~3 填充,x2 从 +4 开始。

2025-15计组
32 位计算机按字节编址,采用小端方式,按边界对齐为 struct record { int id; char name[10]; int salary; } employee[200]; 分配存储空间。数组 employee 的起始地址为 0000A0B0H,employee[1].id 的机器数为 12345678H,则 56H 的地址是( )。
  • A. 0000A0C3H
  • B. 0000A0C4H
  • C. 0000A0C5H
  • D. 0000A0C6H
展开解析

考点2.7 小端与边界对齐

C两步:① 边界对齐算出结构体大小,找到 employee[1].id 的地址;② 小端方式下低位字节放低地址。

  1. 一个 record:id 占偏移 0 ~ 3;name 占 4 ~ 13;salary 是 int,要从 4 的倍数开始 → 16 ~ 19(14、15 填充);大小 = 20 字节(也是 4 的倍数 ✓)。
  2. employee[1] 起始 = A0B0H + 20 = A0B0H + 14H = A0C4H,id 就在这里。
  3. 小端:A0C4H 放 78H,A0C5H 放 56H,A0C6H 放 34H,A0C7H 放 12H。
  • A 错A0C3H 是忘了填充、把结构体按 18 字节算:employee[1] 从 A0B0H + 18 = A0C2H 开始,56H 落在 A0C3H。salary 必须 4 字节对齐,要填 2 个字节。
  • B 错A0C4H 是 id 的首地址,小端方式下这里放的是最低字节 78H,不是 56H。
  • C 对employee[1].id 从 A0C4H 开始,56H 是第 2 个字节,在 A0C5H。
  • D 错A0C6H 是大端方式下 56H 的位置(12H、34H、56H、78H 依次放),本题是小端。

结构体大小:每个成员按自身大小对齐,总大小再补成最大成员大小的倍数。小端:低字节在低地址。

2019-15计组
某计算机采用大端方式,按字节编址。某指令中操作数的机器数为 1234FF00H,该操作数采用基址寻址方式,形式地址(用补码表示)为 FF12H,基址寄存器内容为 F000 0000H,则该操作数的 LSB(最低有效字节)所在的地址是( )。
  • A. F000 FF12H
  • B. F000 FF15H
  • C. EFFF FF12H
  • D. EFFF FF15H
展开解析

考点2.7 大端与小端3.2 基址寻址

D两步:① 基址寻址算出操作数首地址(形式地址是 16 位补码,要先符号扩展成 32 位再加);② 大端方式下,最低有效字节放在地址最大的那个字节。

  1. FF12H 最高位为 1,是负数,符号扩展成 FFFF FF12H。
  2. EA = F000 0000H + FFFF FF12H = 1 EFFF FF12H,丢掉进位 → EFFF FF12H(操作数首地址)。
  3. 1234FF00H 占 EFFF FF12H ~ EFFF FF15H;大端:12H 在最低地址,LSB = 00H 在最高地址 EFFF FF15H。
  • A 错两处错:形式地址没做符号扩展(当成正数 0000 FF12H 加),得到的又是首地址——大端方式下首地址放的是最高有效字节 12H。
  • B 错+3 找 LSB 的思路对,但形式地址没做符号扩展:FF12H 是负数,F000 0000H 加上它应该变小,不会变成 F000 FF15H。
  • C 错有效地址算对了,但 EFFF FF12H 放的是最高有效字节 12H;大端方式下 LSB 在最大地址,还要 +3。
  • D 对EA = EFFF FF12H,LSB 在 EFFF FF15H。

补码形式地址要符号扩展;大端“高位在低地址”,LSB 在首地址 + (字节数 − 1);小端 LSB 就在首地址。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2012-15、2020-14 改
按字节编址、小端方式、按边界对齐存储。struct { char a; int b; short c; char d; } s; 首地址为 1000H,s.b = 12345678H,则 34H 所在的地址和 sizeof(s) 分别为( )。
  • A. 1005H、12
  • B. 1006H、12
  • C. 1006H、11
  • D. 1002H、8
展开解析

Ba 在 1000H;b 要 4 字节对齐,从 1004H 开始,小端存放 78、56、34、12,所以 34H 在 1006H。c 在 1008H~1009H,d 在 100AH。已用到 100AH,共 11 字节,结构体的总大小还要是最大成员(4)的整数倍,取 12。

变化点:真题只问一个字节的地址。这里多问了 sizeof:结构体末尾也要补齐到最大成员大小的整数倍,所以是 12,不是 11。

变式计组由 2018-15 改
某 32 位计算机按字节编址、小端方式。语句 int i = 0; 对应的机器码为 C7 45 FC 00 00 00 00,则 int i = 1000; 对应的机器码是( )。
  • A. C7 45 FC 00 00 03 E8
  • B. C7 45 FC E8 03 00 00
  • C. C7 45 FC 03 E8 00 00
  • D. C7 45 FC 00 00 E8 03
展开解析

B1000 = 0000 03E8H,小端方式低字节在前:E8 03 00 00。

变化点:2018-15 是负数 −64(FFFF FFC0H → C0 FF FF FF)。正数、负数都一样:先写出 32 位机器数,再从低字节到高字节依次排。

变式计组由 2016-14、2019-15 改
某计算机按字节编址,采用大端方式。int 型变量 x 的机器数为 89AB CDEFH,存放在 2000H 开始的单元中,则 2002H 单元的内容是( )。
  • A. ABH
  • B. CDH
  • C. EFH
  • D. 89H
展开解析

B大端方式高字节在前(低地址放高字节):2000H 放 89H,2001H 放 ABH,2002H 放 CDH,2003H 放 EFH。

变化点:2016-14 是小端。同一个数,大端和小端恰好倒过来:小端时 2002H 放的是 ABH。

2.8BCD 码、字符编码与校验码(了解即可)

★ 考过选择题

BCD 码:用 4 位二进制表示 1 位十进制

有些场合(计算器、财务、数码管显示)希望机器里直接存“十进制的每一位”,于是用 4 位二进制表示一个十进制数字 0~9,叫 BCD 码(二进制编码的十进制数)。4 位有 16 种组合,只用其中 10 种,剩下 6 种是非法码。

十进制0123456789特点
8421 码0000000100100011010001010110011110001001有权码,各位权重 8、4、2、1;1010~1111 非法
余 3 码0011010001010110011110001001101010111100= 8421 码 + 3(0011),无权码
2421 码0000000100100011010010111100110111101111有权码,权重 2、4、2、1;5 及以上最高位为 1

8421 码加法要“加 6 修正”:两个 BCD 数字按二进制相加,如果和 > 9(出现非法码)或者向高位产生了进位,就再加 6(0110),跳过那 6 个非法码。例:8 + 7 → 1000 + 0111 = 1111(> 9,非法)→ 1111 + 0110 = 1 0101,即十位 1、个位 5,得 15 ✓。

字符编码:ASCII 与汉字

  • ASCII 码用 7 位表示 128 个字符(存储时占 1 字节,最高位为 0)。要记住三个起点:'0' = 30H(48),'A' = 41H(65),'a' = 61H(97)。数字字符减 30H 就是它的数值;小写字母 = 大写字母 + 20H(第 5 位 0 → 1)。2016-44 里设备传送的就是 7 位 ASCII 字符。
  • 汉字 拓展:输入时用输入码(拼音、五笔);机器内部存储、处理用机内码(每个汉字 2 字节,每字节最高位为 1,以区别于 ASCII;机内码 = 国标码 + 8080H);显示、打印时用字形码(点阵或轮廓)。今天更常用 Unicode(UTF-8 中一个汉字一般 3 字节)。

校验码

  • 奇偶校验:加 1 位让“1 的个数”为奇数(奇校验)或偶数(偶校验),只能发现奇数个位出错,不能纠错。2016-44 里异步串行通信每个字符 = 1 起始位 + 7 数据位 + 1 奇校验位 + 1 停止位 = 10 位。
  • 海明码:能发现并纠正 1 位错。n 位数据需要 k 位校验位,满足 2k ≥ n + k + 1。8 位数据:k=3 时 8 ≥ 12 不成立,k=4 时 16 ≥ 13 成立 → 4 位(2013-15 选 C)。
  • CRC 循环冗余校验:网络里用得多(计网课),只要知道能检错、按生成多项式做模 2 除法。

海明码怎么排、怎么纠错(以 4 位数据为例,23 ≥ 4 + 3 + 1,要 3 个校验位):

  1. 排位置:7 个位从 1 编号到 7,编号是 2 的幂的位置(1、2、4)放校验位 P1、P2、P3,其余位置依次放数据位:H7 H6 H5 H4 H3 H2 H1 = D4 D3 D2 P3 D1 P2 P1。
  2. 分组:把位置编号写成二进制,编号第 1 位为 1 的(1、3、5、7)归 P1 管,第 2 位为 1 的(2、3、6、7)归 P2 管,第 3 位为 1 的(4、5、6、7)归 P3 管。偶校验:P1 = D1⊕D2⊕D4,P2 = D1⊕D3⊕D4,P3 = D2⊕D3⊕D4。
  3. 编码:数据 D4D3D2D1 = 1010 → P1 = 0⊕1⊕1 = 0,P2 = 0⊕0⊕1 = 1,P3 = 1⊕0⊕1 = 0 → 海明码 H7~H1 = 1010010。
  4. 纠错:假设 H5 在传输中出错,收到 1000010。每组重新异或:S1 = H1⊕H3⊕H5⊕H7 = 1,S2 = H2⊕H3⊕H6⊕H7 = 0,S3 = H4⊕H5⊕H6⊕H7 = 1。S3S2S1 = 101 = 5,指出第 5 位错,把它取反就纠正了;三个 S 全 0 表示没有(一位)错。

CRC 拓展:数据 101001、生成多项式 1101(x3+x2+1),在数据后面补 3 个 0 得 101001000,按模 2 除法(异或,不借位)除以 1101,余数 001,发送 101001001;接收端用同一个生成多项式去除,余数为 0 就认为没错。(已用程序验算。)

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2013-15 改
用海明码对 16 位数据进行检错和纠错,若要能纠正一位错,校验位至少需要( )位。
  • A. 4
  • B. 5
  • C. 6
  • D. 16
展开解析

Bk 个校验位要满足 2k ≥ n + k + 1。n = 16:k = 4 时 16 < 21,不够;k = 5 时 32 ≥ 22,满足。至少 5 位。

变化点:2013-15 是 8 位数据(4 位)。若还要能发现两位错,再加 1 位总校验位,即 6 位。

第 2 篇自测
  1. 32 位 int 变量 x 的机器数为 FFFFFF9EH,x 等于多少?
    答案
    100000000H − FFFFFF9EH = 62H = 98 → x = −98。(2018-13 中 x−y 的结果。)
  2. short si = −32767; unsigned short usi = si; usi 是多少?unsigned int ui = si; 呢?
    答案
    8001H,usi = 32769;ui 先符号扩展成 FFFF8001H = 4294934529 = 232−215+1。
  3. 8 位数 x = 90H、y = 80H,做 x + y,CF、OF、ZF、SF 各是多少?
    答案
    90H + 80H = 110H,结果 10H。无符号 144+128 = 272 ≥ 256 → CF=1;有符号 (−112)+(−128) = −240 超范围 → OF=1;结果非 0 → ZF=0;最高位 0 → SF=0。
  4. 把 0.75 写成 float 机器数。
    答案
    0.75 = 0.11₂ = 1.1₂ × 2−1,E = 126 = 0111 1110,M = 100…0 → 0 01111110 1000…0 = 3F400000H。(可用上面的拆解器验证。)
  5. 8 位定点小数用补码表示,范围是多少?−1 的补码是什么?有没有原码?
    答案
    −1 ~ 1 − 2−7(即 −1 ~ 127/128);[−1]补 = 1.0000000;−1 没有原码和反码(原码、反码小数的范围只到 −(1 − 2−7))。
  6. 用双符号位计算 4 位补码的 (−6) + (−3),判断是否溢出。
    答案
    11 010 + 11 101 = (1) 10 111,丢掉最高进位后符号位是 10 → 负溢出(−9 < −8)。
  7. 尾数用补码表示时,下面哪些是规格化尾数:0.0110、1.1010、1.0110、0.1100、1.1000?
    答案
    符号位与最高数值位不同才是规格化:1.0110、0.1100。1.1000 是 −1/2,不是规格化数。
  8. 8421 码计算 5 + 6。
    答案
    0101 + 0110 = 1011(> 9,非法)→ 加 0110 修正 → 1 0001,即 11 ✓。
  9. struct { char c; double d; short s; } 按边界对齐占多少字节?
    答案
    c 占 0,1~7 填充,d 占 8~15,s 占 16~17,总大小要是 8 的倍数 → 24 字节(gcc 实测)。调成 double、short、char 的顺序则只要 16 字节。
  10. 为什么 f2(24) = 33554432.0 而 f1(24) = 33554431(2017-43 第 3 问)?
    答案
    f(24) = 225−1,是 25 个 1,而 float 只有 24 位有效位,存不下最后一位 1,舍入进位后变成 225 = 33554432。int 能精确存。

2.✎本篇真题闯关(47 题)

本篇各小节挂的真题全部在这里,按小节排好。可以展开后一道道做,也可以用弹窗模式连续做。做题记录(对/错)只保存在你这台电脑的浏览器里,小节标题下的题号会随之变绿或变红。

第 3 篇 · 计组第 4 章 + 操作系统 3.1.1(编译、链接、装入)

指令系统与程序的一生:从 C 代码到进程

上一篇讲“数据”怎么变成 0 和 1,这一篇讲“程序”怎么变成 0 和 1:一条机器指令长什么样、怎么找到操作数(寻址方式)、C 语言的 if/for/函数调用对应什么汇编;最后跟着一个程序走完它的一生——编译、链接、装入内存变成进程,在内存里怎么摆放。后半部分就是操作系统“内存管理”的起点。

对应:王道计组第 4 章;王道操作系统 3.1.1难度:★★★高频:寻址方式 10 题;相对寻址/PC 计算(2009、2013、2014、2019、2023);汇编代码大题(2012、2014、2015、2017、2019、2023、2024)

3.1一条指令长什么样

★★★ 高频选择题综合题

一条机器指令 = 操作码(做什么:加、减、跳转……)+ 地址码(对谁做:哪个寄存器、哪个内存地址、或者直接给出的数)。

OP操作码 4 位Ms源寻址方式 3Rs源寄存器 3Md目的寻址方式 3Rd目的寄存器 3

2010-43 的 16 位指令格式(位 15~12 是 OP,11~6 是源操作数,5~0 是目的操作数)

字段位数 ⇄ 能表示多少个

这类题全是第 0 篇那句话:k 位字段能表示 2k 种。
OP 4 位 → 最多 16 条指令;寄存器编号 3 位 → 最多 8 个通用寄存器;偏移量 16 位补码 → −32768 ~ +32767;直接寻址的地址字段 8 位 → 只能访问 0~255(地址无负数)。

指令字长:定长还是变长,“几字长”指令

  • 指令字长 = 一条指令包含的二进制位数。它和机器字长比较,分为半字长指令、单字长指令、双字长指令(多字长指令)。指令字长大于存储字长时,取一条指令要访存多次,取指时间变长。
  • 定长指令字结构:所有指令长度相同(RISC,如 32 位 MIPS/RISC-V)——取指、译码简单,利于流水线;变长指令字结构:不同指令长度不同(CISC,如 x86 的 1~15 字节)——功能强、代码紧凑,但取指、译码复杂。
  • 定长操作码:操作码位数固定(n 位最多 2n 条指令),译码简单;可变长操作码:不同指令的操作码长度不同,典型做法是下面的“扩展操作码”。注意这两对概念是独立的:定长指令字也可以用可变长(扩展)操作码。

按“地址码有几个”分类

做一次加法要说清楚三件事:两个操作数在哪、结果放哪。把这三个位置都写进指令就是三地址指令;为了让指令短一些,可以让结果覆盖掉一个操作数(二地址),甚至让一个操作数默认就在某个寄存器里(一地址):

三地址 OP A1 A2 A3 (A1) OP (A2) → A3 二地址 OP A1(目的) A2(源) (A1) OP (A2) → A1结果覆盖 A1 一地址 OP A1 (ACC) OP (A1) → ACC另一个隐含在累加器 零地址 OP NOP、HLT;或堆栈计算机:操作数都在栈顶 指令字长一定时:地址码越多、越长,留给操作码的位就越少 → 这就是下面“扩展操作码”要解决的矛盾。
图 3-1 按地址码个数分类。A1、A2、A3 可以是寄存器编号,也可以是内存地址(怎么解释由寻址方式决定,3.2 节)。二地址指令里若两个都是内存地址,执行一条指令要访存 4 次:取指 1 次、取两个操作数 2 次、写回结果 1 次。
格式含义访存次数(A 都是主存地址)32 位指令、8 位 OP 时每个地址的寻址范围
零地址① 不需要操作数:空操作、停机、关中断;② 堆栈计算机:操作数隐含在栈顶,如 ADD = 弹出两个数,相加后压回取指 1 次(堆栈型还要访问栈)——
一地址① 单操作数:OP(A1) → A1,如加 1、取反;② 隐含约定另一个操作数在 ACC:(ACC) OP (A1) → ACC① 3 次(取指、读、写)
② 2 次(取指、读)
24 位 → 224
二地址(A1) OP (A2) → A1,结果覆盖第一个操作数4 次(取指、读两个、写一个)12 位 → 212
三地址(A1) OP (A2) → A34 次8 位 → 28
四地址(A1) OP (A2) → A3,A4 = 下一条指令的地址(不用 PC)4 次6 位 → 26

规律:在指令字长固定时,地址码越多,每个地址码越短,能直接访问的范围越小。所以现代机器让地址码大多是寄存器编号(几位就够),按操作数在哪分为 RR 型(寄存器—寄存器,不访存,最快)、RS 型(寄存器—存储器)、SS 型(存储器—存储器,最慢)。

指令有哪些种类:重点看“会不会改 PC”

类别例子对 PC(下一条指令地址)的影响
数据传送mov、load/lw、store/sw、push、pop顺序执行:PC = 本条地址 + 本条长度
算术逻辑、移位add、sub、and、shl、cmp(只设标志)顺序执行
条件转移je、jle、bne条件满足才跳;不满足就顺序执行 → 可能不改变执行顺序
无条件转移jmp一定跳到目标地址
过程调用call f先把返回地址(call 的下一条)保存起来(压栈),再跳到 f 的入口
返回ret把保存的返回地址放回 PC,回到调用者“原本的下一条”
陷入(自陷、访管)int、syscall、trap一定转去操作系统的处理程序,CPU 切到内核态(第 5 篇)
输入输出in、out(I/O 端口独立编址时才有,第 9 篇)顺序执行
特权指令(系统控制)开 / 关中断、修改 PSW 的模式位、设置页表基址寄存器、停机;多数系统的 I/O 指令也是特权指令顺序执行;但只能在内核态执行,用户态执行会引起异常(第 5 篇,2022-27、2026-21)

教材按功能把指令分成:数据传送类、算术和逻辑运算类、移位类、转移类(无条件转移、条件转移、调用与返回、陷入)、输入输出类,以及只有操作系统能用的特权指令。上表把它们和“会不会改 PC”放在一起,是因为条件转移、调用、返回、陷入这几类正是第 4 篇流水线“控制冒险”和第 5 篇中断的来源。

2014-17计组
某计算机有 16 个通用寄存器,采用 32 位定长指令字,操作码字段(含寻址方式位)为 8 位,Store 指令的源操作数和目的操作数分别采用寄存器直接寻址和基址寻址方式。若基址寄存器可使用任一通用寄存器,且偏移量用补码表示,则 Store 指令中偏移量的取值范围是( )。
  • A. −32768 ~ +32767
  • B. −32767 ~ +32768
  • C. −65536 ~ +65535
  • D. −65535 ~ +65536
展开解析

考点3.1 指令格式2.1 补码表示范围

A先画出 32 位指令格式,求出偏移量占几位,再用 n 位补码的范围 −2n−1 ~ 2n−1 − 1。

  1. 16 个通用寄存器 → 寄存器编号要 4 位。
  2. 格式:OP 8 位 | 源寄存器 4 位 | 基址寄存器 4 位 | 偏移量 32 − 8 − 4 − 4 = 16 位。
  3. 16 位补码范围:−215 ~ 215 − 1 = −32768 ~ +32767。
  • A 对偏移量 16 位,补码范围 −32768 ~ +32767。
  • B 错把补码范围的两端对调了:补码是“负数比正数多一个”,+32768 = 215 已超出 16 位补码的最大值 32767。
  • C 错−65536 ~ +65535 是 17 位补码的范围,相当于偏移量有 17 位;但 32 − 8 − 4 − 4 只剩 16 位。
  • D 错上界 +65536 = 216,16 位补码最大只到 32767,连 16 位无符号数最大也只有 65535,不可能达到。

算字段宽度:总长减去所有已知字段;n 位补码 −2n−1 ~ 2n−1 − 1。同类:2020-16(48 条指令 → OP 6 位,4 种寻址 → 2 位,剩 8 位地址,直接寻址范围 0 ~ 255)。

条件码(标志位):比较指令和条件转移指令怎么“接力”

问题引入:C 语言的 if (a < b) 编译成两条指令:cmp a, b 和 jl 目标。cmp 只做减法、不保存结果;jl 根本不看 a、b 是多少——它凭什么知道 a < b?答案是两条指令之间靠条件码传话。

打个比方

cmp 像裁判:比完一场只在记分牌上写下几个结论——“差是不是 0”“差是不是负的”“有没有借位”“有没有溢出”。后面的跳转指令只看记分牌,不再重新比赛。不像的地方:记分牌会被下一条算术指令改写,所以 cmp 和条件转移之间一般不能夹着别的会改标志的指令。

教材定义:条件码(又叫标志位、状态标志)存放在程序状态字寄存器 PSW(x86 叫 EFLAGS)中,由运算类指令根据结果自动设置,供条件转移等指令测试。常用四个:ZF 零标志(结果为 0)、SF(也写作 NF)符号标志(结果最高位)、CF 进位 / 借位标志(看无符号数是否越界)、OF 溢出标志(看有符号数是否越界)。它们怎么由加法器电路产生,见第 2 篇 2.4。

cmp al, blal=80H bl=01H ALU 算 a − b80H − 01H = 7FH 差值 7FH 丢掉(cmp 不写回) 条件码(在 PSW 中) ZF=0 SF=0 CF=0 OF=1 写 jl(有符号 <)SF ≠ OF?0 ≠ 1 → 跳 jb(无符号 <)CF = 1?否 → 不跳 同一对位串:当有符号数是 −128 < 1(jl 跳),当无符号数是 128 > 1(jb 不跳) 编译器根据 C 里变量是 int 还是 unsigned,决定生成 jl 还是 jb——标志都算好了,选哪个看类型
图 3-2 条件码的“接力”。cmp(以及 sub、add、test 等)把结论写进 PSW,条件转移指令只读 PSW 决定 PC 的去向:满足条件 PC ← 转移目标,否则顺序执行。

跟我做一遍(8 位,已用 verify/08/p03_calc.py 验算):cmp a, b,a = 80H,b = 01H。

  1. ALU 算 80H − 01H = 7FH(实际做 80H + FFH,丢掉最高位进位)。
  2. ZF = 0(结果不为 0);SF = 0(7FH 最高位是 0);CF = 0(无符号 128 − 1 不需要借位);OF = 1(负数减正数却得到正数,有符号溢出)。
  3. 有符号比较:a = −128 < b = 1 成立。jl 的条件是 SF ≠ OF:0 ≠ 1 → 跳。注意这里不能只看 SF——溢出时 SF 给出的符号是反的,所以要用 SF ⊕ OF。
  4. 无符号比较:a = 128 < b = 1 不成立。jb 的条件是 CF = 1:CF = 0 → 不跳。

再换一组:cmp 05H, FBH → 结果 0AH,CF = 1、OF = 0、SF = 0。有符号 5 < −5 不成立(jl 不跳),无符号 5 < 251 成立(jb 跳)。完整的条件转移表在 3.3 节 常用指令。

真题怎么考:2011-17(无符号“大于”转移:CF = 0 且 ZF = 0)、2018-19 和 2023-16(给出操作数求 CF、OF)、2013-44(指令里的 C、Z、N 三位决定检测哪些标志,“无符号小于等于”取 C = 1、Z = 1)、2019-45(5)(imul 后用 OF 判乘法溢出)。判别法:CF 管无符号、OF 管有符号;“小于”有符号看 SF ≠ OF、无符号看 CF = 1。失效的情况:并不是每条指令都改标志(x86 的 mov 不改标志、inc 不改 CF);题目自定义指令系统时,要按题目说的“哪些指令设置哪些标志”来。

自测
  1. 8 位 cmp FFH, 01H 之后,ZF、SF、CF、OF 各是多少?jl、jb 跳不跳?
    答案
    FFH − 01H = FEH:ZF = 0,SF = 1,CF = 0,OF = 0。有符号 −1 < 1,jl(SF ≠ OF:1 ≠ 0)跳;无符号 255 < 1 不成立,jb(CF = 1?)不跳。
  2. 为什么说 cmp 和 sub 的区别只有一点?
    答案
    两者都做减法、都设置标志;sub 把差写回目的操作数,cmp 不写回(只要标志)。

扩展操作码:常用的指令给短操作码

定长指令字里,地址码多的指令留给操作码的位少,地址码少的指令能用更长的操作码。扩展操作码的规则:短操作码不能是长操作码的前缀(否则译码分不清)。做法是在短操作码里留几个编码不用,拿它们当“前缀”扩展出下一级。

16 位指令字,地址码每个 4 位 0000~1110 A1A2A3 三地址指令 15 条 1111 0000~1110 A1A2 二地址指令 15 条 1111 1111 0000~1110 A1 一地址指令 15 条 1111 1111 1111 0000 ~ 1111 1111 1111 1111 零地址指令 16 条 灰色 = 上一级留出来当前缀的编码 橙色 = 这一级真正分配的操作码
图 3-3 扩展操作码。计算规则:某一级剩下 r 个编码没用,下一级就有 r × 2(让出来的地址码位数) 个编码可用。

和定长操作码比一比:同样是 16 位指令、每个地址码 4 位,若操作码定长 4 位,最多只有 24 = 16 条指令,而且每条都得按三地址格式编码(地址码少的指令也浪费着那几位);用扩展操作码,同样 16 位能编出 15 + 15 + 15 + 16 = 61 条。定长操作码的好处是译码简单、速度快,RISC 多用它(3.4)。

扩展操作码的三条规矩和一个万能式

① 短操作码不能是长操作码的前缀,否则译码时分不清(所以上一级必须“留”编码当前缀);② 各条指令的操作码一定不能重复;③ 通常让使用频率高的指令用短操作码,减少平均指令长度(可变长指令字时效果明显)。
万能式(定长指令字 L 位,各级地址码总长分别为 a1 > a2 > …):把每条 k 级指令看成“占掉”2ak 个完整编码,则 Σ(该级指令条数 × 2该级地址码总位数)≤ 2L。求“最多还能有多少条某级指令”,就用 2L 减去已占的,再除以 2该级地址位数。2022-19 的一步式就是它。

2022-19计组
假设采用 16 位定长指令字格式,操作码使用扩展编码方式,地址码为 6 位,包含零地址、一地址和二地址 3 种格式的指令。若二地址指令有 12 条,一地址指令有 254 条,则零地址指令的条数最多为( )。
  • A. 0
  • B. 2
  • C. 64
  • D. 128
展开解析

考点3.1 扩展操作码

D扩展操作码:长操作码的指令用剩下的“前缀”往后扩展。每少一个地址码,操作码就多 6 位。

  1. 二地址:OP = 16 − 6 × 2 = 4 位,共 24 = 16 个编码,用掉 12,剩 4 个作扩展前缀。
  2. 一地址:每个前缀后再拼 6 位 → 4 × 26 = 256 个编码,用掉 254,剩 2 个前缀。
  3. 零地址:2 × 26 = 128 条。一步式验算:216 − 12 × 212 − 254 × 26 = 128 ✓。
  • A 错0 条是以为一地址 254 条已经把编码用完;其实一地址最多可有 256 个编码,还剩 2 个可以继续扩展。
  • B 错2 只是一地址剩下的前缀个数,忘了每个前缀后面还要再拼 6 位(原来的地址码位)当操作码,要乘 26。
  • C 错64 = 26,只算了 1 个前缀的扩展;一地址剩下的是 2 个前缀,应为 2 × 64。
  • D 对2 × 26 = 128。

扩展操作码一步式:零地址最多条数 = 216 − Σ(各类条数 × 2该类地址码总位数)。

2017-16计组
某计算机按字节编址,指令字长固定且只有两种指令格式,其中三地址指令 29 条,二地址指令 107 条,每个地址字段为 6 位,则指令字长至少应该是( )。
  • A. 24 位
  • B. 26 位
  • C. 28 位
  • D. 32 位
展开解析

考点3.1 扩展操作码

A三地址指令地址码最多,所以它的操作码最短;剩下没用的操作码编码当作前缀,扩展出二地址指令。

  1. 三地址指令 29 条:操作码至少 5 位(25 = 32 ≥ 29),用掉 29 个,剩 3 个编码作前缀。
  2. 二地址指令:每个前缀后面多出一个地址字段的 6 位可作操作码,共 3 × 26 = 192 ≥ 107,够用。
  3. 指令字长 = 5 + 3 × 6 = 23 位;按字节编址,指令长度应为字节的整数倍 → 24 位。
  • A 对24 位。
  • B 错26 位不是 8 的整数倍,按字节编址时不合适,而且也不是“至少”。
  • C 错28 位同样不是字节的整数倍。
  • D 错32 位当然够用,但 24 位已能放下 5 位操作码 + 3 个 6 位地址(共 23 位),32 不是最短。

扩展操作码:某一级剩 r 个编码,下一级可用 r × 2让出的位数 个。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2022-19 改
16 位定长指令字,操作码采用扩展编码,地址码为 5 位,有二地址、一地址、零地址三种格式。若二地址指令有 60 条,一地址指令有 120 条,则零地址指令最多有( )条。
  • A. 8
  • B. 32
  • C. 256
  • D. 512
展开解析

C二地址指令的操作码有 16 − 10 = 6 位,共 64 个编码,用去 60 个,剩 4 个可扩展。一地址指令:4 × 25 = 128 个编码,用去 120 个,剩 8 个。零地址指令最多 8 × 25 = 256 条。

变化点:2022-19 的地址码 6 位。每一级都是“剩下的编码 × 2地址码位数”,一级一级往下乘。

变式计组由 2014-17 改
某计算机有 32 个通用寄存器,采用 32 位定长指令字,操作码字段(含寻址方式位)为 7 位。Load 指令的目的操作数采用寄存器直接寻址,源操作数采用基址寻址,基址寄存器可以是任一通用寄存器,偏移量用补码表示。偏移量的取值范围是( )。
  • A. −16384 ~ +16383
  • B. −32768 ~ +32767
  • C. −8192 ~ +8191
  • D. 0 ~ 32767
展开解析

A32 个寄存器,每个寄存器号 5 位。偏移量 = 32 − 7 − 5(目的寄存器)− 5(基址寄存器)= 15 位,补码范围 −214 ~ 214 − 1。

变化点:2014-17 是 16 个寄存器(4 位)、操作码 8 位,偏移 16 位。寄存器数和操作码位数一变,偏移的位数跟着变。

变式计组由 2020-16 改
某计算机采用 16 位定长指令字,操作码和寻址方式字段的位数固定。指令系统有 60 条指令,支持 8 种寻址方式。单地址指令中,相对寻址的位移量用补码表示,其范围是( )。
  • A. 0 ~ 127
  • B. −64 ~ +63
  • C. −128 ~ +127
  • D. 0 ~ 511
展开解析

B操作码 ⌈log260⌉ = 6 位,寻址方式 ⌈log28⌉ = 3 位,地址字段 16 − 6 − 3 = 7 位。相对寻址的位移是补码:−64 ~ +63。

变化点:2020-16 问直接寻址(地址是无符号数,0 ~ 2n − 1)。同样 n 位,相对寻址是补码,范围是 −2n−1 ~ 2n−1 − 1。

变式计组由 2011-17、2018-19 改(考纲补充:条件码与条件转移)
8 位机器中执行 cmp al, bl(计算 al − bl,只设置标志),al = 90H,bl = 10H。之后分别执行有符号小于转移 jl 和无符号小于转移 jb,结果是( )。
  • A. jl 转移,jb 不转移
  • B. jl 不转移,jb 转移
  • C. 两条都转移
  • D. 两条都不转移
展开解析

A90H − 10H = 80H:ZF = 0,SF = 1,CF = 0(无符号 144 ≥ 16,不借位),OF = 0(−112 − 16 = −128,在范围内)。jl 的条件 SF ≠ OF:1 ≠ 0,转移(有符号 −112 < 16 ✓);jb 的条件 CF = 1:不满足,不转移(无符号 144 > 16 ✓)。

变化点:真题问“某条件转移看哪几个标志”或“CF、OF 是多少”,这里把两步连起来:先算标志,再判断跳不跳(条件码的接力)。

3.2寻址方式:怎么找到操作数

★★★ 高频选择题综合题

先分清:指令寻址和数据寻址

“寻址”要找两样东西:下一条指令在哪(指令寻址),这条指令的操作数在哪(数据寻址)。前者只有两种:

顺序寻址
(PC) + “1” → PC,按顺序取下一条。“1”是一条指令的长度:按字节编址、指令 4 字节时 PC + 4;变长指令时加本条指令的字节数(2023-44 第 19 条指令长 11 字节)。
跳跃寻址
下一条指令的地址由本条指令给出——转移、调用、返回、陷入指令。给法有两种:直接给出目标地址(绝对转移),或给出相对 PC 的偏移(相对转移,见下面相对寻址)。跳跃寻址是通过修改 PC 的内容实现的。

下面讲的都是数据寻址。一条指令里可能有好几个操作数,每个操作数的寻址方式由指令中的寻址特征位(或操作码隐含)指明——CPU 正是靠寻址方式来区分寄存器里放的是“操作数”还是“操作数的地址”(2023-17)。

指令里的地址字段叫形式地址 A,它不一定就是操作数所在的地址。根据寻址方式,CPU 用 A 算出真正的地址——有效地址 EA,再去拿操作数。先看一个例子,同一组数据、六种寻址方式能拿到六个不同的结果:

设:形式地址 A = 2000H,寄存器 R = 1000H;内存 [1000H] = 2000H,[2000H] = 3000H,[3000H] = 4000H
寻址方式记法有效地址 EA取到的操作数取操作数要访存几次用途
立即寻址#A(没有,A 本身就是数)2000H0常数
直接寻址AA = 2000H3000H1全局变量
间接寻址(A)(A) = 3000H4000H2指针
寄存器寻址R(在寄存器里)1000H0最快
寄存器间接(R)(R) = 1000H2000H1指针在寄存器里
变址 / 基址A(R)(R) + A = 3000H4000H1数组、局部变量
相对寻址A(PC)(PC) + A一般用于跳转目标地址转移指令

“访存次数”只算取操作数,不算取指令本身。2013-17 考的就是变址寻址这一行:EA = 1000H + 2000H = 3000H,操作数 4000H(选 D)。

把表里的几种方式画成“寻宝路线”:从指令出发,箭头走几步才找到操作数,就访存几次。

① 直接寻址:EA = A OPA=2000H [2000H]操作数 3000H 访存 1 次 ② 间接寻址:EA = (A) OPA=2000H [2000H]EA = 3000H [3000H]操作数 4000H 访存 2 次(“指针”) ③ 寄存器间接:EA = (R) OPR R = 1000H [1000H]操作数 2000H 取 R 不访存,只访存 1 次 ④ 基址 / 变址:EA = (R) + A OPRA=2000H R = 1000H + [3000H]操作数 4000H 基址:R 放区域起点,A 放偏移(栈帧里的 [ebp−8]) 变址:A 放数组首址,R 放 i×元素大小(循环里改 R) ⑤ 相对寻址:EA = (PC) + A PC 是已经指向下一条的 PC,A 是补码偏移; 算出来的一般是跳转目标:目标 = 本条地址 + 本条长度 + A 好处:整段程序搬到内存别处,相对距离不变,指令不用改
图 3-4 寻址方式的路线图(数据同上表)。判断一种寻址方式要访存几次:从“拿到指令”开始数,箭头每进一次内存就是一次访存;只经过寄存器不算。

十种数据寻址方式全表(考研要逐个会)

寻址方式有效地址 EA取数访存优点缺点 / 注意
隐含寻址不写地址,操作数的位置由操作码隐含规定(如一地址指令的另一个操作数在 ACC)0缩短指令字长需要专门硬件存放隐含的操作数
立即寻址无,形式地址 A 本身就是操作数(#A,常用补码)0取指时操作数就到手,最快A 的位数限制了立即数的范围
直接寻址EA = A1简单,不需要计算A 的位数限制寻址范围(2A 位数);程序中地址固定,不便修改
一次间接寻址EA = (A)2扩大寻址范围(地址宽度 = 存储字长);便于编指针、子程序返回多访存一次,慢
多次间接 拓展主存字的最高位是标志:1 表示“还是地址,继续间接”,0 表示找到了 EA≥ 3——更慢;地址少 1 位
寄存器寻址操作数在寄存器 Ri 中(不是地址)0指令短(只写寄存器编号),不访存,快寄存器数量有限
寄存器间接EA = (Ri)1比一次间接少访存一次;范围大——
相对寻址EA = (PC) + A,A 为补码1程序在内存里整体搬家也不用改,便于编写浮动程序;用于转移PC 是“取指后”的值(下一条的地址)
基址寻址EA = (BR) + A1扩大寻址范围;便于多道程序重定位BR 面向操作系统,程序执行中一般不变
变址寻址EA = (IX) + A1处理数组、循环最方便IX 面向用户,程序执行中不断修改
堆栈寻址EA = (SP),操作数在栈顶,由 SP 隐含指出硬堆栈 0
软堆栈 1
指令极短(零地址)硬堆栈用一组寄存器,快但小;软堆栈在主存里

上面“取数访存”都不含取指令本身那 1 次。速度大致是:立即 > 寄存器 > 直接 ≈ 寄存器间接 ≈ 偏移(相对/基址/变址)> 间接。复合方式按字面顺序算:先变址后间址 EA = ((IX) + A)(2016-17);先间址后变址 EA = (A) + (IX)。变址或寄存器间接还常带自动增减:(R)+ 表示先用 R 的内容作地址取数、再把 R 加 1(后增),−(R) 表示先把 R 减 1 再用(先减);“加 1”指加一个操作数的长度:按字编址时就是 +1(2010-43 按字编址,(R5)+ 执行后 R5 从 5678H 变成 5679H),按字节编址、操作数 2 字节时是 +2,看题目规定。

基址寻址 vs 变址寻址:公式一样,用法不同

基址寻址 EA = (BR) + A
基址寄存器 BR 放一块区域的起点(由操作系统或程序设置,程序执行中一般不变),A 是区域内的偏移。用来访问“以某处为起点”的数据,比如栈帧里的局部变量 [ebp−8]。
变址寻址 EA = (IX) + A
A 放数组首地址(固定),变址寄存器 IX 放下标×元素大小,每次循环改它。最适合按下标顺序访问数组(2017-15 选 D)。
偏移寻址公式寄存器由谁设置、变不变A 是什么典型用途
基址(BR) + A操作系统或管理程序设置,程序执行中不变;也可以用通用寄存器当基址(显式)相对基址的偏移多道程序的重定位;局部变量 [ebp−8]
变址(IX) + A用户程序设置,程序执行中不断改变(每次循环 +1 或 + 元素大小)数组首地址(固定)数组、循环
相对(PC) + APC 由硬件自动修改相对当前指令的偏移(补码)转移指令、浮动程序

三者的共同点是“某个寄存器 + 形式地址”,统称偏移寻址;间接寻址 EA = (A) 不用寄存器,不属于偏移寻址(2011-16)。

堆栈寻址:push 和 pop 时 SP 怎么变

堆栈是按“后进先出”存取的一块区域,栈顶的地址放在堆栈指针 SP 里。主存里的软堆栈通常从高地址向低地址长(x86 就是这样),这时 push/pop 的动作是:

初始 push A 之后 pop 之后 1008H(更早的数据) 1004HB(栈顶) 1000H 0FFCH SP = 1004H 1008H(更早的数据) 1004HB 1000HA(新栈顶) 0FFCH SP = 1000H 1008H(更早的数据) 1004HB(栈顶) 1000HA(还在,但已无效) 0FFCH SP = 1004H push A:(SP) − 4 → SP,A → M(SP)(先减后写)  pop X:M(SP) → X,(SP) + 4 → SP(先读后加) 每个数据 4 字节;SP 始终指向“当前栈顶那个数据”;出栈只是移动 SP,并不会把内存清零
图 3-5 向低地址增长的堆栈。这是 x86 的约定:SP(esp)指向栈顶元素,push 先减后写、pop 先读后加。有的题目约定 SP 指向“栈顶的下一个空单元”,那就变成 push 先写后减、pop 先加后读——做题前先看题目怎么约定。
2018-18计组
按字节编址的计算机中,某 double 型数组 A 的首地址为 2000H,使用变址寻址和循环结构访问数组 A,保存数组下标的变址寄存器初值为 0,每次循环取一个数组元素,其偏移地址为变址值乘以 sizeof(double),取完后变址寄存器内容自动加 1。若某次循环所取元素的地址为 2100H,则进入该次循环时变址寄存器的内容是( )。
  • A. 25
  • B. 32
  • C. 64
  • D. 100
展开解析

考点3.2 变址寻址

B变址寻址 EA = 首地址 + 变址值 × 元素大小;反过来,变址值 = (EA − 首地址) ÷ 元素大小。注意 H 是十六进制。

  1. 偏移 = 2100H − 2000H = 100H = 256 字节。
  2. double 占 8 字节 → 下标 = 256 ÷ 8 = 32,就是进入该次循环时变址寄存器的值(取完才加 1)。
  • A 错25 = 100 ÷ 4:把 100H 当成了十进制 100,又把 double 当成 4 字节,两处都错。
  • B 对100H = 256,256 ÷ 8 = 32。
  • C 错64 = 256 ÷ 4:十六进制换算对了,但 sizeof(double) 是 8,不是 4。
  • D 错100 是把偏移 100H 直接当成十进制的下标,既没换算进制,也没除以元素大小 8。

数组下标 = (元素地址 − 首地址) ÷ sizeof(元素);带 H 的数先换成十进制。同类:2016-17(先变址后间址 EA = ((I) + D),选 C);2011-16(间接寻址 EA = (A) 不用寄存器,不属于偏移寻址)。

寄存器、隐含、堆栈寻址:三种“几乎不写地址”的寻址,各跟做一例

问题引入:全表里这三种寻址的“取数访存”都是 0 次或很少,指令也特别短。它们是怎么省掉地址的?代价又是什么?

打个比方

点外卖时说地址有三种偷懒法:“送到 3 号柜”(寄存器寻址:只写一个很短的编号);“老地方”(隐含寻址:什么都不写,双方事先约好);“放在最上面那一格”(堆栈寻址:位置由“栈顶”决定)。不像的地方:硬件必须专门准备好“老地方”和“栈顶指针”(ACC、SP),这就是“需要专门硬件”的代价。

  1. 寄存器寻址:ADD R1, R2 表示 (R1) + (R2) → R1。两个操作数都在寄存器里,指令里只写两个寄存器号(8 个寄存器各 3 位)。访存:取指 1 次,取操作数 0 次。
  2. 隐含寻址:单累加器机器的 ADD X 表示 (ACC) + (X) → ACC。X 用直接寻址,另一个操作数和结果的位置都隐含在 ACC 里,指令里一个字也没写。访存:取指 1 次 + 读 X 1 次 = 2 次。
  3. 堆栈寻址:在“堆栈计算机”上算 x = (a + b) × c,用 6 条指令,栈的变化如下图。
a = 3,b = 4,c = 5;后缀式 a b + c ×(数据结构“表达式求值”的做法,硬件直接执行) push a 3 读 a push b 3 4 读 b add 7 不访存 push c 7 5 读 c mul 35 不访存 pop x (空) 写 x = 35 蓝色 = 当前栈顶。add、mul 是零地址指令:两个操作数都隐含在栈顶,结果压回栈顶 硬堆栈(栈在 CPU 内的寄存器组)时,取数据只有“读 a、读 b、读 c、写 x”4 次访存
图 3-6 零地址指令在栈上计算 (a + b) × c。push、pop 是一地址指令(要写变量的地址),add、mul 是零地址指令(操作数隐含在栈顶)。若栈放在主存里(软堆栈),每次读写栈也要访存:push 2 次、add 3 次、mul 3 次、pop 2 次,合计 2+2+3+2+3+2 = 14 次(不含取指,已用 verify/08/p03_calc.py 验算)。

真题怎么考:2017-15(哪种寻址方式适合按下标访问数组:变址;寄存器寻址这类“速度最快”的结论也常作干扰项)、2022-19(零地址指令条数)、3.1 的地址码分类表。判别法:看操作数“写在指令里的是什么”——写的是数(立即)、寄存器号(寄存器)、什么都没写(隐含 / 堆栈)。失效的情况:“堆栈寻址不访存”只对硬堆栈成立;软堆栈的栈在主存,取栈顶要访存 1 次。

自测
  1. 用栈机指令写出 x = a × b + c × d,共几条?
    答案
    push a;push b;mul;push c;push d;mul;add;pop x,共 8 条(后缀式 a b × c d × +)。
  2. 立即寻址和寄存器寻址取操作数都不访存,哪个更快?
    答案
    立即寻址:操作数随指令一起取进来,不用再读寄存器堆;寄存器寻址还要按编号读一次寄存器(很快,但多一步)。

相对寻址:最容易错在“PC 已经加过了”

易错

相对寻址 EA = (PC) + A 里的 PC,是取完这条指令之后的 PC,已经指向下一条指令了。所以跳转目标 = 本指令地址 + 本指令长度 + 偏移量。偏移量是补码,可正可负;有的机器偏移量以“字/指令”为单位,还要先乘指令长度(看题目怎么说,或者从代码里反推)。

2009-16计组
某机器字长 16 位,主存按字节编址,转移指令采用相对寻址,由两个字节组成,第一字节为操作码字段,第二字节为相对位移量字段。假定取指令时,每取一个字节 PC 自动加 1。若某转移指令所在主存地址为 2000H,相对位移量字段的内容为 06H,则该转移指令成功转移后的目标地址是( )。
  • A. 2006H
  • B. 2007H
  • C. 2008H
  • D. 2009H
展开解析

考点3.2 相对寻址

C相对寻址的基准是取完这条指令之后的 PC。题目说每取一个字节 PC 加 1,这条转移指令 2 字节。

  1. 取完指令后 PC = 2000H + 2 = 2002H。
  2. 目标地址 = 2002H + 06H = 2008H。
  • A 错2006H:用了本条指令的地址 2000H 作基准,忘了 PC 已经自增。
  • B 错2007H:只加了 1(只算了取操作码那一个字节)。
  • C 对2002H + 6 = 2008H。
  • D 错2009H = 2002H + 06H + 1,比正确值多加了 1:PC 只在取指令的两个字节时各加 1,转移时不再加。

相对转移目标 = 本条地址 + 本条长度 + 位移量。

2013-44计组综合题
16 位定长指令字,条件转移指令格式:OP(15~11,00000)| C | Z | N | OFFSET(7~0,补码)。C、Z、N 为 1 表示需检测 CF、ZF、NF,需检测的标志中只要有一个为 1 就转移。转移时目标 = (PC)+2+2×OFFSET,否则下一条为 (PC)+2。(1)按字节还是按字编址?向后最多跳多少条指令?(2)某指令地址 200CH,内容为 00000 011 1110 0011(C=0,Z=1,N=1,OFFSET=E3H):CF=0、ZF=0、NF=1 时执行后 PC=?CF=1、ZF=0、NF=0 时呢?(3)“无符号数比较小于等于时转移”的 C、Z、N 应是什么?
展开解析
  1. 编址:指令 16 位 = 2 字节,顺序执行 PC+2 → 每个地址是 1 字节,按字节编址。OFFSET 8 位补码 −128~127,目标 − 本指令地址 = 2 + 2×OFFSET,最负是 2 − 256 = −254 字节 = 127 条指令。
  2. 第一种情况:Z=1、N=1 要检测 ZF 和 NF,NF=1 → 转移。OFFSET = E3H = −29,目标 = 200CH + 2 + 2×(−29) = 200EH − 3AH = 1FD4H。
  3. 第二种情况:只检测 ZF、NF,它们都是 0 → 不转移,PC = 200CH + 2 = 200EH(CF=1 不在检测范围内)。
  4. (3)无符号 A ≤ B ⇔ A−B 借位(CF=1)或为零(ZF=1),不看符号 → C=1、Z=1、N=0。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2009-16 改
某机器按字节编址,相对转移指令由 3 个字节组成:第 1 字节为操作码,后 2 字节为用补码表示的相对位移量。取指令时每取一个字节 PC 自动加 1。若该转移指令位于 2000H,相对位移量为 FFF0H,则转移成功后的目标地址是( )。
  • A. 1FF0H
  • B. 1FF3H
  • C. 2003H
  • D. 2013H
展开解析

B取完这条指令后 PC = 2000H + 3 = 2003H。FFF0H 是 −16 的补码,目标地址 = 2003H − 10H = 1FF3H。

变化点:2009-16 的指令 2 字节、位移为正数(2002H + 6 = 2008H)。这里有两处变化:指令长 3 字节,PC 要加 3;位移是负数,要先把补码换成真值。A 是用指令自己的地址去加。

变式计组由 2013-17、2016-17 改
变址寄存器 R 的内容为 1000H,指令中的形式地址 A 为 2000H;主存 1000H、2000H、3000H、4000H 单元的内容依次为 2000H、3000H、4000H、5000H。采用“先间址后变址”的寻址方式,访问到的操作数是( )。
  • A. 3000H
  • B. 4000H
  • C. 5000H
  • D. 2000H
展开解析

C先间址:取 (A) = (2000H) = 3000H;再变址:EA = 3000H + (R) = 4000H;操作数 = (4000H) = 5000H。

变化点:2016-17 是“先变址后间址”:EA = ((R) + A) = (3000H) = 4000H,操作数也是 5000H,但计算路线不同,要分清。B 是把有效地址当成了操作数。

变式计组由 2018-18 改
按字节编址。int 型数组 A 的首地址为 3000H,用变址寻址逐个访问 A 的元素:偏移地址就等于变址寄存器的内容,每取一个元素后变址寄存器加 4。若某次取的元素地址为 3100H,则此时变址寄存器的内容是( )。
  • A. 32
  • B. 64
  • C. 100
  • D. 256
展开解析

D偏移地址 = 3100H − 3000H = 100H = 256。变址寄存器里放的是字节偏移,它就等于 256。

变化点:2018-18 中变址寄存器放的是下标,偏移 = 下标 × 8,所以要除以 8。这里寄存器直接放字节偏移,不用再除。若按下标算,会得到 B(256 ÷ 4 = 64)。

变式计组考纲补充(尚无真题):堆栈寻址
某堆栈计算机的 sub 指令约定为:弹出栈顶 y,再弹出 x,把 x − y 压入栈。设 a = 8,b = 2,c = 3,执行 push a;push b;add;push c;sub;pop x 后,x 的值为( )。
  • A. −7
  • B. 13
  • C. 3
  • D. 7
展开解析

D栈依次为 [8] → [8, 2] → add → [10] → [10, 3] → sub:y = 3、x = 10,压入 10 − 3 = 7 → pop x 得 7。A 是把减数和被减数弄反(3 − 10);B 是当成加法。add、sub 是零地址指令,操作数都隐含在栈顶。

考点:堆栈寻址与零地址指令。

3.3看懂汇编:C 代码的机器级表示

★★★ 高频综合题

x86 的寄存器:先认识“演员”

3116 158 70 ax(低 16 位) eax高 16 位ahal ebxbhbl ecxchcl edxdhdl esiedi ebpesp eip= PC eflagsZF SF… eax 累加器;函数返回值放这里;乘除法用 ebx 基址寄存器(常放数组首地址) ecx 计数器(循环变量常放这里) edx 数据;乘法积 / 除法被除数的高 32 位 esi / edi 源 / 目的变址寄存器 ebp 帧指针:当前栈帧的底 esp 栈指针:永远指向栈顶 eip、eflags 程序员可见,但不能用 mov 直接写 eip
图 3-7 IA-32(32 位 x86)的寄存器。8 个 32 位通用寄存器,名字前的 e 表示 extended(32 位)。前 4 个的低 16 位可以单独用(ax、bx…),低 16 位里的高、低 8 位也能单独用(ah、al…)。mov al, 5 只改 eax 的最低 8 位,其余 24 位不变。

同一条指令的两种写法:Intel 格式与 AT&T 格式

Intel 格式(Windows、408 大题最常见)AT&T 格式(Linux 下 gcc 默认)
操作数顺序目的在前:mov eax, ebx(ebx → eax)源在前:movl %ebx, %eax
寄存器 / 立即数eax、8%eax、$8
内存操作数[ebp+8]、[ebx+ecx*4+8]8(%ebp)、8(%ebx,%ecx,4)
操作数长度dword ptr(32 位)、word ptr(16 位)、byte ptr(8 位)指令后缀 l、w、b:movl、movw、movb

两种写法翻译出的机器码完全一样。看到 %、$ 就是 AT&T,记得把源和目的倒过来读。内存操作数的一般形式是 [基址寄存器 + 变址寄存器 × 比例因子 + 偏移],比例因子只能是 1、2、4、8,正好对应 char、short、int、double 数组。

常用指令

408 大题里的汇编大多是 x86(Intel 格式),少数是 MIPS/RISC-V 风格。不需要会写,只要看得懂下面这些:

指令意思备注
mov eax, [ebp+8]eax ← 内存[ebp+8](方括号 = 取内存)Intel 格式:目的在左
mov [ebp-4], 0内存[ebp−4] ← 0立即数写内存
add / sub / imul eax, ecxeax ← eax ± × ecx会设置标志位
shl edx, 1edx 左移 1 位(×2)整数才能这样乘 2(2017-44)
cmp a, b算 a − b,只设标志不存结果后面跟条件跳转
jmp / je / jne / jle / jge / jb …无条件跳 / 按标志跳jl/jg 有符号,jb/ja 无符号
push x / pop x入栈 / 出栈(esp 减 4 / 加 4)栈向低地址增长
call f / retcall:把下一条指令地址压栈,再跳到 f;ret:弹出返回地址到 PC过程调用
lw r5, 0(r4) / swRISC:r5 ← M[r4+0],只有 load/store 能访存2014-44、2024-43

再补几条大题代码里常出现、又容易看错的:

指令意思易错点
lea eax, [ebx+ecx*4]eax ← ebx + ecx×4(只算地址,不访存)和 mov eax, [ebx+ecx*4] 不同:mov 会去内存取数。编译器也常用 lea 做“乘常数 + 加法”
inc / dec xx ← x ± 1循环变量 i++ 常编译成它
neg x / not xx ← −x(求补)/ x ← 按位取反neg = 取反加 1
and / or / xor a, b按位与 / 或 / 异或xor eax, eax 是把 eax 清零的惯用写法
test a, b算 a AND b,只设标志不存结果和 cmp 的关系:cmp 做减法、test 做与;test eax, eax 用来判断 eax 是否为 0 / 负
shl/sal、shr、sar左移、逻辑右移、算术右移无符号数右移用 shr,有符号数右移用 sar(2.3 节)
imul ecx / idiv ecx单操作数形式:edx:eax ← eax × ecx;eax ← edx:eax ÷ ecx 的商,edx ← 余数积是 64 位,高 32 位在 edx(2019-45 第 5 问判溢出就看它)
条件转移(在 cmp a, b 之后)有符号数无符号数看哪些标志
a = b / a ≠ bje(jz)/ jne(jnz)ZF
a > bjg(jnle)ja(jnbe)有符号:ZF=0 且 SF=OF;无符号:CF=0 且 ZF=0
a ≥ bjge(jnl)jae(jnb)有符号:SF=OF;无符号:CF=0
a < bjl(jnge)jb(jnae)有符号:SF≠OF;无符号:CF=1
a ≤ bjle(jng)jbe(jna)有符号:ZF=1 或 SF≠OF;无符号:CF=1 或 ZF=1

记法:g/l(greater/less)用于有符号,a/b(above/below)用于无符号;n 表示 not,e 表示 equal。表中标志的判断就是 2.4 节“比较 A 和 B”那张表。

高地址 调用者的栈帧 参数 n 返回地址 旧 ebp 局部变量 1 局部变量 2 (更多) 低地址 ↓ 栈向这边长 ← ebp+8← ebp+4← ebp← ebp−4← ebp−8← esp(栈顶) 调用者的栈帧f 的栈帧ebp~esp 调用 f(n) 时依次发生: ① 调用者 push n(压参数) ② call f:压入 返回地址 = call 的下一条指令地址 ③ 被调用者 push ebp(保存旧帧基址) ④ mov ebp, esp(新帧基址 = 当前栈顶) ⑤ sub esp, … 为局部变量留空间 ……函数体用 [ebp+8] 取参数,[ebp−4] 取局部变量 ⑥ 恢复 esp、pop ebp ⑦ ret:弹出返回地址 → PC,回到调用者 递归调用 n 层,栈上就叠 n 个栈帧
图 3-8 函数调用的栈帧(32 位 x86 常见约定)。每个函数的栈帧由 ebp(帧底)和 esp(栈顶)界定;参数和返回地址由调用者压栈。参数在 ebp 的正偏移、局部变量在负偏移——这就是“基址寻址”的典型用法。栈是进程地址空间里的一块区域(3.6 节)。
// if (a > b) x = 1;   → 条件相反时跳过
    mov  eax, [a]
    cmp  eax, [b]          ; 算 a−b 设标志
    jle  skip              ; a ≤ b 就跳过(有符号用 jle,无符号用 jbe)
    mov  [x], 1
skip:
// for (i = 0; i < n; i++) sum += a[i];   → 循环 = 条件跳转往回跳
    mov  ecx, 0            ; i = 0
loop: cmp ecx, [n]
    jge  done
    add  eax, [ebx+ecx*4]  ; a[i]:首地址 ebx + i×4(变址)
    inc  ecx
    jmp  loop
done:
看汇编认出 C 结构的套路

if-else:cmp + 条件相反的跳转(条件不成立就跳过 then 部分);then 部分末尾一条 jmp 跳过 else 部分。
while / for:开头(或末尾)一条 cmp + 条件跳转,循环体末尾一条往回跳(目标地址比自己小)的 jmp——看到“往回跳”就是循环。
do-while:先执行循环体,末尾 cmp + 条件跳转往回跳,所以循环体至少执行一次。
数组 a[i]:[首地址 + i × 元素大小],常见 [ebx+ecx*4] 或先 shl/sll 左移 2 位再加首地址(2014-44)。
函数调用:一串 push(参数)+ call;函数开头 push ebp; mov ebp, esp,结尾 leave; ret(或 mov esp, ebp; pop ebp; ret)。

过程调用:一步一步看栈怎么变

图 3-8 是一个栈帧的“静态照片”。下面把 r = add(1, 2); 的整个调用过程逐条执行一遍(32 位 x86 常见约定,每格 4 字节):

; 调用者 main 中
    push 2          ; 参数从右往左压栈
    push 1
    call add        ; 压返回地址,跳转
    add  esp, 8     ; 调用者清掉两个参数
    mov  [ebp-4], eax  ; r = 返回值
; 被调用者 add(a, b)
add: push ebp            ; 保存调用者的 ebp
     mov  ebp, esp       ; 建立自己的栈帧
     sub  esp, 4         ; 给局部变量 s 留位置
     mov  eax, [ebp+8]   ; a
     add  eax, [ebp+12]  ; a + b
     mov  [ebp-4], eax   ; s = a + b
     mov  eax, [ebp-4]   ; 返回值放 eax
     leave               ; = mov esp,ebp; pop ebp
     ret                 ; 弹出返回地址 → PC
执行完这一条espebp栈里新增 / 发生了什么
(调用前)1000H1010Hmain 的栈帧
push 20FFCH1010H[0FFCH] = 2(参数 b)
push 10FF8H1010H[0FF8H] = 1(参数 a)
call add0FF4H1010H[0FF4H] = 返回地址(call 下一条 add esp, 8 的地址),PC ← add 的入口
push ebp0FF0H1010H[0FF0H] = 1010H(旧 ebp)
mov ebp, esp0FF0H0FF0H新栈帧的底:[ebp+4] = 返回地址,[ebp+8] = a,[ebp+12] = b
sub esp, 40FECH0FF0H[ebp−4] = [0FECH] 是局部变量 s
……函数体……0FECH0FF0Heax = 1 + 2 = 3,存入 s,再放回 eax 作返回值
leave0FF4H1010Hesp ← ebp(丢掉局部变量),pop ebp(恢复调用者的帧)
ret0FF8H1010HPC ← [0FF4H],回到 main 的 add esp, 8
add esp, 81000H1010H两个参数被清掉,栈恢复到调用前的样子
过程调用的几个约定(大题常问“为什么”)

① 参数由调用者从右往左压栈,被调用者用 [ebp+8]、[ebp+12]……取([ebp+4] 是返回地址,[ebp] 是旧 ebp);局部变量在 [ebp−4]、[ebp−8]……。
② 返回值放在 eax。
③ 寄存器保存:eax、ecx、edx 由调用者保存(调用者若调用后还要用,得自己先存);ebx、esi、edi、ebp 由被调用者保存(被调用者要用就先 push、返回前 pop)。
④ 每调用一次就在栈上叠一个新栈帧,所以递归 n 层就有 n 个栈帧,递归太深会栈溢出;返回时按相反顺序一层层拆掉(2019-45)。
⑤ call 的返回地址是 call 的下一条指令的地址,也就是执行 call 时 PC 的值——和相对寻址“PC 已经指向下一条”是同一件事。

大题精讲:2019-45 + 2019-46(递归、call、大小端、分页、Cache 一题串起来)

2019-45/46计组融合综合题
计算 f(n)=n! 的函数 f1 在 32 位机器 M(按字节编址)上的部分机器级代码(行号、虚拟地址、机器指令、汇编指令):
 1  00401000  55                 push ebp
11  00401018  83 7D 08 01        cmp  dword ptr [ebp+8], 1      ; if (n > 1)
12  0040101C  7E 17              jle  f1+35h (00401035)
13  0040101E  8B 45 08           mov  eax, dword ptr [ebp+8]
14  00401021  83 E8 01           sub  eax, 1
15  00401024  50                 push eax
16  00401025  E8 D6 FF FF FF     call f1 (00401000)              ; return n*f1(n-1)
19  00401030  0F AF C1           imul eax, ecx
20  00401033  EB 05              jmp  f1+3Ah (0040103a)
21  00401035  B8 01 00 00 00     mov  eax, 1                     ; else return 1
26  00401040  3B EC              cmp  ebp, esp
30  0040104A  C3                 ret
(1)计算 f(10) 调用 f1 几次?哪条指令递归调用?(2)哪条是条件转移?哪几条一定跳转?(3)第 17 行的地址?call 的偏移量?大端还是小端?(4)f(13)=6227020800,f1(13) 却返回 1932053504,为什么?怎么改?(5)imul 的高、低 32 位满足什么条件时 OF=1?编译器应在 imul 后加什么指令?
(2019-46)主存地址 32 位,页 4KB:第 1 行 push 和第 30 行 ret 在同一页吗?指令 Cache 64 行、4 路组相联、块 64B:32 位地址哪几位是块内地址、组号、标记?读 call 指令只可能在哪一组命中?
展开解析
  1. (1)f1(10) → f1(9) → … → f1(1),共 10 次;第 16 行 call 递归调用。
  2. (2)第 12 行 jle 是条件转移;call、jmp、ret 一定跳转。
  3. (3)call 在 00401025H,机器码 E8 D6 FF FF FF 共 5 字节 → 第 17 行地址 = 0040102AH。call 用相对寻址:偏移 = 目标 − (PC) = 00401000H − 0040102AH = −2AH = FFFFFFD6H。机器码里写成 D6 FF FF FF,最低字节在前 → 小端。(顺手验算 jle:0040101CH + 2 + 17H = 00401035H ✓)
  4. (4)6227020800 > 231−1,超出 int 表示范围,乘法溢出,只留下了低 32 位。把返回值类型改成 long long 或 double。
  5. (5)有符号乘法,64 位乘积的高 33 位不全为 0 也不全为 1时 OF=1(结果不能用 32 位补码表示)。在 imul 后加一条溢出自陷指令(如 INTO),OF=1 时自动进入操作系统的溢出异常处理程序——这就是第 5 篇的“自陷”。
  6. (2019-46 分页)4KB = 212,页号是地址高 20 位 = 去掉最后 3 个十六进制位。00401000H 和 0040104AH 的页号都是 00401H → 同一页。
  7. (Cache)64 行 ÷ 4 路 = 16 组 → 组号 4 位;块 64B → 块内地址 6 位;标记 32−4−6 = 22 位。即 A5~A0 块内,A9~A6 组号,A31~A10 标记。
  8. (哪一组)页内偏移(低 12 位)在虚→实转换中不变,而 Cache 用的低 10 位都落在页内偏移里,所以直接用虚拟地址的低 12 位 025H = 0000 0010 0101B 就能算组号:A9~A6 = 0000 → 第 0 组。这一步是第 8 篇的“页内偏移原样传递”。
2014-44(1)~(3)计组综合题
for (int i=0; i<N; i++) sum += A[i]; 的 RISC 机器代码(32 位定长指令字):
08048100H  00022080H  loop: sll  R4, R2, 2      ; (R2)<<2 → R4
08048104H  00083020H        add  R4, R4, R3     ; (R4)+(R3) → R4
08048108H  8C850000H        load R5, 0(R4)      ; M[(R4)+0] → R5
0804810CH  00250820H        add  R1, R1, R5
08048110H  20420001H        add  R2, R2, 1
08048114H  1446FFFAH        bne  R2, R6, loop   ; if (R2)≠(R6) goto loop
bne 格式:OP(31~26)| Rs | Rd | OFFSET(15~0,补码)。(1)编址单位?(2)数组元素占多少位?(3)bne 的 OFFSET 是多少?当前 PC 为 bne 指令地址,推断转移目标地址计算公式。
展开解析
  1. 32 位指令 = 4 字节,相邻指令地址差 4 → 按字节编址。
  2. i 左移 2 位 = i×4 作为字节偏移 → 元素 4 字节 = 32 位。
  3. 机器码 1446FFFAH 低 16 位 OFFSET = FFFAH = −6。bne 在 08048114H,要跳回 08048100H,差 −18H = −24 字节 = −6 × 4。所以偏移以“指令(4 字节)”为单位,并且从下一条(+4)算起:目标 = (PC) + 4 + OFFSET × 4。验算:08048114H + 4 − 24 = 08048100H ✓。第(4)问流水线见第 4 篇。

64 位程序怎么传参数:IA-32 与 x86-64 的调用约定 拓展

问题引入:408 真题里的汇编都是 32 位 x86(IA-32),参数在栈上,用 [ebp+8] 取。可如果你在自己的 64 位电脑上编译、反汇编一个函数,往往找不到 [ebp+8],参数却出现在 rdi、rsi 或 rcx、rdx 里——因为 64 位换了一套“调用约定”。

大白话:调用约定是调用者和被调用者之间的“交接规矩”:参数放哪、返回值放哪、哪些寄存器谁负责保存。32 位 x86 只有 8 个通用寄存器,参数只好全放栈上;x86-64 有 16 个,就把前几个参数直接放进寄存器——少访存、更快,和 RISC“多用寄存器传参数”(3.4)是同一个思路。

参数 1 参数 2 参数 3 参数 4 参数 5 参数 6 参数 7 IA-32(408 真题) [ebp+8] [ebp+12] [ebp+16] [ebp+20] [ebp+24] [ebp+28] [ebp+32] x86-64 System V(Linux、macOS) rdi rsi rdx rcx r8 r9 [rsp+8] x86-64 Windows rcx rdx r8 r9 [rsp+40] [rsp+48] [rsp+56] 蓝色 = 寄存器传(不访存);灰色 = 栈传(要访存)。IA-32 按建好栈帧后的 ebp 写,x86-64 按刚进入函数时的 rsp 写
图 3-9 7 个整数参数放在哪。System V 的第 7 个参数在 [rsp+8],因为 [rsp] 是 call 压入的返回地址;Windows 约定还要求调用者在栈上预留 32 字节“影子空间”,所以第 5 个参数从 [rsp+40] 开始。
IA-32(cdecl,408 真题用)x86-64 System Vx86-64 Windows
整数参数全部从右往左压栈,被调用者用 [ebp+8]、[ebp+12]… 取前 6 个:rdi、rsi、rdx、rcx、r8、r9;其余压栈前 4 个:rcx、rdx、r8、r9;其余压栈
返回值eaxraxrax
指针宽度32 位64 位64 位

跟我做一遍(verify/08/p03_abi.c,gcc 13.2 生成的汇编,摘关键行):

; IA-32,-m32 -O0
push ebp
mov  ebp, esp
mov  edx, [ebp+8]   ; a
mov  eax, [ebp+12]  ; b
add  edx, eax
mov  eax, [ebp+16]  ; c
add  eax, edx
; x86-64 System V,-O1
add rdi, rsi        ; a+b
add rdi, rdx        ; +c
add rdi, rcx        ; +d
add rdi, r8         ; +e
lea rax, [rdi+r9]   ; +f
add rax, [rsp+8]    ; +g
; x86-64 Windows,-O1
add rcx, rdx        ; a+b
add rcx, r8         ; +c
lea rax, [rcx+r9]   ; +d
add rax, [rsp+40]   ; +e
add rax, [rsp+48]   ; +f
add rax, [rsp+56]   ; +g

考试怎么办:408 的 2017-44、2019-45、2023-44 都是 IA-32 代码,按题目给的代码读即可,不需要背 64 位约定;这一段只是让你自己做实验时不困惑。

3.4CISC 与 RISC

★ 考过选择题

RISC 的出发点是一个统计规律——80-20 规律:典型程序中 80% 的运行时间只用到了指令系统里 20% 的简单指令,复杂指令既少用又拖慢整台机器。于是 RISC 只保留常用的简单指令,把它们做得很快;复杂功能交给编译器用多条简单指令组合。

CISC 复杂指令集(x86)RISC 精简指令集(ARM、MIPS、RISC-V)
指令数目多(几百条),有很多功能复杂的专用指令少,只选使用频率高的简单指令
指令长度与格式变长,格式多定长(一般 32 位),格式少、字段位置固定
寻址方式多少
访存很多指令都能直接访存(如 add 的一个操作数可以在内存)只有 load/store 访存,其余指令只在寄存器之间操作
通用寄存器较少多
各指令执行时间相差很大,多数要多个时钟周期大多数指令一个时钟周期完成
控制器多用微程序控制多用硬布线控制(快)
流水线难以实现高效流水线容易,几乎必然采用
过程调用传参数多用栈(访存)寄存器多,多用寄存器传参数,少访存
编译难以用优化编译生成高效代码重视编译优化,靠编译器合理安排指令顺序、分配寄存器
目标代码短(一条指令干得多)较长(要多条简单指令)

两者不是非此即彼:RISC 并不是“指令越少越好”,CISC 为了兼容老程序一直保留复杂指令,而现代 x86 处理器内部会先把 CISC 指令拆成类似 RISC 的微操作再送进流水线 拓展。

为什么 RISC 容易做流水线?流水线就像工厂的装配线,要求每件“产品”走同样的工序、每道工序时间差不多(第 4 篇 4.4)。RISC 的三个特点正好满足(2011-18):① 指令定长、格式规整——取指一步就能取完一条,译码时各字段位置固定;② 数据按边界对齐——一次访存就能取到一个数据,不会一个数据跨两个存储字要访存两次;③ 只有 load/store 访存——其他指令都在寄存器之间操作,“访存”这道工序只有少数指令用,时间好控制。CISC 指令长短不一、有的指令一条就要访存好几次,各条指令走的工序不一样,装配线就排不齐。

2009-17计组
下列关于 RISC 的叙述中,错误的是( )。
  • A. RISC 普遍采用微程序控制器
  • B. RISC 大多数指令在一个时钟周期内完成
  • C. RISC 的内部通用寄存器数量相对 CISC 多
  • D. RISC 的指令数、寻址方式和指令格式种类相对 CISC 少
展开解析

考点3.4 CISC 与 RISC4.3 控制器

ARISC 的设计目标是“简单、快”:指令少而规整,控制信号容易直接用组合逻辑产生,所以多用硬布线控制器;微程序控制器灵活但慢,适合指令多而复杂的 CISC。

  • A 错“RISC 普遍采用微程序控制器”错误——本题选它。
  • B 对RISC 以流水线方式执行,大多数指令平均一个时钟周期完成一条。
  • C 对RISC 只让 load/store 访存,运算都在寄存器间进行,所以通用寄存器多。
  • D 对RISC 指令数、寻址方式、格式种类都比 CISC 少。

RISC:定长、规整、load/store、寄存器多、硬布线、易流水;CISC:变长、复杂、指令可直接访存、多用微程序。

RISC 到底多整齐:RISC-V 的三种基本格式 拓展

问题引入:上表说 RISC“定长、格式少、字段位置固定”,到底有多整齐?看一眼 RISC-V(一种开源的 RISC 指令集;2014-44、2024-43 那种 lw / sw 风格的代码就是这一类)。

31 25 24 20 19 15 14 12 11 7 6 0 R 型(寄存器运算) funct7 rs2 rs1 funct3 rd opcode I 型(立即数、load) imm[11:0] rs1 funct3 rd opcode S 型(store) imm[11:5] rs2 rs1 funct3 imm[4:0] opcode rs1、rs2、rd、opcode 在三种格式里位置完全一样;S 型把立即数拆成两段,就是为了不挪动 rs2 和 rd 的位置
图 3-10 RISC-V 的 R、I、S 三种格式(都是 32 位)。32 个通用寄存器 → 寄存器号 5 位;I、S 型的立即数 12 位补码,范围 −2048 ~ 2047。
指令格式字段机器码
add x5, x6, x7Rfunct7 = 0,rs2 = 7,rs1 = 6,funct3 = 000,rd = 5,opcode = 0110011007302B3H
addi x5, x6, -1Iimm = FFFH(−1 的 12 位补码),rs1 = 6,funct3 = 000,rd = 5,opcode = 0010011FFF30293H
lw x5, 8(x6)Iimm = 8,rs1 = 6,funct3 = 010,rd = 5,opcode = 000001100832283H
sw x5, 8(x6)Simm[11:5] = 0,rs2 = 5,rs1 = 6,funct3 = 010,imm[4:0] = 01000,opcode = 010001100532423H

机器码已用 verify/08/p03_calc.py 按字段拼接验算(并用已知编码 add a0, a1, a2 = 00C58533H 校对)。字段位置固定带来的好处:译码器还没认出“这是哪条指令”,就可以先按 rs1、rs2 的固定位置去读寄存器——第 4 篇流水线的“译码 / 读寄存器”能放在同一段里做,靠的就是这一点(2011-18 的“指令格式规整”)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2009-17、2025-17 改
下列关于 CISC 的叙述中,正确的是( )。
  • A. 指令长度固定
  • B. 只有 Load/Store 指令能访问存储器
  • C. 寻址方式多,控制器常用微程序实现
  • D. 大多数指令在一个时钟周期内完成
展开解析

CCISC 指令多、格式多、寻址方式多,用微程序控制器实现比较方便。A、B、D 都是 RISC 的特点。

变化点:两道真题都问 RISC 的错误说法,这里换成问 CISC 的正确说法:把 RISC 的特点反过来就是 CISC。

变式计组由 2011-18 改
下列指令系统特点中,不利于实现指令流水线的是( )。
  • A. 指令长度一致
  • B. 只有 Load/Store 指令能访问存储器
  • C. 指令中寄存器字段的位置固定
  • D. 指令长度可变,且运算指令可以直接对存储器操作数运算
展开解析

D指令长度可变,取指和译码的时间就不一样;运算指令直接访存,一条指令要多次访存,各段的时间就难以统一。这两点都让流水线难以均衡。

变化点:2011-18 问“有利于”的特点,这里反过来问“不利于”的。

3.5融合程序的一生(上):编译、链接、装入

★ 考过选择题

现在换成操作系统的视角。你写的 hello.c 要经过下面这条流水线,才会变成内存里一个正在运行的进程:

hello.c源程序 hello.i展开 #include hello.s汇编程序 hello.o可重定位目标文件 printf.o 等库 hello可执行文件(磁盘上) 进程在内存里运行 预处理 编译 汇编 链接 装入 每个 .o 的地址都从 0 开始 链接后:一个完整的逻辑(虚拟)地址空间 装入程序由 OS 执行(内核态,2026-23) 预处理器 cpp编译器 cc1汇编器 as链接器 ld
图 3-11 从源程序到进程。顺序是 预处理 → 编译 → 汇编 → 链接(2022-20)。编译汇编后的每个目标模块都以 0 为起始地址,链接把它们和库函数拼起来、修改模块间的地址引用,形成完整的逻辑地址(2011-30 选“链接”)。最后由操作系统的装入程序把可执行文件放进内存,变成进程。
步骤谁来做输入 → 输出干了什么
预处理预处理器(cpp)hello.c → hello.i处理 # 开头的命令:把 #include 的头文件原样插进来,展开 #define 宏,删掉注释。输出仍是 C 源程序
编译编译器(cc1)hello.i → hello.s词法、语法、语义分析和优化,翻译成汇编语言源程序(文本)
汇编汇编器(as)hello.s → hello.o把汇编指令逐条翻译成机器指令,得到可重定位目标文件(二进制,但地址从 0 开始,外部函数的地址还空着)
链接链接器(ld)hello.o + printf.o … → hello把各目标模块和库函数合并成一个可执行目标文件,填好模块之间的地址引用

用 gcc 可以分别停在每一步:gcc -E 只预处理,gcc -S 编译到 .s,gcc -c 汇编到 .o,不加选项一路做完链接。

逻辑地址、物理地址,链接到底要做什么

  • 逻辑地址(相对地址、虚拟地址):编译、链接后程序里使用的地址,每个目标模块都从 0 开始编址,链接后整个程序形成一个统一的、从 0 开始的逻辑地址空间。
  • 物理地址(绝对地址):内存中真实存储单元的地址,是地址转换的最终结果。进程运行时执行指令、访问数据,最后都要落到物理地址上。
  • 地址重定位(地址变换):把逻辑地址转换成物理地址。什么时候转,就是下面“三种装入方式”的区别。
  • 链接器的两件主要工作 拓展:① 符号解析——把每个模块里引用的外部符号(如 printf)和它的定义对应起来;② 重定位——把各模块合并后,修改指令和数据里涉及地址的部分(各模块原来都从 0 开始,合并后要统一加上自己在整个程序里的起始位置)。所以“形成完整的逻辑地址”发生在链接阶段(2011-30)。

问题引入:printf 的代码不是你写的,它什么时候进到你的程序里?有的程序拷到别的电脑上提示“找不到 xxx.dll”,有的却能直接运行——区别就在链接发生的时机。同一条时间线上,还有另一件事也分三个时机:逻辑地址什么时候变成物理地址(下面的三种装入方式)。两组“三种”最容易记混,先画在一起:

编译、汇编 链接 装入 运行 什么时候把库拼进来 静态链接运行前拼成一个整体 装入时动态链接边装入边链接 运行时动态链接第一次用到时才装入并链接 什么时候确定物理地址 绝对装入编译时写死 静态重定位装入时一次改好 动态重定位每次访存时硬件转换
图 3-12 两组“三种”放在一条时间线上。上一行回答“库模块什么时候拼进来”(链接),下一行回答“物理地址什么时候确定”(装入 / 重定位)。两件事互相独立:例如一个静态链接的程序,照样可以用动态重定位的方式装入运行。
链接方式库模块什么时候拼进来优点缺点
静态链接程序运行前,链接器把所有目标模块和库函数拼成一个完整的可执行文件,以后不再拆开运行时不依赖外部库;装入后直接执行每个程序各带一份库代码,文件大、占内存;库更新后要重新链接
装入时动态链接装入内存时,边装入边链接(可执行文件里只记“需要哪些模块”)便于修改和更新各目标模块;便于多个程序共享同一模块装入时要把可能用到的模块全部链接进来,哪怕这次运行根本用不上
运行时动态链接执行过程中第一次用到某模块时,才把它装入内存并链接用不到的模块不装入:加快装入、节省内存;便于共享和更新第一次调用有额外开销;运行时可能找不到库(“缺少 xxx.dll”)

Windows 的 .dll、Linux 的 .so 是“动态链接库”;程序用 LoadLibrary / dlopen 在运行中按需加载,就是典型的运行时动态链接。gcc -static 生成的是静态链接程序。

真题怎么考:2011-30(形成完整逻辑地址发生在链接)、2022-20(预处理 → 编译 → 汇编 → 链接)。链接和重定位的三种方式目前多出现在王道习题里,选择题的判别法就是看上图:问“库什么时候拼进来”看上一行,问“物理地址什么时候确定 / 程序能不能移动”看下一行。

自测
  1. “运行时动态链接要求在装入时把所有模块都链接好”对吗?
    答案
    不对,那是装入时动态链接。运行时动态链接是用到哪个模块才装入、链接哪个。
  2. 某程序采用静态链接,它装入内存后还能被操作系统整体移到另一处吗?
    答案
    能不能移动取决于装入方式而不是链接方式:若用动态重定位(运行时才转换地址)就能移动,只要改重定位寄存器;若用静态重定位就不能。

装入的三种方式:物理地址是什么时候确定的

CPU逻辑地址 100 界地址寄存器程序长度 3000 100 ≥ 3000?是 → 越界中断 + 重定位寄存器10000 物理地址10100 主存 进程的程序 1000013000 10100:要访问的数
图 3-13 动态重定位(动态运行时装入)。程序在内存里保持逻辑地址不变,每次访存时由硬件把逻辑地址加上重定位寄存器的值得到物理地址,同时和界地址寄存器比较防越界(2009-26:分区分配的主要保护措施是界地址保护)。程序被整体搬到别处,只要改重定位寄存器就行。第 8 篇的“分页”就是它的升级版:不再整个程序一个基址,而是每页一个。
装入方式物理地址何时确定特点
绝对装入编译时就写死只适合单道程序
可重定位装入(静态重定位)装入时一次性把所有地址 + 起始地址装入后不能移动;要一次分配全部内存
动态运行时装入(动态重定位)运行时每次访存由硬件转换需要重定位寄存器;可以移动;现代系统都是这种

用同一个例子对比三种方式:程序里逻辑地址 100 处有一条指令 LOAD 1, 2500(把逻辑地址 2500 处的数据读进 1 号寄存器),操作系统把程序装到从物理地址 10000 开始的内存区:

装入方式内存里 10100 处的指令长什么样执行时访问的物理地址怎么来
绝对装入LOAD 1, 12500编译时就知道程序要放在 10000,直接生成了绝对地址 12500。换个地方装就错了
静态重定位LOAD 1, 12500编译出来是 2500,装入时由装入程序把所有地址统一加上 10000 改写进去。装好以后程序不能再搬家
动态重定位LOAD 1, 2500内存里保持逻辑地址不变,执行这条指令时,硬件用 2500 + 重定位寄存器(10000)= 12500 去访存,同时检查 2500 < 界地址寄存器(程序长度),否则越界

动态重定位时,操作系统若把程序整体移到 20000 开始,只需把重定位寄存器改成 20000,程序本身一个字节都不用改。这也是为什么“进程切换时要保存/恢复重定位寄存器(或页表基址寄存器)”(第 6、8 篇)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2011-30、2022-20 改
下列关于程序的编译、链接和装入的叙述中,正确的是( )。
  • A. 编译得到的各目标模块中,地址都已是最终的逻辑地址
  • B. 链接时要合并各模块并修改相对地址,形成完整的逻辑地址空间
  • C. 动态运行时装入在装入时就把逻辑地址全部转换为物理地址
  • D. 汇编在编译之前进行
展开解析

B各模块单独编译时地址都从 0 开始,链接时合并成一个整体并修改地址,才形成完整的逻辑地址(2011-30)。C 说的是可重定位装入,动态运行时装入要到执行时才转换。D 错:顺序是预处理 → 编译 → 汇编 → 链接(2022-20)。

变化点:把 2011-30(逻辑地址在哪一步形成)和 2022-20(四个步骤的顺序)合成一题,并加入三种装入方式的区别。

变式操作系统考纲补充(尚无真题):链接的三种方式
下列关于程序链接方式的叙述中,正确的是( )。
  • A. 静态链接的程序运行时还要另外装入库函数
  • B. 装入时动态链接是在程序执行中用到某模块时才去链接
  • C. 运行时动态链接可以加快程序的装入并节省内存
  • D. 采用静态链接的程序装入后一定不能在内存中移动
展开解析

C运行时动态链接只在用到某模块时才装入并链接,用不到的不装,所以装入快、省内存。A 错:静态链接已把库拼进可执行文件。B 说的是运行时动态链接。D 错:能不能移动取决于装入方式(动态重定位就能移动),与链接方式无关。

考点:链接的三种时机。

3.6融合程序的一生(下):进程在内存里长什么样

★★ 常考选择题综合题

每个进程都有一个自己独立的虚拟地址空间(比如 32 位系统是 0 ~ 4GB),它以为整台机器的内存都是它的(第 8 篇讲操作系统怎么变这个“魔术”)。这个空间按用途分成几段:

高地址(4GB) 内核区用户程序不可见(PCB 在这) 用户栈局部变量、参数 ↓ ↓ 空闲 ↑ 共享库(内存映射区) ↑ 空闲 堆malloc 分配 ↑ 读/写数据段全局变量、static 只读代码段机器指令、常量字符串 低地址(0 附近不用) char *ptr; // 全局变量 void main() { int length; // 局部变量 ptr = (char*)malloc(100); scanf("%s", ptr); length = strlen(ptr); printf("length=%d\n", length); free(ptr); } ptr 本身(全局)→ 读/写数据段 length(局部)→ 用户栈 ptr 指向的 100 字节 → 堆 main 的机器指令 → 只读代码段 scanf 等待输入时,进程 → 阻塞态 scanf/printf 最终要执行驱动程序
图 3-14 进程的虚拟地址空间(2025-46 原题模型)。栈从高往低长,堆从低往高长。代码段只读、数据段可读写——不同的段可以有不同的访问权限(2023-28)。malloc 返回的是虚拟地址。

程序里的每样东西住在哪一段

程序里的东西放在说明
函数的机器指令只读代码段(.text)只读、可执行;同一程序的多个进程可以共享
字符串常量 "hello"、const 全局常量只读数据(.rodata)和代码段一起装在只读区域,写它会出错
已初始化的全局变量、static 变量读/写数据段(.data)初值存在可执行文件里,装入时读进来
未初始化(或初值为 0)的全局变量、static 变量.bss(紧跟在 .data 后,属读写数据区)可执行文件里只记“要多大”,不占文件空间,装入时统一清 0
malloc / new 得到的空间堆运行时向高地址增长;要程序员 free,否则内存泄漏
函数的局部变量(非 static)、参数、返回地址用户栈每次调用建一个栈帧,函数返回就失效;向低地址增长
动态链接库(如 libc 的 printf 代码)共享库映射区多个进程共享同一份物理页
PCB、页表、内核栈、内核代码内核区所有进程映射同一个内核区,用户态不能访问

32 位 Linux 的具体数字 拓展:用户空间是 0 ~ BFFF FFFFH(低 3GB),内核空间是 C000 0000H ~ FFFF FFFFH(高 1GB);可执行文件的代码段通常从 0804 8000H 开始——2014-44 里那段循环代码的地址 08048100H 就落在这里。(2017-44、2019-45 的 00401000H 是 Windows 程序常见的起始地址。)

2025-46操作系统融合综合题
某进程的虚拟地址空间如图 3-14 所示,执行上面的 C 程序。(1)程序执行时,PCB 位于哪个区域?执行 scanf() 等待键盘输入时,该进程处于什么状态?(2)main() 的代码位于哪个区域?其直接调用的哪些函数的功能需要通过执行驱动程序实现?(3)变量 ptr 被分配在哪个区域?若 length 没有被分配在寄存器中,会被分配在哪个区域?ptr 指向的字符串位于哪个区域?
展开解析
  1. (1)PCB 是操作系统管理进程的数据结构,属于内核,位于内核区。scanf 要等键盘输入(I/O),进程让出 CPU,处于阻塞态(第 6 篇)。
  2. (2)main 是机器指令,在只读代码段。scanf 和 printf 要读键盘、写显示器,最终通过系统调用进入内核执行设备驱动程序(第 9 篇);malloc、strlen、free 不操作设备。
  3. (3)ptr 是全局变量 → 读/写数据段;length 是局部变量 → 用户栈;malloc 分配的空间 → 堆。
2023-28操作系统
对于采用虚拟内存管理方式的系统,下列关于进程虚拟地址空间的叙述中,错误的是( )。
  • A. 每个进程都有自己独立的虚拟地址空间
  • B. C 语言中 malloc() 函数返回的是虚拟地址
  • C. 进程对数据段和代码段可以有不同的访问权限
  • D. 虚拟地址的大小由主存和硬盘的大小决定
展开解析

考点3.6 进程的虚拟地址空间8.7 虚拟内存

D虚拟地址空间有多大,只取决于虚拟地址有几位(32 位 → 4GB),与实际装了多大的内存和硬盘无关。

  • A 对每个进程都有自己独立的虚拟地址空间,靠各自的页表映射。
  • B 对用户程序看到、用到的地址都是虚拟地址,malloc() 返回的也是。
  • C 对代码段只读可执行、数据段可读写,靠页表项中的权限位区分。
  • D 错“虚拟地址的大小由主存和硬盘的大小决定”错误——本题选它。

虚拟地址空间大小 = 2虚拟地址位数;实际能用多少还受内存+外存限制,但这是“虚存的实际容量”,不是地址空间大小(同 2012-25)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2023-28、2025-46 改
下列关于进程虚拟地址空间中各类数据存放位置的叙述中,正确的是( )。
  • A. 全局变量存放在栈区
  • B. 函数的局部变量存放在堆区
  • C. malloc 分配的空间在堆区,函数的局部变量在栈区
  • D. 代码段一般可读可写
展开解析

C全局变量和静态变量在数据段;局部变量、参数、返回地址在栈区;malloc 动态分配的在堆区;代码段一般只读(2023-28 中“不同段有不同的访问权限”)。

变化点:2023-28 考虚拟地址空间的一般性质,2025-46 大题考变量在哪个区。这里把两者合成一道选择题。

第 3 篇自测
  1. 2010-43:指令 OP 4 位、Ms 3、Rs 3、Md 3、Rd 3;add 的 OP = 0010,寄存器间接 = 001,寄存器间接自增 = 010;R4 = 100、R5 = 101。写出 add (R4), (R5)+ 的机器码。若 (R4)=1234H、(R5)=5678H、[1234H]=5678H、[5678H]=1234H,执行后哪些内容变了?
    答案
    0010 001 100 010 101 = 0010 0011 0001 0101 = 2315H。源操作数 ((R4)) = [1234H] = 5678H,目的 ((R5)) = [5678H] = 1234H,和 = 68ACH 写回 [5678H];然后 R5 自增 → 5679H。变化:[5678H] = 68ACH,R5 = 5679H(PC 也会顺序加 1)。
  2. 2023-44:第 2 条 jmp 在 00401079H,机器码 EB 09(2 字节),跳到哪里?第 19 条指令 C7 84 82 00 20 42 00 0A 00 00 00 在 004010AEH,第 20 条地址是多少?
    答案
    00401079H + 2 + 09H = 00401084H(相对寻址);第 19 条长 11 字节 → 004010AEH + 0BH = 004010B9H。机器码里的 00 20 42 00 表示 00422000H,也说明是小端。
  3. 静态重定位和动态重定位,哪个允许程序装入后在内存里移动?为什么?
    答案
    动态重定位。它在每次访存时才加重定位寄存器,程序移动后只要改寄存器的值;静态重定位装入时已把地址改死。
  4. 全局变量、局部变量、malloc 出来的空间、函数的机器指令分别在进程地址空间哪一段?
    答案
    读写数据段;栈;堆;只读代码段。
  5. 32 位定长指令、操作码 8 位的二地址指令,每个地址码都是主存地址,直接寻址时每个操作数能访问多大范围?执行一条这样的加法指令要访存几次?
    答案
    每个地址码 (32 − 8)/2 = 12 位 → 212 = 4K 个单元;访存 4 次(取指 1、取两个操作数 2、写回结果 1)。
  6. 堆栈向低地址增长,SP 指向栈顶元素,SP = 2000H。连续 push 两个 4 字节数据后 SP = ?再 pop 一次呢?
    答案
    push 先减后写:1FFCH、1FF8H → SP = 1FF8H;pop 先读后加 → SP = 1FFCH。
  7. AT&T 格式 movl 8(%ebp), %eax 写成 Intel 格式是什么?它和 lea eax, [ebp+8] 有什么不同?
    答案
    mov eax, dword ptr [ebp+8]:到内存 ebp+8 处取 4 字节(第一个参数)送 eax。lea 只把地址 ebp+8 本身送 eax,不访存。
  8. 全局变量 char *s = "abc"; 和 static int n;(未初始化):s、"abc"、n 分别在哪一段?
    答案
    s 是已初始化的全局指针 → 读写数据段(.data);字符串 "abc" → 只读数据(.rodata);n → .bss(装入时清 0)。

3.✎本篇真题闯关(29 题)

本篇各小节挂的真题全部在这里,按小节排好。可以展开后一道道做,也可以用弹窗模式连续做。做题记录(对/错)只保存在你这台电脑的浏览器里,小节标题下的题号会随之变绿或变红。

第 4 篇 · 计组第 5 章(5.1~5.4、5.6~5.7)

CPU 内部:数据通路、控制器与流水线

第 1 篇说 CPU“取指—译码—执行”,这一篇打开 CPU 看它具体怎么做到:数据在寄存器之间走哪条路(数据通路)、每一步谁发出什么命令(控制信号、控制器)、怎么让多条指令像流水线一样重叠执行。中断机制放到第 5 篇和操作系统一起讲。

对应:王道计组第 5 章(5.5 异常和中断在第 5 篇)难度:★★★★高频:数据通路/控制信号大题(2009、2013、2015、2022、2024、2026);流水线冒险(2010、2014、2016、2019、2023、2024)

4.1几种“周期”和几种 CPU

★★ 常考选择题

先看全貌:CPU 的功能和基本组成

CPU = 运算器 + 控制器。它要完成五件事:

功能意思
指令控制按程序规定的顺序取指令、分析指令(程序的顺序控制,靠 PC)
操作控制一条指令要让很多部件动作,CPU 产生相应的控制信号送到各部件
时间控制各种操作按严格的时间顺序进行(时序系统、节拍)
数据加工对数据做算术、逻辑运算
中断处理对运行中出现的异常情况和外部请求作出处理(第 5 篇)
运算器(数据加工)
ALU;暂存寄存器(给 ALU 暂存从总线来的操作数,程序员不可见);累加器 ACC;通用寄存器组;程序状态字寄存器 PSW(标志位);移位器;计数器(控制乘除运算的步数)。
控制器(指挥协调)
PC;IR;指令译码器(对操作码译码);MAR、MDR;时序系统(产生时钟、节拍等时序信号);微操作信号发生器(根据 IR、PSW、时序信号产生控制信号,就是 4.3 节的硬布线电路或微程序部件)。

哪些寄存器程序员可见,见第 1 篇 1.2 节的表。考试中把 MAR、MDR 算在控制器里(王道的分法),也有教材把它们单独列出,不影响做题。

时钟周期、机器周期、指令周期

打个比方

时钟周期像节拍器的“嘀嗒”一下,是 CPU 做一个最小动作(比如把一个寄存器的内容送上总线)的时间。一条指令要若干个“嘀嗒”才能完成,这些嘀嗒合起来叫指令周期。

  • 时钟周期 = 1/主频,CPU 最小时间单位,长度取决于相邻寄存器之间组合逻辑电路的最大延迟(2019-16)。
  • 指令周期:取出并执行一条指令的全部时间,可分为取指周期(取指令)、间址周期(取有效地址,间接寻址才有)、执行周期(取操作数、运算)、中断周期(有中断请求时,保存断点等,第 5 篇)。不同指令的指令周期长短不同;“处理器每来一个时钟脉冲就开始一条新指令”是错的。
  • 机器周期(CPU 周期):有的教材在两者之间再分一层——完成一次“基本操作”(如一次访存)的时间,一个机器周期含若干时钟周期。机器周期通常以访问一次主存的时间(存取周期)为基准,因为访存是最慢的基本操作。

三者的关系:一个指令周期 = 若干个机器周期,一个机器周期 = 若干个时钟周期(节拍)。不同指令的指令周期所含的机器周期数可以不同;同一台机器里各机器周期所含的时钟周期数也可以不同(见 4.3 节“控制方式”里的“不定长机器周期”)。

一条指令的时间是怎么分层的 指令周期(这一条指令从取到做完) 取指周期 间址周期(可无) 执行周期 中断周期(可无) 每一格 = 一个时钟周期(节拍) CPU 怎么决定走哪些周期(控制器里用 FE、IND、EX、INT 四个触发器记住“现在在哪个周期”) 取指周期 间址? 间址周期 执行周期 有中断? 中断周期 否 是 是 否 → 下一条
图 4-1 指令周期的组成。所有指令都有取指周期和执行周期;只有用间接寻址的指令才有间址周期;只有执行完这条指令时恰好有(允许响应的)中断请求,才进入中断周期(第 5 篇 5.4)。中断周期做完直接去取中断服务程序的第一条指令。

四个机器周期里,数据各怎么流(教材的标准写法)

CPU 用四个标志触发器 FE、IND、EX、INT 记住“现在处在哪个机器周期”(哪个为 1 就在哪个周期),据此决定这一拍发出哪些控制信号。四个周期的数据流是固定套路,大题会直接让你写(记号见第 1 篇 1.3 节;1→R 表示发读命令,1→W 表示发写命令):

周期目的数据流(按先后顺序)
取指周期
FE
按 PC 取出指令,PC 指向下一条(PC)→MAR;1→R;M(MAR)→MDR;(MDR)→IR;OP(IR)→CU;(PC)+1→PC
间址周期
IND
取出操作数的有效地址(只有间接寻址的指令才有)Ad(IR)→MAR;1→R;M(MAR)→MDR;(MDR)→Ad(IR)
把读出的有效地址替换 IR 里的形式地址(也可以直接送 MAR),执行周期再按它取操作数
执行周期
EX
完成指令规定的操作因指令而异:LOAD/STORE/ADD 见第 1 篇 1.3;无条件转移 JMP X 只需 Ad(IR)→PC;条件转移根据标志决定是否 Ad(IR)→PC
中断周期
INT
保存断点,转去中断服务程序(SP)−1→SP,(SP)→MAR(断点存入栈;或把约定的固定地址送 MAR);1→W;(PC)→MDR;(MDR)→M(MAR);中断服务程序入口地址→PC;0→EINT(关中断)
这些都是硬件自动完成的,称为“中断隐指令”(第 5 篇 5.4)
常考的几句话

① 取指周期和执行周期每条指令都有;间址周期只有间接寻址的指令才有;中断周期只在“一条指令执行完、有中断请求且允许响应”时才有。② 中断周期里保存的断点是 PC 的当前值——此时 PC 已经指向下一条指令,所以中断返回后接着执行的正是被打断指令的下一条。③ 不同指令的指令周期长度不同:有的教材指出,像 JMP X 这种执行阶段只需 Ad(IR)→PC 的指令,可以在取指周期末尾就完成,指令周期只含一个取指周期;乘法指令的执行周期比加法长;间接寻址的指令多一个间址周期。④ CPU 区分取出的是指令还是数据,靠的就是“当前处在哪个周期”(2009-11)。

三种指令执行方案(三种 CPU)

CPU 类型做法CPI时钟周期
单周期 CPU每条指令一个(很长的)时钟周期做完= 1以最慢的指令为准,所以很长 → 主频低;控制信号在整个指令周期内不变;不能用单总线(一个周期里要同时传多个数据)(2016-20)
多周期 CPU一条指令拆成几步,每步一个时钟周期> 1以最慢的一步为准
基本流水线 CPU多条指令的不同步骤重叠理想 = 1以最慢的流水段为准
超标量流水线每个周期发射多条指令理想 < 1

2020-17:理想情况下 CPI 为 1 的是单周期 CPU 和基本流水线 CPU(选 B);2026-20:单周期 CPI = 1、多周期 CPI > 1、流水线 CPI = 1 三句都对(选 D);2025-18:程序的 CPI 与 Cache 缺失率有关(缺失要等主存,访存指令变慢)。

再补两个对比:① 单周期 CPU 的控制信号只由操作码决定,整条指令期间不变,控制器是纯组合逻辑;多周期 CPU 的控制器还要记住“现在走到第几步”(相当于一个状态机)。所以单周期 CPU 的控制器更简单(2025-19 C 是对的)。② 单周期 CPU 的每个部件在一条指令里只能用一次(例如取指和读数据要用两个存储器、PC+4 和 ALU 运算要用两个加法器),多周期 CPU 可以在不同步骤复用同一个部件。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2016-20 改
下列关于多周期处理器的叙述中,正确的是( )。
  • A. 每条指令都需要相同的时钟周期数
  • B. 同一个功能部件可以在一条指令的不同时钟周期中重复使用
  • C. 所有指令的 CPI 都等于 1
  • D. 时钟周期由最慢的一条指令决定
展开解析

B多周期 CPU 把一条指令分成几个时钟周期完成,不同周期可以复用同一个部件(例如 ALU 既算 PC + 4 又做运算),所以可以用单总线。A、C 错:不同指令的周期数不同,CPI > 1。D 是单周期 CPU 的特点。

变化点:2016-20 考单周期 CPU(不能用单总线)。这里换成多周期 CPU,两者的特点正好相反。

变式计组由 2020-17、2026-20 改
理想情况下,CPI 可以小于 1 的是( )。
  • A. 单周期 CPU
  • B. 多周期 CPU
  • C. 5 段基本流水线 CPU
  • D. 超标量流水线 CPU
展开解析

D超标量 CPU 每个时钟周期能发射、完成多条指令,CPI 可以小于 1。单周期 CPU 和理想的基本流水线的 CPI 为 1,多周期 CPU 的 CPI 大于 1。

变化点:2020-17 问 CPI 为 1 的(单周期、基本流水线),这里问小于 1 的。

4.2数据通路:数据在 CPU 里走的路

★★★ 高频选择题综合题

数据通路是指令执行时数据流经的部件和路径:ALU、寄存器、多路选择器、总线,也包括 Cache、MMU、异常检测逻辑等(2021-18:说数据通路“不包含异常检测电路”是错的);不包括产生控制信号的控制部件(2017-19)。数据通路里的元件分两类:

组合逻辑元件(操作元件)
输出只取决于当前输入,没有记忆:ALU、多路选择器 MUX、译码器、加法器、三态门。
时序逻辑元件(状态元件)
能存东西,在时钟控制下改变:PC、IR、通用寄存器组、MAR、MDR、暂存器。2023-18 选“ALU、MUX”。

408 最常考的是单总线结构:CPU 内部只有一条总线,所有寄存器挂在上面,同一时刻总线上只能有一个数据。每个寄存器有两个开关:Xin(总线上的数打进 X)和 Xout(X 的内容送上总线,用三态门控制,不送的时候断开,避免多个部件同时往总线上送数)。

内总线 R0 R1 R0in/out R1in/out A Ain ALU ADD AC ACin ACout PCPC+1 PCin/out IR IRin 至指令译码/CU MAR MARin MDR MDRin/out 存储器 M 地址总线 AB 数据总线 DBMDRinE / MDRoutE 控制总线:MemR / MemW
图 4-2 单总线数据通路(2009-44 原题)。蓝字是控制信号:Xin 为 1 时总线上的数打入 X,Xout 为 1 时 X 送上总线。MDR 有两套开关:MDRin/MDRout 连内总线,MDRinE/MDRoutE 连外部数据总线。ALU 一端固定接暂存器 A,另一端接内总线;结果先放进 AC。MAR 的输出一直连在地址总线上。

写微操作序列的方法

数据要从 X 到 Y,就在同一个时钟周期令 Xout=1, Yin=1。读内存 = “地址送 MAR → 读 → 数据进 MDR”;写内存 = “地址送 MAR,数据送 MDR → 写”。单总线每个周期只能传一个数,这是排节拍的唯一约束。取指令阶段是所有指令共用的:

节拍功能有效控制信号
C1MAR ← (PC)PCout, MARin
C2MDR ← M(MAR),PC ← (PC)+1MemR, MDRinE, PC+1
C3IR ← (MDR)MDRout, IRin
C4指令译码无

单总线里四类基本操作的写法

任何指令的执行阶段,都是下面四类动作的组合。以一个 ALU 输入端接暂存器 Y、输出端接暂存器 Z 的单总线 CPU 为例(2022-43 就是这种结构):

操作类型例子节拍与控制信号
寄存器 → 寄存器(PC) → MAR1 拍:PCout, MARin
主存 → CPU(读)M((R1)) → R2① R1out, MARin ② MemR, MDRinE(等主存读出) ③ MDRout, R2in
CPU → 主存(写)(R2) → M((R1))① R1out, MARin ② R2out, MDRin ③ MDRoutE, MemW
算术逻辑运算(R1) + (R2) → R3① R1out, Yin(一个操作数先进 Y) ② R2out, ADD, Zin(另一个走总线,结果进 Z) ③ Zout, R3in
排节拍的三条规矩

① 同一拍里只能有一个 Xout——总线上同时只能有一个数;② 同一拍里可以有多个 Yin——同一个数可以同时打进好几个寄存器(例如 PCout 时 MARin 和 Yin 可以同时有效);③ 不走内部总线的动作可以和总线传送并行:读写主存走的是外部数据总线,PC+1 是 PC 自己的计数功能,它们都可以和另一个经内部总线的传送安排在同一拍。2009-44 的解析里“省一个节拍”的做法、取指 C2 同时做 PC+1,都是用的第 ③ 条。

再用图 4-2 的数据通路练两条(先自己写,再对答案):

指令(执行阶段)节拍:功能 / 有效控制信号
LOAD R0, (R1)
((R1)) → R0
C5:MAR ← (R1) / R1out, MARin
C6:MDR ← M(MAR) / MemR, MDRinE
C7:R0 ← (MDR) / MDRout, R0in
STORE (R1), R0
(R0) → M((R1))
C5:MAR ← (R1) / R1out, MARin
C6:MDR ← (R0) / R0out, MDRin
C7:M(MAR) ← (MDR) / MDRoutE, MemW
2009-44计组综合题 13 分
用图 4-2 的数据通路,写出指令 ADD (R1), R0(功能:(R0) + ((R1)) → (R1),即 R0 的数与 R1 所指内存单元的数相加,结果存回 R1 所指的内存单元)执行阶段每个节拍的功能和有效控制信号。
展开解析

先想清楚要做哪几件事:① 按 R1 的内容读内存得到操作数;② 和 R0 相加;③ 结果写回同一个内存地址。再按“单总线每拍一个数”排节拍:

节拍功能有效控制信号说明
C5MAR ← (R1)R1out, MARin要读的地址送 MAR
C6MDR ← M(MAR)MemR, MDRinE读出操作数
C7A ← (MDR)MDRout, AinALU 一个输入必须先放进 A
C8AC ← (A) + (R0)R0out, ADD, ACinR0 经总线进 ALU 另一端
C9MDR ← (AC)ACout, MDRin结果送 MDR 准备写
C10M(MAR) ← (MDR)MDRoutE, MemWMAR 里还是 (R1),直接写

答案不唯一:C6 读内存时内总线是空闲的,可以同时做 A ← (R0)(R0out, Ain),C7 再 AC ← (MDR)+(A),省一个节拍。

2022-43(2)~(5)计组综合题
某 CPU 单总线数据通路中,ALU 的 A 端接暂存器 Y,输出 F 接暂存器 Z;通用寄存器组 GPRs 的读、写寄存器编号 rs、rd 都是 4 位。(2)为什么要设置暂存器 Y 和 Z?(3)GPRs 最多几个寄存器?rs、rd 来自哪个寄存器?GPRs 内有一个地址译码器和一个多路选择器,rd 连哪个?(4)取指令阶段(不考虑 PC 增量)的控制信号序列?若从发出读命令到数据进 MDR 需 5 个时钟周期,取指至少几个时钟周期?(5)控制信号由什么部件产生?哪些寄存器的输出连到它?
展开解析
  1. Y、Z:单总线每时刻只有一个数据,而 ALU 有两个输入、一个输出。先把一个操作数放进 Y,下一拍另一个操作数走总线;ALU 的结果此时不能马上上总线(总线正被占用),先存进 Z。
  2. 寄存器数:4 位编号 → 24 = 16 个;rs、rd 来自指令寄存器 IR(指令里的寄存器字段);rd 选择“写哪个寄存器”,要把编号变成一根选中信号 → 接地址译码器(rs 选“读哪个”,接多路选择器)。
  3. 取指:① PCout, MARin(1 周期)② Read(读主存到 MDR,5 周期)③ MDRout, IRin(1 周期),共 7 个时钟周期。
  4. 控制部件 CU 产生控制信号;IR(操作码)和标志寄存器 FR(条件转移要看标志)的输出送到 CU。

寄存器堆:两个读口、一个写口(组合读、时序写)

问题引入:2022-43(3)问“GPRs 内有一个地址译码器和一个多路选择器,rd 连哪个?”;2023-18 问“通用寄存器组属于操作元件还是状态元件”。要答好这两问,得先把寄存器堆(也叫寄存器文件、通用寄存器组 GPRs)拆开看一看。

打个比方

寄存器堆像一排带玻璃门的储物柜:看柜子里有什么(读)不用开锁,报个柜号就能隔着玻璃看到,而且两个人可以同时看两个柜子;往里放东西(写)必须等管理员摇铃(时钟边沿)、而且你手里有“写入许可”(写使能)才行。不像的地方:真实电路里“看”也要经过多路选择器,有一点点延迟,只是不需要等时钟。

教材说法:寄存器堆由若干个寄存器组成,常见的是两个读端口、一个写端口:读端口给出寄存器编号(rs、rt),经多路选择器直接输出对应寄存器的内容——这是组合逻辑,不需要时钟;写端口给出编号 rd,经译码器选中一个寄存器,在写使能 RegWr 有效时、于时钟边沿把写入数据打进去——这是时序逻辑。整个寄存器堆能“存东西”,所以归类为状态元件(时序逻辑元件,2023-18)。

寄存器堆(16 个 32 位寄存器) R0 R1 R2 ⋮ R15 每个都是时序元件 4-16译码器 写编号 rd(4 位) 写数据(32 位)→ 送到每个寄存器的输入端 RegWr = 1 且时钟边沿到来 → 被译码器选中的那一个寄存器才写入 MUX MUX 16 路16 路 读数据 1= (R[rs]) 读数据 2= (R[rt]) 读编号 rs(4 位) 读编号 rt(4 位) 读:组合逻辑,编号一给,数据就出来
图 4-3 寄存器堆的读口和写口。“选一个输出”用多路选择器(读口,rs、rt 接 MUX 的选择端);“选一个写入”用译码器(写口,rd 接译码器)——这就是 2022-43(3)“rd 连地址译码器”的道理。

跟我做一遍:

  1. 数一数部件:16 个寄存器 → 编号 4 位;两个读口各要一个 16 选 1 的 MUX;一个写口要一个 4-16 译码器。若寄存器有 32 个,就是 5 位编号、32 选 1 MUX × 2、5-32 译码器 × 1。
  2. 单周期 CPU 执行 add R1, R1, R2:一个时钟周期内,读口立即读出旧的 R1 和 R2 → ALU 算出和 → 写口在周期结束的时钟边沿才把和写回 R1。
  3. 为什么写入必须等时钟边沿:如果写入也像读一样“随时生效”,新 R1 会立刻又从读口出来进 ALU,再加一次 R2、再写回……一个周期里反复相加,结果完全不可控。状态元件“只在时钟边沿更新”,正是为了把每个周期隔开(这也是 2019-16 C“时钟周期按组合逻辑最大延迟定”的原因)。
组合逻辑元件(操作元件)时序逻辑元件(状态元件)
输出由什么决定只由当前输入决定,没有记忆由存储的状态决定,状态只在时钟边沿(且写使能有效)时更新
要不要时钟不要要
例子ALU、加法器、多路选择器 MUX、译码器、符号扩展部件、三态门PC、IR、通用寄存器组、MAR、MDR、暂存器、标志寄存器、存储器

真题怎么考:2023-18(ALU、MUX 是操作元件;PC、GPRs 是状态元件)、2021-18 和 2017-19(数据通路由组合逻辑和时序逻辑组成,不包含控制部件,但包含异常检测电路)、2022-43(3)(rd 接译码器、rs 接多路选择器)、2025-19 A(PC 一般单独设置,说通用寄存器组“应该包含”PC 是错的)。判别法:问“它能不能存住一个值”——能存就是状态元件。易错:寄存器堆的“读”是组合逻辑,但寄存器堆整体仍算状态元件。

自测
  1. 32 个通用寄存器、两读一写的寄存器堆,需要几个多路选择器、几选一?写口用几线-几线译码器?
    答案
    两个 32 选 1 的多路选择器(每个读口一个);一个 5-32 译码器(写口)。
  2. 符号扩展部件、标志寄存器分别属于操作元件还是状态元件?
    答案
    符号扩展部件只是把位“复制”出来,没有记忆 → 操作元件;标志寄存器保存运算结果的特征 → 状态元件。

单总线之外:专用数据通路

单总线便宜,但一个周期只能传一个数,一条指令要好几拍。另一种做法是在部件之间拉专用连线(用多路选择器 MUX 选择数据来源),一个周期里多个数据可以同时各走各的路——单周期 CPU 和流水线 CPU 都是这样的(所以 2016-20“单周期处理器可以采用单总线”是错的)。2024-43 的图就是专用通路的一小块:ALU 的 B 端前面放一个 MUX,由 ALUBsrc 选“寄存器 rs2”还是“扩展后的立即数”。

CPU 内部单总线CPU 内部多总线(双总线、三总线)专用数据通路
连接方式所有寄存器挂在一条总线上,Xin/Xout 控制寄存器同时连到两三条总线上。例如三总线:两条源总线分别送 ALU 的两个输入,一条结果总线送回寄存器部件之间直接连线,MUX 选择来源
一个周期能传几个数1 个2~3 个(三总线一拍就能做完 (R1)+(R2)→R3)多个同时传
需要暂存器要(ALU 一端的 A/Y/T,输出端的 AC/Z)基本不要不需要
优缺点结构简单、成本低;总线冲突多、效率低折中没有冲突、性能高;连线多、成本高
用在多周期 CPU、教学模型机(2009、2015、2022 大题)——单周期、流水线 CPU(2024-43、2026-44)

下面这张图是一个简化的单周期专用数据通路(只画了运算、取数、存数三类指令要用的部分,转移指令的通路略去)。2024-43、2026-44 的图都是它的一部分,认清每个多路选择器(MUX)和控制信号在选什么,这类题就不难:

PC +4 PC ← PC + 4(顺序执行) 指令存储器(指令 Cache) rs1 rs2 rd imm 通用寄存器组 读 1读 2写入号 数据 1数据 2 RegWr Ext 扩展器 ExtOp A 01 ALUBsrc B ALU ALUctr 写入数据(rs2) 数据存储器(数据 Cache) 地址 MemWr 10 MemtoReg 写入数据 写回:rd ← ALU 结果 或 从存储器读出的数据
图 4-4 简化的单周期专用数据通路。橙色是控制信号,由控制器根据操作码产生:ALUBsrc 选 ALU 的 B 端是寄存器 rs2 还是扩展后的立即数;ExtOp 选零扩展还是符号扩展;ALUctr 选 ALU 做哪种运算;MemWr 控制写数据存储器;MemtoReg 选写回的是 ALU 结果还是读出的数据;RegWr 控制是否写寄存器。取指令用指令存储器、取数据用数据存储器,PC+4 用单独的加法器——单周期里每个部件每条指令只能用一次。
指令ALUBsrcExtOpALUctrMemWrMemtoRegRegWr
add rd, rs1, rs20(rs2)×加00(ALU 结果)1
addi rd, rs1, imm1(imm)符号扩展加001
lw rd, imm(rs1)1(imm)符号扩展加(算地址)01(读出的数据)1
sw rs2, imm(rs1)1(imm)符号扩展加(算地址)1×0

“×”表示取什么都行(don't care):add 用不到立即数,所以扩展器输出什么都不影响结果;sw 不写寄存器,所以 MemtoReg 选哪路都无所谓。2024-43 第 3 问“slli 的 Ext 取 0 或 1 都行”也是这个道理——slli 的 12 位立即数字段是“7 个 0 + 5 位 shamt”,最高位一定是 0,零扩展和符号扩展都在高位补 0,结果一样。真题里信号名可能不同(如 RegWsrc、MemtoReg),看图认准“这个 MUX 在选什么”即可。

另外两个常考判断:① PC 是专门的寄存器,不在通用寄存器组里(x86、MIPS 都如此;2025-19 A 错)。② 看一条指令在“取数和执行阶段”用到哪些部件(2019-17):R[r2] ← R[r1] + M[R[r0]] 要读寄存器(GPRs)、按 R[r0] 读存储器、用 ALU 相加;指令译码器只在译码阶段用,不算。

同类大题:2015-43(单总线加暂存器 T 的原因同上;ALU 7 种操作 → ALUop 至少 3 位,移位器 3 种 → SRop 2 位;SRout 控制的是三态门;MUX 一端接常数 2 是为了 PC+2,因为 16 位指令、按字节编址);2015-44(写出 inc R1、shl R2,R1、sub R3,(R1),R2 的机器码 0240H、0488H、06EAH);2024-43、2026-44(多路选择器、扩展器 Ext 的控制信号取值)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2023-18 改
数据通路由操作元件(组合逻辑)和状态元件(时序逻辑)组成。下列元件中属于状态元件的是( )。
Ⅰ. ALU Ⅱ. 程序计数器 PC Ⅲ. 通用寄存器组 Ⅳ. 多路选择器 Ⅴ. 指令寄存器 IR
  • A. 仅Ⅰ、Ⅳ
  • B. 仅Ⅱ、Ⅲ
  • C. 仅Ⅱ、Ⅲ、Ⅴ
  • D. 仅Ⅰ、Ⅱ、Ⅲ
展开解析

C能保存信息的是状态元件:PC、通用寄存器组、IR(还有存储器)。ALU、多路选择器、译码器、加法器的输出只取决于当前输入,是操作元件。

变化点:2023-18 问操作元件(答 Ⅰ、Ⅳ),这里问状态元件,还多了一个 IR。判断标准:有没有存储功能,需不需要时钟打入。

变式计组由 2025-19、2017-19 改
下列关于数据通路的叙述中,正确的是( )。
  • A. 控制器属于数据通路的一部分
  • B. 程序计数器 PC 是数据通路中的状态元件
  • C. 数据通路中只包含组合逻辑元件
  • D. 单周期 CPU 中,同一个功能部件在一条指令内可以使用多次
展开解析

BPC 能保存下一条指令的地址,是状态元件。A 错:控制器产生控制信号,不属于数据通路(2017-19)。C 错:数据通路还包含寄存器等时序元件。D 错:单周期 CPU 一条指令只有一个时钟周期,每个部件在一条指令中最多用一次,所以指令存储器和数据存储器要分开。

变化点:2025-19 考“通用寄存器组包含 PC”(错)。这里正面问 PC 的归类,并把单周期 CPU 的特点也放进来。

变式计组由 2023-18、2022-43 改(考纲补充:寄存器堆)
某 CPU 的寄存器堆有 32 个通用寄存器,2 个读端口、1 个写端口。下列叙述中,正确的是( )。
  • A. 每个读端口用一个 5-32 译码器选择要读的寄存器
  • B. 写端口用 5-32 译码器选中被写的寄存器,写入在写使能有效时于时钟边沿完成
  • C. 寄存器堆属于组合逻辑元件
  • D. 读出寄存器的内容必须等到时钟边沿
展开解析

B写口:编号经译码器选中一个寄存器,在 RegWr 有效时于时钟边沿写入(时序逻辑)。A 错:读口用 32 选 1 的多路选择器“选一个输出”。C 错:寄存器堆能保存数据,是状态(时序)元件。D 错:读是组合逻辑,编号一给数据就出来,不用等时钟。

考点:寄存器堆:组合读、时序写。

4.3控制器:谁来发出这些控制信号

★★ 常考选择题

上一节的表格里,每个节拍要让哪些信号为 1(PCout、MARin、MemR……),是我们手写出来的。CPU 里负责“在正确的时刻让正确的信号为 1”的部件就是控制器(CU)。它要根据三样东西决定输出:

  • 现在是什么指令——IR 里的操作码(ADD 和 LOAD 要的信号不一样);
  • 现在是第几拍——节拍发生器给出的时序(同一条指令,C5 和 C6 要的信号不一样);
  • 运算结果的状态——标志寄存器(条件转移指令要看 ZF 等才知道改不改 PC)。

所以 控制信号 = f(操作码,节拍,状态)。实现这个 f 有两种思路:一种是用门电路把 f 直接“焊”出来(硬布线);另一种是把每一拍该发哪些信号事先写成一张表存起来,执行时一行一行地读(微程序)。

硬布线控制器 IR 操作码 节拍发生器 标志 PSW 组合逻辑网络(门电路直接算) 控制信号 快;设计复杂、难修改扩展;RISC 用 微程序控制器 IR 操作码 微地址形成部件 CMAR 控制存储器 CSROM,存微程序 CMDR 微指令 下地址 控制字段 → 控制信号 慢(每步要读 CS);规整、易修改;CISC 用
图 4-5 两种控制器。微程序控制器把每条机器指令的控制过程写成一段“微程序”存在 CPU 内部的控制存储器里:一条机器指令 → 一段微程序 → 若干条微指令 → 每条微指令发出若干个微命令(控制信号)。“存储程序”的思想用在了控制器里(2009-19 选“硬布线快、难修改”)。

时序系统和控制方式

控制信号不仅要“对”,还要在“对的时刻”发出。CPU 用三级时序来划分时间:机器周期(取指、间址、执行、中断,由 FE/IND/EX/INT 标出)→ 每个机器周期分成若干节拍(T0、T1、T2……,每拍一个时钟周期)→ 每拍里再用时钟脉冲的边沿把结果打入寄存器。时序怎么安排,有三种控制方式:

控制方式做法特点
同步控制所有操作都由统一的时钟控制,每个机器周期的节拍数事先定好。又分:定长机器周期(按最复杂的操作定节拍数,简单操作也得等够)、不定长机器周期(不同机器周期节拍数不同)、中央控制与局部控制结合(大多数操作用固定节拍,少数复杂操作如乘除临时延长)控制简单;但时间按最坏情况安排,有浪费
异步控制没有统一的节拍,每个操作需要多少时间就占多少时间,部件之间靠“请求—应答”信号联系时间利用充分;控制复杂
联合控制同步与异步结合:CPU 内部大部分用同步,与速度差别大的主存、I/O 交换数据时用异步应答现代计算机多用

硬布线控制器:把控制信号“焊”出来

硬布线控制器(组合逻辑控制器)的每个控制信号,都是一个组合逻辑表达式的输出,输入是:指令译码器的输出(现在是哪条指令)、机器周期标志和节拍(现在是哪一步)、标志位(运算结果状态)以及外部请求(中断、DMA)。设计步骤是:

  1. 分析每个阶段的微操作序列:把每条指令在取指、间址、执行、中断各周期里要做的动作列出来(就是上一节写的那些表)。
  2. 选择控制方式:同步、异步还是联合,定长还是不定长机器周期。
  3. 安排微操作时序:把微操作排进节拍。原则:有先后依赖的必须按顺序;被控对象不同、互不冲突的微操作尽量放在同一拍;占用时间很短的几个操作可以放在同一拍的前后(如 MDR 读出后立即送 IR)。
  4. 电路设计:列出操作时间表(每个微操作在哪个周期、哪一拍、被哪些指令用到)→ 写出每个微命令的最简逻辑表达式 → 画出逻辑图。

举个表达式的例子:“M(MAR) → MDR”(读主存)这个微命令,在取指周期的 T1 所有指令都要用;在间址周期的 T1 只有带间接寻址的访存/转移类指令要用;在执行周期的 T1 只有 ADD、LDA(取数)等需要读操作数的指令要用。于是它的表达式形如:

M(MAR)→MDR = FE·T1 + IND·T1·(ADD + STA + LDA + JMP + BAN) + EX·T1·(ADD + LDA)

式中的指令名是教材模型机里的:ADD 加法、STA 存数、LDA 取数、JMP 无条件转移、BAN 结果为负则转移。间址周期里这五条指令只要用了间接寻址,都要先读主存取有效地址;执行周期里只有 ADD、LDA 要读操作数(STA 是写,JMP、BAN 只改 PC)。每一项读作“在哪个周期 · 哪一拍 · 哪些指令”。所有微命令的表达式合起来就是整个控制器的电路。可见指令越多、越复杂,表达式就越乱——这就是硬布线控制器设计复杂、难以修改扩充的原因;但它用门电路直接产生信号,速度快,所以 RISC 多用它(2009-19)。

微程序控制器的几个名词(按从小到大)

名词意思对应上一节的例子
微命令 / 微操作一个控制信号(微命令)让一个部件做的一个最小动作(微操作)PCout = 1 → “PC 送总线”
微指令一个节拍里要同时发出的那一组微命令,再加上“下一条微指令在哪”的信息C1 那一行:{PCout, MARin}
微周期读出并执行一条微指令的时间,一般 = 一个时钟周期一个节拍
微程序一条机器指令对应的一串微指令ADD (R1),R0 的 C5~C10
控制存储器 CS放所有微程序的 ROM,在 CPU 里;用微地址(CMAR)去读,读出放进 CMDR

取指令那几拍(C1~C3)所有指令都一样,所以写成一段公共的取指微程序;取指完成后,由操作码找到这条指令自己的微程序入口,接着执行。

微程序控制器的组成和工作过程

IR(操作码) 微地址形成部件 顺序逻辑下地址 / 分支判断 标志(PSW)、外部条件 CMAR 入口地址 微地址 控制存储器 CM(ROM,在 CPU 内)存放全部微程序 读出一条微指令 顺序控制字段 操作控制字段 微指令寄存器 CMDR 下一条微指令的地址信息 微命令→ 数据通路各部件
图 4-6 微程序控制器的组成。核心是控制存储器 CM(存微程序)、微指令寄存器 CMDR(放当前微指令)、微地址寄存器 CMAR(放下一条微指令的地址)和微地址形成部件(由操作码得到微程序入口)。它和“CPU + 主存”执行程序的结构一模一样:CM 相当于主存,CMAR 相当于 PC/MAR,CMDR 相当于 IR。
  1. 开机或一条指令执行完,CMAR 指向公共取指微程序的入口,逐条执行它,把机器指令从主存取到 IR。
  2. IR 的操作码送微地址形成部件,得到这条指令对应微程序的入口地址,送入 CMAR。
  3. 从 CM 逐条读出这段微程序的微指令到 CMDR:操作控制字段发出这一拍的微命令;顺序控制字段(配合标志位)决定下一条微指令的地址。
  4. 这段微程序的最后一条微指令让 CMAR 回到取指微程序入口,开始取下一条机器指令(有中断请求时先转去执行中断周期的微程序)。
几个数量关系

① 微程序个数:每条机器指令对应一段微程序,再加上公共的取指微程序;若有间址周期、中断周期,它们也各对应一段公共微程序。所以 n 条机器指令 → 控存里至少有 n + 1 段微程序。
② 微指令总条数 = 各指令微程序的微指令数之和 + 公共微程序的微指令数(2014-18:32×4 + 2 = 130)。
③ 控存容量 = 微指令条数 × 微指令字长;用断定法时,下地址字段 k 位,控存按 2k 个单元设计。
④ 对应关系:程序 ↔ 机器指令序列,微程序 ↔ 微指令序列;一条机器指令对应一段微程序,一条微指令包含若干微命令。

一条微指令 = 操作控制字段(发哪些信号) + 顺序控制字段(下一条在哪) 操作控制字段 判断测试 下地址 ① 直接编码:每个微命令 1 位 10010… PCout MARin MemR … 各占一位,为 1 就发不用译码、最快,但 33 个微命令就要 33 位 ② 字段直接编码:互斥的微命令编成一个字段,字段内用编码 字段 1:3 位 字段 2:2 位 字段 3:4 位 7 个互斥微命令 + “都不发” = 8 种 → 3 位每个字段要译码器,稍慢,但短很多 一次能发多个微命令的叫水平型微指令(①②都是);像机器指令一样用“微操作码”、一次只发一两个微命令的叫垂直型——字长短、微程序长、慢。
图 4-7 微指令格式。“互斥”= 不会在同一个节拍里同时出现的微命令(如 ALU 的加、减、与),它们才能共用一个字段。下地址的两种给法:计数器法(像 PC 一样顺序 +1,遇转移另算)和断定法(每条微指令里直接写下一条的地址,位数 = ⌈log2(微指令总数)⌉)。
微指令格式考点方法真题
操作控制字段:直接编码每个微命令占 1 位,不用译码,最快但最长
字段直接编码互斥的微命令放同一字段,字段内编码;每个字段要多留一个编码表示“什么都不发”:含 k 个微命令的字段需要 ⌈log2(k+1)⌉ 位2012-18:7、3、12、5、6 → 3+2+4+3+3 = 15 位
下地址字段(断定法)位数 = ⌈log2(微指令总条数)⌉2014-18:32 条指令×4 + 公共取指 2 = 130 条 → 8 位

操作控制字段的第三种编码:字段间接编码——一个字段里某些微命令的含义,还要由另一个字段来“解释”(比如字段 A 的某个编码到底选哪个寄存器,要看字段 B 的值)。它能进一步缩短微指令,但会多一层译码、变慢,只作辅助手段。三种编码对比:直接编码最快、最长;字段直接编码折中(最常用、真题考的就是它);字段间接编码最短、最慢。

水平型微指令垂直型微指令
一条微指令定义并执行多个并行的微命令类似机器指令,有“微操作码”字段,一条只能执行一两个微操作
编码直接编码、字段直接编码对微操作码译码
并行能力 / 速度强;执行一条机器指令所需的微指令少,速度快弱;要的微指令多,速度慢
微指令字长 / 微程序字长长,微程序短,控存容量大字长短,微程序长
编写难,用户不易掌握和写机器指令类似,容易

还有介于两者之间的混合型微指令。拓展 动态微程序设计:控存用可改写的存储器(如 EPROM),通过改写微程序来改变机器的指令系统;毫微程序:用更低一级的毫微程序去解释执行微指令。

下一条微指令的地址怎么来:① 取指微程序结束后,由机器指令的操作码经微地址形成部件得到入口地址(一般用硬件映射);② 断定法:微指令的下地址字段直接给出;③ 增量计数器法:(CMAR) + 1 → CMAR,顺序执行时用;④ 根据标志或外部条件在几个地址中选一个(微程序分支,要用微指令里的判别测试字段);⑤ 中断周期等由硬件直接产生固定的微程序入口地址。

硬布线控制器微程序控制器
工作原理控制信号由组合逻辑电路即时产生控制信号事先存在控存里,执行时读出
执行速度快较慢(每拍都要访问一次控存)
规整性繁琐、不规整较规整
修改与扩充困难(要改电路)容易(改微程序即可)
适用场合RISC CPUCISC CPU
例计组微指令格式综合计算
某微程序控制器,机器有 60 条指令,每条指令对应的微程序平均 4 条微指令;另有公共的取指微程序 2 条、间址微程序 1 条、中断周期微程序 2 条。全部微命令分成 4 个互斥类,分别含 5、8、13、3 个微命令,采用字段直接编码;判别测试字段用 3 位,下地址用断定法。求微指令各字段位数、微指令字长和控存容量。
展开解析
  1. 操作控制字段:每个字段多留一个“不发任何微命令”的编码 → ⌈log26⌉ + ⌈log29⌉ + ⌈log214⌉ + ⌈log24⌉ = 3 + 4 + 4 + 2 = 13 位。
  2. 微指令总数 = 60 × 4 + 2 + 1 + 2 = 245 条 → 下地址字段 ⌈log2245⌉ = 8 位。
  3. 微指令字长 = 13 + 3 + 8 = 24 位。
  4. 控存容量:8 位微地址可寻址 28 = 256 个单元 → 按 256 × 24 位设计(实际用到 245 个单元)。

2017-18:主存 MM 在 CPU 外、存指令和数据、用 RAM+ROM;控制存储器 CS 在 CPU 内、存微指令、用 ROM;两者都按地址访问(说 CS“按内容访问”是错的)。

字段间接编码:让一个字段去“解释”另一个字段(例)

问题引入:上面一句话带过的“字段间接编码”到底怎么省位?省了位又丢了什么?用一个自己设计的小例子算一遍就清楚了。

打个比方

同一个手势“竖大拇指”,在饭桌上是“好吃”,在路边是“搭车”——手势本身(字段 B)不变,场合(字段 S)决定它的意思。不像的地方:人可以同时在饭桌上又在路边吗?不行——微指令也一样,被“解释”的两组微命令不能在同一拍里同时发出,这就是代价。

设计要求(示意):微命令里有 4 个 ALU 运算(加、减、与、或,互斥)和 4 个移位操作(左移 1 位、右移 1 位、左移 2 位、右移 2 位,互斥)。

微指令的这一段(共 4 位) S B₂ B₁ B₀ 1 位3 位 3-8译码器 y1 y2 y3 y4 B = 000:本拍不发这类微命令 S = 0:解释为 ALU 运算 y1 → 加y2 → 减y3 → 与y4 → 或 S = 1:解释为移位 y1 → 左移 1 位y2 → 右移 1 位y3 → 左移 2 位y4 → 右移 2 位 S 决定 y1~y4 选哪一组(经与门控制)
图 4-8 字段间接编码示意。字段 B 的同一个编码,在 S = 0 和 S = 1 时代表不同的微命令:B 的含义要由 S 来“解释”,所以叫间接编码(也叫隐式编码)。
  1. 直接编码:8 个微命令各占 1 位 → 8 位,任意组合都能同拍发出。
  2. 字段直接编码:ALU 运算一个字段(4 + 1 种 → 3 位),移位一个字段(4 + 1 种 → 3 位)→ 6 位;ALU 运算和移位可以同拍(各在自己的字段里)。
  3. 字段间接编码:规定“移位和 ALU 运算不在同一拍做”,用 1 位 S 选择 B 的含义 → 1 + 3 = 4 位。省了 2 位,代价是加法和移位不能同拍(并行能力下降),而且多了一级与门,信号出得更慢。
直接编码字段直接编码字段间接编码
本例位数864
译码不要每个字段一个译码器译码之后还要由另一字段“解释”
速度最快较快最慢
并行能力最强不同字段可同拍被解释的几组不能同拍
地位微命令多时太长最常用(2012-18 考它)只作字段直接编码的辅助手段

数字已用 verify/08/p04_calc.py 验算。真题目前只考过字段直接编码的位数计算;字段间接编码考概念:“可以进一步缩短微指令字长,但削弱了并行控制能力,通常作为辅助手段”。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2012-18 改
某微程序控制器的微指令采用字段直接编码,共 40 个微命令,分成 6 个互斥类,分别包含 8、3、15、6、4、4 个微命令。操作控制字段至少有( )位。
  • A. 6
  • B. 15
  • C. 19
  • D. 40
展开解析

C每类要留一个“不发出任何微命令”的编码,n 个微命令要 ⌈log2(n + 1)⌉ 位:8 → 4 位,3 → 2 位,15 → 4 位,6 → 3 位,4 → 3 位,4 → 3 位,共 19 位。

变化点:2012-18 的各类大小为 7、3、12、5、6。注意 15 个微命令要 4 位(16 个编码),3 个要 2 位:看的是 n + 1。D 是直接编码(每个微命令 1 位)的位数。

变式计组由 2014-18 改
某微程序控制器共有 64 条机器指令,公共的取指微程序有 3 条微指令,间址周期和中断周期的微程序各有 2 条微指令,每条机器指令的微程序平均有 5 条微指令。采用断定法确定下一条微指令地址,下址字段至少( )位。
  • A. 7
  • B. 8
  • C. 9
  • D. 10
展开解析

C微指令总数 = 64 × 5 + 3 + 2 + 2 = 327 条,下址字段至少 ⌈log2327⌉ = 9 位。

变化点:2014-18 是 32 × 4 + 2 = 130 条(8 位)。公共部分(取指、间址、中断)要单独加上,只算一次。

变式计组考纲补充(尚无真题):微指令的编码方式
下列关于微指令操作控制字段编码方式的叙述中,正确的是( )。
  • A. 字段间接编码的并行控制能力最强
  • B. 直接编码需要的译码电路最多
  • C. 字段直接编码时,同一字段内的几个微命令可以在同一拍发出
  • D. 字段间接编码可进一步缩短微指令字长,但会削弱并行控制能力
展开解析

D字段间接编码让一个字段去“解释”另一个字段,能省位,但被解释的几组微命令不能同拍发出,并且多一级译码、更慢,所以只作辅助手段。A 错:并行能力最强的是直接编码。B 错:直接编码不需要译码。C 错:同一字段内的微命令互斥,一拍只能发其中一个。

考点:字段间接编码与三种编码对比。

4.4指令流水线:让指令像洗衣服一样重叠

★★★ 高频选择题综合题
打个比方

洗一筐衣服要“洗→烘→叠”三步,各 30 分钟。一个人等上一筐全弄完才洗下一筐,3 筐要 270 分钟;聪明的做法是第 1 筐进烘干机时第 2 筐就进洗衣机,3 筐只要 30×(3+2) = 150 分钟。每一筐的时间没变短,但单位时间完成的筐数(吞吐率)变大了。

408 用的是五段流水线:IF 取指 → ID 译码/读寄存器 → EX 执行/算地址 → MEM 访存 → WB 写回寄存器。不同类型的指令在各段做的事不同,有的段是“空”的(但照样要占一拍,保证每条指令流过的段数一样):

指令类型IFIDEXMEMWB
运算类
add rd, rs, rt
按 PC 取指令,PC + 4译码,读 rs、rtALU 运算空结果写 rd
取数
lw rt, imm(rs)
读 rs,imm 符号扩展算地址 rs + imm读数据存储器数据写 rt
存数
sw rt, imm(rs)
读 rs、rt算地址 rs + imm写数据存储器空
条件转移
beq rs, rt, L
读 rs、rt比较(相减看 ZF),算转移目标地址根据比较结果决定是否把目标地址送 PC(在哪一段改 PC 因设计而异,题目会说明)
无条件转移
j L
译码把目标地址送 PC(通常越早越好)

相邻两段之间有流水段寄存器(IF/ID、ID/EX、EX/MEM、MEM/WB),保存上一段算出的结果和后面要用的控制信号,让五条指令在五个段里互不干扰。所以流水线的时钟周期 = 最慢一段的时间 + 流水段寄存器的延迟(下面 2018-20);每条指令从头到尾的时间不但没缩短,还因为段寄存器延迟、各段向最慢段看齐而变长了——流水线提高的是吞吐率。

指令\时钟 123456789 I1I2I3I4I5 IFIDEXMEMWB IFIDEXMEMWB IFIDEXMEMWB IFIDEXMEMWB IFIDEXMEMWB 第 5 拍起每拍完成一条
图 4-9 理想流水线的时空图。第 1 条指令要 k=5 拍才出来(“装满”流水线),之后每拍出一条。n 条指令共需 k + (n − 1) 个时钟周期。
n 条指令、k 段流水线、时钟周期 Δt:总时间 T = (k + n − 1) × Δt 吞吐率 TP = n / T  加速比 S = 不用流水线的时间 / 用流水线的时间 = nkΔt / T 时钟周期 = 最长流水段的时间(+ 流水段寄存器延迟)

第三个指标:效率(时空图里“格子”的利用率)

效率 E = 时空图中被占用的格子数 ÷ 全部格子数 = nkΔt ÷ (k × T) = n ÷ (k + n − 1) n → ∞ 时:吞吐率 → 1/Δt,加速比 → k,效率 → 1(全是理想值,实际达不到)
段 \ 时间 段 1段 2段 3段 4 12345 12345 12345 12345 t1t2t3t4t5t6t7t8 占用的格子:20 全部格子:4 × 8 = 32 效率 E = 20/32 = 62.5% 吞吐率 = 5/(8Δt) 加速比 = 20Δt/8Δt = 2.5 格子里的数字是指令编号;灰格子是流水线“装满”(t1~t3)和“排空”(t6~t8)时闲着的段
图 4-10 4 段流水线执行 5 条指令。k = 4,n = 5,总时间 T = (4 + 5 − 1)Δt = 8Δt。不用流水线要 5 × 4Δt = 20Δt。效率就是彩色格子占全部格子的比例,也等于 加速比 ÷ 段数 = 2.5/4。指令条数越多,两头的空格占比越小,效率越高。
2018-20计组
某计算机最复杂指令的执行需要完成 5 个子功能,分别由功能部件 A~E 实现,各功能部件所需时间分别为 80ps、50ps、50ps、70ps 和 50ps,采用流水线方式执行指令,流水段寄存器延时为 20ps,则 CPU 时钟周期至少为( )。
  • A. 60ps
  • B. 70ps
  • C. 80ps
  • D. 100ps
展开解析

考点4.4 流水线时钟周期

D流水线所有段共用一个时钟,每段都要在一个周期内做完,还要把结果打入流水段寄存器:时钟周期 = 最慢段 + 段寄存器延时 = 80 + 20 = 100ps。

  • A 错60ps 比最慢段 80ps 还短,最慢的那一段做不完。
  • B 错70ps 比最慢的 A 段(80ps)还短,A 段在一个周期内做不完;而且还要再加段寄存器延时 20ps。
  • C 错80ps:只取了最慢段,忘了加流水段寄存器的 20ps(2009-18 是题目说明“忽略缓存时间”才不加)。
  • D 对100ps。

流水线时钟周期 = max(各段时间) + 段间寄存器延迟。

2013-18计组
某 CPU 主频为 1.03GHz,采用 4 级指令流水线,每个段的执行需要 1 个时钟周期。假定 CPU 执行了 100 条指令,在其执行过程中没有发生任何流水线阻塞,此时流水线的吞吐率为( )。
  • A. 0.25×109 条/秒
  • B. 0.97×109 条/秒
  • C. 1.0×109 条/秒
  • D. 1.03×109 条/秒
展开解析

考点4.4 流水线吞吐率

C吞吐率 = 完成的指令数 ÷ 所用时间;有“装满流水线”的时间,所以不是简单的“主频 ÷ 1”。

  1. 4 级流水、100 条指令、无阻塞:共 4 + (100 − 1) = 103 个时钟周期。
  2. 时间 = 103 ÷ 1.03GHz = 100ns。
  3. 吞吐率 = 100 条 ÷ 100ns = 1.0 × 109 条/秒。
  • A 错0.25 × 109 大约是“不用流水线、每条指令 4 个周期”的速度,没有利用流水线的重叠。
  • B 错0.97 × 109:计算有误(100 条 ÷ 103 个周期 ≈ 0.97 条/周期,但还要乘主频 1.03GHz)。
  • C 对1.0 × 109 条/秒。
  • D 错1.03 × 109 是“每个周期完成一条”的极限,忽略了前 3 个周期流水线还没装满。

n 条指令、k 段流水线:周期数 = k + n − 1;吞吐率 = n ÷ 总时间。

冒险:流水线为什么会“卡住”

冒险(相关)原因解决办法
结构冒险
资源冲突
两条指令同一时刻要用同一个部件。典型:I1 在 MEM 访问数据时,I4 在 IF 取指令,都要用存储器让后一条等(阻塞);或者指令 Cache 和数据 Cache 分离(2014-16:分离的主要目的是减少流水线资源冲突)
数据冒险
数据相关
后一条要用前一条的结果,结果还没写回。最常考“写后读 RAW”① 插入气泡(阻塞);② 数据旁路/转发:把 ALU 的结果直接送给下一条的 ALU 输入;③ 编译器调整指令顺序、插 nop
控制冒险遇到转移指令,要到后面某段才知道下一条从哪取阻塞(等分支结果);分支预测;延迟转移

三种冒险的对策(完整版)

冒险对策
结构冒险① 让后一条指令暂停一个周期再用该部件(插入气泡);② 资源重复配置:指令存储器与数据存储器分开(分离的指令 Cache、数据 Cache,2014-16),多设几个功能部件
数据冒险① 硬件阻塞:插入气泡,等前一条写回再读;② 数据旁路(转发):把结果从流水段寄存器直接送到需要它的部件(2010-19:转发本身不会引起阻塞);③ 软件插空:编译器调整指令顺序,把不相关的指令插进去,或者插 nop;④ 寄存器前半周期写、后半周期读,使写回与读寄存器可以在同一拍完成,少等一拍
控制冒险① 硬件阻塞:遇到转移指令就暂停取指,直到知道转不转、转到哪;② 分支预测:静态预测(事先固定“总是预测不转移”或“总是预测转移”,或由编译器按统计决定)、动态预测(根据这条分支过去的执行历史来猜,如用 1 位、2 位预测位);猜错了要把错取的指令作废;③ 尽早判断:把比较和目标地址计算提前到 ID 段,缩短阻塞;④ 两个方向的指令都预取;⑤ 延迟转移:转移指令后面的“延迟槽”由编译器放一条无论转不转都要执行的指令,让等待时间不浪费

数据相关的三种类型(按“两条指令对同一个寄存器先后做了什么”分):

类型例子(按顺序执行)会不会出问题
写后读 RAW(真相关)I1: add r1, r2, r3
I2: sub r4, r1, r5
I2 要读 I1 写的 r1。按序流水线里只有它会造成数据冒险,408 考的都是它
读后写 WAR(反相关)I1: add r1, r2, r3
I2: add r2, r6, r7
I2 写的 r2 是 I1 要读的。按序执行时 I1 早就在 ID 读完了,没问题;乱序执行时若 I2 抢先写,I1 读错 拓展
写后写 WAW(输出相关)I1: add r1, r2, r3
I2: mul r1, r8, r9
两条都写 r1,最后应当是 I2 的值。按序完成时没问题;乱序完成时可能被 I1 覆盖 拓展
不转发,且同一寄存器不能在同一周期读和写 → 用到结果的指令,其 ID 要排在产生者 WB 的下一拍 1234567891011121314 I1 LOAD R1,[a]I2 LOAD R2,[b]I3 ADD R1,R2I4 STORE R2,[x] IFIDEXMWB IFIDEXMWB IFID 被阻塞IDEXMWB IF(被阻塞,等 I3 离开 ID)ID 被阻塞IDEXMWB x = a + b:I3 要读 R1、R2 → 等 I2 在第 6 拍写回,第 7 拍才能 ID;I4 要读 I3 写的 R2 → 第 11 拍 ID。共 14 拍
图 4-11 数据冒险造成阻塞(2012-44 第 3 问)。“按序发射”:I3 卡在 ID,后面的 I4 只能卡在 IF。I3 的 ID 被阻塞,原因是与 I1、I2 存在数据相关(要读的 R1、R2 还没写回);I4 的 IF 被阻塞,原因是前一条 I3 阻塞在 ID 段。
判断数据冒险的三步
  1. 对每条指令写出:写哪个寄存器、读哪些寄存器(store 指令也“读”它要存的那个寄存器;load 的地址寄存器也是“读”)。
  2. 后面的指令读的寄存器,恰好是前面某条(还没写回的)指令要写的 → 写后读 RAW,有数据冒险。只写同一个寄存器(WAW)或先读后写(WAR)在按序流水线里不造成问题。
  3. 有转发时:一般的 ALU 结果能及时转发,不阻塞;但 load 的结果要到 MEM 段末才有,紧跟着用它的指令仍要阻塞 1 拍(load-use,2024-19:“所有数据冒险都能靠转发解决”是错的)。
① 有转发:ALU 结果一出来就“抄近路”送给下一条 add r1,r2,r3sub r4,r1,r5 IFIDEXMEMWB IFIDEXMEMWB r1 在第 3 拍末算出,直接送进第 4 拍 sub 的 ALU 输入→ 不用等第 5 拍写回,不阻塞 ② load-use:数据要到 MEM 段末才有,转发也赶不上,必须插 1 个气泡 lw r1,0(r2)sub r4,r1,r5 IFIDEXMEMWB IFID气泡EXMEMWB r1 第 4 拍末才从内存取到sub 的 EX 推迟到第 5 拍→ 阻塞 1 拍(2023-19 的 I3) 所以“所有数据冒险都能靠转发解决”是错的(2024-19);但插气泡、调整指令顺序、插 nop 总能解决。
图 4-12 转发(旁路)和 load-use 冒险。转发 = 在 ALU 输出和 ALU 输入之间加一条“捷径”,结果不必先写回寄存器再读出来。没有转发时,按 408 的常见约定,用到结果的指令要等产生者 WB 完成后的下一拍才能 ID(图 4-11);题目若说“寄存器前半周期写、后半周期读”,则可以在 WB 的同一拍 ID——一定看清题目约定。

动手数一次:同一段代码,三种约定各要几拍

I1: add r1, r2, r3
I2: sub r4, r1, r5     ; 读 I1 写的 r1
I3: and r6, r1, r7     ; 读 r1
I4: or  r8, r1, r9     ; 读 r1

“阻”表示这一拍被阻塞:按序发射时,被阻塞的指令停在原地(I2 停在 ID 等数据),后面的指令也只能停在自己的段里(I3 停在 IF)。

① 不转发,后一条的 ID 要在前一条 WB 的下一拍(408 常见约定,图 4-11 用的就是它)
指令1234567891011
I1IFIDEXMWB
I2IF阻阻阻IDEXMWB
I3IF阻阻阻IDEXMWB
I4IFIDEXMWB
② 不转发,但寄存器“前半周期写、后半周期读”:ID 可以和前一条的 WB 在同一拍
指令12345678910
I1IFIDEXMWB
I2IF阻阻IDEXMWB
I3IF阻阻IDEXMWB
I4IFIDEXMWB

③ 有转发:I1 的结果在第 3 拍 EX 结束时就有了,直接转发给 I2 第 4 拍的 EX;I3 第 5 拍 EX 时,I1 的结果正在 MEM/WB 段寄存器里,同样可以转发;I4 第 6 拍 EX 时,I1 已在第 5 拍写回,I4 在第 5 拍 ID 读寄存器,若“前半周期写、后半周期读”就能直接读到,否则也从旁路取。所以一拍都不用等,4 条指令 5 + 3 = 8 拍完成。三种约定分别是 11、10、8 拍(已用程序模拟验算)。若 I1 换成 lw r1, 0(r2),数据要到第 4 拍 MEM 结束才有,即使转发 I2 也得等 1 拍(load-use),共 9 拍。

2019-18计组
五段流水线执行:I1: add s2,s1,s0(R[s2]←R[s1]+R[s0]);I2: load s3,0(t2)(R[s3]←M[R[t2]+0]);I3: add s2,s2,s3;I4: store s2,0(t2)(M[R[t2]+0]←R[s2])。下列指令对中,不存在数据冒险的是( )。
  • A. I1 和 I3
  • B. I2 和 I3
  • C. I2 和 I4
  • D. I3 和 I4
展开解析

考点4.4 数据冒险

C列出每条指令“写哪个、读哪些”寄存器;后面的指令读前面指令要写的寄存器,就是 RAW 数据冒险。

  1. I1 写 s2;I2 写 s3、读 t2;I3 写 s2、读 s2 和 s3;I4 读 s2 和 t2(store 不写寄存器)。
  • A 错I3 要读 s2,而 I1 刚写 s2,是 RAW 数据冒险。
  • B 错I3 要读 s3,而 I2(load)刚写 s3,是 RAW 数据冒险,而且是 load-use,转发也要阻塞 1 拍。
  • C 对I2 写 s3,I4 读的是 s2 和 t2,没有读 s3;两条都只读 t2,读读不冲突,无数据冒险。
  • D 错I4(store)要读 s2 写进内存,而 I3 刚写 s2,是 RAW 数据冒险。

store 只读寄存器(要存的数 + 基址),load 写目的寄存器。只有“写后读”同一个寄存器才算 RAW。同类:2016-19(选“I2 和 I3”:I2 写 R5,I3 读 R5)。

2023-19计组
五段流水线 RISC 处理器执行:I1 add s2,s1,s0;I2 load s3,0(s2);I3 beq t2,s3,L1;I4 addi t2,t2,20;I5 L1: …。若采用转发(旁路)处理数据冒险、硬件阻塞处理控制冒险,则 I1~I4 执行过程中,发生流水线阻塞的指令有( )。
  • A. 仅 I3
  • B. 仅 I2、I4
  • C. 仅 I3、I4
  • D. 仅 I2、I3、I4
展开解析

考点4.4 三种冒险的对策4.4 动手数一次

C逐对看:转发能解决“ALU 结果给下一条用”,解决不了“load 刚取的数给紧接着的下一条用”(load-use);分支用硬件阻塞,则分支后面那条要等。

  1. I1 → I2(s2):add 的结果在 EX 段末就有,可转发给 I2 的 EX,I2 不阻塞。
  2. I2 → I3(s3):load 在 MEM 段末才取到数,I3 紧跟其后要用 → load-use,转发也来不及,I3 阻塞 1 拍。
  3. I3 是条件分支 beq:在它算出是否转移之前,不知道下一条该取 I4 还是 L1 处的指令 → 控制冒险用硬件阻塞,I4 阻塞。
  • A 错漏了 I4:题目规定控制冒险用硬件阻塞,beq 后面的 I4 必须等分支结果出来才能继续,会被阻塞。
  • B 错I2 不会阻塞:它要的 s2 是 add 在 EX 段算出来的,可以直接转发;而真正要阻塞的 I3(load-use)被漏掉了。
  • C 对I3(load-use 数据冒险)和 I4(控制冒险)阻塞。
  • D 错多了 I2:add → load 的数据相关通过转发就能解决,不需要阻塞。

有转发时只剩 load-use 要阻塞 1 拍;控制冒险按题目说的处理方式(阻塞 / 预测 / 延迟槽)判。

2014-44(4)计组接第 3 篇
对第 3 篇 2014-44 的 6 条指令(sll R4,R2,2 / add R4,R4,R3 / load R5,0(R4) / add R1,R1,R5 / add R2,R2,1 / bne R2,R6,loop),若采用“按序发射、按序完成”的 5 级流水线,不转发,分支指令引起 3 个时钟周期阻塞,哪些指令会因数据相关而阻塞?哪条发生控制冒险?为什么指令 1 不会因为与指令 5 的数据相关而阻塞?
展开解析

数据相关:2 读 1 写的 R4;3 读 2 写的 R4;4 读 3 写的 R5;6 读 5 写的 R2 → 2、3、4、6 阻塞。6(bne)发生控制冒险。下一轮的指令 1 读 R2 虽然依赖指令 5,但中间隔着 bne 的 3 个周期阻塞,等它开始译码时指令 5 早已写回。

2012-44(2)(4)计组
(2)连续 4 条指令无阻塞地进入 5 段流水线,需要几个时钟周期?(4)x = x*2 + a(x、a 为 unsigned int)用 LOAD/SHL/ADD/STORE 实现,在上述不转发的流水线中至少需要多少个时钟周期?
展开解析

(2)5 + (4−1) = 8。(4)指令序列 I1 LOAD R1,[x];I2 LOAD R2,[a];I3 SHL R1(或 ADD R1,R1);I4 ADD R1,R2;I5 STORE R2,[x]。按图 4-11 的规则:I3 等 I1 第 5 拍写回 → 第 6 拍 ID,第 9 拍 WB;I4 等 I3 → 第 10 拍 ID,第 13 拍 WB;I5 等 I4 → 第 14 拍 ID,第 17 拍 WB。至少 17 个时钟周期。

其他选择题:2010-19 不会引起阻塞的是“数据旁路(转发)”;2011-18 指令定长规整、按边界对齐、只有 Load/Store 访存都有利于流水线;2017-17 超标量流水线能在一个周期内发射多条指令、能结合动态调度,但不能缩短每段的处理时间(选 C)。

有阻塞时,流水线的实际 CPI

理想流水线每拍完成一条指令(CPI = 1);每发生一次阻塞,就相当于多花了几拍。所以:

实际 CPI = 1 + 平均每条指令的阻塞周期数 = 1 + Σ(某类事件发生的频率 × 每次阻塞的周期数)
例计组阻塞对 CPI 的影响
某五段流水线采用转发。程序中 load 指令占 25%,其中 40% 的 load 后面紧跟着使用其结果的指令(每次阻塞 1 拍);转移指令占 20%。(1)若遇到转移指令一律阻塞 2 拍,平均 CPI 是多少?(2)若改为“总是预测不转移”,只有真正转移时才阻塞 2 拍,已知 60% 的转移指令实际不转移,平均 CPI 是多少?性能提高了多少?
展开解析
  1. load-use 带来的阻塞:0.25 × 0.4 × 1 = 0.1 拍/条。
  2. (1)转移阻塞:0.2 × 2 = 0.4 拍/条 → CPI = 1 + 0.1 + 0.4 = 1.5。
  3. (2)只有转移成功(占转移指令的 40%)才阻塞:0.2 × 0.4 × 2 = 0.16 → CPI = 1 + 0.1 + 0.16 = 1.26。
  4. 主频、指令数都没变,执行时间之比 = CPI 之比:1.5 / 1.26 ≈ 1.19,即性能提高约 19%。

高级流水线技术:超标量、超流水线、超长指令字

基本流水线的 CPI 最好也只能到 1。想更快,有两条路:让每拍能同时处理多条指令(多发射),或者把流水线切得更细、让时钟更快(超流水线)。

技术做法要点
超标量
动态多发射
CPU 里配置多套功能部件(多条流水线),每个时钟周期同时发射多条相互独立的指令;由硬件在运行时检查相关性、决定哪些指令能一起发射,可结合动态调度(乱序执行)理想 CPI < 1;不能缩短每段的时间(2017-17);硬件复杂
超流水线把每个流水段再细分成更多、更短的段,时钟周期更短、主频更高;相当于在一个“原来的时钟周期”里流水线推进了好几次段数多了,冒险造成的阻塞代价(拍数)也更大;“是否采用超流水线”属于组成,不由 ISA 规定(2026-16)
超长指令字 VLIW
静态多发射
由编译器在编译时找出可以并行的操作,把它们拼成一条很长的指令,一次发射给多个功能部件硬件简单;并行性靠编译器挖掘,代码膨胀

区分超标量和 VLIW 的关键:谁来决定哪些指令一起执行——超标量是硬件在运行时决定(动态),VLIW 是编译器在编译时决定(静态)。

动态流水线:指令可以“插队”执行,但必须按原顺序“交卷”

问题:上面讲的流水线都是按序的——前一条卡住,后面的全得等。设想 lw r1, 0(r2) 没命中 Cache,要等好几拍才拿到数据;紧跟着的 add r3, r1, r4 要用 r1,只能等,这没办法。可再往后的 sub r5, r6, r7 跟 r1 毫无关系,也被堵在后面白等——功能部件明明空着。能不能让它先做?

打个比方银行柜台按号码叫人。按序流水线是:3 号客户材料没带齐、去复印了,4 号、5 号只能干站着。动态流水线是:先给材料齐全的 4 号、5 号办理(乱序执行),但入账(交易记录写进总账)仍按 3、4、5 的号码顺序来(按序提交)——万一 3 号最后办不成(出了异常),4 号、5 号的记录还没入账,可以作废重办,总账不会乱。哪里不像:银行作废的是一张单据,CPU 作废的是还没写进寄存器的计算结果,代价只是“白算了”。

教材说法(袁春风、王道口径):

  • 静态调度:硬件按程序顺序发射、执行,遇到相关就阻塞;能做的优化(调整指令顺序、插 nop)都由编译器在编译时完成。前面的五段流水线、VLIW 都属于这一类。
  • 动态调度(动态流水线调度):由硬件在运行时检查指令之间的相关性,按序取指、译码,然后让操作数已经准备好的指令先执行——这叫乱序执行(out-of-order execution);执行完的结果先放进重排序缓冲(ROB)里,再按程序顺序提交(写回寄存器或存储器)。
  • 为什么一定要按序提交:保证精确异常——某条指令出了异常(如缺页),它后面的指令即使已经算完,结果也还没提交,可以整体作废,中断返回后从这条指令重新执行,程序员看到的效果和按序执行一模一样(第 5 篇“故障”的返回地址就是这么来的)。分支预测猜错时,作废已执行的错误路径指令也靠它。
  • 代价:乱序执行会让读后写 WAR、写后写 WAW 也变成真问题(上面的表格里标了拓展),硬件要用寄存器重命名来消除它们;控制逻辑比按序流水线复杂得多 拓展。
  • 和超标量的关系:超标量说的是“每拍能发射几条”(宽度),动态调度说的是“能不能不按顺序执行”(顺序)。两者常常一起用(2017-17 Ⅲ“超标量能结合动态调度技术提高指令执行并行性”),但超标量不一定乱序,简单的超标量处理器仍按序发射。
① 按序执行(静态):I2 等 I1 的数据,I3、I4 跟着白等 全部执行完:第 8 拍;全部写回 / 提交:第 9 拍 1 2 3 4 5 6 7 8 9 I1 lw r1, 0(r2) 执行 等内存 等内存 等内存 等内存 提交 I2 add r3, r1, r4 等 等 等 等 执行 提交 I3 sub r5, r6, r7 等 等 等 等 执行 提交 I4 and r8, r5, r9 等 等 等 等 执行 提交 ② 动态流水线:I3、I4 先执行(乱序执行),但仍按 I1→I2→I3→I4 的顺序提交 全部执行完:第 6 拍;全部提交:第 8 拍。I3 第 3 拍就算完,结果先放在重排序缓冲里“等提交” 1 2 3 4 5 6 7 8 9 I1 lw r1, 0(r2) 执行 等内存 等内存 等内存 等内存 提交 I2 add r3, r1, r4 等 等 等 等 执行 提交 I3 sub r5, r6, r7 执行 等提交 等提交 等提交 提交 I4 and r8, r5, r9 执行 等提交 等提交 等提交 提交
图 4-13 按序执行与动态流水线,同一段代码对比。示意模型:每拍最多开始执行 1 条;lw 缺失 Cache 要执行 5 拍,其余指令 1 拍;结果在执行结束的下一拍可用;每拍最多提交 2 条。I3、I4 和 I1、I2 没有数据相关,动态流水线让它们在 I1 等内存时先执行,全部执行完从第 8 拍提前到第 6 拍。数字由 verify/08/p13/a_dyn.py 模拟得到。

跟我做一遍(按图中的模型逐拍看动态流水线):

  1. 第 1 拍:I1(lw)开始执行,要到第 5 拍末才拿到 r1。
  2. 第 2 拍:I2 已译码,但要读 r1 → 不能开始(这是真正的 RAW 相关,谁也救不了)。
  3. 第 3 拍:I3 译码完毕,只读 r6、r7,操作数都就绪 → 越过 I2 先执行,第 3 拍末算出 r5。
  4. 第 4 拍:I4 要读 r5,r5 在第 3 拍末已算出 → 执行。
  5. 第 6 拍:r1 到了,I2 执行。
  6. 提交:I1 第 6 拍提交;I2 第 7 拍;I3 虽然第 3 拍就算完,也只能排在 I2 后面,第 7 拍和 I2 一起提交;I4 第 8 拍提交。执行是乱的,提交是按序的。
真题怎么考
  • 2017-17:超标量“能在一个时钟周期内同时发射多条指令”“能结合动态调度技术提高指令执行并行性”都对;“能缩短流水线功能段的处理时间”错——每段的时间由电路决定,多发射、乱序都不改变它。
  • Ⅰ/Ⅱ/Ⅲ 判断的常见说法:“动态调度由编译器完成”✗(编译器做的是静态调度);“乱序执行的处理器结果也按乱序写回寄存器”✗(按序提交);“乱序执行可以消除所有数据冒险”✗(真正的 RAW 只能等,I2 就是例子);“超标量处理器一定乱序执行”✗。
  • 另一种口径(唐朔飞等教材讲“流水线分类”时用):按“同一时间内各段的连接方式”分,多功能流水线在同一时间只能按一种功能连接的叫静态流水线,不同段可以同时按不同功能工作的叫动态流水线。两种“动态”说的不是一件事;408 真题(2017-17)用的是“动态调度”这个意思。若题目出现“多功能流水线”“连接方式”字样,按后一种口径答。
自测
  1. 动态流水线为什么要“按序提交”,而不是谁先算完谁先写回?
    答案
    为了精确异常和分支猜错后能作废:前面的指令若发生异常或分支预测错误,后面已经算完的指令结果还没提交,可以整体丢弃,机器状态和按序执行完全一样。
  2. 把图中 I3 改成 sub r5, r1, r7(也读 r1),动态流水线还能让 I3 提前执行吗?
    答案
    不能。I3 也要等 I1 的 r1(第 6 拍才可用),它和 I2 一样只能等;I4 又依赖 I3 的 r5,也被拖后。乱序执行只能让不相关的指令提前。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2018-20、2009-18 改
某 5 段指令流水线各段的时间分别为 60ps、90ps、70ps、80ps、50ps,流水段寄存器的延迟为 10ps。不发生阻塞时,执行 20 条指令需要的时间是( )。
  • A. 1800ps
  • B. 2000ps
  • C. 2400ps
  • D. 7000ps
展开解析

C时钟周期 = 最长段 + 段寄存器延迟 = 90 + 10 = 100ps。T = (5 + 20 − 1) × 100 = 2400ps。

变化点:两道真题只问时钟周期,这里接着问总时间。D 是不用流水线、每条指令用 350ps 的结果;B 是 20 × 100,漏了装入流水线的 4 拍。

变式计组由 2013-18 改
某 CPU 主频为 2GHz,采用 5 级指令流水线,每段需 1 个时钟周期。执行 1000 条指令且没有发生阻塞,此时流水线的吞吐率约为( )。
  • A. 0.4 × 109 条/秒
  • B. 1.99 × 109 条/秒
  • C. 2.0 × 109 条/秒
  • D. 2.5 × 109 条/秒
展开解析

BT = (5 + 1000 − 1) × 0.5ns = 502ns,吞吐率 = 1000 ÷ 502ns ≈ 1.99 × 109 条/秒。

变化点:2013-18 只有 100 条指令,装入流水线的 3 拍影响较大。指令越多,吞吐率越接近主频(每拍 1 条),但永远达不到。

变式计组由 2016-19、2019-18 改
在无转发的五段流水线中执行下列指令,不存在数据冒险的指令对是( )。
I1: load R1, 0(R2) // R1 ← M[R2]
I2: add R3, R1, R4 // R3 ← R1 + R4
I3: sub R1, R5, R6 // R1 ← R5 − R6
I4: store R3, 4(R1) // M[R1 + 4] ← R3
  • A. I1 和 I2
  • B. I2 和 I3
  • C. I2 和 I4
  • D. I3 和 I4
展开解析

BI2 读 R1、I3 写 R1,是读后写(WAR)。按序流水线中 I2 早在 ID 段就读完了 R1,不会出错,也不阻塞。其余三对都是写后读:I2 用 I1 取来的 R1,I4 用 I2 算出的 R3,I4 用 I3 算出的 R1 作地址。

变化点:真题的干扰项多是“没有相关”的指令对,这里放了一对读后写。另外,store 的地址寄存器 R1 也是“读”,I3 和 I4 之间是写后读。

变式计组由 2024-19、2010-19 改
在有转发(旁路)电路的 5 段流水线中,下列情况下仍然需要阻塞的是( )。
  • A. add 指令后紧跟一条使用其结果的 sub 指令
  • B. load 指令后紧跟一条使用其结果的 add 指令
  • C. add 指令后隔一条指令再使用其结果
  • D. store 指令后紧跟一条对不同地址的 load 指令
展开解析

Bload 的数据要到 M 段末才取出来,下一条指令的 EX 段已经开始了,转发也来不及,要阻塞 1 拍(load-use 冒险)。运算指令的结果在 EX 段末就有,转发给下一条的 EX 正好赶上。

变化点:2024-19 问“所有数据冒险都能靠转发解决”对不对,答案是不能。这里直接问哪一种不能:load-use。

4.5多处理器、多核与硬件多线程

★ 考过选择题
分类(按指令流和数据流)含义例子
SISD单指令流单数据流传统单核处理器(可以有流水线)
SIMD一条指令同时处理多个数据(数据级并行)向量处理器、GPU;处理数组的 for 循环最有效
MISD多条指令流处理同一个数据流几乎没有实际产品(原因见下面 Flynn 分类细看)
MIMD多条指令流处理多个数据流(线程级并行)多核、多处理器;分共享存储(SMP,单一物理地址空间)和消息传递(多计算机)
  • 多核:一块芯片上多个核,各核真正并行;每个核可以有自己的 Cache,也可以共享;通常共享主存。单线程程序在多核上并不会变快。
  • SMP(共享内存多处理器):所有处理器共享单一物理地址空间,用普通的存取指令就能访问任何内存;访问时间相同叫 UMA,不同叫 NUMA。
  • 硬件多线程:一个核里为每个线程准备一套寄存器和 PC,切线程只要换一套寄存器,开销很小。三种:细粒度(每个时钟周期轮换线程)、粗粒度(遇到 Cache 缺失等长阻塞才换)、同时多线程 SMT(一个周期里同时发射多个线程的指令,Intel 超线程就是它)。单核也能用硬件多线程(2022-22 选 C 为错误项)。
只有一个线程 A 线程 A 等 Cache 时整行空着 细粒度:每个周期换一个线程 A、B 轮流,一次只发一个线程 粗粒度:遇到长阻塞才换 A 缺失 Cache → 换 B 同时多线程 SMT 同一个周期里混着发 A、B 的指令 ↓ 每一行是一个时钟周期,每一格是一个“发射槽”(这个核一个周期最多发射 4 条指令)。蓝 = 线程 A 的指令,绿 = 线程 B 的指令,灰 = 空着浪费。
图 4-14 三种硬件多线程。一个线程常常填不满一个核的所有发射槽(要等数据、等 Cache)。硬件多线程给每个线程准备一套寄存器和 PC,切换几乎不花时间:细粒度每拍轮换,粗粒度只在长时间阻塞时换,SMT 干脆在同一拍里混着发射几个线程的指令,把空格填得最满(Intel 的“超线程”就是 SMT)。所以单核也能用硬件多线程(2022-22)。
细粒度多线程粗粒度多线程同时多线程 SMT
什么时候换线程每个时钟周期轮流换只在遇到长时间阻塞(如 Cache 缺失)时才换不用“换”:同一个周期里就发射多个线程的指令
能掩盖的阻塞长的、短的都能只能掩盖长阻塞长的、短的都能
代价单个线程被拖慢(要隔几拍才轮到它一次)换线程时流水线要清空重新填,有开销需要多发射(超标量)硬件
共同点每个线程有自己的一套通用寄存器和 PC,切换线程不必保存/恢复寄存器;各线程共享功能部件和 Cache
  • 向量处理器(SIMD 的典型):有向量寄存器和向量指令,一条指令对一整组数据(向量)做同样的运算,适合矩阵、图像这类“同一操作重复很多次”的计算。
  • MIMD 的两种组织:多处理器——多个 CPU 共享一个主存(单一物理地址空间),靠读写共享变量通信,要用锁等同步机制(第 7 篇);多计算机——每台机器有自己私有的主存,靠消息传递通信(机群、分布式系统)。
  • 拓展 多核各有私有 Cache 时,同一个主存块可能在几个 Cache 里各有一份副本,一个核改了,其他核的副本就过期了——这就是 Cache 一致性问题,由硬件一致性协议(如 MESI)解决。
融合点:两种“线程”别混
计组:硬件多线程
CPU 硬件里多套寄存器,让一个核交替/同时执行几个线程的指令,目的是把流水线填满(比如一个线程等 Cache 时另一个线程接着用)。
操作系统:线程(第 6 篇)
软件概念:进程里的多个执行流,共享进程的地址空间,是 CPU 调度的单位。操作系统把线程调度到核(或硬件线程)上运行。
硬件线程越多,操作系统能同时“放上去跑”的软件线程就越多;但软件线程的数量不受硬件线程数限制(轮流上 CPU)。

Flynn 分类细看:为什么 MISD 几乎没有,向量处理器快在哪

问题引入:2022-22 问“多核属于 MIMD、向量处理器属于 SIMD”对不对。Flynn 分类只看两件事:同时有几条指令流、同时有几条数据流。把它画成一张 2 × 2 的格子,四类一目了然。

单数据流多数据流 单指令流多指令流 SISD:传统单核 CU PU 数据 一次一条指令处理一个数据(可以有流水线,仍是 SISD) SIMD:向量处理器、GPU CU PUPUPU 数据 1数据 2数据 3 同一条指令广播给所有 PU MISD:几乎没有 CUCU PUPU 同一数据 多条指令流处理同一个数据流,需求少 MIMD:多核、多处理器、机群 CUPU数据 1 CUPU数据 2 每个核有自己的指令流和数据流CU = 控制部件(发指令流),PU = 处理单元(运算)
图 4-15 Flynn 分类四格图。横向看数据流有几条,纵向看指令流有几条。SIMD 只有一个控制部件,把同一条指令“广播”给许多处理单元,各自处理自己的数据——这就是“数据级并行”;MIMD 每个处理器各跑各的程序——“线程级并行”。
类含义典型要点
SISD单指令流单数据流传统单核处理器用了流水线、超标量也还是 SISD(一个指令流)
SIMD一条指令同时处理多个数据向量处理器、GPU、CPU 的向量扩展指令适合“同一操作做很多遍”(数组、矩阵、图像);遇到分支多、数据不规则的程序就没优势
MISD多条指令流处理同一个数据流几乎没有实际产品“同一份数据同时被多条不同指令处理”的需求很少;需要对同一数据流依次做多步处理时,流水线就能满足。有教材把某些容错冗余系统勉强归入此类
MIMD多条指令流处理多个数据流多核、多处理器、机群按存储组织分:共享存储的多处理器(SMP,UMA / NUMA)和消息传递的多计算机

跟我做一遍:向量处理器省在哪(示意,已用 verify/08/p04_calc.py 计算):计算 C[i] = A[i] + B[i],i = 0 ~ 63。标量 RISC 每个元素要 lw、lw、add、sw、改下标、条件转移约 6 条指令,共约 384 条;向量处理器设向量长度为 64,只要“向量取 A、向量取 B、向量加、向量存 C”4 条。省下的是取指、译码的次数和循环控制的开销;加法本身仍做了 64 次(由多个处理单元或向量流水线完成)。

共享存储多处理器:SMP、UMA 与 NUMA

问题引入:2022-22 D“SMP 中所有处理器共享单一物理地址空间”是对的。可同样是共享内存,服务器为什么还要分 UMA、NUMA?操作系统又为什么要尽量把进程的内存分在它运行的那颗 CPU 旁边?

教材说法:多个处理器共享存储时,使用单一物理地址空间,普通的 load / store 就能访问任何位置,靠读写共享变量通信,需要锁等同步机制(第 7 篇)——这样的系统叫共享存储多处理器(SMP)。按访存时间是否相同分为 UMA(一致存储访问:每个处理器访问任何单元的时间都一样)和 NUMA(非一致存储访问:访问本地存储器快、访问其他节点的存储器慢)。与之相对,多计算机(如机群)每台机器有私有的地址空间,靠消息传递通信。

UMA(一致存储访问) CPU 0 CPU 1 CPU 2 共享总线 主存(一份) 任何 CPU 访问任何地址,时间都一样总线成为瓶颈,CPU 不能太多 NUMA(非一致存储访问) 节点 0 CPU 0 本地内存 0 节点 1 CPU 1 本地内存 1 互连 CPU 0 访问本地内存 0:快;访问内存 1:要过互连,慢 仍是单一物理地址空间(普通 load / store 就能访问别的节点的内存) 例:本地 80ns、远程 140ns,本地占 90% → 平均 86ns;只占 50% → 110ns
图 4-16 UMA 与 NUMA。UMA 结构简单,但所有 CPU 挤一条总线,处理器多了就成瓶颈;NUMA 把内存分散到各个节点,能扩展到更多处理器,代价是访存时间“分远近”。所以操作系统做多处理机调度时要讲亲和性:进程尽量留在原来的 CPU 上,内存尽量分在本地节点(第 6 篇)。

图中平均访存时间 = 本地比例 × 80 + 远程比例 × 140:0.9 × 80 + 0.1 × 140 = 86ns;0.5 × 80 + 0.5 × 140 = 110ns(数值为示意,已验算)。

物理核与逻辑核:“8 核 16 线程”是什么意思

问题引入:买电脑时常看到“8 核 16 线程”。任务管理器里显示 16 个 CPU,可芯片上明明只有 8 个核。多出来的 8 个是什么?

大白话:每个物理核是一套完整的运算部件(ALU、流水线、私有 Cache);用了 SMT(Intel 叫超线程)以后,一个物理核里放了两套寄存器和 PC,能同时保存两个线程的现场——操作系统看来就是两个 CPU,叫逻辑核(逻辑处理器、硬件线程)。两个逻辑核共用同一套运算部件,所以性能远到不了两倍,只是把一个线程用不满的发射槽填上(上面硬件多线程图的最后一格)。

物理核(多核)逻辑核(同一物理核上的硬件线程)
独立的寄存器组和 PC有有(每个硬件线程一套)
独立的 ALU、流水线有没有,同一物理核上的逻辑核共用
私有 L1 / L2 Cache每个物理核一份同一物理核上的逻辑核共用
能否真正同时执行能(线程级并行,MIMD)SMT 能在同一周期混着发射,但抢同一套部件
操作系统看到的 CPU 数物理核数 × 每核硬件线程数(8 × 2 = 16)

真题怎么考:2022-22(多核属于 MIMD、向量处理器属于 SIMD、SMP 单一物理地址空间都对;“硬件多线程只能用于多核”错——单核也能用)。判别法:多核 = 多套运算部件;硬件多线程 = 一套运算部件 + 多套寄存器现场。

多核的 Cache 一致性:MESI 的思路 拓展

问题引入:两个核各有私有 Cache,都读了变量 x = 5。现在核 0 把 x 改成 6,只改了自己 Cache 里的副本——核 1 的 Cache 里还是 5,它再读就读到旧值了。这就是 Cache 一致性问题,必须由硬件协议解决。

大白话:MESI 给每个 Cache 行贴一个四选一的标签:M(Modified,我改过了,只有我有,主存里是旧的)、E(Exclusive,只有我有,和主存一致)、S(Shared,大家都有,都和主存一致)、I(Invalid,作废)。核心规矩只有一条:要写,先通知别人把副本作废;别人再读时,持有 M 的那个核负责把新值交出来(并写回主存)。

两个核各有私有 Cache,变量 x 在主存里初值为 5 步骤C0 的 CacheC1 的 Cache主存中的 x ① C0 读 x E 独占,x = 5 I 无效 5 ② C1 读 x S 共享,x = 5 S 共享,x = 5 5 ③ C0 写 x = 6 M 已修改,x = 6 I 被作废 5(旧) ④ C1 再读 x S,x = 6 S,x = 6 6(C0 写回) 关键是 ③:写之前先“广播作废”别人的副本,所以任何时刻最多只有一个核持有可写(M / E)的副本
图 4-17 MESI 走一遍(两个核)。每一步的状态都已用 verify/08/p04_calc.py 的小模拟核对。E 状态的好处:核 0 若在 ① 之后直接写 x,不用通知任何人(反正只有它有),E 直接变 M。

和第 8 篇的关系:单核时 Cache 的“写回 / 全写”解决的是 Cache 与主存之间的一致;MESI 解决的是多个 Cache 之间的一致。408 目前只要求知道“多核私有 Cache 会有一致性问题、由硬件协议解决”,状态转换的细节不作要求。

自测
  1. 一台 4 核、每核支持 2 个硬件线程的处理器,操作系统看到几个 CPU?
    答案
    4 × 2 = 8 个逻辑 CPU;但只有 4 套运算部件。
  2. NUMA 系统中,进程从节点 0 的 CPU 被调度到节点 1 的 CPU 上运行,它的访存会变快还是变慢?
    答案
    一般变慢:它的数据还在节点 0 的本地内存里,现在成了远程访问,Cache 里的内容也要重新加载。所以调度器讲究亲和性,尽量让进程留在原来的 CPU 上。
  3. MESI 中,某行在核 0 是 M 状态,核 1 要读它,会发生什么?
    答案
    核 0 把新值提供出来并写回主存,两个核的该行都变成 S。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2022-22 改
下列关于硬件多线程的叙述中,正确的是( )。
  • A. 细粒度多线程可以在每个时钟周期切换线程
  • B. 硬件多线程技术只能用于多核处理器
  • C. 同时多线程(SMT)在一个时钟周期内只能执行一个线程的指令
  • D. 粗粒度多线程在每条指令执行后都切换线程
展开解析

A细粒度多线程每个时钟周期都可以换一个线程发射指令;粗粒度多线程只在遇到长时间停顿(如 Cache 缺失)时才切换;SMT 在同一个时钟周期里可以发射多个线程的指令。硬件多线程单核上也能用(2022-22)。

变化点:2022-22 考并行处理的分类(SIMD、MIMD、SMP)。这里专考三种硬件多线程的区别。

变式计组由 2022-22 改(考纲补充:UMA / NUMA、MISD、逻辑核)
下列关于并行体系结构的叙述中,错误的是( )。
  • A. NUMA 结构中各处理器使用各自独立的物理地址空间,只能靠消息传递通信
  • B. MISD 结构几乎没有实际产品
  • C. 一个物理核采用同时多线程(SMT)后,操作系统可看到多个逻辑处理器
  • D. 多核处理器各核的私有 Cache 之间需要由硬件协议维持一致性
展开解析

ANUMA 仍是共享存储多处理器:所有处理器共享单一物理地址空间,用普通 load / store 就能访问其他节点的内存,只是远程访问更慢。“各自独立的地址空间、靠消息传递”说的是多计算机(机群)。B、C、D 都对。

考点:UMA 与 NUMA、Flynn 分类、物理核与逻辑核、MESI。

第 4 篇自测
  1. 单总线结构中,ALU 的两个输入为什么不能都直接接总线?
    答案
    单总线每个时刻只能传一个数据,两个操作数不能同时从总线送进 ALU,所以一个输入端必须接暂存器(A、Y、T),先存一个操作数;输出也常要暂存器(Z、AC),因为结果出来时总线可能被占用。
  2. 一条 4 段流水线,各段 60、100、80、50ns,段间寄存器延迟 10ns,执行 20 条指令(无阻塞)要多少时间?
    答案
    时钟周期 = 100 + 10 = 110ns;周期数 = 4 + 19 = 23;时间 = 2530 ns。
  3. 微程序控制器有 6 个互斥类微命令,分别含 3、7、2、15、1、4 个,采用字段直接编码,操作控制字段至少多少位?
    答案
    ⌈log24⌉ + ⌈log28⌉ + ⌈log23⌉ + ⌈log216⌉ + ⌈log22⌉ + ⌈log25⌉ = 2+3+2+4+1+3 = 15。
  4. load r1,0(r2) 后面紧跟 add r3,r1,r4,有转发时会不会阻塞?
    答案
    会阻塞 1 拍:load 的数据在 MEM 段结束才得到,而 add 在紧接的 EX 段就要用,转发也来不及(load-use 冒险)。
  5. 写出间址周期的数据流。间址周期结束时,有效地址放在哪里?
    答案
    Ad(IR)→MAR;1→R;M(MAR)→MDR;(MDR)→Ad(IR)。有效地址替换了 IR 中的形式地址(或直接送 MAR),执行周期用它取操作数。
  6. 在图 4-2 的单总线数据通路中,同一个节拍里能否同时有 R0out 和 R1out?能否同时有 MARin 和 Ain?
    答案
    不能同时 R0out、R1out:总线上一时刻只能有一个数据。可以同时 MARin、Ain:同一个数可以同时打入多个寄存器。
  7. 中断周期里保存的断点是哪个寄存器的内容?由谁完成保存?
    答案
    PC 的内容(此时已指向被打断指令的下一条);由硬件在中断周期自动完成(中断隐指令),不是由中断服务程序保存。
  8. 一条 4 段流水线执行 20 条指令、无阻塞,效率是多少?指令条数趋于无穷时呢?
    答案
    E = 20 × 4 / (4 × (4 + 20 − 1)) = 80/92 ≈ 87%;n → ∞ 时 E → 1。
  9. 水平型微指令和垂直型微指令,哪种执行速度快?哪种微指令字长短?
    答案
    水平型并行能力强、执行快,但字长长;垂直型字长短、微程序长、执行慢。
  10. 超标量和超长指令字(VLIW)都能一个周期执行多条指令,区别在哪?
    答案
    超标量由硬件在运行时动态决定哪些指令一起发射;VLIW 由编译器在编译时静态地把可并行的操作拼成一条长指令。

4.✎本篇真题闯关(29 题)

本篇各小节挂的真题全部在这里,按小节排好。可以展开后一道道做,也可以用弹窗模式连续做。做题记录(对/错)只保存在你这台电脑的浏览器里,小节标题下的题号会随之变绿或变红。

第 5 篇 · 融合 · 计组 5.5 + 7.3.2(中断机制) × 操作系统 1.3(运行环境)

融合一:中断、异常与系统调用 —— CPU 和操作系统握手的地方

操作系统也是一段程序,它凭什么能“管”别的程序?答案是硬件给它留了一扇只有它能开的门:用户态/内核态,以及进门的唯一途径——中断和异常。计组讲这扇门的硬件怎么做(中断响应、隐指令、屏蔽字),操作系统讲进门以后做什么(中断处理程序、系统调用)。这两部分在 408 里是同一个考点的两面,本篇一起讲。

对应:王道计组 5.5、7.3(中断系统);王道操作系统 1.3(运行环境)难度:★★★本机题库计数:计组“中断 I/O”10 题、“异常和中断”9 题;操作系统“用户态和内核态”9 题、“系统调用”7 题

5.1用户态与内核态:CPU 里的一个开关

★★ 常考选择题

如果任何程序都能直接操作磁盘、关掉中断、改别人的内存,一个写错的程序就能让整台机器崩掉。所以 CPU 设计了两种运行模式,用程序状态字 PSW 里的一位来标记:

内核态(管态、核心态)
也叫特权态、系统态、内核模式(kernel mode)。操作系统内核运行在这里。可以执行所有指令,包括特权指令。
用户态(目态)
也叫用户模式(user mode)。应用程序运行在这里。只能执行非特权指令;只能访问自己的地址空间。
例子
特权指令
只能在内核态执行
I/O 指令、关中断/开中断指令、中断返回指令、内存清零、修改 PSW、修改存储保护寄存器(如页表基址寄存器)、设置时钟、切换到用户态的指令
非特权指令算术逻辑运算、数据传送、跳转、压栈、trap/陷入指令(系统调用指令)、设置断点指令
最容易错的一点

trap(陷入/访管)指令本身是在用户态执行的,它是非特权指令——正因为用户程序能执行它,才能用它来请求操作系统服务;执行它的结果是 CPU 切换到内核态。2014-25(不能在用户态执行的是“关中断指令”)、2021-23(只能在内核态执行的是“I/O 指令”)、2026-21(不属于特权指令的是“系统调用指令”)都是在考这个。

模式怎么切换?越权会怎样?

  • 用户态 → 内核态:只有一条路——发生中断或异常(包括执行陷入指令)。CPU 在响应时由硬件自动把 PSW 里的模式位改成内核态,并转到内核里的处理程序(5.2 节图 5-1)。
  • 内核态 → 用户态:操作系统执行一条特权指令(典型是“中断返回”,它把先前保存的 PSW 恢复回去,PSW 里的模式位自然回到用户态)。
  • 用户程序硬要执行特权指令:CPU 译码时发现“在用户态执行特权指令”,产生一个异常(非法指令 / 保护违例),转入内核;操作系统一般会终止这个进程。所以用户程序根本没法直接关中断、直接做 I/O。
运行在程序
用户态应用程序;命令解释程序(shell);编译程序、链接程序;C 库函数(如 printf 里格式化字符串的那部分);trap 指令本身
内核态中断处理程序(时钟中断、键盘中断……);异常处理程序(缺页处理、除 0 处理);进程调度程序、进程切换;系统调用服务例程;设备驱动程序;装入程序(要建进程、分配内存,2026-23)

记法:凡是要动“全局资源”(CPU 分给谁、内存怎么分、设备怎么用)或要执行特权指令的,都在内核态;只是在自己的地址空间里算来算去的,都在用户态。

2022-27操作系统
下列关于 CPU 模式的叙述中,正确的是( )。
  • A. CPU 处于用户态时只能执行特权指令
  • B. CPU 处于内核态时只能执行特权指令
  • C. CPU 处于用户态时只能执行非特权指令
  • D. CPU 处于内核态时只能执行非特权指令
展开解析

考点5.1 用户态与内核态

C内核态“什么都能执行”(特权指令和非特权指令都行);用户态只能执行非特权指令。

  • A 错用户态恰恰不能执行特权指令;若用户态“只能执行特权指令”,普通程序连加法都做不了。
  • B 错内核态也能执行非特权指令(加法、传送等),不是“只能执行特权指令”。
  • C 对用户态只能执行非特权指令。
  • D 错内核态可以执行特权指令(如关中断、I/O 指令),说它“只能执行非特权指令”正好把内核态的作用说反了。

特权指令 ⊂ 内核态能执行的指令;用户态执行特权指令会引起异常。

2011-24操作系统
下列选项中,在用户态执行的是( )。
  • A. 命令解释程序
  • B. 缺页处理程序
  • C. 进程调度程序
  • D. 时钟中断处理程序
展开解析

考点5.1 用户态与内核态

A判断标准:要不要直接操作硬件、改内核数据结构。要 → 内核态;只是普通应用程序 → 用户态(需要时发系统调用)。

  • A 对命令解释程序(shell)就是一个普通应用程序:读你敲的命令、解析,需要建进程、读文件时再通过系统调用请内核做。
  • B 错缺页处理程序是缺页异常的处理程序,要修改页表、启动磁盘 I/O,必须在内核态执行。
  • C 错进程调度程序要修改 PCB、就绪队列并切换 CPU 现场,是内核的核心功能,在内核态执行。
  • D 错时钟中断处理程序是中断处理程序,进入时 CPU 已由硬件切到内核态,要更新系统时间、检查时间片。

中断 / 异常处理程序、调度程序、设备驱动都在内核态;shell、编译器、链接器、普通应用在用户态。同类:2026-23(编译、链接、命令解释都在用户态,装入程序要建立进程、分配内存,在内核态)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2014-25、2021-23 改
下列指令中,可以在用户态执行的是( )。
Ⅰ. 陷入(trap)指令 Ⅱ. 设置时钟的指令 Ⅲ. 算术运算指令 Ⅳ. 修改页表基址寄存器的指令
  • A. 仅Ⅰ、Ⅲ
  • B. 仅Ⅱ、Ⅳ
  • C. 仅Ⅰ、Ⅱ、Ⅲ
  • D. 仅Ⅲ
展开解析

A陷入指令就是给用户程序进入内核用的,必须能在用户态执行。设置时钟、修改页表基址寄存器会影响整个系统,是特权指令,只能在内核态执行。

变化点:真题问“不能在用户态执行的”。这里反过来问能执行的,陷入指令仍是最容易错的一项:它不是特权指令。

变式操作系统由 2011-24、2026-23 改
下列程序中,在内核态执行的是( )。
  • A. C 语言编译器
  • B. shell 命令解释程序
  • C. 缺页异常处理程序
  • D. 文本编辑器
展开解析

C缺页异常处理程序是操作系统内核的一部分,在内核态执行。编译器、命令解释程序、编辑器都是普通的用户程序。

变化点:2011-24 问“在用户态执行的”(命令解释程序)。命令解释程序虽然属于操作系统,但它运行在用户态,这一点两道题都在考。

5.2进入内核的唯一大门:中断和异常

★ 考过选择题
用户态 用户程序第 i 条指令正在执行 内核态 中断/异常处理程序操作系统内核 ① 外部中断设备、时钟 ② 异常·故障缺页、除 0 ③ 陷入 trap系统调用 ④ 执行“中断返回”等特权指令恢复 PSW → 回到用户态 模式切换由硬件自动完成:响应中断/异常时 PSW 的模式位被置为“内核态”
图 5-1 用户态 → 内核态只有一条路:中断或异常(系统调用也是一种异常:陷入)。内核态 → 用户态则是操作系统执行一条特权指令(中断返回)主动退出。所以“进程切换”“时钟中断处理”这些事只能在内核态发生(2012-23:不可能在用户态发生的是进程切换)。
两门教材的叫法对照

计组教材:中断(外部中断,来自 CPU 外部)和异常(内部异常,由 CPU 执行指令时内部产生)。操作系统教材:外中断(= 中断,狭义的中断)和内中断(= 异常,也叫例外)。“广义的中断”把两者都包括在内。另一对常用词:中断是异步的(何时发生与正在执行哪条指令无关),异常是同步的(由某条指令引起,重复执行这条指令就会重复出现)。“访管指令”“陷入指令”“trap 指令”“自陷指令”说的是同一类东西。

2013-28操作系统
下列选项中,会导致用户进程从用户态切换到内核态的操作是( )。Ⅰ. 整数除以零 Ⅱ. sin() 函数调用 Ⅲ. read 系统调用
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅰ、Ⅲ
  • C. 仅Ⅱ、Ⅲ
  • D. Ⅰ、Ⅱ和Ⅲ
展开解析

考点5.2 中断和异常:进入内核的大门

B用户态 → 内核态只有一条路:中断或异常(含系统调用的陷入指令)。逐个看会不会触发。

  • Ⅰ 对整数除以零触发除法错异常(故障),CPU 转去内核的异常处理程序,进入内核态。
  • Ⅱ 错sin() 是数学库里的普通函数,只做浮点计算,在用户态就算完了,不需要内核服务。
  • Ⅲ 对read 是系统调用,通过陷入(trap)指令进入内核,由内核去读文件。

干扰项从哪来:A 是把 sin() 当成系统调用,又漏了 read;C 是把 sin() 当成系统调用,又以为除以零只是得到一个错误结果、不会进内核;D 是把 sin() 这种库函数也当成要进内核

库函数 ≠ 系统调用:printf、read 的封装最终会发系统调用,sin、strlen 不会。同类:2015-24(“NOT R0”永远不会引起异常;DIV 可能除 0、MOV R0, addr 可能缺页、INT n 是软中断,都可能进内核)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2013-28、2015-24 改
下列指令在执行时,可能导致 CPU 从用户态进入内核态的是( )。
Ⅰ. ADD R0, R1(寄存器相加) Ⅱ. MOV R0, [addr](读主存) Ⅲ. trap 指令 Ⅳ. DIV R0, R1(整数除法)
  • A. 仅Ⅲ
  • B. 仅Ⅱ、Ⅲ
  • C. 仅Ⅱ、Ⅲ、Ⅳ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

C读主存可能缺页;trap 就是用来进入内核的;整数除法可能除以 0。三者都会进入内核。寄存器相加即使溢出,也只是置 OF 标志,不会自动进入内核。

变化点:2015-24 找“不可能”的(NOT R0,只操作寄存器)。这里换成加法:加法溢出也不会引起异常,这一点容易错。

变式操作系统由 2012-23 改
下列事件中,一定在内核态下进行的是( )。
  • A. 进程调用库函数 printf
  • B. 进程执行 trap 指令
  • C. 进程切换
  • D. 外部设备发出中断请求
展开解析

C进程切换由操作系统内核完成,一定在内核态。printf 的库函数部分在用户态执行;trap 指令本身是在用户态执行的,执行后才进入内核;外部中断请求可能在 CPU 处于任何状态时到来。

变化点:2012-23 问“不可能在用户态发生的”(进程切换)。这里换个问法,答案不变,干扰项换成了 trap 指令和 printf。

5.3中断和异常的分类

★★★ 高频选择题综合题
广义的“中断” 异常(内中断)CPU 执行指令时内部发现 · 同步 中断(外中断)来自 CPU 外部 · 与当前指令无关 · 异步 故障 Fault缺页、除 0、溢出非法操作码、越界能修复→重执行本条 自陷 Trap系统调用、断点单步跟踪→ 返回下一条 终止 Abort硬件故障:控制器出错、存储器校验错→ 无法继续 可屏蔽中断 INTRI/O 完成、键盘时钟到时、网络包到达DMA 传送结束 不可屏蔽中断 NMI电源掉电等紧急硬件故障关中断时也响应 异常都不能被屏蔽
图 5-2 分类。判断口诀:跟“正在执行的这条指令”有关 → 异常;跟当前指令无关、从外面来的 → 中断。缺页、除 0 是指令自己引起的(异常);键盘、定时器、网卡、DMA 结束、打印机缺纸是外部设备的事(中断)。
类型例子什么时候检测处理完返回到哪
故障 fault(可恢复)缺页、缺段指令执行过程中回到发生故障的那条指令,重新执行(缺页调入后再访存一次)
故障(无法恢复)整数除以 0、非法操作码、地址越界、存储保护错、用户态执行特权指令指令执行过程中处理程序无法修复,通常终止进程,不再返回
自陷 trap系统调用(陷入指令)、断点指令、单步跟踪、溢出自陷指令(如 INTO,OF=1 时触发)执行这条陷阱指令时回到陷阱指令的下一条
终止 abort指令执行过程中发生了使计算机无法继续的硬件故障:控制器出错、存储器校验错、总线错误指令执行过程中无法继续,终止程序,由异常服务程序做善后(甚至停机)
可屏蔽中断 INTRI/O 设备:键盘、磁盘、网卡、DMA 传送结束、定时器每条指令执行完之后检查回到下一条指令(被打断的那条已执行完)
不可屏蔽中断 NMI掉电、严重的硬件故障每条指令执行完之后紧急处理(如掉电时抢在断电前保存现场),处理完能否继续看故障情况;关中断也挡不住它

注意两个“看起来像”:整数加法溢出本身只是把 OF 置 1,不会自动进内核,要靠程序里的“溢出自陷指令”检查 OF 才会触发自陷(2019-45 第 5 问);而整数除法的除 0、商溢出会由硬件直接触发除法异常(2025-44)。三类内部异常(故障、自陷、终止)都是在指令执行过程中由 CPU 内部检测的;但它们不一定都回到原指令(2015-22 D 错)。

2021-21计组
异常事件在当前指令执行过程中进行检测,中断请求则在当前指令执行后进行检测。下列事件中,相应处理程序执行后,必须回到当前指令重新执行的是( )。
  • A. 系统调用
  • B. 页缺失
  • C. DMA 传送结束
  • D. 打印机缺纸
展开解析

考点5.3 中断和异常的分类

B处理完回到哪里,看事件的类型:故障(fault)回到当前指令重新执行;陷阱(trap,如系统调用)和外部中断回到下一条指令。

  • A 错系统调用是陷阱(自陷):陷入指令本身已执行完,内核服务完回到它的下一条指令。
  • B 对页缺失是故障:这条指令访存没有成功,调入页面后必须重新执行这条指令。
  • C 错DMA 传送结束是外部中断,在当前指令执行完后才响应,处理完回到下一条指令。
  • D 错打印机缺纸是外部设备发来的 I/O 中断,同样在当前指令执行完后才响应,返回下一条指令。

故障(如缺页)→ 处理完重新执行当前指令;陷阱 → 下一条;终止 → 不返回;外部中断 → 下一条。反面题:2019-14(“缺页处理完成后回到下一条指令”错);2015-22(“内部异常处理后都返回到发生异常的指令”错:自陷回下一条,除 0 一般直接终止进程)。

2016-22计组
异常是指令执行过程中在处理器内部发生的特殊事件,中断是来自处理器外部的请求事件。下列关于中断或异常情况的叙述中,错误的是( )。
  • A. “访存时缺页”属于中断
  • B. “整数除以 0”属于异常
  • C. “DMA 传送结束”属于中断
  • D. “存储保护错”属于异常
展开解析

考点5.3 中断与异常的分类

A判断口诀:和正在执行的这条指令有关 → 异常;从 CPU 外部来、与当前指令无关 → 中断。

  • A 错缺页是正在执行的访存指令在地址转换时引起的,属于异常(故障),不是中断——本题选它。
  • B 对整数除以 0 是除法指令自己出错,属于异常。
  • C 对DMA 传送结束由 CPU 外部的 DMA 控制器发出请求,属于中断。
  • D 对存储保护错是访存指令越权引起的,属于异常。

外部中断:键盘、定时器、网卡、DMA 结束、打印机缺纸;异常:缺页、除 0、越界、非法指令、陷入(系统调用、断点)。

CPU 什么时候响应中断:三个条件同时满足

① 有中断请求(设备把中断请求触发器置 1);② CPU 允许中断(开中断,中断允许位 IF = 1),而且这个中断没有被屏蔽字屏蔽;③ 当前指令刚执行完(中断在指令之间响应;异常则是在指令执行过程中检测到的)。几个请求同时来时,由硬件排队电路按响应优先级选一个;不可屏蔽中断(如掉电)不受 IF 和屏蔽字控制。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2021-21、2019-14 改
下列事件的处理程序执行完后,返回到“引起该事件的指令的下一条指令”继续执行的是( )。
Ⅰ. 系统调用 Ⅱ. 缺页 Ⅲ. 时钟中断 Ⅳ. 执行断点指令
  • A. 仅Ⅰ、Ⅲ
  • B. 仅Ⅰ、Ⅳ
  • C. 仅Ⅰ、Ⅲ、Ⅳ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

C系统调用、断点指令都属于自陷,返回陷阱指令的下一条;时钟中断是外部中断,在指令执行完后才响应,返回下一条。只有缺页是故障,要回到原指令重新执行。

变化点:2021-21 问哪个“必须回到当前指令重新执行”(缺页)。这里反过来问哪些回到下一条,要把每一类都判断一遍。

变式计组由 2016-22、2020-20 改
下列事件中,属于内部异常(而不是外部中断)的是( )。
Ⅰ. 整数除以 0 Ⅱ. 打印机缺纸 Ⅲ. 用户态下执行特权指令 Ⅳ. 键盘按键
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅰ、Ⅲ
  • C. 仅Ⅱ、Ⅳ
  • D. 仅Ⅰ、Ⅲ、Ⅳ
展开解析

B内部异常由正在执行的指令引起:除以 0、在用户态执行特权指令都是。打印机缺纸、键盘按键来自外部设备,是外部中断。

变化点:判断标准只有一个:事件是不是当前指令引起的。打印机缺纸虽然是“出错”,但它来自设备,与当前指令无关。

变式计组由 2015-22、2020-18 改
下列关于故障、自陷和终止的叙述中,正确的是( )。
  • A. 三者都在一条指令执行结束后才被检测
  • B. 缺页属于自陷
  • C. 终止发生后,一般不能返回原程序继续执行
  • D. 自陷是由外部设备发出的中断请求
展开解析

C终止由严重的硬件错误引起(如控制器出错、存储器校验错),处理后一般不能继续执行原程序。A 错:内部异常在指令执行过程中检测。B 错:缺页是故障。D 错:自陷由陷阱指令引起,是内部异常(2020-18)。

变化点:把三类异常的“检测时机、典型例子、返回位置”混在一道题里考。

5.4融合一次中断的完整接力:硬件做一半,操作系统做一半

★★★ 高频选择题

这是本篇最重要的图。以“程序正在运行,键盘按了一个键”为例:

执行用户程序第 i 条指令用户态 这条指令执行完,检查:有未屏蔽的中断请求?且处于开中断状态(IF=1)? 否→下一条 中断响应(中断周期)硬件自动完成 = “中断隐指令” ① 关中断(IF ← 0)② 保存断点(PC)和 PSW③ 识别中断源,把服务程序入口地址送 PC;切内核态 是 中断服务程序(操作系统 / 驱动,内核态,软件) ④ 保护现场:保存通用寄存器 ⑤ 保存旧屏蔽字,设置新屏蔽字 ⑥ 开中断(允许更高级的打断) ⑦ 中断服务:从键盘接口读字符 ⑧ 关中断 ⑨ 恢复现场、恢复屏蔽字 ⑩ 开中断 ⑤⑥⑧ 只有“多重中断”才有;单级中断整个服务过程都保持关中断 ⑦ 才是“真正干活” ⑪ 中断返回指令(特权指令)恢复 PC、PSW → 回用户态 回到第 i+1 条继续(缺页等故障则回到第 i 条) 谁做的?蓝 = 硬件自动绿底 = 操作系统程序
图 5-3 中断的“接力”。左边蓝色框是计组讲的中断响应:CPU 在执行完一条指令后,由硬件自动完成①②③,这几步没有对应的机器指令,所以叫“中断隐指令”。右边是操作系统(设备驱动)写的中断服务程序。
“谁保存什么”——必考

硬件(中断隐指令):关中断、保存断点 PC 和 PSW、形成服务程序入口地址。
软件(中断服务程序,属于操作系统):保存通用寄存器(现场)、保存/设置屏蔽字、开中断、服务、恢复、中断返回。
为什么 PC 必须硬件保存?因为一旦 PC 被改成服务程序入口,原来的 PC 就丢了,软件来不及保存;而通用寄存器在服务程序开头还没被动过,可以由软件自己保存。

2012-22计组
响应外部中断的过程中,中断隐指令完成的操作,除保护断点外,还包括( )。Ⅰ. 关中断 Ⅱ. 保存通用寄存器的内容 Ⅲ. 形成中断服务程序入口地址并送 PC
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅰ、Ⅲ
  • C. 仅Ⅱ、Ⅲ
  • D. Ⅰ、Ⅱ、Ⅲ
展开解析

考点5.4 中断隐指令

B中断隐指令是 CPU 硬件在响应中断时自动完成的一串操作:关中断、保存断点(PC、PSW)、识别中断源并把服务程序入口地址送 PC。

  • Ⅰ 对关中断:防止响应过程被别的中断打断。
  • Ⅱ 错保存通用寄存器是中断服务程序(操作系统,软件)在开头做的“保护现场”,不是隐指令。
  • Ⅲ 对形成入口地址送 PC(向量中断:按中断类型号查中断向量表)。

硬件:关中断、保存 PC 和 PSW、转入口;软件:保存通用寄存器、屏蔽字,服务,恢复,开中断,返回(同类 2015-23、2026-22)。

2010-21计组
单级中断系统中,中断服务程序内的执行顺序是( )。Ⅰ. 保护现场 Ⅱ. 开中断 Ⅲ. 关中断 Ⅳ. 保存断点 Ⅴ. 中断事件处理 Ⅵ. 恢复现场 Ⅶ. 中断返回
  • A. Ⅰ→Ⅴ→Ⅵ→Ⅱ→Ⅶ
  • B. Ⅲ→Ⅰ→Ⅴ→Ⅶ
  • C. Ⅲ→Ⅳ→Ⅴ→Ⅵ→Ⅶ
  • D. Ⅳ→Ⅰ→Ⅴ→Ⅵ→Ⅶ
展开解析

考点5.4 中断响应与中断服务程序

A先分清谁做什么:关中断、保存断点是 CPU 响应中断时由硬件(中断隐指令)完成的,不在服务程序里;服务程序(软件)做的是保护现场 → 中断事件处理 → 恢复现场 → 开中断 → 中断返回。单级中断在服务期间一直关着中断,直到返回前才开。

  • A 对Ⅰ保护现场 → Ⅴ处理 → Ⅵ恢复现场 → Ⅱ开中断 → Ⅶ中断返回。
  • B 错以Ⅲ关中断开头(那是隐指令做的),而且漏了恢复现场和开中断。
  • C 错Ⅲ、Ⅳ都是隐指令的工作,而且没有开中断。
  • D 错Ⅳ保存断点是硬件做的,顺序也不完整。

开中断指令要到下一条指令执行完才生效,所以“开中断 → 中断返回”之间不会被打断。

2020-25操作系统
下列与中断相关的操作中,由操作系统完成的是( )。Ⅰ. 保存被中断程序的中断点 Ⅱ. 提供中断服务 Ⅲ. 初始化中断向量表 Ⅳ. 保存中断屏蔽字
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅰ、Ⅱ、Ⅳ
  • C. 仅Ⅲ、Ⅳ
  • D. 仅Ⅱ、Ⅲ、Ⅳ
展开解析

考点5.4 中断处理每一步谁做

D中断处理是“硬件做一半、操作系统做一半”:响应阶段由硬件(中断隐指令)完成,进入服务程序后才是操作系统的事。

  • Ⅰ 错保存断点(PC)由硬件的中断隐指令完成——此时还没进入任何程序,软件来不及保存。
  • Ⅱ 对中断服务程序是操作系统(内核中的处理程序或驱动)提供的。
  • Ⅲ 对中断向量表在操作系统初始化时建立,把各类中断的服务程序入口地址填进去。
  • Ⅳ 对保存中断屏蔽字(以及通用寄存器等现场)由中断服务程序在开头用指令完成,属于操作系统。

干扰项从哪来:A 是把硬件保存断点当成操作系统做的,又漏了Ⅲ和Ⅳ;B 是把硬件保存断点当成操作系统做的,又漏了初始化中断向量表;C 是漏了Ⅱ,没意识到中断服务程序本身就由操作系统提供

硬件做:关中断、保存断点、找入口(中断隐指令);OS 做:保存现场和屏蔽字、执行服务、恢复现场。中断向量表是一个数组(2023-24),在 OS 初始化时建立(2022-24);进程切换时不用改中断向量表基址寄存器(2025-23)。

图 5-3 第 ③ 步“识别中断源、把服务程序入口地址送 PC”具体是怎么做的?现代计算机普遍用向量中断:

键盘 磁盘 定时器 中断控制器判优、屏蔽给出类型号 CPU中断响应时取得类型号 n INTR n 中断向量表(内存里的数组) 0除 0 处理入口 …… n键盘服务入口 …缺页处理入口 …系统调用入口 按 n 查表 取出入口地址 → 送 PC,开始执行服务程序 表由操作系统初始化时建好(2022-24); 用数组是因为按下标 n 一步就能找到(2023-24); 装新设备时要把它的服务例程“注册”进表(2024-23 D)。
图 5-4 向量中断。每类中断/异常有一个编号(类型号、向量号),中断向量表的第 n 项放第 n 类的服务程序入口地址。异常和系统调用也走同一张表(x86 里 int 80H 就是“去查第 80H 项”)。所有进程共用同一个内核,所以进程切换时不用换中断向量表(2025-23)。
中断 vs 子程序调用

子程序调用(call)是程序自己安排的,只保存 PC(返回地址);中断是随机发生的,要保存 PC 和 PSW(PSW 里有标志位和模式位,被打断的程序回来后必须原样继续)。2012-24:中断处理一定会保存而子程序调用不需要保存的是“程序状态字寄存器”。

中断请求怎么“记下”、怎么“挑”、怎么找到入口

  1. 记下请求:每个中断源有一个中断请求触发器,设备要请求就把它置 1;所有触发器合起来叫中断请求寄存器。CPU 在每条指令执行完时采样这些请求信号来发现中断(2017-22 D 对)。
  2. 判优(挑一个):几个请求同时到,按响应优先级选出一个。实现方法:硬件排队(排队电路、优先编码器,快)或软件查询(程序按优先顺序逐个查请求位,先查到谁就先服务谁,慢但灵活)。中断控制器是按优先级排队,不是按请求到达的先后(2018-22 A 错)。
  3. 识别中断源、找到入口:软件查询法——所有中断都先进一个公共程序,由它依次查询是哪个设备,再跳到对应的服务程序;硬件向量法(向量中断)——由硬件直接给出中断类型号,据此查中断向量表得到入口(下图),快,现代计算机都用它。
名词意思
中断向量中断服务程序的入口地址
中断向量地址存放中断向量的那个主存单元的地址(中断向量表中第 n 项的地址),可以理解为“入口地址的地址”;由硬件的向量地址形成部件根据中断源产生。有的机器在向量地址处放的是一条跳转到服务程序的指令
中断向量表所有中断向量按类型号排成的数组,由操作系统初始化时建立(2022-24、2023-24)
响应外部中断响应异常(含陷入)
什么时候一条指令执行完之后指令执行过程中发现就处理
受开关中断、屏蔽字控制吗可屏蔽中断受控制;NMI 不受不受(异常不能屏蔽,也不用等开中断)
类型号从哪来由中断控制器送给 CPU由 CPU 内部根据异常种类确定
相同之处都要保存断点和 PSW、切换到内核态、按类型号查中断向量表转入处理程序

再看两个“谁先拿到”的问题:用户从键盘登录时,首先拿到键入字符的是键盘中断处理程序(2010-32)——字符先由它从键盘控制器读进内核缓冲区,登录程序、命令解释程序是之后才从缓冲区里读到的(第 9 篇 9.6)。

中断判优:硬件排队电路与软件查询

问题引入:上面一句话说“几个请求同时到,按响应优先级选一个”。可电路怎么“选”?2018-22 A 说“中断控制器按请求的先后次序排队”为什么错?把两种判优办法画出来就明白了。

打个比方

链式排队像传话游戏:老师(CPU)只对第一排说“谁有问题?”,第一排有问题就直接举手、不再往后传;没问题才把话传给第二排……坐得越靠前,越先轮到。软件查询像老师拿着点名册一个个问:先问 1 号,再问 2 号……点名册上的顺序就是优先级,想改顺序只要改点名册。不像的地方:电路传话几乎不花时间,点名(执行查询程序)却要一条条执行指令,慢得多。

① 硬件链式排队(菊花链) CPU A有请求 B无请求 C有请求 INTA A 截住 INTA,不再往后传 公共请求线 INTR(任一设备有请求就有效) 离 CPU 越近,优先级越高:A > B > C A、C 同时请求 → 响应 A(与谁先到无关) 优点:快、线少;缺点:优先级焊死在连线顺序里,离 CPU 远的设备可能长期得不到响应 ② 软件查询 进入公共的中断查询程序 A 请求? B 请求? C 请求? 否否 是 → A 的服务 是 → B 的服务 是 → C 的服务 查询顺序 = 优先级:容易修改,但要执行多条指令,慢
图 5-5 两种判优办法。无论哪种,优先级都是事先定好的顺序,与请求先到后到无关(2018-22 A 错)。除了链式排队,硬件判优还常用独立请求线 + 优先编码器:每个设备一根请求线,编码器直接输出优先级最高者的编号,速度最快。
硬件判优(链式排队、优先编码器)软件查询判优
谁决定先响应谁电路的连接顺序 / 编码器查询程序里查询的先后顺序
速度快(不执行指令)慢(要执行查询程序)
改优先级难(要改电路)容易(改程序)
决定的是响应优先级(同时到达时先响应谁);处理优先级(能不能打断)另由屏蔽字决定(5.5)

真题怎么考:2018-22 A(按请求先后排队,错)、2024-21 A(屏蔽字决定的是处理优先级而不是响应优先级)、2011-21(响应优先级是干扰条件,屏蔽字只看处理优先级)。判别法:同时到达比“响应优先级”,正在处理时被打断比“处理优先级”。

中断类型号 → 向量地址 → 中断向量:跟我算一遍

问题引入:三个名字都带“向量”,选择题专挑它们的定义互换。它们其实是一条链上的三环:拿到类型号,算出向量地址,从那里读出中断向量(入口地址),送 PC。

中断类型号 n“第几号中断” 向量地址“入口地址存在哪” 中断向量= 服务程序入口地址 PC 表基址 +n × 表项大小 读主存 送入 硬件的“向量地址形成部件”完成前两步;中断向量表由操作系统初始化时填好
图 5-6 三个“向量”是一条链。向量地址是“地址的地址”:它指向的那个单元里,放的才是服务程序的入口地址。有的机器在向量地址处放的不是入口地址,而是一条“转移到服务程序”的指令,道理相同。

跟我做一遍(已用 verify/08/p05_calc.py 验算):

  1. 例 1:中断向量表从主存 0 号单元开始,按字节编址,每个表项 4 字节(32 位入口地址),小端存放。类型号 n = 21H → 向量地址 = 0 + 4 × 21H = 84H。若 84H ~ 87H 依次存 00H、10H、40H、00H,则中断向量 = 0040 1000H,送 PC 后开始执行服务程序。
  2. 例 2:表基址 1000H,每项 8 字节,n = 12(十进制)→ 向量地址 = 1000H + 8 × 12 = 1000H + 60H = 1060H。
  3. 反过来:表基址 2000H、每项 4 字节,已知向量地址 2040H → n = (2040H − 2000H) ÷ 4 = 40H ÷ 4 = 16。
易错

① 中断向量 ≠ 向量地址:前者是入口地址本身,后者是存放入口地址的单元的地址。② 中断向量表在主存里,由操作系统初始化时建立(2022-24),不是硬件生成的。③ 异常、系统调用也有类型号,同样查这张表(2023-24:用数组存放,按下标直接找)。

中断处理 10 步:每一步谁做、为什么是它做

问题引入:2012-22、2015-23、2022-31、2026-22 年年换着问“哪一步由硬件做、哪一步由操作系统做”。死记容易混,弄懂每一步为什么必须由它来做,就不会错。

步做什么谁做为什么必须是它
①关中断硬件(隐指令)接下来要保存断点和 PSW,这几步若被别的中断插进来,断点就会被覆盖、乱掉——必须先“锁门”
②保存断点(PC)和 PSW硬件(隐指令)第 ③ 步要把入口地址写进 PC,旧 PC 一被覆盖就找不回来了;而服务程序的任何一条指令要执行,都得先改 PC——软件根本来不及保存。PSW 也一样:响应时模式位要改成内核态、中断允许位被清零,旧 PSW 必须在改之前存好
③识别中断源,入口地址送 PC硬件(向量法)向量中断由硬件给出类型号、查表取入口;若用软件查询法,这一步在公共查询程序里完成
④保护现场:保存通用寄存器(和旧屏蔽字)软件(服务程序)服务程序开头还没动过通用寄存器,软件来得及保存;而且不同的服务程序用到的寄存器不同,只保存自己要用的最省时间——让硬件每次把全部寄存器都存一遍反而慢
⑤设置新屏蔽字,开中断软件只有多重中断才需要;屏蔽字体现的“处理优先级”是操作系统的策略,可以随时改
⑥执行中断服务(真正干活)软件读键盘、搬数据、唤醒等待的进程……这是设备驱动 / 操作系统的工作
⑦关中断软件多重中断时,恢复现场要“一口气”做完,不能被打断
⑧恢复现场和屏蔽字软件与 ④ 对称,出栈顺序与入栈相反
⑨开中断软件开中断指令要到下一条指令执行完才生效,所以 ⑨ 和 ⑩ 之间不会被打断(2010-21)
⑩中断返回软件执行指令
硬件恢复
服务程序执行中断返回指令(特权指令),由硬件把保存的 PC、PSW 恢复回去,回到被打断的程序(和用户态)
① 硬件响应之后 ④ 软件保护现场之后 ⑧ 恢复现场 → ⑩ 中断返回 PSW(旧) PC(断点) 硬件自动压入 PSW(旧) PC(断点) R0 ~ R3 旧屏蔽字 服务程序用 push 指令压入 (已弹出) (已弹出) 软件先 pop 屏蔽字、R3 ~ R0;中断返回指令再恢复 PC、PSW 栈恢复原样,回到被打断的程序 栈向低地址增长,图中上方是栈顶;多重中断时每嵌套一层,栈上就再叠一组这样的内容
图 5-7 一次中断里内核栈的变化。蓝色两格是硬件压的(断点和 PSW),绿色两格是服务程序压的(现场和屏蔽字)。弹出顺序与压入相反,最后两格由中断返回指令一次恢复。

真题怎么考:2012-22(隐指令 = 关中断 + 保存断点 + 形成入口)、2015-23(由操作系统保存的是通用寄存器)、2022-31(系统调用时由操作系统完成的是保存通用寄存器、执行服务程序;保存断点 / PSW、切内核态是硬件做的)、2026-22(必须由硬件完成的是保存断点)、2018-22(“隐指令保护通用寄存器”错)。判别法:涉及 PC、PSW、关中断、取入口 → 硬件;涉及通用寄存器、屏蔽字、服务本身 → 软件。失效的情况:题目若明确说“某机器由硬件自动保存全部寄存器”(有的处理器确实如此),就按题目来。

自测
  1. 为什么中断要保存 PSW,而普通的子程序调用只保存返回地址?
    答案
    中断随时可能发生,被打断的程序事先不知道,回来后必须连标志位、模式位、中断允许位都原样恢复;子程序调用是程序自己安排的,调用前后的状态由程序自己负责(2012-24)。
  2. 中断向量表从 0 开始、每项 4 字节,某中断的向量地址是 0040H,它的类型号是多少?
    答案
    40H ÷ 4 = 10H,即类型号 16。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2012-22、2026-22 改
响应外部中断时,由硬件(中断隐指令)完成的操作是( )。
Ⅰ. 关中断 Ⅱ. 保存断点 Ⅲ. 保存通用寄存器 Ⅳ. 识别中断源,形成中断服务程序入口地址 Ⅴ. 设置新的中断屏蔽字
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅰ、Ⅱ、Ⅳ
  • C. 仅Ⅱ、Ⅲ、Ⅳ
  • D. Ⅰ、Ⅱ、Ⅳ、Ⅴ
展开解析

B中断隐指令做三件事:关中断、保存断点(和 PSW)、引出中断服务程序(识别中断源、送入口地址)。保存通用寄存器(保护现场)和设置屏蔽字都在中断服务程序里用软件完成。

变化点:2012-22 只给了三项。这里多了“设置屏蔽字”:它是多重中断时服务程序做的事,不是硬件做的。

变式计组由 2010-21 改
在多重中断系统中,中断服务程序内的执行顺序是( )。
Ⅰ. 保护现场 Ⅱ. 开中断 Ⅲ. 关中断 Ⅳ. 设置新的屏蔽字 Ⅴ. 中断事件处理 Ⅵ. 恢复现场 Ⅶ. 中断返回
  • A. Ⅰ→Ⅳ→Ⅱ→Ⅴ→Ⅲ→Ⅵ→Ⅱ→Ⅶ
  • B. Ⅲ→Ⅰ→Ⅳ→Ⅴ→Ⅵ→Ⅱ→Ⅶ
  • C. Ⅰ→Ⅴ→Ⅵ→Ⅱ→Ⅶ
  • D. Ⅰ→Ⅱ→Ⅳ→Ⅴ→Ⅵ→Ⅶ
展开解析

A进入服务程序时已经是关中断(硬件关的)。先保护现场、设置新屏蔽字,再开中断,允许更高级的中断嵌套进来;处理完后先关中断,再恢复现场(和屏蔽字),然后开中断、中断返回。

变化点:2010-21 是单级中断,答案是“保护现场 → 中断事件处理 → 恢复现场 → 开中断 → 中断返回”(即本题的 C),处理期间一直关中断。多重中断多了三步:设置屏蔽字、处理前开中断、恢复前关中断。

变式计组由 2015-23、2012-24 改
处理外部中断时,由硬件自动保存的是( )。
  • A. 程序计数器(PC)的内容
  • B. 通用寄存器的内容
  • C. 快表(TLB)中的内容
  • D. Cache 中的内容
展开解析

APC(断点)和 PSW 由中断隐指令自动保存。通用寄存器由中断服务程序(操作系统)按需保存;TLB、Cache 不需要保存。

变化点:2015-23 问“由操作系统保存的”(通用寄存器)。选项完全相同,只把“操作系统”换成“硬件”,答案就换了。

变式计组由 2018-22 改(考纲补充:中断判优)
某系统用链式排队电路判优,设备 A、B、C 在链上离 CPU 由近到远。在同一条指令执行期间,C 先、A 后提出了中断请求(B 无请求),CPU 开中断且两者均未被屏蔽。这条指令执行结束时,CPU 先响应( )。
  • A. C,因为它的请求先到
  • B. A 和 C 同时被响应
  • C. A,因为它在链上离 CPU 最近
  • D. 不确定,取决于两个请求到达的时间差
展开解析

CCPU 在指令结束时才统一检查请求,此时 A、C 都在请求。中断响应信号沿链从 CPU 依次传给 A、B、C,第一个有请求的 A 把它截住,所以先响应 A。判优只看事先定好的顺序,与请求到达先后无关(2018-22 A 错的原因);CPU 一次只能响应一个中断。

考点:中断判优。

变式计组考纲补充(尚无真题):向量地址的计算
某机中断向量表从主存 2000H 单元开始存放,按字节编址,每个表项 4 字节。类型号为 0AH 的中断,其向量地址以及该地址单元中存放的内容分别是( )。
  • A. 2028H,该中断服务程序的入口地址
  • B. 200AH,该中断服务程序的入口地址
  • C. 2028H,该中断的类型号
  • D. 0AH,该中断服务程序的入口地址
展开解析

A向量地址 = 表基址 + n × 表项大小 = 2000H + 0AH × 4 = 2000H + 28H = 2028H;这个单元里存放的是中断向量,即服务程序的入口地址。B 忘了乘表项大小;C 把表项内容说成了类型号;D 把类型号当成了向量地址。

考点:类型号 → 向量地址 → 中断向量(已用 verify/08/p05_calc.py 验算)。

5.5多重中断与中断屏蔽字

★★ 常考选择题

中断服务程序正在跑的时候,又来了一个中断请求,能不能打断它?这由两个“优先级”决定:

响应优先级(硬件排队,固定)
好几个请求同时到达时,CPU 先响应谁。由硬件排队电路决定,不能改。
处理优先级(由屏蔽字决定,可改)
正在处理 A 时,B 来了能不能打断 A。操作系统在每个服务程序里设置屏蔽字来控制。

响应优先级的一般规律(排队电路按这个顺序设计,选择题偶尔考):硬件故障中断 > 软件中断;不可屏蔽中断 > 可屏蔽中断;DMA 请求 > I/O 设备的中断请求;高速设备 > 低速设备;输入设备 > 输出设备;实时设备 > 普通设备。

实现多重中断(中断嵌套)的两个条件:① 中断服务程序里要提前执行开中断指令(否则 CPU 一直处于关中断,谁也打不断它);② 只允许处理优先级更高的中断源打断优先级低的(靠屏蔽字实现)。

写屏蔽字的规则

屏蔽字每一位对应一个中断源,1 = 屏蔽(不许打断),0 = 开放。Li 的服务程序里:处理优先级不比 Li 高的(包括 Li 自己)全置 1,比 Li 高的置 0。

2011-21计组经典
某计算机有五级中断 L4~L0,中断屏蔽字为 M4M3M2M1M0,Mi=1 表示对 Li 级中断进行屏蔽。若中断响应优先级从高到低的顺序是 L0→L1→L2→L3→L4,且要求中断处理优先级从高到低的顺序是 L4→L0→L2→L1→L3,则 L1 的中断处理程序中设置的中断屏蔽字是( )。
  • A. 11110
  • B. 01101
  • C. 00011
  • D. 01010
展开解析

考点5.5 中断屏蔽字

D屏蔽字只看处理优先级:处理某级中断时,自己和处理优先级比它低的置 1(屏蔽),比它高的置 0(放行)。响应优先级在这里是干扰信息。

  1. 处理优先级:L4 > L0 > L2 > L1 > L3。
  2. 处理 L1 时:L4、L0、L2 比它高 → 0;L1 自己和 L3 → 1。
  3. 按 M4 M3 M2 M1 M0 排:0 1 0 1 0 = 01010。
  • A 错11110 是按响应优先级(L0 → L4)设的:屏蔽了 L1 自己和响应优先级更低的 L2、L3、L4。但改变处理顺序正是屏蔽字的作用,应该按处理优先级设。
  • B 错M0 = 1 屏蔽了 L0,可 L0 的处理优先级比 L1 高,应能打断 L1;M1 = 0 又没屏蔽 L1 自己,同级中断会重复嵌套。
  • C 错00011 屏蔽了 L1 和 L0、放行了 L3,正好反了:L0 比 L1 高应放行,L3 比 L1 低应屏蔽。
  • D 对01010:放行 L4、L0、L2,屏蔽 L1 和 L3。

检查方法:处理优先级越高,屏蔽字里的 1 越多。本题五个屏蔽字(M4 … M0):L4 11111、L0 01111、L2 01110、L1 01010、L3 01000。

处理优先级 A > B > C;屏蔽字(A B C):A = 111,B = 011,C = 001 用户程序C 服务B 服务A 服务 C 请求 B 请求:比 C 高,打断 A 请求:比 B 高,打断 此时 C 又请求:被 A 屏蔽,只能等 回 B回 C回程序
图 5-8 中断嵌套。每一层都是一次完整的“隐指令 + 服务程序”,靠栈保存各层的断点和现场,像函数调用一样层层返回。所以多重中断系统中,服务程序执行期间 CPU 是开中断的(2017-22:“中断处理期间 CPU 处于关中断状态”错;只有保存/恢复现场那几步关中断)。

单重中断和多重中断,服务程序各做几步

谁做单重中断多重中断
硬件
中断隐指令
关中断 → 保存断点(PC)和 PSW → 送服务程序入口地址到 PC
软件
服务程序
① 保护现场(通用寄存器)
② 执行设备服务(中断事件处理)
③ 恢复现场
④ 开中断
⑤ 中断返回
① 保护现场和原屏蔽字,设置本级的新屏蔽字
② 开中断(从这里起允许更高级的中断打断)
③ 执行设备服务
④ 关中断(恢复现场时不能被打断)
⑤ 恢复现场和原屏蔽字
⑥ 开中断
⑦ 中断返回

为什么保护现场、恢复现场时要关中断?这几步如果执行到一半被打断,寄存器和屏蔽字只保存(或恢复)了一部分,返回后就乱了。2010-21 的单级中断顺序就是上表左列(Ⅰ→Ⅴ→Ⅵ→Ⅱ→Ⅶ)。

动手画一次:响应顺序 ≠ 处理顺序

例计组多重中断时间线
有 L0、L1、L2 三个中断源,硬件响应优先级 L0 > L1 > L2,用屏蔽字实现的处理优先级 L0 > L2 > L1。每个中断服务程序需要 10μs(保存/恢复现场等开销忽略不计,服务程序一开始就设置好屏蔽字并开中断)。用户程序运行到 t = 5μs 时,L1、L2 同时提出请求;t = 8μs 时 L0 提出请求。(1)写出三个服务程序中的屏蔽字;(2)画出 CPU 的运行轨迹,三个中断各在何时处理完?
展开解析
  1. 屏蔽字(顺序 M0 M1 M2,1 = 屏蔽):处理优先级不高于自己的都屏蔽。L0 最高 → 111;L2 屏蔽自己和更低的 L1 → 011;L1 最低,只屏蔽自己 → 010。
  2. t = 5:L1、L2 同时请求,硬件按响应优先级先响应 L1。L1 的服务程序设置屏蔽字 010 后开中断,此时还在排队的 L2 没有被屏蔽,于是立刻被响应——L1 实际一步都没做就被 L2 打断了。L2 从 t = 5 开始服务。
  3. t = 8:L0 请求。L2 的屏蔽字 011 不屏蔽 L0 → L0 打断 L2(L2 已做 3μs),L0 服务 8 ~ 18,完成。
  4. 返回 L2,继续做剩下的 7μs:18 ~ 25,完成;返回 L1,做 10μs:25 ~ 35,完成;返回用户程序。
L0 服务L2 服务L1 服务用户程序 t=5 先响应 L1,开中断后立刻被 L2 打断 L0 完成于 18 L2 完成于 25 L1 完成于 35 058182535t / μs ↓ L1、L2 请求↓ L0 请求 虽然 L1 比 L2 先被响应,但 L2 先处理完:响应顺序由硬件排队决定,处理顺序由屏蔽字决定

处理完成的顺序是 L0(18μs)→ L2(25μs)→ L1(35μs),正好符合处理优先级 L0 > L2 > L1(已用程序模拟验算)。画这类图的要点:纵轴按处理优先级从高到低排,每来一个请求就问一句“当前正在运行的程序的屏蔽字挡不挡它”,不挡就往上跳,处理完就原路跳回。

2024-21计组
下列关于中断 I/O 方式的叙述中,错误的是( )。
  • A. 中断屏蔽字用于确定中断响应的优先级
  • B. 保存断点和程序状态字在中断响应阶段完成
  • C. 保存通用寄存器和设置新中断屏蔽字由软件实现
  • D. 单重中断方式下,中断处理时 CPU 处于关中断状态
展开解析

考点5.5 中断屏蔽字5.4 中断处理每一步谁做

A选“错误”的说法。关键区分:响应优先级由硬件排队电路固定;处理优先级才由中断屏蔽字改变。

  • A 错“屏蔽字确定响应优先级”错误——本题选它。响应优先级由硬件判优电路决定、不能改;屏蔽字改变的是处理优先级(谁能打断谁)。
  • B 对中断响应阶段由硬件(中断隐指令)关中断、保存断点(PC)和程序状态字、找到服务程序入口,说法正确。
  • C 对进入服务程序后,保存通用寄存器(保护现场)和设置新的屏蔽字都是服务程序用指令做的,即由软件实现,说法正确。
  • D 对单重中断在服务程序执行期间不开中断,不允许被别的中断打断,所以 CPU 处于关中断状态,说法正确。

硬件做:关中断、保存断点和 PSW、找入口;软件做:保存现场和屏蔽字、开中断(多重)、服务、恢复。同类:2020-21(“一旦可屏蔽中断请求有效,CPU 将立即响应”错:要等当前指令结束、开中断且未被屏蔽;不可屏蔽中断 NMI 关中断时也能响应);2021-22(“仅在用户态下才能检测和响应中断”错:内核态执行服务程序时也能响应更高级的中断);2018-22(“中断控制器按请求的先后次序进行优先级排队”错)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2011-21 改
某计算机有 4 级中断 L3 ~ L0,中断屏蔽字为 M3M2M1M0,Mi = 1 表示屏蔽 Li。中断响应优先级从高到低为 L0→L1→L2→L3,要求处理优先级从高到低为 L2→L0→L3→L1。则 L0 的中断处理程序中设置的屏蔽字是( )。
  • A. 1011
  • B. 0100
  • C. 1110
  • D. 1101
展开解析

A处理 L0 时,要屏蔽它自己和处理优先级比它低的 L3、L1,只让 L2 能打断它。所以 M3 = 1、M2 = 0、M1 = 1、M0 = 1,屏蔽字 = 1011。

变化点:2011-21 是 5 级中断、问 L1。方法相同:在处理优先级序列中找到自己,自己和排在后面的都置 1,排在前面的置 0。

变式计组由 2017-22、2020-21、2024-21 改
下列关于中断屏蔽字的叙述中,正确的是( )。
  • A. 中断屏蔽字决定中断的响应优先级
  • B. 通过设置中断屏蔽字可以改变中断的处理优先级
  • C. 不可屏蔽中断也受中断屏蔽字的控制
  • D. CPU 处于关中断状态时,只要屏蔽字允许,仍会响应可屏蔽中断
展开解析

B响应优先级由硬件排队电路决定,改不了;屏蔽字改变的是处理优先级(谁能打断谁)。NMI 不受屏蔽字和关中断的影响。关中断时所有可屏蔽中断都不响应,与屏蔽字无关。

变化点:2024-21 的错误说法是“屏蔽字用于确定响应优先级”,这里给出正确的说法;C、D 对应 2020-21 的考点。

5.6融合系统调用:程序请操作系统办事的正规渠道

★★ 常考选择题

程序要读文件、创建进程、申请内存、收发网络数据——这些都涉及共享资源,必须由操作系统代办。系统调用就是操作系统提供给程序的接口,每个系统调用有一个编号(系统调用号)。按功能分:设备管理、文件管理、进程控制、进程通信、内存管理。

用户态 n = read(fd,buf,100);你的程序 C 库函数 read()① 系统调用号、参数 → 寄存器 ② 执行陷入指令trap / int 80H / syscall 内核态 ③ 硬件:保存 PC、PSW切内核态,转内核入口 ④ 内核:保存通用寄存器按调用号查系统调用表 ⑤ 执行服务例程 sys_read可能要等磁盘 → 进程阻塞 ⑥ 恢复现场,执行中断返回(可能先调度别的进程) ⑦ 回到用户态,read 返回读到的字节数
图 5-9 一次系统调用。顺序:传递参数 → 执行陷入指令 → 执行服务程序 → 返回用户态(2017-24 选 ③→②→④→①)。其中保存 PC/PSW、切换到内核态由硬件在执行陷入指令时完成;保存通用寄存器、执行服务例程由操作系统完成(2022-31 选“仅Ⅱ、Ⅲ”)。
融合点:同一件事,两门课各说一半
计组视角
陷入指令是一种“自陷”异常;CPU 响应它的过程和响应中断一样:关中断、保存断点和 PSW、根据类型号查中断向量表、转入口。返回时执行中断返回指令。
操作系统视角
系统调用是 OS 的“程序接口”;入口程序根据系统调用号查系统调用表,调用对应的内核服务例程;服务期间进程可能阻塞,返回前可能发生进程调度。
判断“是不是系统调用”:要不要操作系统管理的资源?创建进程(fork)、读写文件、申请内存、收发数据要;页面置换、进程调度是 OS 自己主动做的,不是谁“调用”的;生成随机数是普通函数(2021-32 选“创建新进程”)。

系统调用、库函数、普通函数调用:三者对比

普通函数调用(call)库函数(如 printf、fopen)系统调用(如 write、open)
由谁提供程序自己语言的运行库(C 库)操作系统内核
执行在用户态用户态服务例程在内核态
怎么进入call 指令call 指令陷入指令(trap / int / syscall)
保存什么返回地址返回地址断点 和 PSW,切换模式、切换到内核栈
开销小小(除非它内部又发了系统调用)大(模式切换、保存恢复现场)

关系:库函数有的完全在用户态完成(sin、strlen、生成随机数),有的是对系统调用的包装(printf 做完格式化后调用 write,fopen 调用 open)。所以“库函数是 OS 的接口”是错的,OS 给程序的接口是系统调用(2010-23)。

参数怎么交给内核 拓展:① 放在寄存器里(最常用,快;32 位 Linux 用 eax 放系统调用号、ebx/ecx/edx… 放参数,再执行 int 80H);② 参数太多时放在主存的一个参数块里,把块的地址放进寄存器;③ 压到栈里。内核根据系统调用号查系统调用表找到对应的服务例程。因为用户态和内核态用的栈不同,陷入时 CPU 还要从用户栈切换到该进程的内核栈。

2019-25操作系统
下列关于系统调用的叙述中,正确的是( )。Ⅰ. 在执行系统调用服务程序的过程中,CPU 处于内核态 Ⅱ. 操作系统通过提供系统调用避免用户程序直接访问外设 Ⅲ. 不同的操作系统为应用程序提供了统一的系统调用接口 Ⅳ. 系统调用是操作系统内核为应用程序提供服务的接口
  • A. 仅Ⅰ、Ⅳ
  • B. 仅Ⅱ、Ⅲ
  • C. 仅Ⅰ、Ⅱ、Ⅳ
  • D. Ⅰ、Ⅲ、Ⅳ
展开解析

考点5.6 系统调用1.5 操作系统的接口

C系统调用是操作系统内核提供给应用程序的服务接口,服务程序在内核态执行。

  • Ⅰ 对陷入之后 CPU 进入内核态,系统调用服务程序在内核态执行。
  • Ⅱ 对外设由操作系统统一管理,用户程序只能通过系统调用请求 I/O,不能直接执行 I/O 指令(特权指令)。
  • Ⅲ 错不同操作系统的系统调用各不相同(Windows 和 Linux 的系统调用号、名称、参数都不一样);统一的是编程语言的库函数(如 C 标准库),不是系统调用。
  • Ⅳ 对系统调用就是操作系统的程序接口。

系统调用:用户态发起(陷入)→ 内核态执行 → 返回用户态;它因操作系统而异。

2012-28操作系统
若一个用户进程通过 read 系统调用读取一个磁盘文件中的数据,则下列关于此过程的叙述中,正确的是( )。Ⅰ. 若该文件的数据不在内存中,则该进程进入睡眠等待状态 Ⅱ. 请求 read 系统调用会导致 CPU 从用户态切换到核心态 Ⅲ. read 系统调用的参数应包含文件的名称
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅰ、Ⅲ
  • C. 仅Ⅱ、Ⅲ
  • D. Ⅰ、Ⅱ和Ⅲ
展开解析

考点5.6 系统调用10.9 open 与 read

Aread 是通过文件描述符读已打开文件的系统调用。

  • Ⅰ 对数据不在内存要启动磁盘读,进程在 I/O 完成前阻塞(睡眠等待)。
  • Ⅱ 对任何系统调用都要执行陷入指令,CPU 从用户态切换到内核态(核心态)。
  • Ⅲ 错按文件名查找是 open 的工作;open 返回文件描述符 fd,read(fd, buf, n) 的参数是 fd、缓冲区地址和字节数,不含文件名。

open 按名查找、建立打开文件表项;read / write / close 只用文件描述符(2024-29)。

2024-23操作系统
下面关于中断和异常的说法中,错误的是( )。
  • A. 中断或异常发生时,CPU 处于内核态
  • B. 每个系统调用都有对应的内核服务例程
  • C. 中断处理程序开始执行时,CPU 处于内核态
  • D. 系统添加新类型设备时,需注册相应的中断服务例程
展开解析

考点5.2 中断和异常是进入内核的大门5.6 系统调用

A区分“发生时”和“处理时”:中断/异常发生时 CPU 可能正在用户态运行用户程序(也可能在内核态);CPU 响应之后,处理程序才一定在内核态执行。

  • A 错“中断或异常发生时 CPU 处于内核态”错误——本题选它。
  • B 对每个系统调用号都对应内核中的一个服务例程(查系统调用表)。
  • C 对响应中断时硬件已把 CPU 切换到内核态,中断处理程序开始执行时一定处于内核态。
  • D 对新设备有新的中断,驱动程序要向内核注册相应的中断服务例程(填中断向量/处理程序表)。

用户态 → 内核态只有一条路:中断或异常(包括系统调用);内核态 → 用户态靠执行中断返回等特权指令。

2025-44(2)计组大题小问
32 位补码除法指令 idiv 执行过程中会检测并触发除法异常。哪些情况会发生除法异常(给出被除数和除数的机器数)?发生除法异常时,在异常响应过程中 CPU 需要完成哪些操作?
展开解析

① 除数 x = 0000 0000H(被除数任意)→ 除 0;② 被除数 = 8000 0000H(−231)、除数 = FFFF FFFFH(−1)→ 商 231 超出 int 范围,商溢出。
异常响应(硬件):关中断;保存断点(PC)和程序状态字 PSW;切换到内核态;根据异常类型转到异常处理程序入口(查中断向量表,把入口地址送 PC)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2017-24 改
用户程序调用 C 库函数 printf 在屏幕上输出字符串,下列操作的正确顺序是( )。
① 执行设备驱动程序 ② 执行陷入指令 ③ 在用户态调用库函数 printf ④ 执行系统调用服务程序 ⑤ 返回用户态
  • A. ③→②→④→①→⑤
  • B. ②→③→④→①→⑤
  • C. ③→④→②→①→⑤
  • D. ③→②→①→④→⑤
展开解析

Aprintf 是库函数,先在用户态执行(格式化字符串),然后由它执行陷入指令进入内核;系统调用服务程序(设备无关软件)再调用驱动程序操作设备,完成后返回用户态。

变化点:2017-24 只有“传参 → 陷入 → 服务 → 返回”四步。这里前面加了库函数,中间加了驱动程序,要能把它们放进同一条主线。

变式操作系统由 2021-32 改
下列操作中,需要通过系统调用完成的是( )。
Ⅰ. 创建子进程 Ⅱ. 从磁盘文件读数据 Ⅲ. 求字符串长度(strlen) Ⅳ. 进程调度
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅰ、Ⅳ
  • C. 仅Ⅱ、Ⅲ
  • D. 仅Ⅰ、Ⅱ、Ⅳ
展开解析

A创建进程、读文件都要操作系统代劳,必须通过系统调用。strlen 在用户态就能算完。进程调度是操作系统内部在需要时自己做的,不是应用程序通过系统调用请求的。

变化点:2021-32 是单选(创建新进程)。这里改成组合题,并保留两个干扰项:用户态能完成的库函数,以及操作系统内部的工作。

变式操作系统由 2022-31 改
执行系统调用的过程中,由硬件完成的是( )。
Ⅰ. 保存断点和程序状态字 Ⅱ. 保存通用寄存器的内容 Ⅲ. 将 CPU 模式改为内核态 Ⅳ. 执行系统调用服务程序
  • A. 仅Ⅰ、Ⅲ
  • B. 仅Ⅱ、Ⅳ
  • C. 仅Ⅰ、Ⅱ、Ⅲ
  • D. 仅Ⅲ
展开解析

A执行陷入指令时,CPU 硬件自动保存断点和 PSW,并切换到内核态。保存通用寄存器、执行服务程序都是操作系统(软件)做的。

变化点:2022-31 问由操作系统完成的(保存通用寄存器、执行服务程序)。系统调用中硬件和软件的分工与中断响应完全相同。

5.7本篇总表

基础铺垫
外部中断异常(故障)自陷 / 系统调用终止
来源CPU 外部(设备、时钟)当前指令执行中出错程序主动执行陷入指令硬件出错
与当前指令无关(异步)有关(同步)有关(同步,故意的)在指令执行中检测到
何时检测/响应每条指令执行完指令执行过程中陷入指令执行时指令执行过程中
能否屏蔽INTR 可以,NMI 不行不能不能不能
返回到下一条本条(重新执行)或终止下一条不返回
典型键盘、I/O 完成、定时器、DMA 结束缺页、除 0、越界、非法指令read/write/fork、断点控制器出错、存储器校验错
第 5 篇自测
  1. “CPU 在执行中断服务程序时一定处于关中断状态”对吗?
    答案
    不对。单重中断是;多重中断在保护现场、设好屏蔽字后会开中断,允许更高处理优先级的中断嵌套。
  2. 处理优先级 L2 > L0 > L3 > L1,屏蔽字顺序 M3M2M1M0,写出 L0 的屏蔽字。
    答案
    比 L0 高的只有 L2 → M2 = 0;L0 自己和 L3、L1 置 1 → M3M2M1M0 = 1011。
  3. 下列哪些在用户态执行:① 命令解释程序 ② 时钟中断处理程序 ③ trap 指令 ④ 进程调度程序 ⑤ C 库函数 printf 中格式化字符串的那部分代码?
    答案
    ①③⑤。
  4. 缺页异常处理完,为什么要回到“本条”指令而不是下一条?
    答案
    缺页时这条指令需要的数据(或指令本身)还没取到,指令没执行完。页面调入后必须重新执行这条指令,才能真正完成访存。
  5. 用户程序里执行一条关中断指令,会发生什么?
    答案
    关中断是特权指令,用户态执行会被 CPU 检测到并产生异常(非法指令/保护违例),转入内核处理,通常终止该进程;中断并不会被关掉。
  6. “中断向量”和“中断向量地址”有什么区别?
    答案
    中断向量是中断服务程序的入口地址;中断向量地址是存放这个入口地址的主存单元的地址(中断向量表中那一项的地址),即“入口地址的地址”。
  7. 多重中断的服务程序里,为什么在“恢复现场和屏蔽字”之前要先关中断?
    答案
    恢复到一半若被打断,寄存器和屏蔽字只恢复了一部分,返回后程序状态就错了,所以恢复现场期间必须关中断,恢复完再开中断、中断返回。
  8. printf、sin、write 三者中,哪个一定会使 CPU 进入内核态?
    答案
    write(系统调用)。sin 完全在用户态;printf 是库函数,它自身在用户态,只是内部会调用 write 才进入内核。

5.✎本篇真题闯关(40 题)

本篇各小节挂的真题全部在这里,按小节排好。可以展开后一道道做,也可以用弹窗模式连续做。做题记录(对/错)只保存在你这台电脑的浏览器里,小节标题下的题号会随之变绿或变红。

第 6 篇 · 操作系统 2.1~2.2

进程、线程与 CPU 调度

第 3 篇说程序装入内存就成了“进程”。这一篇回答:操作系统怎么记录一个进程(PCB)、进程会处在哪些状态、怎么在进程之间切换(这里要用到第 5 篇的中断和第 4 篇的寄存器)、线程和进程有什么区别,以及轮到谁上 CPU——调度算法。调度算法是操作系统里出题最多的单个考点。

对应:王道操作系统 2.1、2.2难度:★★★本机题库计数:处理机调度算法 15 题、进程状态 9 题、进程与线程 5 题

6.1进程是什么:程序 + 数据 + PCB

★ 考过选择题

回忆第 1 篇的多道程序:内存里同时放着好几个程序,CPU 在它们之间换来换去。换走之前,A 程序算到了哪一条指令、寄存器里是什么、打开了哪些文件……都得有个地方记下来,下次换回来才能接着跑。于是操作系统给每个“正在跑的程序”建一份档案——这份档案连同它的程序和数据,就叫进程。

打个比方

程序是一本菜谱,进程是“照着菜谱正在做的这一道菜”:同一本菜谱可以同时做两份(两个进程),每一份都有自己做到了第几步、锅里现在有什么(各自的状态)。菜谱是静态的,做菜是动态的。

进程是程序的一次执行过程,是系统进行资源分配和调度的独立单位(引入线程后,调度单位变成线程)。一个进程在内存里由三部分组成,其中最关键的是进程控制块 PCB——操作系统就是通过 PCB 感知和管理进程的,PCB 是进程存在的唯一标志。

PCB(在内核区) 进程标识符 PID、父进程进程状态(就绪/运行/阻塞…)优先级、已用 CPU 时间 CPU 现场:PC、PSW、通用寄存器内存信息:页表基址…打开的文件、I/O 设备 程序段要执行的代码(可被多个进程共享) 数据段全局变量、堆、栈…… 进程的四个特征: · 动态性(最基本):有生有死· 并发性:多个进程同时推进· 独立性:独立获得资源、独立调度· 异步性:走走停停,速度不可预知 橙色两项就是“进程切换”时要保存/恢复的东西(6.5 节)
图 6-1 进程映像 = PCB + 程序段 + 数据段。父子进程有各自的 PCB 和虚拟地址空间,可以共享部分资源(如打开的文件),但不共享虚拟地址空间(2020-29 选 B 为错误项)。

系统里可能有成百上千个 PCB,操作系统按状态把它们串成若干条队列,这样调度时只要看就绪队列,某个事件发生时只要看等这个事件的阻塞队列:

运行指针 PCB 3 就绪队列 PCB 1 PCB 7 PCB 5 等磁盘 I/O PCB 2 PCB 9 等信号量 S PCB 4 调度:从就绪队列里挑一个 PCB 阻塞:把运行的 PCB 挂到对应的阻塞队列尾 唤醒:从阻塞队列摘下一个,挂到就绪队列 优先级调度时,就绪队列按优先级有序:插入要找位置 O(n),取队头 O(1)(2025-25)
图 6-2 PCB 的链接组织。“状态转换”在内核里的样子,就是把 PCB 从一条队列摘下、挂到另一条队列上。也可以用索引方式:每种状态一张索引表,表里存 PCB 的地址。

选择题常考:进程的特征、进程与程序的区别

特征意思
动态性进程是程序的一次执行,有创建、运行、暂停、消亡的生命周期。动态性是进程最基本的特征
并发性多个进程能在一段时间内同时推进;引入进程的目的就是让程序能并发执行
独立性进程是能独立运行、独立获得资源、独立接受调度的基本单位(未建立 PCB 的程序不能作为独立单位运行)
异步性各进程按各自独立的、不可预知的速度推进,需要同步机制保证结果正确(第 7 篇)
结构性每个进程都有一个 PCB,由 PCB + 程序段 + 数据段组成
程序进程
本质静态的指令集合,存在外存上动态的执行过程,有生命周期
组成代码(和初始数据)PCB + 程序段 + 数据段
对应关系同一个程序可以同时对应多个进程(同时打开两个记事本就是两个进程);进程是资源分配和调度的单位,程序不是

PCB 里记了什么(选择题会问“下列哪项不在 PCB 中”):

类别内容
进程描述信息进程标识符 PID、用户标识符 UID
进程控制和管理信息进程当前状态、优先级、代码入口地址、程序的外存地址、进入内存的时间、已占用 CPU 的时间、信号量的使用情况
资源分配清单代码段、数据段、堆栈段的指针(内存管理信息,如页表基址),打开的文件(文件描述符),使用的 I/O 设备
处理机相关信息(CPU 现场)通用寄存器、PC、PSW、栈指针等的值——进程被换下时保存在这里,再次运行时从这里恢复(6.5 节)

不在 PCB 里的:进程要处理的数据本身(在数据段)、程序代码本身(在程序段)、文件的内容(在磁盘上,PCB 里只有打开文件的指针)。PCB 由操作系统创建和管理,存放在内核空间,用户程序不能直接访问(2025-46 第 1 问)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2020-29 改
下列关于进程的叙述中,正确的是( )。
  • A. 程序和进程一一对应
  • B. PCB 是进程存在的唯一标志
  • C. 同一个程序只能对应一个进程
  • D. 进程是静态的,程序是动态的
展开解析

B创建进程就是建立 PCB,撤销进程就是回收 PCB,所以 PCB 是进程存在的唯一标志。同一个程序可以同时运行多次,对应多个进程;进程是动态的,程序是静态的。

变化点:2020-29 考父子进程(不共享虚拟地址空间)。这里考进程最基本的概念。

6.2进程的五种状态和转换

★★ 常考选择题
创建态 就绪态 运行态 阻塞态 终止态 分配完资源 ① 被调度(分到 CPU) ② 时间片用完 / 被抢占 ③ 请求 I/O、P 操作等待、申请资源得不到(主动) ④ 等的事件发生了:I/O 完成、被 V 操作唤醒(被动) 运行结束 没有“阻塞 → 运行”,也没有“就绪 → 阻塞” 单 CPU 上同一时刻最多一个进程在运行态
图 6-3 五状态模型。判断题只要问自己:它现在缺的是 CPU 还是别的东西?只缺 CPU → 就绪;缺别的(等 I/O、等资源、等信号量)→ 阻塞。阻塞的进程即使给它 CPU 也没法跑,所以被唤醒后先进就绪队列,再等调度。
2015-25操作系统
下列选项中,会导致进程从执行态变为就绪态的事件是( )。
  • A. 执行 P(wait) 操作
  • B. 申请内存失败
  • C. 启动 I/O 设备
  • D. 被高优先级进程抢占
展开解析

考点6.2 状态转换:原因和触发者

D运行 → 就绪:只是 CPU 被拿走,其他什么都不缺;运行 → 阻塞:要等某种资源或事件。

  • A 错执行 P 操作时如果信号量不够,进程要等别人 V 操作唤醒它,是运行 → 阻塞。
  • B 错申请内存失败,要等别的进程释放内存才能继续,是运行 → 阻塞。
  • C 错启动 I/O 后进程要等 I/O 完成才能继续,是运行 → 阻塞。
  • D 对被高优先级进程抢占,进程只是暂时没有 CPU,条件都具备,回到就绪队列。

就绪只缺 CPU,阻塞在等事件。同类:2017-27(“时间片用完后变为阻塞态”错,应为就绪);2023-27(主动出让 CPU → 就绪;缺页、wait → 阻塞);2014-26(读磁盘完成后 OS 必做的是把进程改为就绪态)。

2022-28操作系统
下列事件或操作中,可能导致进程 P 由执行态变为阻塞态的是( )。Ⅰ. 进程 P 读文件 Ⅱ. 进程 P 的时间片用完 Ⅲ. 进程 P 申请外设 Ⅳ. 进程 P 执行信号量的 wait() 操作
  • A. 仅Ⅰ、Ⅳ
  • B. 仅Ⅱ、Ⅲ
  • C. 仅Ⅲ、Ⅳ
  • D. 仅Ⅰ、Ⅲ、Ⅳ
展开解析

考点6.2 状态转换:原因和触发者

D执行 → 阻塞:进程要等某个事件(I/O 完成、资源、信号量);只是 CPU 被拿走则是执行 → 就绪。

  • Ⅰ 对读文件要启动磁盘 I/O,进程等数据读入期间阻塞。
  • Ⅱ 错时间片用完只是 CPU 被收回,进程什么都不缺,变为就绪态。
  • Ⅲ 对申请的外设可能正被别的进程占用,申请不到就要阻塞等待。
  • Ⅳ 对wait() 时信号量不够(值减 1 后小于 0),进程进入该信号量的等待队列,阻塞。

干扰项从哪来:A 是漏了Ⅲ,以为申请外设总能立即得到;B 是把时间片用完当成阻塞,又漏了读文件和 wait();C 是漏了Ⅰ,以为读文件不需要等待磁盘

题目问“可能导致”:只要存在一种情况会阻塞就算。同类:2018-27、2019-24(能唤醒进程的是“I/O 结束”“某进程退出临界区”,时间片用完不会唤醒别人)。

每一种状态转换:什么原因、谁触发

转换原因性质
创建 → 就绪PCB 建好、资源分配好,插入就绪队列(资源不够时停在创建态)由 OS 完成
就绪 → 运行被进程调度程序选中,分到 CPU调度
运行 → 就绪时间片用完;被更高优先级进程抢占;主动让出 CPU(yield)只是失去 CPU,别的不缺
运行 → 阻塞请求 I/O、读文件、申请资源失败、wait/P 操作使信号量不够、等待某事件主动行为(进程自己请求而阻塞)
阻塞 → 就绪等待的事件发生:I/O 完成(中断处理程序唤醒)、资源被释放、signal/V 操作被动行为(由别的进程或中断处理程序唤醒)
运行 → 终止正常结束、出错(如除 0)、被外界杀死
三个常考结论

① 不存在“阻塞 → 运行”和“就绪 → 阻塞”:阻塞的进程被唤醒只能先进就绪队列;就绪的进程没在运行,不可能发出会让自己阻塞的请求。
② 单 CPU 系统有 n 个进程时:运行态最多 1 个,就绪态最多 n − 1 个(有一个在运行时),阻塞态最多 n 个(全都在等 I/O,CPU 跑闲逛进程);最少都可以是 0 个。
③ 一个状态转换可能引起另一个:一个进程运行 → 阻塞后 CPU 空了,就会引起某个就绪进程就绪 → 运行;但“某进程由阻塞变就绪”不一定引起调度(非抢占式时要等当前进程让出 CPU)。

七状态模型:挂起与中级调度

问题引入:内存只有那么大。如果内存里塞满了阻塞着、一时半会儿用不上 CPU 的进程,新的进程就进不来,CPU 也可能没活干。能不能把这些“暂时没用的”进程先挪到磁盘上,腾出内存?可以——这就是挂起。

打个比方

食堂座位有限:在座位上等菜的人是“阻塞”(人还占着座位);被请到门外长椅上等的人是“挂起”(座位让出来了)。门外的人要吃饭,得先被叫回座位(激活)。不像的地方:挂起的进程连同它的程序和数据(进程映像)都被换到外存,而“等菜的人”身上什么也没被拿走。

教材说法:把进程映像从内存换到外存叫挂起,换回内存叫激活,由中级调度(内存调度)负责,目的是提高内存利用率和系统吞吐量。五状态模型再加两个状态:就绪挂起(在外存,一旦被激活就能就绪)和阻塞挂起(在外存,还在等事件)。

内存 外存(对换区) 创建 就绪 运行 阻塞 终止 就绪挂起 阻塞挂起 调度时间片到 等事件 事件发生 挂起 激活 挂起 激活 事件发生(仍在外存) 运行时被挂起 内存不足创建后直接
图 6-4 七状态模型。绿色粗箭头(挂起 / 激活)都是中级调度干的;橙色箭头说明:阻塞挂起的进程等到了事件,也只是变成就绪挂起,仍在外存,要激活后才能被进程调度选中。
阻塞挂起
原因进程自己在等某个事件(I/O、资源、信号量)操作系统为了腾内存(或用户、父进程要求)把它换出
进程映像在哪内存外存
谁决定进程主动(请求后得不到)中级调度(系统决定)
怎么解除等的事件发生 → 唤醒激活(换入内存)

真题怎么考:408 选择题对挂起考得很少,常见的是“三级调度各做什么”“中级调度的作用”(6.6 节三级调度图)。判别:问“在不在内存”就分清了阻塞和挂起;问“谁负责换入换出”就是中级调度。

自测
  1. 一个阻塞挂起的进程等待的 I/O 完成了,它会立刻变成就绪态吗?
    答案
    不会。它只变为就绪挂起(仍在外存),要被中级调度激活、换回内存后才是就绪态,再由进程调度选中才能运行。
  2. 中级调度的主要目的是什么?
    答案
    在内存紧张时把暂时不能运行的进程换到外存、需要时再换入,提高内存利用率和系统吞吐量。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2022-28、2015-25 改
在抢占式优先级调度的系统中,下列事件中会使正在运行的进程 P 变为就绪态的是( )。
Ⅰ. P 的时间片用完 Ⅱ. P 执行 V 操作,唤醒了一个优先级比 P 高的进程
Ⅲ. P 请求从磁盘读数据 Ⅳ. P 执行 P 操作后信号量的值小于 0
  • A. 仅Ⅰ
  • B. 仅Ⅰ、Ⅱ
  • C. 仅Ⅲ、Ⅳ
  • D. 仅Ⅰ、Ⅱ、Ⅳ
展开解析

B时间片用完、被更高优先级的进程抢占,都是运行 → 就绪。请求 I/O、P 操作得不到资源,都是运行 → 阻塞。

变化点:2022-28 问“变为阻塞态”(读文件、申请外设、wait)。这里反过来问“变为就绪态”,而且抢占不是来自新到达的进程,而是 P 自己唤醒的进程。

变式操作系统由 2014-26、2019-24 改
下列进程状态的转换中,不可能直接发生的是( )。
  • A. 运行 → 阻塞
  • B. 阻塞 → 就绪
  • C. 就绪 → 运行
  • D. 阻塞 → 运行
展开解析

D被唤醒的进程只能先进入就绪队列,等调度程序选中才能运行,所以阻塞 → 运行不可能直接发生。同理,就绪 → 阻塞也不可能:只有正在运行的进程才会因为请求资源而阻塞。

变化点:2014-26 考“I/O 完成后必做的是改为就绪态”,这里把它推广到所有转换。

变式操作系统考纲补充(尚无真题):挂起与中级调度
下列关于进程挂起的叙述中,正确的是( )。
  • A. 被挂起的进程仍然在内存中等待事件
  • B. 阻塞挂起的进程等待的事件发生后,立即变为就绪态
  • C. 进程的挂起和激活由低级调度(进程调度)完成
  • D. 中级调度把暂时不能运行的进程换到外存,以提高内存利用率和系统吞吐量
展开解析

D挂起 = 把进程映像换到外存,由中级调度(内存调度)负责,目的是腾出内存。A 错:挂起的进程在外存(在内存等事件的是阻塞态)。B 错:事件发生后只变成就绪挂起,仍在外存,要激活后才是就绪态。C 错:低级调度只负责从就绪队列选进程分配 CPU。

考点:七状态模型。

6.3进程控制与进程通信

★★ 常考选择题

创建、终止、阻塞、唤醒、切换这些操作都由操作系统内核的原语完成。原语执行时不能被打断(用关中断实现,第 5 篇),否则 PCB 可能改了一半就被切走,数据乱套。

操作要做的事真题
创建申请空白 PCB → 分配资源(内存等)→ 初始化 PCB → 插入就绪队列(不是直接运行)2021-24 选“申请空白 PCB、初始化 PCB”;2010-24 用户登录、启动程序会创建进程,设备分配不会
终止回收资源、撤销 PCB;子进程不一定跟着终止2024-24
阻塞 / 唤醒成对使用:谁等什么事件就阻塞在哪个队列上,事件发生时由别的进程或中断处理程序唤醒它

每个原语的具体步骤(选择题会拿其中一步问“是否必须”):

原语什么时候用步骤
创建用户登录、作业调度把作业调入内存、系统要提供某种服务、程序请求创建子进程(fork)① 分配唯一的 PID,申请空白 PCB(申请不到则创建失败)② 分配运行所需的资源(内存等;资源不足时停在创建态等待,不算失败)③ 初始化 PCB(标识、状态、优先级、CPU 现场)④ 若就绪队列能接纳,插入就绪队列
终止正常结束、异常结束(越界、非法指令、除 0)、外界干预(用户杀死、父进程请求、OS 干预)① 按 PID 找到 PCB,读出状态 ② 若正在运行,立即终止,让出 CPU ③ 若有子孙进程,视系统策略决定是否一并终止(2024-24:“终止子进程”不一定做)④ 把资源还给父进程或系统 ⑤ 把 PCB 从所在队列删除
阻塞进程需要等待某事件① 找到自己的 PCB ② 若在运行,保存现场,状态改为阻塞,停止运行 ③ 把 PCB 插入该事件的阻塞队列 ④ 转进程调度
唤醒它等待的事件发生了① 在该事件的阻塞队列里找到 PCB ② 从阻塞队列移出,状态改为就绪 ③ 插入就绪队列

父子进程(2020-29):子进程由父进程创建(如 fork),有自己的 PCB 和独立的虚拟地址空间(fork 时复制父进程的地址空间内容,但之后各改各的),可以继承、共享父进程打开的文件等资源;父子进程可以并发执行;父子进程访问同一临界资源时同样要互斥(不能同时使用)。

进程间通信(IPC)——进程的地址空间彼此独立,要交换数据必须借助操作系统:

  • 共享存储:OS 把同一块物理内存映射进两个进程的地址空间(第 8 篇的内存映射文件就能做到),读写要自己用同步互斥保护。
  • 消息传递:通过 send/receive 原语;直接发给对方,或放进“信箱”(间接通信)。
  • 管道:一个固定大小的内存缓冲区,像一根水管,一端写一端读;写满时写进程阻塞,读空时读进程阻塞(2014-31 选 C);同一时刻只能单向传输(半双工)。
  • 信号:通知进程发生了某个事件(如 Ctrl+C),进程可以设置处理函数(谁发、何时处理、三种处理方式见下面 信号)。
方式细节(选择题考点)
共享存储分两种:基于数据结构的共享(只能共享 OS 规定好的固定数据结构,低级);基于存储区的共享(划一块内存区,由进程自己决定放什么,高级)。速度最快(读写不经内核),但同步互斥要进程自己用 PV 实现
消息传递以格式化的消息为单位,用 send/receive 原语。直接通信:发送方把消息挂到接收方的消息缓冲队列上;间接通信:发到中间实体信箱,接收方从信箱取(计算机网络里的电子邮件就是这种思路)
管道本质是内核里一个固定大小的缓冲区(Linux 里是一种特殊的“管道文件”);写满时写进程阻塞,读空时读进程阻塞;数据读出后就从管道里消失;普通管道同一时刻只能单向(半双工),要双向通信就建两个管道;读写要互斥,但允许多个读进程或写进程(2014-31 D 说“只能有一个读进程或一个写进程”是错的)
① 共享存储 进程 A地址空间 进程 B地址空间 同一块物理内存双方直接读写 最快;同步互斥要自己做 ② 消息传递 进程 A 进程 B 内核 / 信箱消息(格式化的数据) send receive 直接通信 / 间接通信(信箱) ③ 管道 写进程 读进程 ■■■□□ 内核里的固定大小缓冲区 满了 → 写进程阻塞空了 → 读进程阻塞 半双工;双向要两根管道
图 6-5 三种进程通信方式。共享存储里,操作系统只负责“把同一块物理内存映射给两个进程”,之后的读写是进程自己的事;消息传递和管道则每次都要经过内核。2014-31:管道读、写都可能阻塞;一个管道只能单向传;容量是固定的内存缓冲区,不是“磁盘有多大就有多大”。

第四种通信方式:信号(signal)

问题:你在终端里运行一个死循环程序,按下 Ctrl+C,它就停了。这个程序里没有一行代码在“读键盘”,它是怎么知道你按了 Ctrl+C 的?——是操作系统给它发了一个信号。2025 年起考纲在“进程间通信”里明确写了“共享内存,消息传递,管道,信号”,这一条要单独会。

打个比方信号像宿管阿姨往你宿舍门上贴的便条:“快递到了”“马上熄灯”。便条上只写是哪件事(信号编号),不写详细内容;你不是一贴上就看到,而是下次回宿舍进门时才看到;看到后你可以按自己的习惯处理(去取快递)、按宿舍规定处理(熄灯),或者干脆不理。同一种便条贴了三张,你进门也只当一件事处理。哪里不像:有两种便条(SIGKILL、SIGSTOP)是“强制执行”的,不能不理。

教材说法:信号是操作系统用来通知进程某个事件已经发生的一种机制,属于异步通知——进程事先不知道什么时候会收到。每种信号有一个编号和名字,例如 SIGINT(2,Ctrl+C 中断)、SIGKILL(9,强制终止)、SIGSEGV(11,非法访存)、SIGFPE(8,算术异常如除 0)、SIGCHLD(17,子进程结束)。

环节谁做、做什么
发送三种来源:① 内核检测到硬件异常后发给出错的进程(除 0 → SIGFPE、非法访存 → SIGSEGV);② 终端驱动程序在用户按 Ctrl+C 时发给前台进程(SIGINT);③ 另一个进程通过 kill(pid, 信号) 系统调用发送(kill 只是“发信号”,不一定杀死对方)。不管哪种,都是内核在目标进程的 PCB 里把“待处理信号”位图的对应位置 1。
阻塞(屏蔽)进程可以暂时屏蔽某些信号(PCB 里还有一张“屏蔽位图”,和中断屏蔽字一个思路)。被屏蔽的信号留在待处理位图里,等解除屏蔽再处理。
处理时机进程从内核态返回用户态之前(系统调用返回、中断或异常处理完返回、被调度重新运行时),内核检查“待处理 且 未屏蔽”的信号,有就处理。所以信号不是“立刻”打断进程的。
处理方式三选一:① 执行默认动作(多数是终止进程,有的是忽略、暂停、继续);② 忽略;③ 捕获:执行进程事先用 signal() / sigaction() 注册的信号处理函数(在用户态执行,执行完再回到原来的断点)。SIGKILL 和 SIGSTOP 不能被捕获、屏蔽或忽略,保证管理员总能终止或暂停进程。
不排队位图每种信号只有 1 位:同一种信号在处理前到达多次,只记一次(WSL Ubuntu 实测:屏蔽期间发 3 次 SIGUSR1,解除屏蔽后处理函数只执行 1 次)。所以信号适合通知,不适合计数或传数据。
发送(三种来源) 硬件异常:除 0、非法访存 终端驱动:用户按 Ctrl+C 另一个进程:kill(pid, sig) 内核:在目标进程 PCB 里置位 待处理:0 1 0 0 1 …屏 蔽:0 0 0 0 1 … 每种信号 1 位 → 同种多次只记一次 何时处理 目标进程从内核态返回用户态之前,检查“待处理且未屏蔽”的信号 默认动作多数是终止进程 忽略当没发生 捕获:跳到处理函数用户态执行,完了回断点 SIGKILL、SIGSTOP:不能捕获、屏蔽、忽略 信号只告诉进程“发生了哪件事”(编号),不携带数据。
图 6-6 信号的一生:谁发、记在哪、何时处理、怎么处理。发送方不管是谁,最后都是内核在目标进程 PCB 的位图里置位;处理一律推迟到目标进程“从内核态回到用户态之前”。

跟我做一遍:进程 P 正在用户态跑一个循环,并事先用 signal(SIGINT, handler) 注册了处理函数。用户按下 Ctrl+C,接下来依次发生:

  1. 键盘中断:CPU 响应中断,转到内核的键盘中断处理程序(P 被打断,断点保存好)。
  2. 发送:终端驱动程序识别出 Ctrl+C,内核把前台进程 P 的 PCB 里 SIGINT 那一位置 1。
  3. 检查:中断处理完、准备回到 P 的用户态时,内核发现 P 有“待处理且未屏蔽”的 SIGINT。
  4. 捕获:P 注册过处理函数,内核让 P 先回到用户态执行 handler(不是直接回原断点);handler 结束后经一个系统调用回到内核,再恢复到原断点继续循环。
  5. 若 P 没有注册处理函数:执行 SIGINT 的默认动作——终止 P,这就是“Ctrl+C 能停下程序”的原因。
信号信号量(第 7 篇)中断(第 5 篇)
是什么内核通知进程“发生了某事件”一个整数 + 等待队列,用 P、V 做同步互斥CPU 暂停当前程序去处理外部或内部事件
谁响应目标进程(内核代为检查,处理函数在用户态运行)做 P、V 的进程自己CPU 硬件 + 内核的中断服务程序
何时生效目标进程从内核态返回用户态之前执行 P、V 原语时立即每条指令执行完的中断周期
带数据吗只有信号编号只有计数值中断类型号

记法:信号常被称为“软件层面的中断”——都是“异步事件 + 打断当前流程 + 处理完回断点”,都能屏蔽;区别是中断由 CPU 硬件在指令之间响应,信号由内核在返回用户态之前替进程检查。不要把信号和信号量混为一谈:前者是通知,后者是计数器。

自测
  1. 进程 A 调用 kill(B, SIGTERM) 后,B 是不是立刻停止运行?
    答案
    不一定。kill 只是让内核在 B 的 PCB 里把 SIGTERM 置位;B 要等到下一次从内核态返回用户态时才处理,而且 B 可能捕获或忽略 SIGTERM(它不是 SIGKILL)。
  2. 为什么说信号“不适合用来计数”?
    答案
    待处理信号用位图记录,每种信号只有 1 位:处理之前同一种信号来了多次也只记一次(实测屏蔽期间发 3 次只处理 1 次)。要计数应当用信号量或共享变量。
  3. 下列关于信号的说法,错误的是:A. 信号可以由内核发出,也可以由进程发出;B. 信号处理函数在内核态执行;C. SIGKILL 不能被捕获;D. 信号可以被屏蔽。
    答案
    B 错。用户注册的处理函数是用户程序的一部分,在用户态执行;内核只负责在返回用户态时安排跳转。

fork:调用一次,返回两次 拓展

问题:上面说“程序请求创建子进程(fork)”,可 fork 到底怎么用?子进程从哪里开始执行?为什么同一段 if 能让父子进程走不同的分支?这几个问题自命题院校和面试题经常问,408 一旦考进程创建的代码题也绕不开。

打个比方fork 像复印一份正在做的作业:复印出来的那份(子进程)和原件(父进程)写到的位置完全一样,之后各写各的,互不影响。为了让两份作业知道自己是谁,复印机在原件上盖“你复印出了第 N 号”(子进程的 PID),在复印件上盖“0”。哪里不像:真正的 fork 不会马上把整个内存都复制一遍,而是先共享、谁要写再复制谁那一页(写时复制,见 8.7)。

要点(UNIX / Linux 的 pid_t fork(void)):

  • 调用一次,返回两次:在父进程里返回子进程的 PID(大于 0),在子进程里返回 0;创建失败时只在父进程里返回 −1。
  • 子进程从 fork 返回的地方接着执行,不是从 main 的开头;它得到父进程用户地址空间的副本(变量值相同、之后各改各的)、自己的 PCB 和 PID,并继承打开的文件。
  • fork 之后父、子进程并发执行,谁先运行由调度决定,输出顺序不确定。
int main(void) {
    int x = 1;
    pid_t pid = fork();                 /* 从这里起有两个进程 */
    if (pid == 0) {                     /* 子进程拿到 0 */
        x = x + 1;  printf("child  x=%d\n", x);
    } else {                            /* 父进程拿到子进程的 PID(> 0) */
        x = x * 10; printf("parent x=%d\n", x);
    }
    return 0;
}

输出两行:child x=2 和 parent x=10(顺序不定)。两个进程各有一份 x,子进程把 x 加 1 不影响父进程那份(WSL Ubuntu 22.04 实测,verify/08/p13/a_fork.c)。

for (i = 0; i < 2; i++) { fork(); printf("-\n"); } i = 0 的 fork 之前i = 0:1 个进程 forki = 1:2 个进程各 fork P0 P0 P1(新) P0 P2(新) P1 P3(新) 这一轮 2 个进程各打印 1 行这一轮 4 个进程各打印 1 行 共 4 个进程(含 P0),fork 执行 3 次,打印 2 + 4 = 6 行
图 6-7 循环里的 fork:每一轮进程数翻倍。每个进程在 fork 之后都会继续执行循环的剩余部分,所以第 i 轮(从 0 数)开始时有 2i 个进程,fork 之后变成 2i+1 个。

跟我做一遍:for (i = 0; i < 3; i++) { fork(); printf("-\n"); } 一共有几个进程、打印几行?

  1. 第 0 轮:1 个进程执行 fork → 2 个进程,各打印 1 行,共 2 行。
  2. 第 1 轮:2 个进程各 fork → 4 个,打印 4 行。
  3. 第 2 轮:4 个进程各 fork → 8 个,打印 8 行。
  4. 合计:进程 23 = 8 个(含最初的那个),fork 被执行 1 + 2 + 4 = 7 次(= 新建的子进程数),打印 2 + 4 + 8 = 14 行。一般地,循环 n 次:进程 2n 个、fork 2n − 1 次、循环体内打印 2n+1 − 2 行(Python 模拟 n = 1 ~ 5,WSL 实测 n = 3 时 14 行)。

细节:printf 不带 \n 或输出被重定向到文件时,C 库的缓冲区会随 fork 一起被复制,打印次数会比上面多——这是 C 库的行为,不是 fork 本身的规律,考研不涉及。

自测
  1. fork(); fork(); printf("hi\n"); 打印几次?
    答案
    4 次。第一个 fork 后 2 个进程,第二个 fork 后 4 个进程,每个都执行 printf。
  2. 子进程里 fork() 的返回值是多少?子进程从哪一句开始执行?
    答案
    返回 0;从 fork 调用返回处(给 pid 赋值那一句)接着执行,不是从 main 的开头重新执行。

融合跟着一次 read 走一遍:阻塞原语和唤醒原语

问题引入:上表的四个原语都是一串步骤,背起来很枯燥。把它们放进一个真实场景——进程 P2 调用 read 读磁盘——看 PCB 在几条队列之间怎么“搬家”,就记住了。

设定:单 CPU,非抢占式调度。开始时 P2 在运行,就绪队列里有 P1、P3,磁盘等待队列为空。

① 开始 ② P2 调用 read → 阻塞原语 ③ 磁盘中断 → 唤醒原语 运行P2 就绪P1P3 等磁盘(空) 运行P1 就绪P3 等磁盘P2 运行P1 就绪P3P2 等磁盘(空) 阻塞原语(P2 自己调用):保存 P2 现场到 PCB → 状态改阻塞→ PCB 挂到“等磁盘”队列 → 转调度调度选中 P1 → 切换到 P1 唤醒原语(磁盘中断处理程序调用):从“等磁盘”队列摘下 P2 → 状态改就绪→ 挂到就绪队列尾非抢占:P1 继续运行,P2 排队 P2 执行 read 系统调用,内核发现数据要从磁盘读,启动磁盘后让 P2 等待
图 6-8 一次读盘里 PCB 的“搬家”。阻塞是进程自己(在内核里)调用阻塞原语;唤醒是别人(这里是磁盘中断处理程序)调用唤醒原语——这就是“阻塞是主动的、唤醒是被动的”。被唤醒的 P2 只进入就绪队列,不会直接运行(2014-26)。

若改成抢占式优先级调度且 P2 优先级比 P1 高,唤醒 P2 之后中断返回前会再调度一次,P2 抢占 P1。两种情况下“阻塞 → 就绪”这一步都一样,区别只在之后会不会立刻调度。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2024-24、2021-24 改
操作系统终止(撤销)一个进程时,必须完成的是( )。
Ⅰ. 回收进程占用的资源 Ⅱ. 撤销进程的 PCB Ⅲ. 终止它的所有子进程
  • A. 仅Ⅰ
  • B. 仅Ⅰ、Ⅱ
  • C. 仅Ⅱ、Ⅲ
  • D. Ⅰ、Ⅱ、Ⅲ
展开解析

B终止进程一定要回收它的资源并撤销 PCB。子进程是否一起终止取决于系统的策略,不是必须的(2024-24)。

变化点:2021-24 考创建进程必须做的(申请并初始化 PCB,不是设为执行态),这里考终止进程必须做的。

变式操作系统由 2014-31 改
下列关于进程通信的叙述中,正确的是( )。
  • A. 共享存储方式中,进程读写共享区时不需要同步与互斥
  • B. 消息传递方式中,进程通过 send 和 receive 原语交换数据
  • C. 一个管道可以同时双向传输数据
  • D. 信号机制主要用于在进程之间传输大量数据
展开解析

B消息传递用 send、receive 原语,由操作系统完成消息的传送。A 错:共享区要由进程自己做同步互斥。C 错:管道同一时刻只能单向传输(2014-31),双向要两根管道。D 错:信号只用来通知某个事件发生了。

变化点:2014-31 只考管道,这里把四种通信方式放在一起比较。

6.4线程:更轻的“执行流”

★★ 常考选择题

进程切换代价大(要换 PCB、换地址空间、刷 TLB)。可是一个程序里的多个任务(比如浏览器一边下载一边渲染)本来就共享数据,没必要各开一个进程。于是有了线程:

一句话记住

进程是资源分配的基本单位,线程是 CPU 调度的基本单位。同一进程的线程共享进程的地址空间(代码段、全局变量、堆)、打开的文件;每个线程私有的只有:线程 ID、栈(和栈指针)、寄存器(包括 PC)、线程控制块 TCB。

比较项进程线程
调度引入线程前是调度的基本单位引入线程后,线程是调度的基本单位;同一进程内线程切换不引起进程切换
资源资源分配的基本单位,拥有地址空间、打开的文件等基本不拥有资源,只有必不可少的一点(栈、寄存器、TCB),共享所属进程的资源
并发性进程之间可以并发同一进程的线程之间、不同进程的线程之间都可以并发,并发度更高
系统开销创建、撤销、切换都要分配/回收地址空间等资源,开销大开销小:同进程线程切换只保存少量寄存器,不用换地址空间
独立性各有独立的地址空间,互不干扰同进程的线程共享地址空间,一个线程可以改另一个线程的数据(所以要同步)
通信要借助 OS(6.3 的 IPC)同进程线程直接读写共享变量(加同步互斥)
多处理机单线程进程只能用一个 CPU一个进程的多个内核级线程可以分到多个 CPU 上并行

TCB 里有什么:线程标识符;一组寄存器(PC、状态寄存器、通用寄存器);线程运行状态;优先级;线程专有存储区(保存现场用);栈指针。线程也有就绪、运行、阻塞三种基本状态,转换规则与进程相同。

进程 P 共享:代码段 · 全局变量(数据段)· 堆 · 打开的文件(fd)· 地址空间(同一张页表)线程之间通信直接读写全局变量即可(所以要互斥!第 7 篇) 线程 1自己的栈、PC、寄存器TCB 线程 2自己的栈、PC、寄存器TCB 线程 3自己的栈、PC、寄存器TCB
图 6-9 线程共享什么、私有什么。2011-25:进程 P 的线程不能共享的是“某线程的栈指针”;2024-28:线程 Ta、Tb 能共享进程的地址空间和 fd,不能共享线程 T 的栈;2026-25:同一进程的线程共享进程的堆,不共享栈。
用户级线程(ULT)内核级线程(KLT)
谁管理用户空间的线程库,内核不知道它存在操作系统内核,每个线程有内核里的 TCB
切换不用进内核,快要进内核,慢一些
缺点一个线程阻塞(如系统调用),整个进程都阻塞;多核上不能真正并行开销大
其他可以在不支持线程的 OS 上实现调度由 OS 完成
多对一 一对一 多对多 用户级内核级 切换快;一个阻塞全阻塞;不能多核并行 并发性最好;每个都要内核线程,开销大 折中:n 个用户线程 → m 个内核线程(m ≤ n)
图 6-10 多线程模型。圆圈是用户级线程,方框是内核级线程。操作系统只“看得见”方框:调度、分配 CPU 的都是内核级线程。

多线程模型(用户级线程怎么对应到内核级线程):多对一(多个 ULT 映射到一个 KLT,一个阻塞全阻塞)、一对一(每个 ULT 一个 KLT,并发好、开销大)、多对多(折中)。2019-23 “操作系统为每个用户级线程建立一个线程控制块”错(那只是一对一模型的情况;纯用户级线程内核根本看不见)。2012-31:“不管系统是否支持线程,进程都是资源分配的基本单位”正确。

多对一一对一多对多(n 个用户线程 → m 个内核线程)
一个线程阻塞整个进程阻塞只阻塞它自己只阻塞它自己,其余用户线程可以映射到别的内核线程上
能否多核并行不能(内核只看见一个线程)能能,最多 m 个同时运行
线程管理开销小(切换在用户态完成)大(每创建一个用户线程就要建一个内核线程)适中
一句话切换快、并发差并发好、开销大两者折中
线程选择题的三个判断套路

① “能不能共享”:属于进程的(代码段、全局变量、堆、打开的文件 fd、地址空间)都能共享;属于某个线程的(它的栈、栈指针、寄存器、PC)不能(2011-25、2024-28、2026-25)。
② “内核知不知道”:纯用户级线程内核看不见,所以 OS 不会为它建 TCB(2019-23)、它的切换不需要内核(2012-31 C 错)、一个线程执行阻塞系统调用会使整个进程阻塞、一个进程的多个用户级线程不能在多个 CPU 上并行。内核级线程这四点都反过来。
③ “映射关系方向”:多对一、一对一、多对多都是“用户级线程 → 内核级线程”;“多个内核级线程映射到一个用户级线程”这种说法本身不存在(2026-25 B 错)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2011-25、2024-28 改
同一进程中的各个线程,下列内容中属于每个线程私有的是( )。
Ⅰ. 程序计数器 Ⅱ. 寄存器上下文 Ⅲ. 栈 Ⅳ. 打开的文件 Ⅴ. 全局变量
  • A. 仅Ⅲ
  • B. 仅Ⅰ、Ⅲ
  • C. 仅Ⅰ、Ⅱ、Ⅲ
  • D. 仅Ⅲ、Ⅳ
展开解析

C线程是独立调度的执行流,每个线程要有自己的 PC、寄存器和栈(保存在线程控制块 TCB 和各自的栈中)。代码、全局变量、堆、打开的文件等资源属于进程,所有线程共享。

变化点:2011-25 只问“栈指针不能共享”。这里把线程私有的三样东西一起列出来,反过来考。

变式操作系统由 2019-23、2012-31 改
采用“多对一”模型(多个用户级线程映射到同一个内核级线程)时,下列叙述中正确的是( )。
  • A. 一个线程执行阻塞式系统调用时,整个进程都会被阻塞
  • B. 多个线程可以在多个 CPU 核上并行运行
  • C. 线程之间的切换必须进入内核
  • D. 操作系统为每个用户级线程建立线程控制块
展开解析

A内核只看得见一个内核级线程。其中一个用户级线程阻塞在系统调用上,内核就把这个内核级线程(整个进程)阻塞了。同理,也不能利用多核并行。用户级线程的切换由线程库在用户态完成,不进内核,内核也不为它们建 TCB(2019-23)。

变化点:真题问用户级线程和内核级线程的一般特点,这里落到具体的多线程模型上:多对一模型的优点是切换快,缺点是一个阻塞全体阻塞、不能并行。

6.5融合进程切换:CPU 里的寄存器怎么“换人”

★★ 常考选择题综合题

CPU 只有一套寄存器。从进程 A 换到进程 B,本质就是把 A 在 CPU 里的“现场”存进 A 的 PCB,再把 B 的现场从 B 的 PCB 装回 CPU。这件事只能在内核态做,通常由时钟中断、系统调用等进入内核后触发。

CPU 中的寄存器 PC(下一条指令地址) PSW(标志、模式) 通用寄存器、栈指针 esp/ebp 页表基址寄存器PTBR / PDBR(第 8 篇) 进程 A 的 PCB状态:运行 → 就绪/阻塞保存的现场 ←页表地址放进就绪/阻塞队列 进程 B 的 PCB状态:就绪 → 运行保存的现场 →页表地址从就绪队列选出 ① 保存 ② 恢复 换了页表基址 → 地址空间换了 → 旧的 TLB 内容作废(要刷新,第 8 篇)
图 6-11 进程切换 = 保存旧现场 + 更新 PCB 和队列 + 选新进程 + 更新内存管理结构 + 恢复新现场。2024-25:进程切换时 OS 要更新 PC、栈基址寄存器 ebp、页表基址寄存器(全选 D)。2018-45:PDBR 存的是页目录的物理地址;进程切换时要变,同一进程的线程切换不变(共享地址空间)——这就是线程切换比进程切换快的原因。
调度 ≠ 切换

调度是“决定下一个让谁上 CPU”(选),切换是“真正把 CPU 交给它”(换现场)。调度之后如果选中的还是原进程,就不用切换。另外,切换一定发生在内核态(2012-23),所以时间片轮转需要用到:进程控制块、时钟中断处理程序(计时、判断时间片到没到)、就绪队列,但不需要阻塞队列(2021-25 选 C)。

  1. 保存当前进程 A 的 CPU 上下文:PC、PSW、通用寄存器、栈指针等存入 A 的 PCB。
  2. 更新 A 的 PCB:状态改成就绪(时间片到、被抢占)或阻塞(等事件),把 PCB 挂到相应队列。
  3. 选择另一个进程 B(这一步就是“调度”),更新 B 的 PCB(状态改为运行)。
  4. 更新内存管理的数据结构:换成 B 的页表(改页表基址寄存器 PDBR),必要时刷新 TLB。
  5. 恢复 B 的 CPU 上下文,从 B 上次停下的地方(它保存的 PC)继续执行。
模式切换 ≠ 进程切换

模式切换是 CPU 在用户态和内核态之间转换(如一次系统调用:用户态 → 内核态 → 用户态),还是同一个进程在运行,开销小。进程切换是换了一个进程运行,必然先进入内核态,开销大。所以:进程切换一定伴随模式切换,但模式切换不一定引起进程切换(系统调用返回时若没有更高优先级的进程,就回到原进程)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2024-25、2025-23 改
进程切换时,下列内容中不需要为被换下的进程保存的是( )。
  • A. 通用寄存器
  • B. 程序计数器
  • C. 程序状态字
  • D. Cache 中的内容
展开解析

D上下文是进程在 CPU 上运行的现场:通用寄存器、PC、PSW、页表基址寄存器等,要保存到 PCB(或内核栈)中。Cache 对程序透明,由硬件管理,不需要保存。

变化点:2025-23 问“不需要更新的寄存器”(中断向量表基址,所有进程共用)。这里换成“不需要保存的内容”,答案换成了 Cache。

变式操作系统由 2021-25 改
下列关于进程切换的叙述中,正确的是( )。
  • A. 进程切换可以在用户态完成
  • B. 进程切换要保存旧进程的上下文并恢复新进程的上下文
  • C. 只要发生中断,就一定会进行进程切换
  • D. 采用分页存储管理时,进程切换不需要改变页表基址寄存器
展开解析

B进程切换由内核完成,核心就是“保存旧的、恢复新的”上下文。中断处理完可能返回原进程,不一定切换。每个进程有自己的页表,切换时要改页表基址寄存器(2024-25)。

变化点:2021-25 问时间片轮转要用到哪些数据结构。这里转到切换本身:切换做什么、什么时候不一定切换。

6.6CPU 调度:何时调度、按什么标准

★ 考过选择题
调度层次做什么频率
高级调度(作业调度)从外存的后备作业中挑一个,装入内存、创建进程最低
中级调度(内存调度)把暂时不跑的进程挂起到外存 / 调回内存中
低级调度(进程调度)从就绪队列挑一个进程,把 CPU 给它(本篇重点)最高

什么时候会发生进程调度?当前进程终止、阻塞(等 I/O、wait)、时间片用完、有更高优先级进程就绪(抢占式)、中断处理结束或系统调用返回时(2021-27 四项都可能)。什么时候不能调度?正在处理中断的过程中、在原语(原子操作)中、在操作系统内核程序的临界区中。注意:普通进程访问打印机之类的临界资源时(用户的临界区)可以被调度走(2012-30 选 C 为错误项)。

后备作业外存上排队 就绪 运行 阻塞 就绪挂起(在外存) 阻塞挂起(在外存) 高级调度作业 → 进程 低级调度(进程调度) 时间片到 / 被抢占 等事件 事件发生 中级调度(内存调度):内存紧张时把进程换出到外存(挂起),宽松时再换入 频率:低级 > 中级 > 高级低级调度每秒几十上百次
图 6-12 三级调度在状态图上的位置。408 计算题几乎只考低级调度(进程调度);高级调度只在“作业调度”题里出现(例如 2017-23 的 J1~J4),中级调度只考概念。

抢占式:有更重要的进程来了可以把 CPU 抢过来;非抢占式:当前进程主动放弃(结束或阻塞)才调度。

评价调度算法的指标:CPU 利用率(越高越好)、系统吞吐量(单位时间完成的作业数)、周转时间、等待时间、响应时间(交互式系统最看重)。调度算法影响的主要是“等待时间”——一个进程真正要用的 CPU 时间和 I/O 时间是它自己决定的,算法只能改变它在就绪队列里等多久。

周转时间 = 完成时刻 − 到达时刻  带权周转时间 = 周转时间 / 实际运行时间 等待时间 = 周转时间 − 运行时间(不考虑 I/O 时)  响应时间 = 首次得到 CPU − 到达 平均周转 = 各进程周转时间之和 / 进程数
  • 周转时间是从提交(到达)到完成的全部时间,包括:作业在外存后备队列等待作业调度、进程在就绪队列等待进程调度、在 CPU 上执行、等待 I/O 完成。所以它≥ 运行时间,带权周转时间 ≥ 1,越接近 1 越好(等得越少)。带权周转时间大,说明相对于自己的运行时间,它等得太久了——短作业等 10 秒和长作业等 10 秒,感受完全不同。
  • 响应时间是从用户提交请求到系统首次产生响应的时间,交互式系统最看重它;周转时间是批处理系统最看重的。
  • CPU 利用率 = CPU 有效工作时间 ÷(有效工作时间 + 空闲等待时间);吞吐量 = 单位时间内完成的作业数(短作业多的系统吞吐量大)。
  • 抢占式调度决定“抢不抢”的三条常见原则:优先权原则(高优先级可抢低的)、短进程优先原则(新到的更短可抢)、时间片原则(时间片到就换)。

调度程序本身、闲逛进程和多处理机

调度程序(调度器)由三部分组成

① 排队器:把就绪进程按策略排成队列;② 分派器:从就绪队列取出选中的进程,把 CPU 分给它;③ 上下文切换器:保存旧进程的现场、装入新进程的现场(6.5 节)。

闲逛进程(idle)

没有任何就绪进程时,调度程序就让 CPU 运行闲逛进程。它的优先级最低,只要有进程就绪就立刻让出 CPU;它不需要 CPU 以外的资源,不会被阻塞。有了它,“CPU 总得跑点什么”这件事就统一了。

两种线程下,调度谁

只有用户级线程时,内核看不见线程,调度的单位是进程,同一进程内的线程切换由线程库在用户态完成;有内核级线程时,调度的单位是内核级线程。同一进程内的两个线程之间切换不用换地址空间(页表基址不变),比跨进程的切换快。

多处理机调度 拓展

多个 CPU 时,就绪队列可以公共(所有 CPU 从一个队列取进程,负载均衡好,但要互斥访问队列)或私有(每个 CPU 一个队列,要定期迁移进程做负载均衡)。还要考虑“亲和性”:进程尽量回到上次运行的 CPU,那里的 Cache 里还有它的数据。

多处理机调度:负载均衡与处理器亲和性 拓展

问题引入:一台 8 核电脑,就绪进程该放在一条大队列里让 8 个核轮流来取,还是每个核一条队列?进程这次在核 3 上跑,下次最好还回核 3 吗?

① 公共就绪队列 P1 P2 P3 P4 P5 P6(一条队列,访问要加锁) CPU 0 CPU 1 CPU 2 自动负载均衡;但所有 CPU 抢一把锁,进程常换核,Cache 里的数据用不上 ② 每个 CPU 一个私有队列 P1 P4 P5P7 P8偏长 P2 P6 (空)闲着 CPU 0 CPU 1 CPU 2 负载均衡:迁移进程 没有锁争用、亲和性好;但要定期做负载均衡
图 6-13 多处理机的两种就绪队列组织。现代操作系统多用私有队列 + 定期负载均衡:忙的 CPU 把进程“推”给闲的(推迁移),或闲的 CPU 从忙的那里“拉”一个过来(拉迁移)。
  • 负载均衡:让各个 CPU 的工作量大致相同,不让一个 CPU 忙死、另一个闲着。
  • 处理器亲和性:进程倾向于留在上次运行的 CPU 上——那个 CPU 的 Cache 里还有它的数据,换核就要重新从主存加载(在 NUMA 机器上换节点还会让访存变慢,第 4 篇 4.5)。软亲和:操作系统尽量但不保证;硬亲和:程序可以指定只能在某些 CPU 上运行。
  • 两者是矛盾的:负载均衡要迁移进程,迁移又破坏亲和性,调度器要在两者之间折中。

考纲写的是“调度准则、调度算法、典型机制”,多处理机调度目前只作了解;能说出“公共 / 私有队列、负载均衡、亲和性”三个词及其含义即可。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2012-30、2021-27 改
下列情况下,不能进行进程调度与切换的是( )。
  • A. 进程运行结束时
  • B. 正在处理中断的过程中
  • C. 进程的时间片用完时
  • D. 进程因等待 I/O 而阻塞时
展开解析

B处理中断的过程中、进程处于操作系统内核程序的临界区中、执行原子操作时,都不能进行调度与切换。A、C、D 都是典型的调度时机(2021-27)。

变化点:2012-30 的错误说法是“进程在临界区时不能调度”:普通的临界区是可以调度的,不能调度的是内核程序的临界区。

6.7调度算法:一张表 + 画甘特图

★★★ 高频选择题综合题
算法规则抢占?饥饿?特点
FCFS 先来先服务按到达顺序否否对长作业有利,短作业等得久
SJF 短作业优先选运行时间最短的否(抢占版叫 SRTN 最短剩余时间优先)会(长作业一直等)平均等待/周转时间最短
HRRN 高响应比优先响应比 = (等待时间 + 要求服务时间)/ 要求服务时间,选最大否不会既照顾短作业,长作业等久了响应比也会变大(2009-24、2011-23)
优先级选优先级最高的(注意题目说“数值大”还是“数值小”优先级高)都有会(静态优先级)动态优先级可防饥饿
RR 时间片轮转按 FCFS 排队,每人最多跑一个时间片,没跑完排到队尾是(时钟中断)不会分时系统用;时间片太大退化为 FCFS,太小切换开销大
多级反馈队列多个队列,优先级从高到低、时间片从小到大;新进程进第 1 级;一个时间片没跑完就降一级是可能综合性最好;设计时要考虑队列数、各队列优先级和算法、迁移条件(2020-26 全选)
画甘特图的四步
  1. 在时间轴上标出每个进程的到达时刻。
  2. 从 0 开始,看当前时刻已经到达的进程里该选谁(非抢占:只在当前进程结束时选;抢占:每有新进程到达都要重新比较)。
  3. 画出每一段,写下每个进程的完成时刻。
  4. 列表算周转 = 完成 − 到达,带权 = 周转/运行,最后求平均。

每种算法的考点细节

算法选择题常考的点可用于
FCFS最简单、公平;有利于长作业、CPU 繁忙型作业,不利于短作业、I/O 繁忙型作业(I/O 型进程每次做完 I/O 回来都要排在长作业后面)作业调度、进程调度
SJF / SPF所有作业同时到达时,SJF 的平均等待时间、平均周转时间最短(不同时到达时,抢占式的 SRTN 更短);缺点:长作业可能饥饿;运行时间是用户估计的,不一定准;不考虑紧迫程度作业调度、进程调度
HRRN响应比 = 1 + 等待时间 / 要求服务时间,每次调度时重新计算。等待时间相同 → 服务时间短的先(照顾短作业,像 SJF);服务时间相同 → 等得久的先(像 FCFS);长作业等久了响应比会升高,不会饥饿(2009-24、2011-23)。非抢占作业调度为主
优先级静态优先级:创建时定死(依据进程类型、对资源的要求、用户要求),可能饥饿;动态优先级:随等待时间增加而提高、随占用 CPU 时间增加而降低(2016-46 的设计思路)。设置原则:系统进程 > 用户进程,交互型(前台) > 非交互型(后台),I/O 型 > 计算型(让 I/O 设备尽早忙起来,2013-31);时间片用完时适合降低优先级(2010-26)作业调度、进程调度
RR时间片太大退化为 FCFS,太小切换太频繁、开销大;决定时间片大小的因素:系统响应时间要求、就绪队列中的进程数、系统的处理能力(2017-27 D)。时间片用完 → 就绪态(不是阻塞,2017-27 B 错)。新到达的进程和刚用完时间片的进程同时进队时,一般新到达的排在前面(题目会说明,没说就按这个约定)只用于进程调度(分时系统)
多级反馈队列① 设多个就绪队列,第 1 级优先级最高、时间片最小,往下优先级逐级降低、时间片逐级增大(常为加倍);② 新进程进第 1 级队尾,按 FCFS 等调度,一个时间片没做完就降到下一级队尾,最后一级按 RR;③ 第 1~(i−1) 级全空时才调度第 i 级;④ 第 i 级的进程运行时若有新进程进入更高级队列,新进程抢占 CPU,被抢占的进程放回第 i 级队尾。短作业在前几级就能做完(照顾短作业和交互作业),长作业最终也能做完进程调度
多级队列按进程的类型把就绪队列分成几个(如系统进程、交互进程、批处理进程),每个进程固定在一个队列里,不会在队列之间移动;队列之间按固定优先级(或按比例分时间片)调度,每个队列内部可以用不同的算法(如交互队列用 RR、批处理队列用 FCFS)。和多级反馈队列的区别就在“反馈”:多级反馈队列会根据进程的运行情况把它降到下一级,多级队列不会进程调度

一组进程,五种算法:画在一起对比

例操作系统算法对比(大题基本功)
五个进程的到达时刻和运行时间:A(0,3)、B(2,6)、C(4,4)、D(6,5)、E(8,2),都只用 CPU、不做 I/O。分别用 FCFS、SJF(非抢占)、HRRN、SRTN(抢占式最短剩余时间优先)、RR(时间片 2)调度,画出甘特图,求平均周转时间和平均带权周转时间。
展开解析
到达:A 0 B 2 C 4 D 6 E 8 平均周转 FCFS A B C D E 8.6 SJF A B E C D 7.6 HRRN A B C E D 8.0 SRTN A B C E B D 7.2 RR q=2 A B A C B D C E B D 10.0 0 2 4 6 8 10 12 14 16 18 20 红色虚线是各进程到达的时刻;每格 1 个时间单位
  1. SJF:t = 3 时 A 完成,只有 B 到了 → B 运行到 9(非抢占,C、D、E 到了也不能打断)。t = 9 时 C(4)、D(5)、E(2)都在,按运行时间 E → C → D。
  2. HRRN:t = 9 时算响应比:C = (5 + 4)/4 = 2.25,D = (3 + 5)/5 = 1.6,E = (1 + 2)/2 = 1.5 → 选 C;t = 13 时 D = (7 + 5)/5 = 2.4,E = (5 + 2)/2 = 3.5 → 选 E,最后 D。
  3. SRTN:t = 3 B 开始(剩 6);t = 4 C 到,C 的 4 < B 剩的 5 → 抢占;t = 6 D 到(5),不比 C 剩的 2 短,C 继续到 8;t = 8 就绪的 B(5)、D(5)、E(2)→ E 先;t = 10 B 与 D 剩余时间都是 5,先到的 B 先;B 到 15,D 到 20。
  4. RR(q = 2):t = 2 A 用完一个时间片(剩 1),恰好 B 到达,按约定 B 先入队 → 队列 [B, A];t = 4 C 到达 → [A, C, B];A 运行 4~5 结束;C 5~7(剩 2),其间 D 在 6 到达 → [B, D, C];B 7~9(剩 2),E 在 8 到达 → [D, C, E, B];之后 D 9~11、C 11~13 完成、E 13~15 完成、B 15~17 完成、D 17~20 完成。
周转时间ABCDE平均周转平均带权周转
FCFS37912128.62.56
SJF37111437.61.84
HRRN3791478.02.14
SRTN31341427.21.59
RR(q = 1)4161314710.82.71
RR(q = 2)515914710.02.54
RR(q = 4)3157141110.02.71

结论(全部用程序模拟验算):SRTN 的平均周转最短,SJF 次之;HRRN 介于 FCFS 和 SJF 之间;最短的 E 在 FCFS 下周转 12、带权 6,在 SJF 下只有 3——这就是“FCFS 不利于短作业”。RR 平均周转最长,但每个进程到达后很快就能第一次拿到 CPU(响应快),这正是分时系统要的。每种算法都可以用下面的工具换成这组数据验证。

2023-29操作系统
进程 P1、P2、P3 进入就绪队列的时刻、优先值(越大优先权越高)以及 CPU 执行时间:P1(0ms,1,60ms)、P2(20ms,10,42ms)、P3(30ms,100,13ms)。系统采用基于优先权的抢占式 CPU 调度算法,从 0ms 时刻开始进行调度,则 P1、P2、P3 的平均周转时间为( )。
  • A. 60ms
  • B. 61ms
  • C. 70ms
  • D. 71ms
展开解析

考点6.7 抢占式优先权调度

B优先值越大越优先,抢占式:新到的进程优先值更大就立即抢占。先画甘特图,再用“周转 = 完成 − 到达”。

  1. 0 ~ 20:只有 P1,运行(P1 还剩 40)。
  2. 20:P2 到达,10 > 1,抢占,P2 运行;30:P3 到达,100 > 10,抢占,P3 运行 30 ~ 43 完成(P2 还剩 32)。
  3. 43 ~ 75:P2 运行完;75 ~ 115:P1 运行完。
  4. 周转:P1 = 115 − 0 = 115,P2 = 75 − 20 = 55,P3 = 43 − 30 = 13;平均 = 183 ÷ 3 = 61ms。
  • A 错平均 60 意味着三个周转时间之和是 180;按甘特图逐个核对完成时刻 P3 = 43、P2 = 75、P1 = 115,和是 183,差的 3ms 出在某个完成时刻算早了。
  • B 对(115 + 55 + 13) ÷ 3 = 61ms。
  • C 错平均 70 意味着周转之和 210,比正确的 183 多 27;按甘特图把完成时刻 43、75、115 逐个减去到达时刻 30、20、0,得 13、55、115,凑不出 210。
  • D 错71 = (115 + 55 + 43) ÷ 3,P3 的周转用了它的完成时刻 43,忘了减去它的到达时刻 30。

抢占式调度:每有新进程到达就重新比一次优先级;周转时间一定要减到达时刻。

2022-25操作系统
P0、P1、P2、P3 进入就绪队列的时刻、优先级(值越小优先权越高)及 CPU 执行时间:P0(0,15,100ms)、P1(10,20,60ms)、P2(10,10,20ms)、P3(15,6,10ms)。采用基于优先权的抢占式调度,从 0ms 开始,到 4 个进程都运行结束为止,发生进程调度的总次数为( )。
  • A. 4
  • B. 5
  • C. 6
  • D. 7
展开解析

考点6.7 抢占式优先权调度

C优先级值越小越优先,抢占式:新到达的进程优先级更高就立刻抢占。每次“选一个进程上 CPU”算一次调度。

  1. 0:只有 P0,调度 P0(第 1 次)。
  2. 10:P1(20)、P2(10)到达,P2 比 P0(15)高,抢占,调度 P2(第 2 次)。
  3. 15:P3(6)到达,抢占 P2,调度 P3(第 3 次);25:P3 完成,调度 P2(第 4 次)。
  4. 40:P2 完成,调度 P0(第 5 次);130:P0 完成,调度 P1(第 6 次);190:P1 完成,没有进程了。
  5. 共 6 次。P1 虽然 10 就到了,但优先级 20 最低,一直等到最后。
  • A 错4 次只是数了“每个进程第一次上 CPU”;P2 被 P3 抢占后在 25 重新被调度、P0 在 40 重新被调度,这两次也是调度。
  • B 错5 次漏了一次:通常是漏了 0 时刻的第一次调度,或漏了 P3 完成后在 25 把 P2 重新调度上来的那一次。
  • C 对调度发生在 0、10、15、25、40、130 共 6 次。
  • D 错7 次是把 190 时 P1 结束也算了一次;那时已没有就绪进程可选,不发生调度。

数调度次数:每个“选出进程上 CPU”的时刻都算——开头 1 次、每次抢占 1 次、每次进程完成后(还有就绪进程时)1 次。

2018-24操作系统
非抢占式优先权调度,完成一次进程调度和进程切换的系统开销为 1μs。T 时刻就绪队列中 P1(已等待 30μs,需 CPU 12μs,优先权 10)、P2(15μs,24μs,30)、P3(18μs,36μs,20),优先权值大的先获得 CPU,从 T 时刻开始调度,系统的平均周转时间为( )。
  • A. 54μs
  • B. 73μs
  • C. 74μs
  • D. 75μs
展开解析

考点6.7 调度算法与周转时间

D非抢占、优先权大的先:P2(30)→ P3(20)→ P1(10)。每次上 CPU 前先花 1μs 调度切换;周转时间从各进程到达(T − 已等待时间)算起。

  1. P2:T + 1 开始,T + 25 完成;周转 = 15 + 25 = 40μs。
  2. P3:T + 26 开始,T + 62 完成;周转 = 18 + 62 = 80μs。
  3. P1:T + 63 开始,T + 75 完成;周转 = 30 + 75 = 105μs。
  4. 平均 = (40 + 80 + 105) ÷ 3 = 75μs。
  • A 错54 = (25 + 62 + 75) ÷ 3,是从 T 时刻起算的,忘了周转时间要从进程到达算起,漏了表里的“等待时间”。
  • B 错73 = (39 + 78 + 102) ÷ 3,三次调度切换的 1μs 开销一次都没算。
  • C 错74 = (39 + 79 + 104) ÷ 3,漏了第一次调度的 1μs:从 T 时刻起系统要先做一次调度,P2 才能上 CPU。
  • D 对(40 + 80 + 105) ÷ 3 = 75μs。

周转时间 = 完成时刻 − 到达时刻;“已等待时间”要加进去;调度开销每次上 CPU 前都要算。

2019-27操作系统
二级反馈队列:Q1 用时间片轮转(10ms),Q2 用短进程优先;优先调度 Q1,Q1 空时才调度 Q2;新进程先进 Q1,执行一个时间片未结束就转入 Q2。系统依次创建 P1、P2 后开始调度,P1、P2 需要 CPU 时间分别为 30ms、20ms,则 P1、P2 的平均等待时间为( )。
  • A. 25ms
  • B. 20ms
  • C. 15ms
  • D. 10ms
展开解析

考点6.7 多级反馈队列

C先画甘特图:Q1 时间片轮转 10ms,用完未结束降到 Q2;Q2 短进程优先,只在 Q1 空时调度。等待时间 = 完成时刻 − 到达时刻 − 运行时间。

  1. 0 ~ 10:P1(剩 20,降入 Q2);10 ~ 20:P2(剩 10,降入 Q2)。
  2. Q1 空,Q2 短进程优先:20 ~ 30:P2(剩 10 比 P1 的 20 短)完成;30 ~ 50:P1 完成。
  3. 等待:P1 = 50 − 30 = 20,P2 = 30 − 20 = 10;平均 = 15ms。
  • A 错25 = (20 + 30) ÷ 2:P2 那一项用了它的完成时刻 30,没减去它自己运行的 20ms;等待时间 = 周转时间 − 运行时间。
  • B 错20 = (10 + 30) ÷ 2,是把 Q2 当成先来先服务(P1 先降入 Q2 先运行 20 ~ 40,P2 40 ~ 50);题目说 Q2 用短进程优先,剩 10 的 P2 先运行。
  • C 对(20 + 10) ÷ 2 = 15ms。
  • D 错10 只是 P2 的等待时间;P1 在 10 ~ 30 也一直在等,等了 20ms,平均应是 (20 + 10) ÷ 2。

多级反馈队列:先看本级的规则(RR / SPF / FCFS),再看降级条件。等待时间 = 周转时间 − 运行时间。

2024-30操作系统
时间片轮转,时间片 5ms,10 个进程初始都在就绪队列,执行结束前只在执行态和就绪态。若队尾的进程 P 所需 CPU 时间最短,为 25ms,不考虑开销,则 P 的周转时间为( )。
  • A. 200ms
  • B. 205ms
  • C. 250ms
  • D. 295ms
展开解析

考点6.7 RR 的时间片

CP 需要 25 ÷ 5 = 5 个时间片。其他 9 个进程都比它长,P 完成前每一轮都还在;P 在队尾,每轮都最后一个运行。

  1. 一轮 = 10 个进程 × 5ms = 50ms,P 在每轮的最后 5ms 运行。
  2. P 的第 5 个时间片在第 5 轮末结束:5 × 50 = 250ms。初始就在就绪队列,到达时刻为 0,周转时间 = 250ms。
  • A 错200 = 4 × 50,只算了 4 轮;P 要 5 个时间片,第 5 轮末才完成。
  • B 错205 = 4 × 50 + 5,相当于 P 在第 5 轮排在最前面;但其他进程都没做完,轮转顺序不变,P 仍排在第 5 轮的最后。
  • C 对5 轮 × 50ms = 250ms。
  • D 错295 = 250 + 9 × 5,相当于 P 做完后还要再等其余 9 个进程各跑一个时间片;但 P 在第 5 轮末已用满 25ms,此时就结束了。

RR 中求某进程完成时刻:先数它要几个时间片,再看它每轮排第几、每轮有多少进程还没完成。

2012-29操作系统CPU 与 I/O 并行
多道批处理系统中仅有 P1、P2 两个作业,P2 比 P1 晚 5ms 到达。P1:计算 60ms,I/O 80ms,计算 20ms;P2:计算 120ms,I/O 40ms,计算 40ms。不考虑调度和切换时间,完成两个作业需要的时间最少是( )。
  • A. 240ms
  • B. 260ms
  • C. 340ms
  • D. 360ms
展开解析

考点6.7 调度:画甘特图

B让 CPU 和 I/O 尽量同时忙。画时间轴:一个作业做 I/O 时,CPU 去算另一个作业。

  1. 0 ~ 60:P1 计算(P2 在 5 到达,等着)。
  2. 60 ~ 180:P2 计算 120;同时 P1 做 I/O 60 ~ 140。
  3. 180 ~ 200:P1 计算 20,P1 完成;同时 P2 做 I/O 180 ~ 220。
  4. 200 ~ 220:CPU 空等 P2 的 I/O;220 ~ 260:P2 计算 40,完成。共 260ms。
  5. 如果 140 时让 P1 抢占 P2:P2 的计算推迟到 200 才完,I/O 200 ~ 240,再算到 280,反而更慢。程序枚举了所有调度方式,最少就是 260。
  • A 错240 = 60 + 20 + 120 + 40,只是四段计算时间之和,等于假设 CPU 从不空闲;但 P2 做最后的 I/O(180 ~ 220)时 P1 只剩 20ms 计算,CPU 至少空等 20ms。
  • B 对按上面的时间轴,260ms 两个作业都完成,而且已是最少。
  • C 错340 比完全串行(360)只少 20,几乎没让 CPU 和 I/O 重叠;而按上面“P1 做 I/O 时 P2 计算”的排法 260 就能完成,所以 340 不是最少。
  • D 错360 = 160 + 200,是一个作业全部做完再做另一个的单道串行时间,没有利用多道程序让 CPU 与 I/O 并行。

求“最少完成时间”:画 CPU、I/O 两条时间轴,一个作业做 I/O 时 CPU 立刻算另一个;抢占不一定更快。同类:2016-24(3 个作业各“输入 2、计算 3、输出 4”,三个设备各一台,流水起来最少 17ms)。

2016-46操作系统综合题
调度程序选择优先数最小的进程运行,进程创建时由用户指定 nice 作为静态优先数。引入运行时间 cpuTime 和等待时间 waitTime(执行态时 cpuTime 定时加 1、waitTime 置 0;就绪态时 cpuTime 置 0、waitTime 定时加 1)。(1)若 priority = nice,可能出现饥饿,为什么?(2)设计一种动态优先数计算方法避免饥饿,说明 waitTime 的作用。
展开解析

(1)静态优先数:只要一直有优先数更小的进程就绪,优先数大的进程就永远轮不到。(2)priority = nice + k1 × cpuTime − k2 × waitTime(k1、k2 > 0)。运行越久优先数越大(优先级降低);等待越久优先数越小(优先级升高),waitTime 让长期等待的进程最终能被调度,避免饥饿。同类:2010-26 降低优先级的合理时机是“时间片用完”;2013-31 I/O 多的进程优先级应设高(让 I/O 设备尽早忙起来)。

其他选择题:2014-23 不可能导致饥饿的是时间片轮转;2017-23 t=2 时 FCFS 选 J1、SJF 选 J3;2025-25 优先队列用单链表按优先级有序存放,插入 O(n)、选出(取队头)O(1)。

FCFS 的护航效应,和 RR 的时间片该取多大

问题引入:上面的对比例子里,FCFS 的平均周转最差之一。原因可以用一个更极端的场景说清楚:一辆慢吞吞的大卡车开在单车道最前面,后面几辆小轿车只能跟着它爬——这叫护航效应(convoy effect)。

教材说法:FCFS 下,一个长的(CPU 繁忙型)进程先占了 CPU,后面许多短进程(常常是 I/O 繁忙型的)都得排在它后面等很久,平均等待时间、平均周转时间大大增加;而且这些短进程的 I/O 设备也跟着闲着。SJF / SPF 正是为了解决这个问题。(SJF 用于作业调度时叫“短作业优先”,用于进程调度时叫 SPF“短进程优先”,规则相同。)

FCFSP1 先到 P1(24) P2 P3 等待 0、24、27平均 17 短的先(SJF) P2 P3 P1(24) 等待 0、3、6平均 3 02430
图 6-14 护航效应。三个进程同时到达,总工作量一样是 30,只是顺序不同:FCFS 让两个短进程“护送”长进程,平均等待 17、平均周转 27;短的先做,平均等待 3、平均周转 13(已用 verify/08/p06_calc.py 验算)。

RR 的时间片取多大:每次换进程都要花一次上下文切换的时间 s(保存、恢复现场,可能还要刷 TLB)。时间片为 q 时,切换开销约占 s ÷ (q + s)。设 s = 0.1ms:

时间片 q10ms4ms1ms
切换开销占比 s ÷ (q + s)0.99%2.44%9.09%

时间片太小,CPU 有近一成时间在“换人”而不是干活;时间片太大,RR 退化成 FCFS,护航效应又回来了,交互响应变慢。经验上让时间片略大于一次典型交互所需的时间,使大多数交互请求在一个时间片内做完。决定时间片大小的因素:系统的响应时间要求、就绪进程数、系统处理能力(2017-27 D)。

自测
  1. 为什么说 FCFS“有利于 CPU 繁忙型作业、不利于 I/O 繁忙型作业”?
    答案
    I/O 繁忙型进程每次只用 CPU 一小会儿就去做 I/O,做完回来又要排在长的 CPU 繁忙型进程后面,等的时间远大于它要用的 CPU 时间;它的 I/O 设备也因此经常闲着(护航效应)。
  2. 切换开销 s = 0.2ms,要让开销占比不超过 5%,时间片至少取多大?
    答案
    s ÷ (q + s) ≤ 5% → q + s ≥ 4ms → q ≥ 3.8ms。

实时系统怎么调度:EDF 与 RMS 拓展

问题引入:实时系统的任务常常是周期性的(每 5ms 采样一次传感器、每 7ms 刷新一次控制量),每个任务的每次执行都有截止时间。前面的算法关心平均周转,实时调度关心的是“有没有任务错过截止时间”。

算法规则可调度条件(周期任务、截止时间 = 周期)
EDF 最早截止时间优先动态优先级:谁的截止时间最近谁先运行,可抢占总利用率 U = Σ Ci/Pi ≤ 1 就一定能调度
RMS 速率单调调度静态优先级:周期越短(频率越高)优先级越高,可抢占U ≤ n(21/n − 1) 一定能调度(n = 2 时约 0.828);超过这个界不一定不行,要实际排一下

跟我做一遍(已用 verify/08/p06_calc.py 逐时刻模拟到 35):T1 每 5 个单位来一次、每次要 2;T2 每 7 个单位来一次、每次要 4。U = 2/5 + 4/7 ≈ 0.971 ≤ 1,但超过 RMS 的界 0.828。

RMST1 优先 EDF截止早优先 T1 T2(3/4) T1 T2 T1 T2 t=7:T2 第 1 次还差 1,错过截止! T1 T2(完成于 6) T1 T2(完成于 12) T1 全部按时完成 0571014 截止时间:T1 在 5、10、15……;T2 在 7、14……(每格 = 1 个时间单位)
图 6-15 同样两个任务,RMS 错过截止、EDF 全部满足。t = 5 时 T1 第 2 次到达:RMS 按“周期短的优先”让 T1 抢占,T2 来不及在 7 之前做完;EDF 比较截止时间,T2(7)比 T1(10)更急,继续让 T2 做完。

408 目前不考实时调度的计算;了解“EDF 按截止时间、动态优先级;RMS 按周期、静态优先级”即可。它也说明了第 1 篇的结论:实时系统追求的是“保证按时”,而不是平均周转最短。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2016-24、2012-29 改
某单 CPU 系统有输入、输出设备各 1 台。3 个作业并发执行,每个作业都依次进行输入 3ms、计算 2ms、输出 3ms。执行完这 3 个作业最少需要( )。
  • A. 14ms
  • B. 16ms
  • C. 18ms
  • D. 24ms
展开解析

A三台设备像流水线一样重叠工作。输入设备:J1 0~3、J2 3~6、J3 6~9。CPU:J1 3~5、J2 6~8、J3 9~11。输出设备:J1 5~8、J2 8~11、J3 11~14。

变化点:2016-24 中计算和输出较长(3ms、4ms)。一般规律:总时间 = 一个作业走完全程的时间 + (作业数 − 1) × 最慢一段的时间,这里 = 8 + 2 × 3 = 14ms。D 是完全不重叠的结果。

变式操作系统由 2023-29、2022-25 改
进程 P1、P2、P3 进入就绪队列的时刻分别为 0、10、15ms,优先级分别为 1、5、3(值越大优先级越高),CPU 执行时间分别为 30、20、10ms。系统采用抢占式优先级调度,则三个进程的平均周转时间为( )。
  • A. 30ms
  • B. 35ms
  • C. 40ms
  • D. 45ms
展开解析

B0~10 P1;10 时 P2(5)到达,抢占 P1,运行到 30;15 时 P3(3)到达,优先级低于 P2,不抢占;30~40 P3;40~60 P1。周转时间:P1 60、P2 20、P3 25,平均 105 ÷ 3 = 35ms。

变化点:P3 在 P2 运行期间到达,但优先级比 P2 低,不能抢占,只能排在 P1 前面。抢占式调度也不是“新来的就抢”,要比较优先级。

变式操作系统由 2019-27 改
系统采用二级反馈队列调度:Q1 采用时间片轮转(时间片 10ms),Q2 采用短进程优先;Q1 为空时才调度 Q2;新进程进入 Q1,在 Q1 中运行一个时间片未结束则转入 Q2。若 Q1、Q2 原来为空,系统依次创建进程 P1、P2 后开始调度,P1、P2 需要的 CPU 时间分别为 40ms 和 15ms,则两个进程的平均等待时间为( )。
  • A. 10ms
  • B. 12.5ms
  • C. 15ms
  • D. 20ms
展开解析

BQ1:P1 运行 0~10,转入 Q2(剩 30);P2 运行 10~20,转入 Q2(剩 5)。Q2 按短进程优先:P2 运行 20~25 结束,P1 运行 25~55 结束。等待时间:P1 = 55 − 40 = 15,P2 = 25 − 15 = 10,平均 12.5ms。

变化点:2019-27 中 P1、P2 为 30ms 和 20ms(平均等待 15ms)。进入 Q2 后要按剩余时间比较:P2 剩 5ms,比 P1 的 30ms 短,先运行。

变式操作系统由 2024-30 改
某系统采用时间片轮转调度,时间片为 4ms。共有 8 个进程,初始时都在就绪队列中,运行结束前只处于执行态或就绪态。队尾的进程 P 所需的 CPU 时间最短,为 12ms。不考虑系统开销,P 的周转时间为( )。
  • A. 84ms
  • B. 96ms
  • C. 100ms
  • D. 108ms
展开解析

BP 需要 3 个时间片。其他进程都至少需要 12ms,所以前 3 轮中每轮 8 个进程都各运行满 4ms。P 排在队尾,在第 3 轮的最后完成:3 × 8 × 4 = 96ms。

变化点:2024-30 是 10 个进程、时间片 5ms、P 需 25ms(5 轮 × 10 × 5 = 250ms)。关键是“P 最短”:P 完成之前,其他进程都还没结束,每轮都满员。

变式操作系统由 2014-23、2017-23 改(护航效应)
三个进程同时进入就绪队列,排队顺序为 P1、P2、P3,所需 CPU 时间分别为 20、2、2(不做 I/O,不计切换开销)。分别采用 FCFS 和 SJF 调度时,平均等待时间分别为( )。
  • A. 14、2
  • B. 14、4
  • C. 7.33、2
  • D. 20、4
展开解析

AFCFS:等待 0、20、22,平均 42 ÷ 3 = 14。SJF:按 P2、P3、P1,等待 0、2、4,平均 6 ÷ 3 = 2。一个长进程排在最前,两个短进程只能跟着等——这就是护航效应。

考点:FCFS 的护航效应。

第 6 篇自测
  1. 三个作业 J1(0 到达,运行 10)、J2(2,4)、J3(4,2),分别用 FCFS、SJF、HRRN 求平均周转时间。
    答案
    FCFS:J1 0~10、J2 10~14、J3 14~16,周转 10、12、12,平均 11.33。SJF:J1 先跑(0 时只有它)到 10,然后 J3(2)10~12、J2 12~16,周转 10、14、8,平均 10.67。HRRN:10 时 J2 响应比 (8+4)/4 = 3,J3 (6+2)/2 = 4 → 先 J3,结果同 SJF。(可用上面的工具验证。)
  2. 同一进程内两个线程切换,需要换页表基址寄存器吗?
    答案
    不需要,它们共享地址空间(同一张页表),只换 PC、寄存器、栈指针。
  3. “一个进程被唤醒,就意味着它马上会运行”对吗?
    答案
    不对。被唤醒是阻塞 → 就绪,要等调度程序选中才会运行(抢占式优先级调度下,若它优先级更高可能立即抢占)。
  4. 管道满了,写进程会怎样?
    答案
    阻塞,直到读进程读走数据腾出空间。
  5. 单 CPU 系统中有 6 个进程,处于就绪态的进程最多几个?处于阻塞态的最多几个?
    答案
    就绪最多 5 个(有 1 个在运行);阻塞最多 6 个(全都在等事件,CPU 运行闲逛进程)。
  6. 下列转换哪些不可能发生:运行→就绪、就绪→阻塞、阻塞→运行、阻塞→就绪?
    答案
    就绪→阻塞、阻塞→运行 不可能。
  7. 下列哪些是同一进程内各线程共享的:堆、全局变量、某线程的局部变量、打开的文件、PC?
    答案
    堆、全局变量、打开的文件共享;某线程的局部变量在它自己的栈里,PC 是线程私有寄存器,都不共享(不过同进程线程若拿到了别的线程栈变量的地址,技术上也能访问,考试按“不共享”答)。
  8. 用户级线程的系统中,一个线程执行 read 系统调用阻塞了,同进程的其他线程还能运行吗?换成内核级线程呢?
    答案
    纯用户级线程(多对一):内核只看得见进程,整个进程被阻塞,其他线程也不能运行。内核级线程:只阻塞这一个线程,其他线程照常被调度。
  9. 作业 A、B、C 同时到达,运行时间 6、2、4,采用 SJF,平均周转时间是多少?若按 FCFS(到达顺序 A、B、C)呢?
    答案
    SJF:B 0~2、C 2~6、A 6~12,平均 (2 + 6 + 12)/3 ≈ 6.67;FCFS:A 0~6、B 6~8、C 8~12,平均 (6 + 8 + 12)/3 ≈ 8.67。同时到达时 SJF 的平均周转最短。

6.✎本篇真题闯关(39 题)

本篇各小节挂的真题全部在这里,按小节排好。可以展开后一道道做,也可以用弹窗模式连续做。做题记录(对/错)只保存在你这台电脑的浏览器里,小节标题下的题号会随之变绿或变红。

第 7 篇 · 操作系统 2.3~2.4(与计组硬件原子指令融合)

并发:同步互斥(PV 大题)与死锁

多个进程(线程)并发执行、共享数据时,结果可能随执行顺序不同而出错。这一篇先从汇编层面看清“为什么会错”,再学操作系统的解决工具——信号量和 PV 操作,这是每年一道 7~8 分综合题;最后讲并发的另一个麻烦:死锁和银行家算法。

对应:王道操作系统 2.3、2.4难度:★★★★PV 综合题:2009、2011、2013、2014、2015、2017、2019、2020、2022、2024、2025;银行家算法 6 题

7.1融合为什么并发会出错:从一条 x++ 说起

★★ 常考选择题综合题

C 语言里 x = x + 1 看起来是一步,但编译成机器指令是三步(第 3 篇):

load  R1, x      ; ① 把内存里的 x 读进寄存器
inc   R1         ; ② 寄存器加 1
store x, R1      ; ③ 写回内存

进程切换可以发生在任意两条指令之间(每条指令结束都会检查中断,第 5 篇),而每个进程有自己的一套寄存器现场。于是:

x 初值 1。P1 做 x+1,P2 做 x−1,正确结果应该是 1 P1P2内存 x说明 load R1,x(R1=1)1 inc R1(R1=2)1← 时间片到,切到 P2 load R2,x(R2=1)1P2 读到的还是旧值 dec R2 ; store(x=0)0← 切回 P1 store x,R1(x=2)2P2 的修改被覆盖了! P1 的 R1 存在它的 PCB 里,切回来时恢复的是切走前的 2,它不知道内存已经变了
图 7-1 竞争条件。结果取决于执行顺序。2011-32:两个操作完成后 x 可能为 0、1 或 2(选 C),不可能是 −1 或 3。2018-25:两线程都对初值 0 的 x 做加 1,得到 x = 2 的执行序列只有“先完整执行一个、再完整执行另一个”这 2 种。
几个概念

临界资源:一次只允许一个进程使用的资源(打印机、共享变量、缓冲区)。临界区:进程中访问临界资源的那段代码。互斥:同一时刻只能有一个进程在临界区。同步:进程之间有先后次序要求(B 必须等 A 做完某事)。同步也叫直接制约关系(为了合作),互斥也叫间接制约关系(因为争用同一资源)。
判断“要不要互斥”:两个操作访问同一个共享变量,且至少一个是写。2016-30:P1 里的两个线程 x+=1 和 x+=2 要互斥;a=1 与 a=2 是各自的局部变量不用;P1 的 x 和 P2 的 x 是不同进程的变量,互不相干。2026-27 的 Bernstein 条件说的就是这件事:两进程能安全并发 ⇔ 一个的写集合与另一个的读集合、写集合都不相交(读读不冲突)。

访问临界资源的代码结构:进入区(检查能不能进、上锁)→ 临界区 → 退出区(解锁)→ 剩余区。互斥机制要遵循四条准则:

准则意思是否必须
空闲让进临界区没人时,想进的应该能进必须
忙则等待有人在里面,别人必须等必须
有限等待等待进入的时间有限,不会“饿死”必须
让权等待进不去时应该释放 CPU(阻塞),不要空转(忙等)最好有,不是必须(2020-32 选前三条)

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2018-25、2011-32 改
同一进程的 3 个线程并发执行,共享初值为 0 的全局变量 x,每个线程都执行一次 x++。x++ 的机器级代码为“读 x 到寄存器、寄存器加 1、写回 x”三条指令。3 个线程都执行完后,x 的值( )。
  • A. 只能为 3
  • B. 可能为 1、2 或 3
  • C. 可能为 0、1、2 或 3
  • D. 可能为 2 或 3
展开解析

B最好的情况,三个线程依次执行,x = 3。最坏的情况,三个线程都先读到 0,然后各自写回 1,x = 1。也可能两个线程冲突、一个不冲突,x = 2。至少有一次写回在最后,而写回的值至少是 1,所以不可能是 0。

变化点:2018-25 只有两个线程(可能为 1 或 2)。线程变成 n 个,结果的范围是 1 ~ n。

变式操作系统由 2016-30、2026-27 改
进程 P 中,x 是全局变量,线程 T1 执行 a = x;(a 是 T1 的局部变量),线程 T2 执行 x = x + 1;,线程 T3 执行 b = 5;(b 是 T3 的局部变量)。下列操作对中,需要互斥执行的是( )。
  • A. T1 的 a = x 与 T3 的 b = 5
  • B. T1 的 a = x 与 T2 的 x = x + 1
  • C. T3 的 b = 5 与 T2 的 x = x + 1
  • D. 两个不同进程中分别对各自的全局变量 x 做加 1
展开解析

BT1 读 x,T2 写 x,读写集合有交集,并发执行时 a 可能读到加 1 前或加 1 后的值,需要互斥(或同步)。局部变量各自私有;不同进程的全局变量在不同的地址空间里,互不影响。

变化点:2016-30 中需要互斥的是两个“写”。这里换成一“读”一“写”:只要一方写、另一方读或写同一个共享变量,就需要互斥(Bernstein 条件,2026-27)。

7.2融合实现互斥:软件方法与硬件原子指令

★★★ 高频选择题综合题

软件方法:只用普通变量

方法做法问题
单标志法turn 表示轮到谁,只有轮到自己才能进必须严格轮流,违背“空闲让进”
双标志先检查先看对方的 flag,再把自己的 flag 置真“检查”和“上锁”之间可能被切换,两个都进去,违背“忙则等待”
双标志后检查先置自己 flag,再看对方两个都置了 flag 后互相等,都进不去,违背“空闲让进/有限等待”
Peterson 算法先置 flag[i] = true 表示想进,再 turn = j 表示谦让,while(flag[j] && turn==j);满足前三条,能互斥、不会饥饿(2010-27 选 D),但仍是忙等,不满足让权等待

四种软件方法的代码长得很像,真题会给出代码让你判断违反了哪条准则(2010-27),所以要能看懂 Peterson 算法为什么对:

bool flag[2] = {false, false};   // flag[i]:Pi 想进临界区
int  turn = 0;                   // 两人都想进时,让谁先进
P0: flag[0] = true; turn = 1;  while (flag[1] && turn == 1);  临界区;  flag[0] = false;
P1: flag[1] = true; turn = 0;  while (flag[0] && turn == 0);  临界区;  flag[1] = false;

为什么对:对方不想进(flag 为 false)时,自己直接进——空闲让进;两人同时想进时,turn 只能是其中一个值,后写 turn 的那个把机会让给了对方,所以恰好一人进入——忙则等待;进去的人出来时把自己的 flag 置 false,等着的人就能进——有限等待,不会饥饿。只是等的时候一直在 while 里转,不让出 CPU。若去掉 turn 就变成“双标志后检查”(可能都进不去),若先检查对方 flag 再置自己的 flag 就是“双标志先检查”(可能都进去)。

三种错误的软件方法:各错在哪一步(时序反例)

问题引入:上表一句话说“违背空闲让进”“违背忙则等待”,真题却是给出代码让你自己判断(2010-27)。判断的办法只有一个:构造一种交替执行的顺序,让它出错。下面把三个反例画出来。

// ① 单标志法:turn 表示轮到谁
P0: while (turn != 0);  临界区;  turn = 1;        P1: while (turn != 1);  临界区;  turn = 0;
// ② 双标志先检查:先看对方,再举自己的旗
P0: while (flag[1]);  flag[0] = true;  临界区;  flag[0] = false;      // P1 对称
// ③ 双标志后检查:先举自己的旗,再看对方
P0: flag[0] = true;  while (flag[1]);  临界区;  flag[0] = false;      // P1 对称
① 单标志法(turn 初值 0) P0P1 进出临界区,置 turn = 1 在做别的事,暂时不想进 又想进:while(turn≠0) 空转 只能等 P1 进出一次才行 临界区空着却进不去 → 违反“空闲让进”(必须严格轮流) ② 双标志先检查(flag 初值都是 false) P0P1 ① 查 flag[1] = false,通过 ② 查 flag[0] = false,通过 ③ flag[1] = true,进临界区 ④ flag[0] = true,也进临界区 两个同时在临界区 → 违反“忙则等待”(“检查”和“举旗”之间被切换了) ③ 双标志后检查 P0P1 ① flag[0] = true ② flag[1] = true ③ while(flag[1]) 空转 ④ while(flag[0]) 空转 两人都举着旗互相谦让,谁都进不去 → 违反“空闲让进”和“有限等待”
图 7-2 三个反例。从左到右是时间顺序,红色格子是出问题的那一步。Peterson 算法把 ③ 的“举旗”保留,再加一个 turn 打破对称:两人同时想进时,后写 turn 的那个会把机会让给对方,所以既不会两人都进,也不会两人都卡住。
方法满足的准则违反的准则反例的关键
单标志法忙则等待空闲让进对方不想进时,自己也进不去
双标志先检查空闲让进忙则等待检查和上锁不是一步完成,中间被切换
双标志后检查忙则等待空闲让进、有限等待同时举旗,互相等待
Peterson 算法空闲让进、忙则等待、有限等待让权等待(忙等)——

真题怎么考:2010-27 给出 Peterson 算法的代码问“能否互斥、会不会饥饿”(能互斥、不饥饿)。判别法:拿到一段代码,先试“两人同时想进”(看会不会都进、或都卡住),再试“只有一个人想进”(看能不能进)。易错:Peterson 只能用于两个进程(推广到 n 个要用更复杂的算法),而且仍然忙等。

硬件方法:让“检查+上锁”一步完成

软件方法的根本麻烦是“读锁”和“写锁”之间可能被打断。硬件的思路:提供一条原子指令,读和写在一条指令内完成,执行期间不会被打断(不会有中断插进来,多核时总线也被锁住)。

① 关中断
进临界区前关中断,出来再开。单 CPU 上最简单。
缺点:关中断是特权指令,用户程序不能用(2021-45 第 3 问);多 CPU 时无效;关太久影响系统。操作系统内核用它实现原语。
② TestAndSet(TSL)/ Swap
TSL(&lock):原子地“返回 lock 旧值并把 lock 置为 true”。while (TSL(&lock)); 拿不到锁就一直循环——忙等,不让权(2016-27 选“等待的进程不会主动放弃 CPU”)。Swap 同理:原子地交换两个变量。

硬件指令法(TSL、Swap)的优缺点:优点——适用于任意数目的进程,单处理机、多处理机都能用;简单;每个临界区设一个锁变量就能支持多个临界区。缺点——忙等,不满足让权等待;从等待的进程中随机选一个进入,可能有进程一直抢不到(饥饿)。

bool lock = false;              // 共享
// 每个进程:
bool key = true;
while (key == true) swap(key, lock);   // 进入区:换到 false 才说明锁原来是空的
临界区;
lock = false;                         // 退出区:释放锁
大纲里的“锁”:互斥锁与自旋锁

互斥锁(mutex lock)是最简单的互斥工具:进临界区前 acquire() 获得锁,出来时 release() 释放锁;acquire 与 release 本身必须是原子操作(用上面的 TSL、Swap 或关中断实现)。自旋锁是拿不到锁就在循环里“转圈”一直检查(忙等)的互斥锁:不需要进程切换,适合多处理器、临界区很短的场合(另一个 CPU 上的持锁者很快就会释放);单处理器上自旋只是白白浪费时间片。能让权等待(拿不到就阻塞)的是信号量。

2023-45操作系统融合综合题 7 分
某同学用 swap 指令和共享布尔变量 lock(TRUE 时不能进入临界区)实现互斥,进入区写成 key = TRUE; if (key == TRUE) swap key, lock;,退出区写成 lock = TRUE;。(1)哪些语句错了?改正(不增加语句条数)。(2)能否用函数 void newSwap(bool *a, bool *b){ bool temp=*a; *a=*b; *b=temp; } 的调用 newSwap(&key, &lock) 代替 swap 指令?为什么?
展开解析

(1)if 改成 while:只换一次,如果换回来的 key 还是 TRUE(锁被别人占着),应该继续尝试,而不是直接进临界区。退出区应是 lock = FALSE(释放锁)。
(2)不能。newSwap 是几条普通指令(读 *b、写 *a、写 *b),执行中间可能被中断、切换,两个线程可能都读到 lock = FALSE,都进入临界区。硬件 swap 指令的价值就在于原子性。

2021-45操作系统融合综合题 7 分
整型信号量:wait(S){ while(S<=0); S=S-1; },signal(S){ S=S+1; }。方法 1:wait 中“关中断; while(S<=0); S=S−1; 开中断;”;方法 2:wait 中“关中断; while(S<=0){ 开中断; 关中断; } S=S−1; 开中断;”,signal 都是“关中断; S=S+1; 开中断;”。(1)为什么 wait、signal 对 S 的访问必须互斥?(2)两种方法是否正确?(3)用户程序能否用开/关中断指令实现临界区互斥?
展开解析

(1)S 是多个进程共享的变量,wait、signal 都要读写它,和 x++ 一样会出竞争条件。
(2)方法 1 错:若 S ≤ 0,在关中断状态下死循环,时钟中断进不来,别的进程永远没机会执行 signal 把 S 加上去 → 系统卡死。方法 2 对:循环里短暂开中断,让别的进程有机会运行并修改 S。
(3)不能,开/关中断是特权指令,只能在内核态执行。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2016-27、2018-32 改
下列关于用 TSL(TestAndSet)或 Swap 指令实现互斥的叙述中,正确的是( )。
  • A. 满足“让权等待”
  • B. 等待进入临界区的进程会不断测试锁,处于忙等状态
  • C. 不会出现饥饿
  • D. 必须在关中断的状态下执行
展开解析

Bwhile (TSL(&lock)); 会一直循环测试,进程不放弃 CPU,即忙等,不满足让权等待(2016-27)。进程谁先抢到锁是随机的,可能饥饿。TSL 本身是原子的硬件指令,不需要关中断。

变化点:2016-27 的正确项就是“等待的进程不会主动放弃 CPU”。这里把“忙等、饥饿、原子性”几条放在一起辨析。

变式操作系统由 2010-27 改
两个进程用软件方法实现互斥。下列方法中,两个进程有可能同时进入临界区的是( )。
  • A. 单标志法
  • B. 双标志后检查法
  • C. 双标志先检查法
  • D. Peterson 算法
展开解析

C双标志先检查:P0 查到 flag[1] = false、还没来得及举旗就被切换,P1 也查到 flag[0] = false,于是两人都进去,违反忙则等待。A 违反的是空闲让进(必须轮流);B 可能两人都进不去;D 能正确互斥。

考点:三种错误的软件方法。

7.3信号量:计数器 + 等待队列

★ 考过选择题

整型信号量拿不到就空转(忙等)。实际用的是记录型信号量:一个整数 value 加一个阻塞队列。P、V 操作由操作系统用原语实现(不可被打断)。

信号量 Svalue(整数)L(阻塞队列) P(S) / wait(S):申请一个 S.value--;if (S.value < 0)把自己放进 S.L,阻塞(不够了就去睡觉 → 让权等待) V(S) / signal(S):释放一个 S.value++;if (S.value <= 0)从 S.L 唤醒一个进程(≤ 0 说明还有人在排队) value 的含义:> 0:还有这么多个资源可用= 0:刚好用完,没人等< 0:|value| 个进程在排队
图 7-3 记录型信号量。2010-25:初值 3、当前值 1 → 可用 1 个、等待 0 个。2026-26:初值 5、有 8 个进程执行“wait→访问→signal”,某时刻 S = −2 → 有 5 个正在访问资源、2 个阻塞(选 A)。2018-32:能实现让权等待的是信号量(Peterson、TSL、swap 都是忙等)。
typedef struct { int value; struct process *L; } semaphore;   // 记录型信号量:计数 + 阻塞队列
void wait(semaphore S){          // P 操作(原语)
    S.value--;
    if (S.value < 0) { 把当前进程插入 S.L; block(S.L); }   // 资源不够:阻塞自己,让出 CPU
}
void signal(semaphore S){        // V 操作(原语)
    S.value++;
    if (S.value <= 0) { 从 S.L 取出一个进程 P; wakeup(P); }  // 还有人在等:唤醒一个(阻塞 → 就绪)
}
value 的含义与取值范围

value > 0:还有 value 个资源可用;value = 0:资源刚好用完,没人等;value < 0:|value| 个进程在阻塞队列里等。
取值范围:m 个同类资源(初值 m)被 n 个进程竞争、每个进程每次申请一个时(n > m),value 最大是 m(没人用),最小是 m − n(全部 n 个进程都执行了 P):范围 m − n ~ m。例:5 个进程共享一个最多允许 2 个进程同时进入的区域 → 信号量范围 −3 ~ 2。

跟着一个例子走一遍:打印机有 2 台,信号量 S 初值 2,进程 A、B、C、D 依次来申请:

事件S.valueS 的阻塞队列说明
初始2空2 台可用
A 执行 P(S)1空value ≥ 0,A 拿到一台
B 执行 P(S)0空B 拿到最后一台
C 执行 P(S)−1Cvalue < 0,C 阻塞(让出 CPU,不空转)
D 执行 P(S)−2C、D|−2| = 2 个进程在等
A 执行 V(S)−1Dvalue ≤ 0,说明有人等 → 唤醒 C(C 直接得到 A 还回的那台)
B 执行 V(S)0空唤醒 D
C、D 各执行 V(S)2空回到初始

注意:被唤醒的进程醒来后不会再做一次减 1——它在执行 P 的时候已经减过了,醒来直接拥有资源。

信号量的三种用法(初值是关键)
用途初值写法
互斥:一次只许一个进mutex = 1临界区前 P(mutex),后 V(mutex),成对出现在同一个进程里
同步:A 做完 x,B 才能做 yS = 0“前 V 后 P”:A 在 x 之后 V(S);B 在 y 之前 P(S)
资源计数:有 n 个同类资源S = n用之前 P,还回去 V(缓冲区空位 empty = n,产品 full = 0)

整型信号量 vs 记录型信号量:差在“等不到时干什么”

问题引入:两种信号量都叫 wait / signal,2021-45 考的是整型信号量,选择题问的“让权等待”却只有记录型信号量能做到。差别就在 wait 里那一行:资源不够时,是原地转圈,还是去排队睡觉。

// 整型信号量:S 只是一个整数
wait(S)  { while (S <= 0);   // 不够就空转
           S = S - 1; }
signal(S){ S = S + 1; }
// 记录型信号量:整数 + 阻塞队列
wait(S)  { S.value--;
           if (S.value < 0) block(S.L); }   // 不够就去睡
signal(S){ S.value++;
           if (S.value <= 0) wakeup(S.L 中的一个); }
单 CPU,S = 0,A 执行 wait(S) 等资源,B 是能释放资源的进程 整型 A 运行 A 在 while(S≤0) 空转,白占 CPU 直到时间片用完 B 运行,signal(S) A 继续 记录型 A 运行 B 运行,signal(S) A 继续 A 一等不到就阻塞、让出 CPU,B 立刻能运行 时间 →(示意)
图 7-4 等不到资源时,CPU 在干什么。单 CPU 上,整型信号量的空转纯属浪费:能释放资源的 B 根本没机会运行。记录型信号量让等待者阻塞,实现了“让权等待”。
整型信号量记录型信号量
数据结构一个整数 S整数 value + 阻塞队列 L
资源不够时while 循环反复测试(忙等)把自己放进队列、阻塞(让权等待)
值能否为负不会(先等到 S > 0 才减)可以,|value| = 等待的进程数
让权等待不满足满足
真题2021-45(用关中断实现 wait 的原子性)2018-32、2026-26、2010-25

注意:整型信号量的 wait 也必须原子执行(2021-45 第 1 问),但它在 while 里等待时又不能一直关着中断(方法 1 会把系统卡死),所以要在循环里短暂开中断——这正说明整型信号量“忙等”的尴尬。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2026-26、2010-25 改
系统中有 6 个进程,都按“wait(S) → 访问资源 → signal(S)”的顺序访问某资源,S 的初值为 4。若此时 S 的值为 −1,则正在访问资源的进程数和阻塞的进程数分别是( )。
  • A. 4、1
  • B. 3、1
  • C. 4、2
  • D. 5、1
展开解析

AS = −1 表示有 1 个进程在 S 上阻塞,资源全部被占用,即 4 个进程正在访问资源。另外 1 个进程还没有执行 wait,或已经访问完了。

变化点:2026-26 是初值 5、当前 −2(答案 5、2)。记住:S < 0 时,|S| = 阻塞的进程数,正在使用资源的进程数 = 初值。

变式操作系统由 2018-32 改
下列同步机制中,等待的进程会“忙等”(不满足让权等待)的是( )。
Ⅰ. Peterson 算法 Ⅱ. TestAndSet 指令 Ⅲ. 记录型信号量 Ⅳ. 管程
  • A. 仅Ⅰ
  • B. 仅Ⅰ、Ⅱ
  • C. 仅Ⅲ、Ⅳ
  • D. 仅Ⅱ
展开解析

BPeterson 算法和硬件指令方法都靠循环测试来等待,是忙等。记录型信号量和管程在条件不满足时把进程阻塞、放进等待队列,让出 CPU。

变化点:2018-32 问“能实现让权等待的”(信号量),这里反过来问“忙等的”。

变式操作系统由 2021-45、2018-32 改
下列关于整型信号量和记录型信号量的叙述中,正确的是( )。
  • A. 记录型信号量的值为负时,其绝对值等于在该信号量上阻塞的进程数
  • B. 整型信号量满足“让权等待”
  • C. 整型信号量的值可以为负数
  • D. 记录型信号量的 wait 操作在资源不足时循环测试信号量的值
展开解析

A记录型信号量 value < 0 时,|value| 就是阻塞队列里的进程数。B 错:整型信号量拿不到就 while 空转,是忙等。C 错:整型信号量要等到 S > 0 才减,不会为负。D 说的是整型信号量的做法,记录型会把进程阻塞。

考点:整型 vs 记录型信号量。

7.4PV 大题五步法(配真题)

★★★ 高频综合题
  1. 找进程:有几类进程,每类进程循环做哪几个动作(先把“骨架”写出来)。
  2. 找约束:逐对问——① 哪些动作要互斥(共享同一个东西)?② 哪些动作有先后(同步)?③ 有没有数量上限(容量、座位数)?
  3. 设信号量:每个约束一个信号量,写清含义和初值(评分点)。
  4. 放 P、V:互斥的 P/V 紧紧夹住临界区;同步“前 V 后 P”;计数资源“用前 P、还后 V”。
  5. 检查:先 P 同步/资源信号量,再 P 互斥信号量,否则可能死锁(拿着 mutex 去等 empty,而能释放 empty 的进程需要先拿 mutex);V 的先后顺序则可以随意交换——V 从不阻塞,换顺序最多改变“先唤醒谁”,不会死锁(生产者写“V(mutex); V(full);”或“V(full); V(mutex);”都对)。两条结论都已用状态空间穷举验证:2 个生产者 + 2 个消费者、缓冲区 2,P 顺序颠倒时可达死锁状态,V 顺序互换时 0 死锁、0 越界(verify/08/p13/pv.py)。最后对照题意逐句检查。

类型 1:前驱关系(最简单,先练这个)

A B C D E SAC SBC SCE SDE 每一条边 = 一个初值为 0 的信号量边的起点做完后 V,终点开始前 P(2020-45 原题)
图 7-5 前驱图 → 信号量:机械翻译。
semaphore SAC = 0, SBC = 0, SCE = 0, SDE = 0;   // 各对应一条先后关系
A(){ 操作A; V(SAC); }          B(){ 操作B; V(SBC); }
C(){ P(SAC); P(SBC); 操作C; V(SCE); }
D(){ 操作D; V(SDE); }          E(){ P(SCE); P(SDE); 操作E; }
2022-46操作系统综合题 8 分
某进程的两个线程 T1、T2 并发执行 A~F 六个操作:T1 依次执行 A、E、F,T2 依次执行 B、C、D。约束:C 在 A 和 B 完成后执行,D 和 E 在 C 完成后执行,F 在 E 完成后执行。用信号量描述 T1、T2 之间的同步关系。
展开解析

同一个线程内部的先后(B→C→D、A→E→F)本来就按顺序执行,不用信号量。只剩跨线程的两条:A→C、C→E。

semaphore AC = 0;   // A 完成后才能执行 C
semaphore CE = 0;   // C 完成后才能执行 E
T1(){ A; V(AC); P(CE); E; F; }
T2(){ B; P(AC); C; V(CE); D; }

常见扣分:给每条边都设信号量(多余的不扣分但浪费时间)、把 V(CE) 放在 D 之后(D 与 E 没有先后要求,会降低并发度)。

类型 2:生产者—消费者(缓冲区)

生产者 消费者 缓冲区(n 格) full = 已有产品数(初值 0)empty = 空位数(初值 n) mutex = 1:同一时刻只许一个进程操作缓冲区 P(empty) → 放 → V(full)P(full) → 取 → V(empty)
图 7-6 生产者—消费者:两个同步(有空位才能放、有产品才能取)+ 一个互斥(缓冲区)。
semaphore mutex = 1, empty = n, full = 0;
producer(){ while(1){ 生产一个产品;
    P(empty);  P(mutex);  放入缓冲区;  V(mutex);  V(full); } }
consumer(){ while(1){
    P(full);   P(mutex);  从缓冲区取出;  V(mutex);  V(empty);  消费; } }
// 如果把生产者的两个 P 颠倒:先 P(mutex) 再 P(empty),缓冲区满时生产者拿着 mutex 睡着,
// 消费者要取产品却拿不到 mutex → 死锁。所以:先 P 资源,再 P 互斥。
2009-45操作系统综合题 7 分
三个进程 P1、P2、P3 互斥使用一个包含 N(N>0)个单元的缓冲区。P1 每次用 produce() 生成一个正整数并用 put() 送入缓冲区某一空单元中;P2 每次用 getodd() 从缓冲区取出一个奇数并用 countodd() 统计奇数个数;P3 每次用 geteven() 取出一个偶数并用 counteven() 统计偶数个数。请用信号量实现三个进程的同步与互斥,说明信号量含义。
展开解析

约束:缓冲区互斥(mutex);有空位才能放(empty = N);有奇数 P2 才能取(odd = 0);有偶数 P3 才能取(even = 0)。P1 放完根据奇偶决定 V 哪个。

semaphore mutex = 1;   // 缓冲区互斥
semaphore empty = N;   // 空单元数
semaphore odd = 0, even = 0;   // 缓冲区中奇数、偶数的个数
P1(){ while(1){ x = produce();
        P(empty); P(mutex); put(); V(mutex);
        if (x % 2 == 0) V(even); else V(odd); } }
P2(){ while(1){ P(odd); P(mutex); getodd(); V(mutex); V(empty); countodd(); } }
P3(){ while(1){ P(even); P(mutex); geteven(); V(mutex); V(empty); counteven(); } }
2014-47操作系统综合题
多个生产者、多个消费者共享一个能存 1000 件产品的环形缓冲区(初始为空)。缓冲区未满时生产者可放入一件,否则等待;未空时消费者可取走一件,否则等待。要求一个消费者从缓冲区连续取出 10 件产品后,其他消费者才可以取产品。
展开解析

在标准模型上加一个消费者之间的互斥信号量,把“连续取 10 次”整个包起来;生产者不受影响,仍可在这 10 次之间往里放。

semaphore empty = 1000, full = 0;
semaphore mutex = 1;      // 互斥访问缓冲区
semaphore mutex2 = 1;     // 消费者之间互斥:保证一个消费者连续取 10 件
producer(){ while(1){ 生产; P(empty); P(mutex); 放入; V(mutex); V(full); } }
consumer(){ while(1){
    P(mutex2);
    for (int i = 0; i < 10; i++){ P(full); P(mutex); 取出; V(mutex); V(empty); 消费; }
    V(mutex2); } }

同类:2015-45 两人通过信箱辩论(每个信箱一组 full/empty/mutex,A 信箱 full=x、empty=M−x);2024-46 缓冲区 B 放一个分组,B 空才能写、非空才能读或改((2) 只需一个 full=0:P1 写完 V(full),P2 先 P(full) 再读;(3) 两个进程都改 → 一个 mutex=1);2025-45 植树(铁锹互斥 mutex=1,最多 3 个未种的坑 pits=3,已挖的坑 hole=0,待浇水的苗 water=0:甲 P(pits)P(mutex)挖V(mutex)V(hole);乙 P(hole) 放苗 P(mutex) 填土 V(mutex) V(pits) V(water);丙 P(water) 浇水——水桶只有丙用,不需要信号量)。这几道的完整解答点小节标题下的题号查看。

类型 3:有容量的“座位/房间”+ 服务

2011-45操作系统综合题 8 分
某银行提供 1 个服务窗口和 10 个顾客等待座位。顾客到达银行时,若有空座位,则到取号机领取一个号,等待叫号;取号机每次仅允许一位顾客使用。营业员空闲时通过叫号选取一位顾客并为其服务。顾客:{ 从取号机获得一个号码; 等待叫号; 获得服务; };营业员:while(TRUE){ 叫号; 为顾客服务; }。请添加信号量和 P、V 操作。
展开解析

约束:座位上限 10(empty = 10);取号机互斥(mutex = 1);有顾客营业员才叫号(full = 0,顾客→营业员);叫到号顾客才被服务(service = 0,营业员→顾客)。

semaphore empty = 10;   // 空座位数
semaphore mutex = 1;    // 取号机互斥
semaphore full = 0;     // 已就座等待的顾客数
semaphore service = 0;  // 叫号(营业员通知顾客)
顾客(){ P(empty); P(mutex); 取号; V(mutex); V(full);
        P(service); 获得服务; }
营业员(){ while(1){ P(full); V(empty); V(service); 叫号; 为顾客服务; } }

同类:2013-45 博物馆最多 500 人、出入口一次一人:empty = 500,mutex = 1;参观者 P(empty); P(mutex); 进门; V(mutex); 参观; P(mutex); 出门; V(mutex); V(empty)。

类型 4:哲学家就餐与读者写者(经典模板)

2019-43操作系统综合题 8 分
n(n≥3)位哲学家围坐圆桌,桌子中心有 m(m≥1)个碗,每两位哲学家之间有一根筷子。每位哲学家必须拿到一个碗和两侧的筷子才能就餐,就餐完放回。为使尽可能多的哲学家同时就餐且防止死锁,用信号量描述。
展开解析

经典哲学家问题的死锁:每人都拿起左边的筷子,然后都等右边的。破解办法之一:同时去抢筷子的人最多 n−1 个,这样至少有一人能拿到两根。这里正好用碗来限制:碗数 m < n 时本身就限制了人数;m ≥ n 时只放 n−1 个碗的“名额”。

semaphore bowl = min(n-1, m);          // 能同时去拿筷子的人数上限
semaphore chopstick[n] = {1, 1, …, 1}; // 每根筷子互斥
philosopher(int i){ while(1){ 思考;
    P(bowl); P(chopstick[i]); P(chopstick[(i+1)%n]);
    就餐;
    V(chopstick[i]); V(chopstick[(i+1)%n]); V(bowl); } }
// 读者—写者(读者优先)模板:多个读者可同时读;写者与任何人互斥
int count = 0;                 // 正在读的读者数
semaphore rw = 1;              // 读写互斥(写者之间也互斥)
semaphore mutex = 1;           // 保护 count
writer(){ P(rw); 写; V(rw); }
reader(){ P(mutex); if (count == 0) P(rw); count++; V(mutex);   // 第一个读者负责上锁
          读;
          P(mutex); count--; if (count == 0) V(rw); V(mutex); }  // 最后一个读者负责解锁

“最大并发”型:2017-46 三个线程共享复数 x、y、z——thread1 读 x、y,thread2 读 y、z,thread3 写 y、z。读和读可以并发,读写要互斥,所以 y 要用两个互斥信号量(一个管 t1 与 t3、一个管 t2 与 t3),z 一个(t2 与 t3),这样 t1、t2 能同时读 y。

经典模板补全:大题几乎都是它们的变形

408 的 PV 大题很少出“全新”的问题,基本是下面几个经典问题换了个场景。每个模板都要记住信号量设了哪几个、初值多少、P/V 放在哪。完整的做题流程和评分要点见第 11 篇 11.9 PV 专题。

模板操作系统多生产者—多消费者(水果盘)
桌上一个盘子,每次只能放一个水果。爸爸往盘里放苹果,妈妈放橘子;女儿只吃苹果,儿子只吃橘子。盘子空时爸爸或妈妈才能放;盘里有自己要的水果时,女儿或儿子才能取。
展开模板
semaphore plate = 1;    // 盘子里还能放几个水果
semaphore apple = 0;    // 盘里的苹果数
semaphore orange = 0;   // 盘里的橘子数
dad(){      while(1){ 准备苹果; P(plate); 放苹果; V(apple); } }
mom(){      while(1){ 准备橘子; P(plate); 放橘子; V(orange); } }
daughter(){ while(1){ P(apple);  取苹果; V(plate); 吃苹果; } }
son(){      while(1){ P(orange); 取橘子; V(plate); 吃橘子; } }

为什么这里不用 mutex?盘子容量是 1,plate、apple、orange 三个信号量里同一时刻最多只有一个是 1,本身就保证了同一时刻最多一个进程在访问盘子。若盘子容量大于 1(比如 2),两个家长可能同时往里放,就必须再加一个 mutex = 1 夹住“放/取”操作。要点:分析同步关系时按“事件”而不是按“进程”来找——“盘子变空”这个事件可以由女儿或儿子引起,“放入苹果”只让女儿能取,所以苹果、橘子各一个同步信号量。

模板操作系统吸烟者(一个生产者轮流供应三种组合)
三个吸烟者各有烟草、纸、胶水中的一种(各不相同),卷一支烟需要三种都有。供应者每次把其中两种放到桌上,拥有第三种的吸烟者拿走、卷烟、抽完后通知供应者,供应者再放下一组(按顺序轮流提供三种组合)。
展开模板
semaphore offer1 = 0, offer2 = 0, offer3 = 0;   // 桌上放着第 1/2/3 种组合
semaphore finish = 0;                            // 抽烟完成
int i = 0;                                       // 轮到第几种组合(只有供应者用,不需要互斥)
provider(){ while(1){
    if (i == 0)      { 放组合一(纸 + 胶水);   V(offer1); }
    else if (i == 1) { 放组合二(烟草 + 胶水); V(offer2); }
    else             { 放组合三(烟草 + 纸);   V(offer3); }
    i = (i + 1) % 3;
    P(finish);                                 // 等这一支抽完再放下一组
} }
smoker1(){ while(1){ P(offer1); 拿走组合一; 卷烟; 抽; V(finish); } }   // smoker2、3 同理,分别 P(offer2)、P(offer3)

要点:“一个生产者、多种产品、各有专门的消费者”——每种产品一个同步信号量,生产者用一个 finish 等消费者“干完”再继续(桌子容量为 1,同样不需要 mutex)。

模板操作系统读者—写者:写者不会饿死(读写公平)
上面的读者优先版本中,只要一直有读者源源不断地来,写者就永远写不了(饥饿)。怎样让写者到来后,后来的读者必须排在它后面?
展开模板
int count = 0;
semaphore mutex = 1;    // 保护 count
semaphore rw = 1;       // 读写互斥
semaphore w = 1;        // 排队“闸门”:写者来了就先占住它,后来的读者进不来
writer(){ while(1){ P(w); P(rw); 写; V(rw); V(w); } }
reader(){ while(1){
    P(w);
    P(mutex); if (count == 0) P(rw); count++; V(mutex);
    V(w);
    读;
    P(mutex); count--; if (count == 0) V(rw); V(mutex);
} }

要点:所有读者、写者都先过闸门 w(按到达顺序排队)。写者过了闸门后不马上放开,而是一直占着 w 直到写完,于是在它后面到达的读者都被挡在门外;它前面已经在读的读者读完后,写者就能写。读者过闸门后立刻放开 w,所以读者之间仍然可以同时读。

模板操作系统哲学家就餐:三种防死锁写法
5 位哲学家围坐,每两人之间一根筷子(chopstick[i] 在哲学家 i 的左手边,chopstick[(i+1)%5] 在右手边)。如果每人都先拿左边、再拿右边,5 人同时拿起左筷子就死锁了。给出三种避免死锁的写法。
展开模板
semaphore chopstick[5] = {1, 1, 1, 1, 1};
// 方法一:最多允许 4 人同时去拿筷子(2019-43 用碗数实现的就是它)
semaphore seat = 4;
Pi(){ while(1){ P(seat); P(chopstick[i]); P(chopstick[(i+1)%5]); 吃;
               V(chopstick[i]); V(chopstick[(i+1)%5]); V(seat); 思考; } }
// 方法二:奇数号先拿左边再拿右边,偶数号先拿右边再拿左边(相邻两人会先争同一根,不会形成环)
Pi(){ while(1){
    if (i % 2 == 1) { P(chopstick[i]); P(chopstick[(i+1)%5]); }
    else            { P(chopstick[(i+1)%5]); P(chopstick[i]); }
    吃; V(chopstick[i]); V(chopstick[(i+1)%5]); 思考; } }
// 方法三:把“拿两根筷子”整体做成互斥操作,一次只有一个人在拿
semaphore mutex = 1;
Pi(){ while(1){ P(mutex); P(chopstick[i]); P(chopstick[(i+1)%5]); V(mutex);
               吃; V(chopstick[i]); V(chopstick[(i+1)%5]); 思考; } }

要点:三种方法的思路分别是:方法一限制同时抢筷子的人数,5 根筷子最多 4 人抢,至少有一人能拿齐;方法二打破循环等待,相邻两人先争同一根筷子,不可能人人都拿着一根等另一根;方法三让拿筷子的过程互斥,一个人拿的时候别人不会插进来各拿一根,也就形不成环。大题里遇到“多种资源、每人要同时拿到几样才能干活”的场景(2019-43 碗 + 筷子),就用方法一:用一个计数信号量把同时去抢资源的人数限制到“一定有人能拿齐”。

两个新场景:多类资源 + 容量限制,阶段同步(屏障)

上面的模板之外,近年大题越来越爱考“约束多一点”的变形。下面两道是按五步法自己设计的练习(是否会死锁、能否同步,已用 verify/08/p07_pv.py 做了状态搜索和多线程随机测试)。

练习操作系统多类资源 + 容量限制(防止一种资源占满)
车间货架最多放 10 个零件。工人甲不断生产零件 A 放上货架,工人乙不断生产零件 B 放上货架,装配工每次从货架取 1 个 A 和 1 个 B 组装成产品。为防止货架被一种零件占满,规定货架上任何一种零件都不能超过 9 个。用 P、V 操作实现三者的同步与互斥。
展开解答
  1. 找约束:① 货架是共享的,放 / 取要互斥;② 货架总空位有限(容量 10);③ 装配工要等货架上有 A、有 B;④ A、B 各自不能超过 9 个。
  2. 设信号量:mutex = 1(互斥访问货架);empty = 10(货架空位);fullA = 0、fullB = 0(货架上的 A、B 个数);maxA = 9、maxB = 9(还能再放几个 A、几个 B)。
semaphore mutex = 1, empty = 10, fullA = 0, fullB = 0, maxA = 9, maxB = 9;
workerA(){ while(1){ 生产 A; P(maxA); P(empty); P(mutex); 放 A; V(mutex); V(fullA); } }
workerB(){ while(1){ 生产 B; P(maxB); P(empty); P(mutex); 放 B; V(mutex); V(fullB); } }
assembler(){ while(1){ P(fullA); P(fullB); P(mutex); 取 A; 取 B; V(mutex);
                       V(empty); V(empty); V(maxA); V(maxB); 组装; } }

为什么要有 maxA、maxB:若不限制,甲可能连续放满 10 个 A,乙一个 B 也放不进去,装配工永远等不到 B——三个进程全部阻塞,死锁(对货架状态做穷举搜索,不加限制时状态 (A=10, B=0) 和 (0, 10) 正是死锁状态;上限改为 9 后没有死锁状态)。上限 9 = 容量 − 1,保证货架上总给另一种零件留着位置。

为什么 P(maxA) 必须在 P(empty) 之前:若反过来,设货架上已有 9 个 A、0 个 B、空 1 格:甲先 P(empty) 占走最后一格,再在 P(maxA) 上阻塞——它手里攥着空位不放;乙拿不到空位放不了 B,装配工等不到 B,又是死锁。规律同五步法第 ⑤ 步:先申请“约束更紧”的资源信号量,最后才 P(mutex)。(同一脚本对两种顺序做了信号量级的状态搜索:错误顺序存在死锁状态,正确顺序没有。)

练习操作系统阶段同步:屏障(barrier)
进程 P1、P2、P3 并发执行,每个进程的工作分两个阶段。要求:三个进程都完成第一阶段之后,才能各自开始第二阶段(先完成的要等后完成的)。用 P、V 操作实现。
展开解答
  1. 找约束:“全都到齐”是一个计数条件——要数一数已经有几个进程完成了第一阶段,计数变量是共享的,要互斥;最后一个到达的进程负责“开门”放行所有人。
  2. 设信号量和变量:int count = 0(已完成第一阶段的进程数);mutex = 1(互斥访问 count);barrier = 0(第二阶段的“门”)。
int count = 0;  semaphore mutex = 1, barrier = 0;
Pi(){  第一阶段;
       P(mutex);  count++;
       if (count == 3) { V(barrier); V(barrier); V(barrier); }   // 最后一个到达者开门:放行 3 次
       V(mutex);
       P(barrier);                 // 门没开就在这里等
       第二阶段;  }

为什么对:前两个到达者 count 为 1、2,不开门,在 P(barrier) 上阻塞;第三个到达者把 count 加到 3,连做 3 次 V,三个进程(包括它自己)都能通过 P(barrier)。V(barrier) 放在 mutex 里外都可以,但 P(barrier) 必须在 V(mutex) 之后——否则先到者拿着 mutex 睡在 barrier 上,后到者进不了计数,死锁。

另一种写法(前驱关系思路):每个进程完成第一阶段后对另外两个进程各 V 一次、再对自己 P 两次,共用 3 个信号量。计数写法的好处是进程再多也只要 3 个变量。拓展 如果要反复分阶段(第二、第三阶段……都要等齐),还要把 count 清零,并用两道“门”交替使用,防止跑得快的进程提前冲进下一轮。

7.5管程

★ 考过选择题

信号量的 P、V 散落在各个进程里,写错一个位置就出错。管程把共享数据和对它的操作封装在一起(类似“类”),由编程语言/编译器保证:任何时刻最多只有一个进程在管程内执行,所以互斥自动实现;同步用管程内的条件变量:

管程 ProducerConsumer(同一时刻只允许 1 个进程在里面) 共享数据缓冲区、count外面不能直接访问 过程(对外接口)insert(item)remove() 条件变量 full等“有空位”的队列 条件变量 empty等“有产品”的队列 insert 发现满了:full.wait() → 自己阻塞在 full 上,并释放管程 remove 取走一个后:full.signal() → 唤醒一个等空位的 入口等待队列P1 P2 P3 …排队等进管程 和信号量对比: 互斥:信号量要自己写 P/V;管程由编译器自动保证 条件变量没有值:wait 一定阻塞;signal 没人等就作废 信号量有值:P 不一定阻塞;V 没人等也会让值 +1
图 7-7 管程。管程是编程语言提供的“高级同步机制”,底层仍要靠信号量或锁实现互斥;但程序员只要调用它的过程,不会写错 P、V 的顺序。
  • x.wait():条件不满足时,阻塞该进程,把它插入 x 的等待队列,并释放管程(2018-28 选 D)。条件变量没有“值”,不像信号量那样计数。
  • x.signal():唤醒一个在 x 上等待的进程;没人等就什么也不做。
  • 2016-32:“管程只能用于实现进程的互斥”错——有条件变量,也能实现同步。

管程由四部分组成:① 管程的名字;② 局部于管程内部的共享数据结构说明;③ 对该数据结构进行操作的一组过程(函数);④ 对共享数据设置初值的语句。基本特征:共享数据只能被管程内的过程访问;进程只能通过调用管程内的过程进入管程;每次只允许一个进程在管程内执行。

信号量管程的条件变量
有没有“值”有,value 记录剩余资源数没有值,只有一个等待队列
P / waitvalue − 1,结果 < 0 才阻塞x.wait() 一定阻塞调用者,并释放管程让别人进来
V / signalvalue + 1,即使没人等也会“记住”这次释放x.signal() 只在有进程等待时唤醒一个;没人等就什么也不发生,不留记录
判断条件靠 value 自己计数要在管程里用普通变量(如 count)自己判断“满不满、空不空”,再决定 wait 或 signal

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2018-28、2016-32 改
进程在管程中执行条件变量 x 的 x.signal() 操作时,若 x 的等待队列为空,则( )。
  • A. x 的值加 1
  • B. 什么也不做
  • C. 执行 signal 的进程阻塞
  • D. 系统报错
展开解析

B条件变量没有值,signal 只负责唤醒一个在 x 上等待的进程;没有进程在等,就什么也不做,这次 signal 不会被“记住”。这是它和信号量 V 操作(值加 1)的区别。

变化点:2018-28 考 x.wait()(阻塞该进程并插入 x 的等待队列)。这里考 x.signal(),重点是“没人等就不留痕迹”。

7.6死锁:四个必要条件

★★ 常考选择题
打个比方

一座窄桥只能过一辆车。东边的车开到桥中间,西边的车也开到桥中间,两车顶在一起:谁都不肯倒车(不可剥夺),谁都占着自己那半座桥(请求并保持),都在等对方让开(循环等待),而桥一次只能被一方用(互斥)。四个条件全凑齐了,就卡死了——这就是死锁:一组进程中的每一个都在等待只有组内其他进程才能释放的资源,谁都无法向前推进。

必要条件大白话怎么破坏它(死锁预防)
① 互斥资源一次只能给一个进程用(打印机、写一个变量)很难破坏;SPOOLing 能把独占设备改造成“共享”的(第 9 篇)
② 不可剥夺
也叫不剥夺条件
进程用完之前,别人不能强行拿走它的资源申请不到新资源时,把已占有的资源也释放(或允许高优先级抢占);代价大,只适合 CPU、内存这种状态好保存的资源
③ 请求并保持
也叫请求与保持
手里拿着一些资源,又去申请新的,申请不到就等,手里的也不放运行前一次性申请全部资源(静态分配);资源利用率低,可能饥饿
④ 循环等待存在一个进程—资源的环:P1 等 P2 的,P2 等 P3 的……Pn 等 P1 的给资源编号,只能按编号递增的顺序申请(顺序资源分配法);会限制编程、浪费资源

四个条件是必要条件:死锁发生时一定都成立,但都成立不一定死锁。还要分清三个容易混的词:

死锁饥饿死循环
是什么一组进程互相等,全都推进不了某个进程一直得不到服务(别人在正常推进)程序自己的逻辑错误,一直在跑
涉及几个进程至少 2 个,都处于阻塞态可以只有 1 个;它可能在就绪态(等 CPU)1 个,处于运行态或就绪态
由谁造成资源分配不当 + 推进顺序不当调度策略不公平(如静态优先级、SJF)程序员

死锁的原因:① 系统资源的竞争——不可剥夺的资源(打印机、磁带机)数量不够分;可剥夺的资源(CPU、内存)不会引起死锁。② 进程推进顺序非法——P1 占着 R1 请求 R2,P2 占着 R2 请求 R1;PV 操作顺序不当也属于这一类(生产者先 P(mutex) 再 P(empty))。

资源分配图:把“谁占着谁、谁在等谁”画出来

圆圈是进程,方框是一类资源,方框里的小圆点是这类资源的个数。资源 → 进程的箭头是“分配边”(已经给了它),进程 → 资源的箭头是“请求边”(它在等)。

P1 P2 R1 R2 分配给 请求 分配给 请求 死锁的四个必要条件(缺一不可): ① 互斥:资源一次只能给一个进程② 不可剥夺:用完之前别人不能抢③ 请求并保持:拿着一些又要新的④ 循环等待:P1 等 P2、P2 等 P1…成环 有环不一定死锁(资源有多个实例时),死锁一定有环
图 7-8 死锁 = 一组进程互相等待对方占着的资源,谁都无法推进。发生死锁时,至少有两个进程处于阻塞态(2019-30 Ⅳ 正确)。2016-25:P1 要 R1R2、P2 要 R2R3、P3 要 R1R3、P4 要 R2,出现死锁时死锁进程至少 3 个(P1、P2、P3 各拿一个成环)。

“最少要多少个资源才不会死锁”

鸽巢法

最坏情况:每个进程都拿到“差一个就够”的数量,然后全部卡住。n 个进程分别最多需要 m1, m2, …, mn 个,这时共占用 Σ(mi − 1) 个。只要再多 1 个,就一定有人能跑完并释放资源 → 不死锁的最少资源数 = Σ(mi − 1) + 1。

2009-25操作系统
某计算机系统中有 8 台打印机,由 K 个进程竞争使用,每个进程最多需要 3 台打印机。该系统可能会发生死锁的 K 的最小值是( )。
  • A. 2
  • B. 3
  • C. 4
  • D. 5
展开解析

考点7.6 最少要多少个资源才不会死锁

C最坏情况:每个进程都拿到“最多需求 − 1”台、都在等最后 1 台。只要这时还剩 1 台,就有进程能做完并释放,不会死锁。

  1. 不死锁的条件:K × (3 − 1) + 1 ≤ 8,即 K ≤ 3.5,所以 K ≤ 3 一定不死锁。
  2. K = 4:4 个进程各拿 2 台,8 台正好分完,每个都在等第 3 台、又都不释放 → 可能死锁。所以最小值是 4。
  • A 错K = 2 时两个进程就算都拿满 3 台也只用 6 台,8 台足够,根本不会缺资源。
  • B 错K = 3 时最坏各拿 2 台用掉 6 台,还剩 2 台,任意一个进程拿到第 3 台就能做完并释放,不会死锁。
  • C 对K = 4 时 4 × 2 = 8 台被分光,四个进程互相等待,可能死锁;比 4 小的 K 都不会。
  • D 错K = 5 确实可能死锁,但题目问“最小值”,K = 4 已经可能死锁了。

资源数 ≥ Σ(各进程最大需求 − 1) + 1 就不会死锁。同类:2014-24(需求 3、4、5 → (2 + 3 + 4) + 1 = 10);2021-31(n 个进程各需 2 个 → n + 1)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2009-25 改
某系统有 10 台打印机,由 K 个进程竞争使用,每个进程最多需要 4 台。该系统可能发生死锁的 K 的最小值是( )。
  • A. 3
  • B. 4
  • C. 5
  • D. 10
展开解析

B不会死锁的条件:K × (4 − 1) + 1 ≤ 10,即 K ≤ 3。K = 3 时,最坏每个进程各拿 3 台,共 9 台,还剩 1 台,总有一个进程能凑齐 4 台。K = 4 时可能出现 3、3、3、1 的分配:10 台全部分完,前三个进程各差 1 台,第四个差 3 台,谁也拿不到,发生死锁。所以最小的 K 是 4。

变化点:2009-25 是 8 台、每个进程最多 3 台(K = 4)。公式都是 K × (最大需求 − 1) + 1 ≤ 资源数。

变式操作系统由 2014-24、2021-31 改
某系统有 n 台同类设备,4 个并发进程分别需要 2、3、4、5 台。能确保不发生死锁的 n 的最小值是( )。
  • A. 10
  • B. 11
  • C. 14
  • D. 15
展开解析

B最坏情况是每个进程都差一台:1 + 2 + 3 + 4 = 10 台。再多 1 台,就一定有一个进程能满足,运行完释放资源,其他进程也能依次完成。n = 11。

变化点:2014-24 是 3、4、5(10 台)。公式:Σ(需求 − 1) + 1。D 是全部需求之和,那是“同时满足所有进程”,比必要的多。

7.7死锁的三种对策与银行家算法

★★ 常考选择题
策略做法特点
预防破坏必要条件之一:一次性申请全部资源(破坏请求并保持);按编号顺序申请(破坏循环等待);允许剥夺最严格,保证不发生;资源利用率低,会限制申请顺序
避免每次分配前判断:分了之后系统还安全吗?不安全就不分(银行家算法)需要事先知道每个进程的最大需求
检测与解除随便分,定期用资源分配图检测(能完全化简 = 无死锁);发现死锁就剥夺资源、撤销进程或回退不限制分配;会给可能导致死锁的进程分配资源

2015-26:S1 用避免、S2 用检测——“S1 需要进程运行所需的资源总量信息而 S2 不需要”“S1 不会给可能导致死锁的进程分配资源而 S2 会”正确;“S1 会限制申请顺序”错(那是预防)。2013-32:安全状态一定无死锁;不安全状态不一定会死锁(只是有可能);银行家算法是避免,不是预防。2019-30:银行家算法不能用来判断系统是否已经死锁。

银行家算法:安全性检查

Need = Max − Allocation(还需要多少) Work = Available;找一个 Need ≤ Work 的进程 → 假设它运行完,Work += 它的 Allocation;重复 所有进程都能这样跑完 → 安全,这个顺序就是一个安全序列
2020-27操作系统
某系统中有 A、B 两类资源各 6 个,t 时刻资源分配及需求情况为:P1 已分配 (2,3)、需求总量 (4,4);P2 已分配 (2,1)、需求总量 (3,1);P3 已分配 (1,2)、需求总量 (3,4)。t 时刻安全性检测结果是( )。
  • A. 存在安全序列 P1、P2、P3
  • B. 存在安全序列 P2、P1、P3
  • C. 存在安全序列 P2、P3、P1
  • D. 不存在安全序列
展开解析

考点7.7 银行家算法:安全性检查

B表里给的是“需求总量”(Max),先算 Need = Max − 已分配,再从 Available 开始做安全性检查。

  1. Available = (6, 6) − (2 + 2 + 1, 3 + 1 + 2) = (1, 0)。Need:P1 = (2, 1),P2 = (1, 0),P3 = (2, 2)。
  2. Work = (1, 0):只有 P2 的 (1, 0) 满足 → P2 做完,Work = (1, 0) + (2, 1) = (3, 1)。
  3. Work = (3, 1):P1 (2, 1) 满足,P3 (2, 2) 不满足(B 类只有 1)→ P1 做完,Work = (3, 1) + (2, 3) = (5, 4)。
  4. Work = (5, 4):P3 满足。安全序列 P2、P1、P3(也是唯一的)。
  • A 错第一步就走不通:P1 的 Need = (2, 1),而 Available = (1, 0),A、B 两类都不够。
  • B 对P2 → P1 → P3 每一步 Need ≤ Work,是安全序列。
  • C 错P2 之后 Work = (3, 1),P3 的 Need = (2, 2) 中 B 类要 2 > 1,P3 不能接在 P2 后面。
  • D 错已经找到安全序列 P2、P1、P3,系统是安全的。常见错法是把“需求总量”直接当成 Need,结果谁都满足不了。

先看清表里给的是 Max(需求总量)还是 Need(尚需);Work 每次加的是“做完进程的已分配量”。

2018-26操作系统
系统中有 4 个同类资源,进程 P1、P2、P3 需要的资源数分别为 4、3、1,已申请到的分别为 2、1、0,则执行安全性检测算法的结果是( )。
  • A. 不存在安全序列,系统处于不安全状态
  • B. 存在多个安全序列,系统处于安全状态
  • C. 存在唯一安全序列 P3、P1、P2,系统处于安全状态
  • D. 存在唯一安全序列 P3、P2、P1,系统处于安全状态
展开解析

考点7.7 银行家算法:安全性检查

A安全性检查:Available = 总数 − 已分配;每次找一个 Need ≤ Work 的进程,假设它做完并归还资源,直到全部做完(安全)或卡住(不安全)。

  1. Available = 4 − (2 + 1 + 0) = 1;Need:P1 = 4 − 2 = 2,P2 = 3 − 1 = 2,P3 = 1 − 0 = 1。
  2. 只有 P3 能满足(Need 1 ≤ Work 1)。假设它做完,归还它原来已占有的 0 个:Work = 1 + 0 = 1(临时给它的那 1 个也还回来,Work 不变)。
  3. 此时 P1、P2 都还需要 2 > 1,卡住 → 不存在安全序列,不安全。
  • A 对P3 做完后 Work 仍为 1,P1、P2 都需要 2,找不到安全序列。
  • B 错连一个完整的安全序列都不存在:P3 之后 Work = 1,P1、P2 都需要 2,更谈不上“多个”。
  • C 错P3 之后 Work = 1,而 P1 还需要 4 − 2 = 2 > 1,P1 无法执行,P3、P1、P2 走不下去。
  • D 错P3 之后 Work = 1,而 P2 还需要 3 − 1 = 2 > 1,P2 无法执行,P3、P2、P1 走不下去。

Work 只增加“做完的进程原来已占有的资源”:P3 原来占 0 个,做完后 Work 不变。这正是本题的陷阱。

处理一个新请求:进程 Pi 请求 Request:① Request ≤ Needi?(否则出错:超过声明的最大值)② Request ≤ Available?(否则等待)③ 试着分配:Available −= Request,Allocation += Request,Need −= Request;④ 做安全性检查,安全才真正分配,否则撤销试分配、让 Pi 等待。

例操作系统银行家算法:处理三个请求
系统有 A、B、C 三类资源,T0 时刻 Available = (3, 3, 2),各进程的 Max 和 Allocation 为:P0 (7,5,3)/(0,1,0);P1 (3,2,2)/(2,0,0);P2 (9,0,2)/(3,0,2);P3 (2,2,2)/(2,1,1);P4 (4,3,3)/(0,0,2)。(1)T0 时刻是否安全?(2)P1 请求 (1,0,2),能否分配?(3)接着 P4 请求 (3,3,0),能否分配?(4)接着 P0 请求 (0,2,0),能否分配?
展开解析
  1. 先算 Need = Max − Allocation:P0 (7,4,3),P1 (1,2,2),P2 (6,0,0),P3 (0,1,1),P4 (4,3,1)。
  2. (1)安全性检查,Work = (3,3,2):P1 的 Need (1,2,2) ≤ Work → P1 完成,Work = (3,3,2) + (2,0,0) = (5,3,2);P3 (0,1,1) ≤ (5,3,2) → Work = (7,4,3);P4 (4,3,1) ≤ (7,4,3) → Work = (7,4,5);P0 (7,4,3) ≤ (7,4,5) → Work = (7,5,5);P2 (6,0,0) → Work = (10,5,7)。全部完成,安全,安全序列 P1, P3, P4, P0, P2(不唯一)。
  3. (2)P1 请求 (1,0,2):① (1,0,2) ≤ Need1 (1,2,2) ✓;② ≤ Available (3,3,2) ✓;③ 试分配:Available = (2,3,0),Allocation1 = (3,0,2),Need1 = (0,2,0);④ 安全性检查:Work = (2,3,0),P1 (0,2,0) ✓ → (5,3,2) → P3 → (7,4,3) → P4 → (7,4,5) → P0 → (7,5,5) → P2。安全,可以分配。
  4. (3)P4 请求 (3,3,0):≤ Need4 (4,3,1) ✓,但 > Available (2,3,0)(A 类只剩 2 个)→ 资源不够,P4 等待,不用做安全性检查。
  5. (4)P0 请求 (0,2,0):≤ Need0 ✓,≤ Available (2,3,0) ✓;试分配后 Available = (2,1,0)。检查:P1 还需 (0,2,0)(B 只有 1)✗,P3 还需 (0,1,1)(C 只有 0)✗,P0、P2、P4 也都不满足 → 找不到能完成的进程,不安全,撤销试分配,P0 等待。注意这时系统并没有死锁,只是分了就可能死锁。

以上结果已用程序验算。做大题时把每一步的 Work 写出来(得分点),并写明“存在安全序列……,系统安全,可以分配”。

死锁检测:资源分配图化简与死锁定理

“有环”和“死锁”的关系

每类资源都只有 1 个时:资源分配图有环 ⇔ 死锁。某类资源有多个时:有环只是死锁的必要条件,有环不一定死锁(下面的化简图就是有环却能完全化简的例子),要用化简来判断。没有环则一定不死锁。

“检测与解除”策略平时随便分配,隔一段时间(或 CPU 利用率下降时)检查一次有没有死锁。检查的方法就是化简资源分配图:找一个“不阻塞”的进程(它的请求都能满足),假设它运行完并释放全部资源——把它的所有边删掉;再用释放出来的资源去满足别人,重复下去。

P1 P2 P3 R2(1 个) R1(2 个) 分配 P1 请求 R1 分配 P2 请求 R2 分配 化简过程: ① P3 没有请求边,不阻塞 → 运行完,释放 R1 的 1 个 ② R1 有空了,P1 的请求可满足 → P1 运行完,释放 R2 ③ P2 拿到 R2 → 运行完 ④ 所有边都能消去 → 可完全化简,没有死锁 图中有环 P1 → R1 → P2 → R2 → P1, 但 R1 有 2 个实例,环不代表死锁 若每类资源都只有 1 个,有环就一定死锁
图 7-9 资源分配图化简。死锁定理:系统处于死锁状态 ⇔ 它的资源分配图不可完全化简。化简到最后还剩下边的那些进程,就是死锁进程。用表格做检测时,算法和银行家算法的安全性检查几乎一样,只是把“Need(还要多少)”换成“Request(当前正在申请多少)”。

死锁解除:检测到了怎么办

  • 资源剥夺法:挂起某些死锁进程,抢占它的资源分给别的死锁进程(要防止被挂起的进程长时间得不到资源而饥饿)。
  • 撤销(终止)进程法:强制撤销部分甚至全部死锁进程,释放它们的资源。按优先级、已运行时间、还需时间等挑“代价最小”的撤销。实现简单,但已做的工作白费。
  • 进程回退法:让一个或多个进程回退到足以避开死锁的地方(需要系统事先保存进程的历史检查点)。

对比三种策略:预防最保守(破坏必要条件,一定不会死锁,资源利用率低);避免要事先知道每个进程的最大需求(银行家算法,安全状态一定不死锁,不安全不一定死锁);检测与解除最乐观(不限制分配,允许死锁发生后再处理)。

预防、避免、检测、解除:四种对策放在一起比

问题引入:2013-32、2015-26、2019-30 都在考“这句话说的是哪种对策”。把四种对策放进同一张表、同一张图,就不会张冠李戴。

死锁预防死锁避免死锁检测死锁解除
分配策略最保守:宁可资源闲着折中:每次分配前先算一算最宽松:只要有就分,出了死锁再处理
具体做法破坏四个必要条件之一:一次申请全部(破坏请求并保持)、按编号顺序申请(破坏循环等待)、允许剥夺(破坏不剥夺)银行家算法:试分配后做安全性检查,不安全就不分化简资源分配图(死锁定理),或用类似银行家的表格算法资源剥夺、撤销进程、进程回退
要事先知道最大需求吗一次性申请时要要不要
会发生死锁吗不会不会(始终停在安全状态)会,发生后再处理
资源利用率低较高高
主要代价限制申请方式,可能饥饿每次分配都要计算;进程和资源数要相对固定定期检测的开销;解除时已做的工作白费
真题2015-26 C(会限制申请顺序的是预防)2013-32、2019-30 Ⅰ2015-26 D(会给可能导致死锁的进程分配资源)
系统所有可能的资源分配状态 安全状态存在安全序列一定不会死锁 不安全状态可能死锁 死锁状态(不安全的一部分) 避免:每一步都只走进左边绿区 检测与解除:随便走,走进红区后再用解除手段拉出来 安全 ⇒ 不死锁;死锁 ⇒ 不安全;但 不安全 ⇏ 死锁(2013-32)
图 7-10 安全、不安全与死锁三种状态的关系。预防则是从源头改变“游戏规则”(破坏必要条件),使系统根本走不到死锁区。银行家算法只能判断“分了以后还安全吗”,不能判断系统现在是否已经死锁(2019-30)。

四个必要条件的名称在不同教材里说法略有不同,做题时要对得上:互斥条件;不剥夺条件(也叫不可剥夺、非抢占);请求并保持条件(也叫请求与保持、部分分配);循环等待条件(也叫环路等待)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2018-26 改
系统中有 5 个同类资源,进程 P1、P2、P3 最多分别需要 4、3、2 个,已分别分配了 2、1、1 个。执行安全性检测算法的结果是( )。
  • A. 不存在安全序列,系统处于不安全状态
  • B. 存在唯一的安全序列 P3、P1、P2
  • C. 存在唯一的安全序列 P3、P2、P1
  • D. 存在多个安全序列,系统处于安全状态
展开解析

D可用 = 5 − 4 = 1。尚需:P1 2、P2 2、P3 1。只有 P3 能先完成,释放后可用 = 2;此时 P1、P2 都能完成,谁先都行。安全序列有 P3、P1、P2 和 P3、P2、P1 两个。

变化点:2018-26 是 4 个资源、已分配 2、1、0:可用 1 个,P3 完成后只还回 1 个(它原来没分到资源),可用仍是 1,P1、P2 都满足不了,所以不安全。这里总数多 1 个,而且 P3 已经占着 1 个,完成后能还回 2 个,结论完全不同。安全序列也可以不唯一。

变式操作系统由 2012-27、2022-26 改
系统有 A、B、C 三类资源,当前可用资源为 (2, 1, 1)。P0、P1、P2 已分配 (1, 0, 1)、(0, 1, 0)、(1, 1, 0),尚需 (1, 1, 1)、(2, 1, 2)、(0, 0, 1)。此时 P1 请求 (1, 0, 1),按银行家算法,系统应( )。
  • A. 立即分配,分配后系统安全
  • B. 拒绝,因为请求超过了 P1 的尚需量
  • C. 让 P1 等待,因为可用资源不足
  • D. 让 P1 等待,因为试分配后系统不安全
展开解析

D请求 (1, 0, 1) ≤ 尚需 (2, 1, 2),也 ≤ 可用 (2, 1, 1),可以试分配。试分配后可用 = (1, 1, 0),三个进程的尚需分别为 (1, 1, 1)、(1, 1, 1)、(0, 0, 1),都需要 1 个 C,而 C 已经没有了,找不到安全序列。所以不分配,P1 等待。

变化点:真题只做安全性检测。这里多了“资源请求算法”:先检查请求 ≤ 尚需、请求 ≤ 可用,再试分配并做安全性检测。注意当前状态本身是安全的(例如 P2、P0、P1)。

变式操作系统由 2013-32、2019-30、2015-26 改
下列关于死锁处理方法的叙述中,正确的是( )。
  • A. 死锁避免要求进程一次性申请全部资源
  • B. 按银行家算法,系统处于不安全状态时一定会发生死锁
  • C. 死锁检测时,若资源分配图不能完全化简,则系统处于死锁状态
  • D. 规定所有进程按资源编号递增的顺序申请资源,属于死锁避免
展开解析

C死锁定理:系统处于死锁状态,当且仅当该状态的资源分配图不能完全简化。A 和 D 都是死锁预防(破坏请求并保持、破坏循环等待)。B 错:不安全状态只是可能进入死锁,不一定死锁(2013-32)。

变化点:把预防、避免、检测三种方法的“做法”打乱了放在一起,考能不能对号入座。

变式操作系统由 2015-26 改
某系统采用“死锁检测与解除”的方法处理死锁。下列叙述中,正确的是( )。
  • A. 必须事先知道每个进程对各类资源的最大需求量
  • B. 系统可能把资源分配给会导致死锁的进程
  • C. 系统始终处于安全状态
  • D. 进程必须按资源编号递增的顺序申请资源
展开解析

B检测与解除对分配不加限制,只要有资源就分,所以可能分给会导致死锁的进程,出了死锁再检测、解除(2015-26 Ⅲ)。A、C 是死锁避免的特点;D 是死锁预防(破坏循环等待)。

考点:四种对策对比。

第 7 篇自测
  1. 信号量初值 2,当前值 −3,有几个进程在等?现在有几个进程占着资源?
    答案
    3 个在等;2 个资源全被占用(2 个进程在用)。
  2. 生产者—消费者中,交换消费者的 P(full) 和 P(mutex) 会怎样?
    答案
    缓冲区空时,消费者先拿到 mutex 再在 full 上阻塞,生产者拿不到 mutex 放不进产品,full 永远是 0 → 死锁。
  3. Peterson 算法满足“让权等待”吗?
    答案
    不满足,进不去就在 while 里空转(忙等)。
  4. 5 个进程共享同类资源,每个最多需要 4 个,不会死锁的最少资源数?
    答案
    5×3 + 1 = 16。
  5. 用 PV 实现:S1 执行完后 S2、S3 才能执行,S2、S3 都完成后 S4 才能执行。
    答案
    四条边四个信号量初值 0:S1 后 V(a) V(b);S2 前 P(a) 后 V(c);S3 前 P(b) 后 V(d);S4 前 P(c) P(d)。
  6. 有 6 个进程共享某资源区,最多允许 3 个进程同时进入,信号量的取值范围是多少?
    答案
    初值 3,最小 3 − 6 = −3,范围 −3 ~ 3。
  7. 水果盘问题中盘子能放 2 个水果,还需要改什么?
    答案
    plate 初值改为 2,并增加 mutex = 1,把“放水果”“取水果”分别用 P(mutex)…V(mutex) 夹住(两个家长或两个孩子可能同时访问盘子)。
  8. 管程中执行 x.signal() 时没有进程在 x 上等待,会发生什么?和信号量的 V 有什么不同?
    答案
    什么都不发生,也不留下记录;信号量的 V 会把 value 加 1,之后来的 P 能直接用上这次释放。
  9. 系统中每类资源都只有 1 个,资源分配图出现了环,一定死锁吗?若某类资源有 2 个呢?
    答案
    每类只有 1 个时一定死锁;有多个实例时不一定,要化简判断。

7.✎本篇真题闯关(37 题)

本篇各小节挂的真题全部在这里,按小节排好。可以展开后一道道做,也可以用弹窗模式连续做。做题记录(对/错)只保存在你这台电脑的浏览器里,小节标题下的题号会随之变绿或变红。

第 8 篇 · 融合 · 计组第 3 章 × 操作系统第 3 章

融合二:存储系统全景 —— 从一个地址到一个数据

这是全书最长、也是 408 综合大题最集中的一篇。计组讲“硬件怎么快速找到数据”(主存芯片、Cache、TLB、地址变换机构),操作系统讲“内存怎么分给多个进程、不够了怎么办”(分页、分段、虚拟内存、页面置换)。两者最终在一次访存上汇合:CPU 给出虚拟地址 → 查 TLB → 查页表 → 得到物理地址 → 查 Cache → 访问主存 → 缺页了交给操作系统从磁盘调入。本篇先分别打好基础,最后用一个可以一步步点的动画把整条路串起来。

对应:王道计组第 3 章;王道操作系统第 3 章难度:★★★★★综合大题:2010、2011、2013、2014、2015、2016、2018、2019、2020、2021、2023、2024、2025 都有至少一道落在本篇

8.1为什么要分层:局部性原理

★ 考过选择题

理想的存储器又快、又大、又便宜,但现实中三者不可兼得:越快的越贵、越小。办法是分层:CPU 旁边放少量极快的,后面放大量便宜慢的,并且想办法让 CPU 大多数时候都在快的那一层找到数据。

寄存器 Cache(SRAM)几 ns,KB~MB 主存(DRAM)几十 ns,GB 辅存(磁盘、SSD)ms 级,TB 越往下:越慢越大越便宜 Cache—主存层次解决 CPU 与主存速度不匹配全部由硬件完成,程序员看不见 主存—辅存层次(虚拟存储)解决主存容量不够硬件 + 操作系统共同完成
图 8-1 存储层次。上一层保存的是下一层内容的副本。Cache 里有的,主存里一定有;主存里有的页,磁盘上也有它的来源(对换区或文件)。
局部性原理:分层能起作用的根本原因

时间局部性:刚访问过的,很可能马上又访问(循环里反复用的变量、循环体的指令)。
空间局部性:访问了某个地址,它附近的地址很可能马上被访问(数组顺序遍历、顺序执行的指令)。
所以把“刚用过的”和“它旁边的一整块”一起搬到快的一层,后面的访问大多就能在快层命中。

2017-14计组
程序段 for (i = 0; i <= 9; i++){ temp = 1; for (j = 0; j <= i; j++) temp *= a[j]; sum += temp; },下列关于数组 a 的访问局部性的描述中,正确的是( )。
  • A. 时间局部性和空间局部性皆有
  • B. 无时间局部性,有空间局部性
  • C. 有时间局部性,无空间局部性
  • D. 时间局部性和空间局部性皆无
展开解析

考点8.1 局部性原理

A内层循环顺序访问 a[0]、a[1]、… → 相邻元素接连被访问 → 空间局部性;外层每一轮都从 a[0] 开始重新访问 → 同一元素在不久后又被访问 → 时间局部性。

  • A 对两种局部性都有。
  • B 错a[0] 被访问了 10 次、a[1] 被访问 9 次……有时间局部性。
  • C 错顺序访问数组元素,有空间局部性。
  • D 错两种局部性都有:a[0] 在每轮外循环都被重新访问(时间局部性),内循环按 a[0]、a[1]… 顺序访问(空间局部性)。

“同一个东西反复用”= 时间局部性;“挨着的东西接连用”= 空间局部性。每个元素只访问一次的循环只有空间局部性(2023-43)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2017-14 改
程序段 for (i = 0; i < n; i++) sum += a[i]; 中,对变量 sum 和数组 a 的访问分别主要体现了( )。
  • A. 时间局部性、空间局部性
  • B. 空间局部性、时间局部性
  • C. 都只有时间局部性
  • D. 都只有空间局部性
展开解析

Asum 在每次循环中都被反复访问,是时间局部性;a[i] 按地址顺序依次访问,是空间局部性(每个元素只访问一次,没有时间局部性)。

变化点:2017-14 的数组 a 在内层循环中被反复访问,所以两种局部性都有。这里每个元素只访问一次,a 只有空间局部性。

8.2主存:芯片、容量扩展与多体交叉

★★★ 高频选择题综合题

存储器的分类和性能指标

SRAM(静态)DRAM(动态)ROM / Flash
存储原理触发器(双稳态电路)电容上的电荷ROM 出厂/编程写入;Flash 用 MOS 管浮栅存电荷
断电丢失(易失性)不丢失(非易失)
刷新不需要需要定期刷新(电荷会漏;SDRAM 也要,2015-17)不需要
速度 / 集成度 / 价格快 / 低 / 贵慢 / 高 / 便宜Flash 写比读慢得多(要先擦除,2012-16)
用途Cache、TLB主存BIOS(引导程序)、U 盘、SSD
  • 存取方式:RAM 和 ROM 都是随机存取(访问任何单元时间都一样,2010-16 选 A:RAM 易失、ROM 非易失,两者都随机存取;ROM 不能做 Cache,也不用刷新);磁带是顺序存取;磁盘是直接存取;CD-ROM 不是随机存取(2011-14);TLB 是相联存储器(按内容查找)。
  • 存取时间:从发出读命令到数据出来;存取周期:连续两次访问之间的最小间隔(比存取时间长,DRAM 读后要恢复);主存带宽 = 每秒传送的数据量。
  • DRAM 地址复用:行地址、列地址分两次从同一组引脚送入,地址引脚减半。4M×8 位 DRAM:地址 22 位 → 引脚 11 根,加数据 8 根 = 19(2014-15)。行列数怎么选:行列尽量接近(引脚少),且行数少一点(刷新按行进行,行少刷新开销小):2K×1 选 r=32、c=64(2018-17)。
按什么分分类(选择题判断用)
存取方式随机存取(RAM、ROM:任何单元的存取时间相同);顺序存取(磁带:只能按顺序找);直接存取(磁盘:先直接找到磁道区域,再在区域内顺序找);相联存储器(按内容查找,如 TLB、全相联 Cache 的标记比较)
信息能否长期保存易失性(SRAM、DRAM:断电即丢);非易失性(ROM、Flash、磁盘、光盘)
读出后信息还在吗破坏性读出(DRAM:读出时电容放电,读后要“再生”写回,所以存取周期 > 存取时间);非破坏性读出(SRAM)
ROM 的种类MROM(出厂写死)、PROM(一次可编程)、EPROM(可擦除重写,紫外线擦除的 UVEPROM、电擦除的 EEPROM)、Flash(由 EEPROM 发展而来,可在线擦写,U 盘、SSD 都用它)。ROM 和 RAM 一样都是随机存取的
存取周期 = 存取时间 + 恢复时间(连续两次独立访问之间的最短间隔) 主存带宽 = 每次存取的数据位数 ÷ 存取周期(例:存取周期 50ns、每次 32 位 → 32bit/50ns = 640Mbit/s = 80MB/s)
DRAM 芯片内部:一个 r 行 × c 列的矩阵(例:2K×1 位,r = 32,c = 64) 被选中的一整行 行译码 行缓冲(一整行读出来暂存) 列译码:从行缓冲选出一位/一字节 地址引脚 ① RAS:送行地址 ② CAS:送列地址 为什么这样设计? 行、列地址分两次从同一组引脚送入 → 地址引脚只要 log₂(max(r, c)) 根 4M×8:22 位地址 → 11 根 + 8 根数据 = 19 刷新 电容会漏电,要定期(如 2ms 内)把每行 读出再写回,一次刷新一整行 → 行数少一点,刷新次数少(2018-17) 集中刷新 / 分散刷新 / 异步刷新(常用)
图 8-2 DRAM 芯片的地址复用与刷新。选行列数的两条原则:① 行、列尽量接近(引脚数由较大的那个决定);② 在引脚数相同的前提下,行数取小的那个(刷新按行进行)。2K = 211:r × c 可以是 32×64 或 64×32,引脚都是 6 根,行少的 32×64 刷新开销小。2022-17:8192×8192 的芯片地址引脚只要 13 根(不是 26 根),一行 8192×8 位进入行缓冲。

DRAM 的三种刷新方式

DRAM 电容上的电荷会慢慢漏掉,必须在刷新周期(常为 2ms)内把每一行都读出再写回一遍。刷新按行进行,一次刷新一整行;由存储器内部的刷新电路自动完成,不需要 CPU 参与,但刷新时这块芯片不能被访问。以“2ms 内刷新 128 行、存取周期 0.5μs”为例:

方式做法优缺点
集中刷新在 2ms 里拿出一段连续时间,一口气把 128 行刷完(占 128 × 0.5 = 64μs),其余时间正常读写读写时不受影响;但这 64μs 内不能访存,叫“死区”
分散刷新每个存取周期分成两半:前半读写、后半刷新一行(存取周期变成 1μs)没有死区;但存取周期加长,而且刷得太勤(128μs 就刷完一遍,远小于 2ms)
异步刷新把 128 行的刷新均匀分散在 2ms 里:每隔 2ms / 128 ≈ 15.6μs 刷新一行前两者的结合,死区很短(每次只有一个存取周期),最常用
一个刷新周期 2ms,128 行,存取周期 0.5μs(图不按比例,灰色 = 正常读写,红色 = 刷新) 集中 正常读写 1936μs 死区 64μs 分散 … 每个存取周期 1μs = 读写 0.5μs + 刷新一行 0.5μs无死区,但访存变慢一倍;128μs 就刷完一遍(刷得过勤) 异步 15.6μs 每隔 2ms ÷ 128 ≈ 15.6μs 刷新一行,每次只“死”一个存取周期 0.5μs
图 8-3 三种刷新方式的时间分布。“死时间”指因刷新而不能访存的时间。集中刷新死时间率 = 64μs ÷ 2ms = 3.2%,且集中在一段;分散刷新没有死区但把每次访存都拖慢一倍;异步刷新把 128 次刷新均匀撒开,每次只占一个存取周期(数字已用 verify/08/p08_calc.py 验算)。

算刷新间隔的套路:异步刷新间隔 = 刷新周期 ÷ 行数;集中刷新死区 = 行数 × 存取周期;分散刷新的存取周期 = 原存取周期 × 2。注意“行数”是芯片矩阵的行数,不是总单元数——这正是 2018-17 要“行数取小”的原因。

用小芯片拼出大内存

位扩展
芯片的字长不够:几片并联,地址线共用,数据线各管几位。8 片 8K×1 → 8K×8。
字扩展
芯片的单元数不够:几片“串联”,数据线共用,用高位地址译码产生片选,决定哪一片工作。
字位同时扩展
先位扩展成一组,再字扩展多组。芯片数 = 总容量 ÷ 单片容量(都换成“单元数×位数”)。
用 2K×4 位芯片组成 8K×8 位存储器(2010-15):共 8K×8 / (2K×4) = 8 片,2 片一组,4 组 CPU地址 A12~A0 2-4 译码器输入 A12 A11 A12A11 A10~A0 送所有芯片(片内地址) 组 0:2 片0000~07FFH 组 1:2 片0800~0FFFH 组 2:2 片1000~17FFH 组 3:2 片1800~1FFFH 片选 CS0~CS3:A12A11 = 00、01、10、11 分别选中一组 每组内:一片提供 D7~D4,另一片提供 D3~D0(位扩展)
图 8-4 字位同时扩展。地址 0B1FH 写成 13 位(A12~A0)是 01011 0001 1111,A12A11 = 01 → 在组 1(0800H~0FFFH),该组芯片的最小地址是 0800H(2010-15 选 D)。
2009-15计组
某计算机主存容量为 64KB,其中 ROM 区为 4KB,其余为 RAM 区,按字节编址。现要用 2K×8 位的 ROM 芯片和 4K×4 位的 RAM 芯片来设计该存储器,则需要上述规格的 ROM 芯片数和 RAM 芯片数分别是( )。
  • A. 1、15
  • B. 2、15
  • C. 1、30
  • D. 2、30
展开解析

考点8.2 用小芯片拼出大内存

D芯片数 = 要的总容量 ÷ 一片芯片的容量(都换成“位”算),位不够先位扩展、字不够再字扩展。

  1. ROM 区 4KB = 4K×8 位,芯片 2K×8 位:位宽够,只做字扩展 → 4K×8 ÷ (2K×8) = 2 片。
  2. RAM 区 64KB − 4KB = 60KB = 60K×8 位,芯片 4K×4 位:先 2 片位扩展成 4K×8,再字扩展 60K ÷ 4K = 15 组 → 2 × 15 = 30 片。
  • A 错ROM 算成 1 片是把 2K×8 的芯片当成了 4K;RAM 算成 15 片是只做了字扩展,忘了 4 位宽的芯片要 2 片并成 8 位。
  • B 错ROM 2 片对;RAM 15 片只够 60K×4 位,每个单元只有 4 位,缺了位扩展(要再乘 2)。
  • C 错RAM 30 片对;ROM 1 片只有 2K×8 = 2KB,装不下 4KB 的 ROM 区。
  • D 对ROM 2 片、RAM 30 片。

芯片数 = (字数 ÷ 芯片字数) × (位数 ÷ 芯片位数)。同类:2016-16(RAM 56KB 用 8K×4 位 → 56K×8 ÷ (8K×4) = 14 片);2021-15(按 32 位字编址 4M 字,用 512K×8 位 → 8 × 4 = 32 片);2023-15(RAM:ROM = 3:1,ROM 在高端 → 30000000H ~ 3FFFFFFFH)。

主存和 CPU 怎么连:完整设计一次

大题常给出“地址空间怎么分配 + 有哪些芯片”,要求选芯片、画连接图、写出每片芯片的地址范围。步骤是固定的:① 按地址分配算每个区域的容量,选芯片、算片数;② 片内地址用低位地址线(芯片有 2k 个单元就接 Ak−1~A0);③ 剩下的高位地址线接译码器产生片选(译码片选法;若直接用某根高位地址线当片选叫线选法,简单但地址空间不连续、浪费);④ 数据线按位扩展分配;⑤ 读写控制线 R/W̄ 接 RAM 的写使能,访存请求 MREQ̄ 接译码器的使能端(不访存时一片都不选)。

例计组存储器与 CPU 的连接
CPU 有 16 根地址线 A15~A0、8 根数据线 D7~D0,按字节编址,访存控制信号 MREQ̄(低电平有效)、读写信号 R/W̄。要求:0000H~1FFFH 为系统程序区(只读),2000H~5FFFH 为用户程序区(可读写)。现有 8K×8 位 ROM、8K×4 位 RAM 芯片和 3-8 译码器,设计连接方案。
展开解析
  1. 算容量、选芯片:系统区 0000H~1FFFH 共 2000H = 8K 字节 → 8K×8 ROM 1 片。用户区 2000H~5FFFH 共 4000H = 16K 字节 → 16K×8 ÷ (8K×4) = 4 片 RAM:每 2 片位扩展成 8K×8 为一组,共 2 组。
  2. 片内地址:每片 8K = 213 个单元 → A12~A0 接所有芯片的地址端。
  3. 片选:剩下 A15、A14、A13 接 3-8 译码器的输入 C、B、A。0000H~1FFFH 时 A15A14A13 = 000 → Y0 选 ROM;2000H~3FFFH 为 001 → Y1 选第 1 组 RAM;4000H~5FFFH 为 010 → Y2 选第 2 组 RAM;MREQ̄ 接译码器使能端。
  4. 数据线:ROM 接 D7~D0;每组 RAM 中一片接 D7~D4、另一片接 D3~D0。读写:R/W̄ 接 4 片 RAM 的写使能;ROM 只读,不接写信号。
CPU A15~A13MREQ̄A12~A0D7~D0R/W̄ 3 根 使能 3-8译码器 CBAG Y0Y1Y2 片选 ROM 8K×8D7~D0 RAM 8K×4D7~D4 RAM 8K×4D3~D0 RAM 8K×4D7~D4 RAM 8K×4D3~D0 0000H~1FFFH 2000H~3FFFH 4000H~5FFFH 所有芯片的地址端接 A12~A0(片内 13 位 = 8K 个单元),图中省略;R/W̄ 接 4 片 RAM 的写使能;ROM 只读。 A15A14A13 = 000 → Y0 选 ROM;001 → Y1 选第 1 组 RAM;010 → Y2 选第 2 组 RAM;Y3~Y7 空着,可以以后扩充。

检查地址范围的方法:把每组的片选码(A15A14A13)拼上片内地址全 0 和全 1。例如 Y1:001 0 0000 0000 0000 = 2000H 到 001 1 1111 1111 1111 = 3FFFH ✓。2010-15 问“地址 0B1FH 所在芯片的最小地址”,就是反过来:先看高位落在哪个片选码,再把片内地址清零。

片选的两种做法:线选法与译码片选法

问题引入:字扩展时要用高位地址决定“哪一片工作”。最省事的办法是一根地址线直接接一片芯片的片选端(线选法);更规范的办法是用译码器(译码片选法)。两者差在哪?画出地址空间就一目了然。

设定:CPU 有 16 根地址线,用 4 片 2K×8 位芯片做字扩展,A10~A0 接所有芯片作片内地址;片选低电平有效;A15 取 0。

① 线选法:A14、A13、A12、A11 各管一片 芯片 0 ← A11A14~A11 = 11107000H~77FFH 芯片 1 ← A12A14~A11 = 11016800H~6FFFH 芯片 2 ← A13A14~A11 = 10115800H~5FFFH 芯片 3 ← A14A14~A11 = 01113800H~3FFFH 地址不连续;任意两根线同时为 0 会同时选中两片(必须避免) 优点:不用译码器,电路最简单 ② 译码片选:A12A11 接 2-4 译码器 2-4译码器 A12 A11 芯片 0(Y0)0000H~07FFH 芯片 1(Y1)0800H~0FFFH 芯片 2(Y2)1000H~17FFH 芯片 3(Y3)1800H~1FFFH 地址连续,同一时刻只会选中一片 代价:多一片译码器;A15~A13 也可接入扩展更多芯片
图 8-5 同样 4 片芯片,两种片选。线选法每片芯片对应“一根线为 0、其余为 1”的组合,四段地址散落在地址空间里,中间大片地址不能用,而且程序不能让两根线同时为 0;译码片选把 2 位编码翻译成 4 个互斥的片选信号,地址连续。地址范围已用 verify/08/p08_calc.py 验算。

真题怎么考:大题(如 8.2 的“完整设计一次”、2016-16、2023-15)默认用译码片选,要写出每片的地址范围;选择题偶尔问线选法的特点(简单、地址空间不连续、浪费地址空间)。判别法:给出片选连接方式后,先写“让这片被选中时高位地址必须是什么”,再拼上片内地址的全 0 和全 1,得到地址范围。

单体多字、多体并行:高位交叉与低位交叉怎么分配地址

问题引入:CPU 越来越快,一个存储体每 200ns 才能读一个字,跟不上。提速的办法有两类:一次多读几个(单体多字),或者多个体一起干(多体并行)。多体又分高位交叉和低位交叉,差别全在“相邻地址落在哪个体”。

① 高位交叉:体号 = 地址高位 体001…1023 体110241025…2047 体22048… 体33072… 顺序读 0、1、2…总在体 0不能流水,只用来扩充容量 ② 低位交叉:体号 = 地址 mod 4 体0048… 体1159 体22610 体33711 顺序读依次落在不同体每隔 T/4 启动一个体,流水 ③ 单体多字(单体四字) 一个存储体,一行 = 4 个字字0 字1 字2 字3 一次读出 4 个字进数据寄存器 要求数据、指令连续存放遇到转移指令或数据不连续,多读出来的字就白读了 设存取周期 T = 200ns,每字 4B,总线传一个字 50ns:单体单字 20MB/s;单体四字、四体低位交叉的最大带宽都是 80MB/s
图 8-6 三种组织方式的地址分配。高位交叉把地址空间“竖着切”,每体一段连续地址,适合多道程序同时访问不同的体;低位交叉“横着切”,相邻地址在相邻体,同一程序顺序访问就能流水。
组织方式体号由什么决定顺序读 n 个字的时间(T = 200ns,r = 50ns,n = 8)最大带宽(字 4B)
单体单字——n × T = 1600ns4B / 200ns = 20MB/s
单体四字——⌈n / 4⌉ × T = 400ns(数据连续时)16B / 200ns = 80MB/s
四体高位交叉地址高位n × T = 1600ns(总在同一个体)顺序访问 20MB/s
四体低位交叉地址低位(地址 mod 4)T + (n − 1) × r = 550ns4B / 50ns = 80MB/s

以上数字已用 verify/08/p08_calc.py 验算。双端口 RAM(下面“多体交叉存储器”末尾的“了解”)是另一条思路:同一个存储体开两套独立的地址、数据、控制端口,让两个部件同时访问;只有两边同时访问同一个单元、而且至少有一边在写时才冲突(同时写 → 写入错误;一写一读 → 读出错误),由判断逻辑让一方等待(BUSY);两边同时读同一单元不冲突。

真题怎么考:2012-43(4)四体交叉的最大带宽;2015-18 低位交叉的访存冲突;2017-13、2026-15 交叉编址下一个变量跨几个存储字。判别法:先算“体号 = 地址 mod m”(低位交叉)或“体号 = 地址 ÷ 每体容量”(高位交叉),再看相邻访问是否落在同一体。

多体交叉存储器:让主存“流水”起来

一个存储体读一次要一个存取周期,这期间不能再读它。如果有 m 个独立的存储体,把相邻的地址放到不同的体(低位交叉:体号 = 地址 mod m),就可以每隔 1/m 个存取周期启动一个体,连续读 m 个字只比读 1 个字多一点点时间。

4 体低位交叉:体号 = 地址 mod 4 体 0048… 体 1159… 体 22610… 体 33711… 时间 →(存取周期 T,每隔 T/4 启动一个体) 体 0 读 体 1 读 体 2 读 体 3 读 连续读 4 个字只要 T + 3×T/4,而不是 4T
图 8-7 低位交叉。最大带宽 = m × 每体字长 / 存取周期:2012-43 第 4 问 4 体、每体 4B、存取周期 50ns → 4×4B/50ns = 320MB/s。冲突:连续 m 次访问中有两次落在同一个体,后一次必须等(2015-18:地址序列 …8004、8000 两个都是体 0 且相邻,可能冲突)。高位交叉(按地址高位分体)相邻地址在同一体,不能并行,只用来扩充容量。
m 体低位交叉,存取周期 T,总线传送一个字的时间 r,理想情况 T = m × r(每隔 r 启动一个体) 连续读 n 个字:低位交叉 t = T + (n − 1) × r;高位交叉(或单体)顺序读 t = n × T 例:m = 4、T = 200ns、r = 50ns,连续读 8 个字:低位交叉 200 + 7 × 50 = 550ns;顺序方式 8 × 200 = 1600ns

另外两种提速办法 了解:双端口 RAM——同一个存储器有两组独立的地址线、数据线和读写控制,两个部件(如 CPU 和 I/O 设备、两个 CPU)可以同时访问;两个端口同时访问同一个单元、且至少一个在写时会冲突,由判断逻辑让其中一个等待(发出 BUSY 信号);同时读同一单元不冲突。单体多字——一个存储体每次读出多个连续的字,要求指令和数据在主存里连续存放。

2017-13计组
某计算机主存按字节编址,由 4 个 64M×8 位的 DRAM 芯片采用交叉编址方式构成,并与宽度为 32 位的存储器总线相连,主存每次最多读写 32 位数据。若 double 型变量 x 的主存地址为 804001AH,则读取 x 需要的存储周期数是( )。
  • A. 1
  • B. 2
  • C. 3
  • D. 4
展开解析

考点8.2 多体交叉存储器2.7 边界对齐

C4 片低位交叉,每个存储周期 4 片各出 1 个字节,拼成一个从 4 的倍数地址开始的 32 位字。数 x 跨了几个这样的“4 字节行”,就要几个存储周期。

  1. double 占 8 字节:804001AH ~ 8040021H。
  2. 按 4 字节一行划分:8040018H ~ 1BH(用到 1A、1B)、804001CH ~ 1FH(全用)、8040020H ~ 23H(用到 20、21)→ 跨 3 行。
  3. 每行一个存储周期 → 需要 3 个存储周期。
  • A 错主存每次最多读写 32 位 = 4 字节,8 字节的 double 至少要 2 个存储周期,1 个周期不可能读完。
  • B 错2 个周期是 x 按 8 字节(至少 4 字节)对齐时的结果;但 804001AH 除以 4 余 2,没有对齐,前后各多跨出半行。
  • C 对x 跨 8040018H、804001CH、8040020H 三行,3 个存储周期。
  • D 错x 的 8 个字节只落在 3 个 4 字节行里,每行一个周期就能整行读出,用不到 4 个周期。

读一个数要几个存储周期 = 它跨了几个“存储字行”。边界对齐(第 2 篇)就是为了让数据不跨行。同类:2026-15(八片交叉,同一片的地址低 3 位相同)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2009-15、2016-16 改
某计算机主存容量为 128KB,按字节编址,其中 ROM 区 16KB,其余为 RAM 区。用 8K×8 位的 ROM 芯片和 16K×4 位的 RAM 芯片设计该存储器,需要的 ROM 芯片数和 RAM 芯片数分别是( )。
  • A. 2、7
  • B. 2、14
  • C. 4、14
  • D. 2、28
展开解析

BROM:16KB ÷ (8K × 8 位 = 8KB) = 2 片。RAM:112KB × 8 位 ÷ (16K × 4 位) = 112K × 8 ÷ 64K = 14 片(每 2 片做位扩展拼成 16K × 8 位,共 7 组)。

变化点:2009-15 是 64KB、2K × 8 位 ROM 和 4K × 4 位 RAM。方法不变:芯片数 = 所需总位数 ÷ 每片的位数,ROM 区和 RAM 区分开算。A 是只数了组数,忘了每组要 2 片做位扩展。

变式计组由 2010-15 改
用若干片 4K×4 位的芯片组成 16K×8 位的存储器(按字节编址),地址 2A5CH 所在芯片的最小地址是( )。
  • A. 0000H
  • B. 2000H
  • C. 2A00H
  • D. 2800H
展开解析

B每 2 片(位扩展)组成一组 4K × 8 位,管 4K = 1000H 个地址。2A5CH 落在第 3 组,即 2000H ~ 2FFFH,所以最小地址是 2000H。

变化点:2010-15 是 2K × 4 位的芯片,每组管 800H 个地址(0B1FH → 0800H)。先算出每组管多少地址,再看目标地址落在第几组。

变式计组由 2014-15、2018-17 改
某 DRAM 芯片的容量为 16M×4 位,地址线采用行、列地址分时复用。该芯片的地址引脚和数据引脚总数至少是( )。
  • A. 16
  • B. 28
  • C. 14
  • D. 20
展开解析

A16M = 224,需要 24 位地址。DRAM 的行地址和列地址分两次从同一组引脚送入,地址引脚只要 24 ÷ 2 = 12 根。数据引脚 4 根,共 16 根。

变化点:2014-15 是 4M × 8 位(11 + 8 = 19)。B 是没有按分时复用来算(24 + 4)。SRAM 不复用地址引脚,DRAM 才复用。

变式计组由 2015-18 改
某计算机采用 4 体低位交叉存储器,每个存储体的存取周期是总线传送周期的 4 倍。存储器总线上依次出现的主存地址(十进制)为 8000、8005、8010、8004、8013、8003,则可能发生访存冲突的地址对是( )。
  • A. 8005 和 8013
  • B. 8000 和 8010
  • C. 8010 和 8003
  • D. 8004 和 8003
展开解析

A体号 = 地址 mod 4:8000→0,8005→1,8010→2,8004→0,8013→1,8003→3。同一个体在 4 个总线周期内被再次访问就会冲突。8000 与 8004 相隔 3 次,冲突;8005 与 8013 也相隔 3 次,冲突。选项中只有 A。

变化点:2015-18 的答案是 8000 和 8004。这里它也冲突,但没放进选项,换成了另一对同体地址。判断方法:体号相同,且在序列中的距离小于 4。

变式计组由 2017-13、2026-15 改
某计算机主存按字节编址,由 4 个 8 位宽的 DRAM 芯片采用低位交叉编址构成,与宽度为 32 位的存储器总线相连,每次最多读写 32 位。若 int 型变量 x 的主存地址为 8040016H,则读取 x 需要的存储周期数是( )。
  • A. 1
  • B. 2
  • C. 3
  • D. 4
展开解析

B每个存储周期读一个 4 字节对齐的字:…14H ~ …17H,…18H ~ …1BH。x 占 …16H ~ …19H,跨了两个字,需要 2 个存储周期。

变化点:2017-13 是 8 字节的 double,地址 …1AH,跨 3 个字。这里 int 只有 4 字节,但没有对齐,也要读 2 次。对齐存放时只要 1 次,这就是要边界对齐的原因。

变式计组考纲补充(尚无真题):刷新方式与片选
某 DRAM 芯片内部为 256 行,要求每行在 4ms 内至少刷新一次,存取周期为 0.5μs。若采用异步刷新,相邻两次刷新的间隔约为( );若采用集中刷新,每个刷新周期中的死区为( )。
  • A. 15.6μs;64μs
  • B. 7.8μs;256μs
  • C. 15.6μs;128μs
  • D. 7.8μs;128μs
展开解析

C异步刷新:把 256 行均匀分布在 4ms 内,间隔 = 4ms ÷ 256 ≈ 15.6μs。集中刷新:一次连续刷完 256 行,死区 = 256 × 0.5μs = 128μs。B、D 把刷新周期当成了 2ms;A 把行数当成了 128。

考点:三种刷新方式(已用程序验算)。

8.3Cache:CPU 和主存之间的“小抄”

★★★ 高频选择题综合题

Cache 用 SRAM 做成,容量小、速度接近 CPU。主存和 Cache 都被划分成大小相同的块(Cache 里的一块也叫一行)。CPU 访存时先看这个地址所在的块在不在 Cache 里:在(命中)就直接读 Cache;不在(缺失)就去主存读,并把整块搬进 Cache(空间局部性)。

命中率 H = 命中次数 / 总访问次数(2009-21:访存 1000 次缺失 50 次 → 95%) 平均访问时间 = H × tc + (1 − H) × (tc + tm) (先查 Cache,缺失再访主存)        = tc + (1 − H) × tm 即“命中时间 + 缺失率 × 缺失损失” 若 Cache 和主存同时启动:平均 = H × tc + (1 − H) × tm Cache—主存系统的效率 e = tc ÷ 平均访问时间(越接近 1,整个系统越像“全是 Cache”)
例计组命中率、平均访问时间、效率
CPU 执行一段程序时,Cache 完成存取 1900 次,主存完成存取 100 次。Cache 存取周期 50ns,主存存取周期 250ns。求命中率、平均访问时间和 Cache—主存系统的效率。
展开解析
  1. 命中率 H = 1900 / (1900 + 100) = 95%。
  2. 若 Cache 与主存同时启动(缺失时只需主存的时间):平均 = 0.95 × 50 + 0.05 × 250 = 60ns,效率 e = 50/60 ≈ 83.3%。
  3. 若先查 Cache、缺失再访主存:平均 = 50 + 0.05 × 250 = 62.5ns,效率 = 50/62.5 = 80%。

两种算法都有教材用,题目一般会说明“同时访问”还是“先访问 Cache”;没说明时写出所用公式即可。

三种映射方式:主存的块能放进 Cache 的哪一行

直接映射全相联映射2 路组相联映射 Cache 8 行 行 0行 1行 2行 3…行 7 主存块 10 行号 = 块号 mod 行数10 mod 8 = 2,只能放行 2 比较器:1 个冲突多、命中率最低 行 0行 1行 2行 3…行 7 主存块 10 放在任意空行地址里没有行号字段 比较器:行数个(全部同时比)命中率最高、硬件最贵 组0 行a组0 行b 组1 行a组1 行b 组2 …组3 … 主存块 10 组号 = 块号 mod 组数10 mod 4 = 1,组 1 内任选一行 比较器:路数个(2 个)折中,最常用 直接映射 = 1 路组相联;全相联 = 只有 1 组的组相联。会了组相联,另外两种就是特例。
图 8-8 三种映射。因为很多主存块会映射到同一行/同一组,Cache 每行要存一个标记(Tag),记下“现在放的是哪一块”;还要一个有效位表示这一行里的东西是否有效(开机时全为 0)。命中 = Tag 相等且有效位为 1。

地址怎么切:Tag | 组号(行号)| 块内地址

标记 Tag剩下的高位组号 / 行号log₂(组数);全相联没有块内地址log₂(块大小)
块内地址位数 = log2(块大小 / 编址单位) 组数 = Cache 行数 / 路数  组号位数 = log2(组数) Tag 位数 = 主存地址位数 − 组号位数 − 块内地址位数
2016-45计组融合综合题
页式虚拟存储,按字节编址,虚拟地址 32 位,物理地址 24 位,页大小 8KB;TLB 采用全相联映射;Cache 数据区大小为 64KB,按 2 路组相联方式组织,主存块大小为 64B。存储访问过程的示意图中:虚拟地址 = A(虚页号)| D(页内),TLB 每项 = B(标记)| 页框号,物理地址 = C(页框号)| D,Cache 用物理地址 = E(标记)| F(组号)| G(块内)。(1)A~G 各多少位?TLB 标记字段 B 中存放的是什么?(2)将块号为 4099 的主存块装入 Cache 时,映射到的组号是多少?对应的 H(Cache 行标记)是什么?(3)Cache 缺失处理和缺页处理哪个开销大?为什么?(4)为什么 Cache 可以采用直写策略,而修改页面内容时总是采用回写策略?
展开解析
  1. 虚拟地址:页 8KB = 213 → D = 13;A = 32 − 13 = 19。TLB 全相联没有组号,标记就是整个虚页号 → B = 19,存的是虚页号。
  2. 物理地址:C = 24 − 13 = 11。
  3. Cache:块 64B → G = 6;行数 64KB/64B = 1024,2 路 → 512 组 → F = 9;E = 24 − 9 − 6 = 9。答案 A=19,B=19,C=11,D=13,E=9,F=9,G=6。
  4. (2)组号 = 4099 mod 512 = 3;标记 = 块号的高位 = 4099 ÷ 512 取整 = 8 = 000001000B(9 位)。(块号 = 物理地址去掉块内 6 位 = E+F 共 18 位,低 9 位是组号、高 9 位是 Tag。)
  5. (3)缺页开销大得多:Cache 缺失只需访问主存(几十 ns),硬件完成;缺页要访问磁盘(ms 级),还要操作系统介入。
  6. (4)直写要求每次写都同时写下一层。写主存可以接受;写磁盘太慢,每次改页面都写磁盘不可接受,所以主存—辅存层次总用回写(页面被换出时才写回,要有修改位)。

工具已填 2018-44 的物理地址 0040040H(28 位:Tag 20 | 组号 3 | 块内 5),可以换成任何题目的地址和字段。

2022-16计组
主存地址为 32 位,按字节编址,Cache 数据区容量为 32KB,主存块大小为 64B,采用 8 路组相联映射方式,该 Cache 中比较器的个数和位数分别为( )。
  • A. 8,20
  • B. 8,23
  • C. 64,20
  • D. 64,23
展开解析

考点8.3 地址怎么切8.3 组相联映射

A组相联:用组号选出一组,组内每一路各配一个比较器同时比 Tag。比较器个数 = 路数,位数 = Tag 位数。

  1. 行数 = 32KB ÷ 64B = 512;组数 = 512 ÷ 8 = 64 → 组号 6 位;块内地址 log264 = 6 位。
  2. Tag = 32 − 6 − 6 = 20 位;比较器 8 个(一组 8 路)。
  • A 对8 个 20 位比较器。
  • B 错23 = 32 − 6 − 3,是把“8 路”当成了 8 组(组号 3 位);实际组数 = 512 ÷ 8 = 64,组号 6 位,Tag 20 位。
  • C 错比较器个数错:组号已经选定了唯一一组,只需比这一组里的 8 行,不必给 64 组各配比较器。
  • D 错两处都错:比较器只要 8 个(路数),Tag 只有 20 位(组号是 6 位不是 3 位)。

比较器个数:直接映射 1 个、k 路组相联 k 个、全相联 = 行数;位数都等于 Tag 位数。同类:2009-14(16 块 2 路 → 8 组,块 32B,地址 129 在块 4 → 组 4);2026-18(1024 行 4 路 → 256 组,块 32B,地址 1028 → 块 32 → 组 32)。

Cache 的总容量:别漏了“附加位”

题目说“Cache 数据区 32KB”指的只是数据。每一行实际还要存:Tag + 有效位 1 位;采用回写时再加修改位(脏位)1 位;采用 LRU 等替换算法时再加替换控制位(组相联常按每行 log2(路数) 位的计数器算)。组号不用存(它就是行所在的位置)。

Cache 总位数 = 行数 × (数据位 + Tag + 有效位 + [脏位] + [替换位])
2021-16计组
若计算机主存地址为 32 位,按字节编址,Cache 数据区大小为 32KB,主存块大小为 32B,采用直接映射方式和回写(Write Back)策略,则 Cache 行的位数至少是( )。
  • A. 275
  • B. 274
  • C. 258
  • D. 257
展开解析

考点8.3 Cache 的总容量:别漏了附加位

ACache 一行 = 数据位 + Tag + 有效位 + 一致性维护位(回写要 1 位脏位)+ 替换位(直接映射不需要)。

  1. 行数 = 32KB ÷ 32B = 1024 → 行号 10 位;块内地址 log232 = 5 位;Tag = 32 − 10 − 5 = 17 位。
  2. 每行 = 数据 32 × 8 = 256 + Tag 17 + 有效位 1 + 脏位 1 = 275 位。直接映射不用替换算法,没有替换位。
  • A 对256 + 17 + 1 + 1 = 275。
  • B 错274 少了 1 位:回写策略必须有脏位(修改位),有效位也不能省,两者缺一都只剩 274。
  • C 错258 = 256 + 1 + 1,漏了 17 位 Tag;没有 Tag 就无法判断这一行装的是主存的哪一块。
  • D 错257 = 256 + 1,既漏了 17 位 Tag,也漏了回写需要的脏位。

行的位数 = 数据 + Tag + 有效位 + (回写? 脏位) + (组相联/全相联? 替换位)。同类:2015-15(直接映射、回写,4K 字、块 4 字 × 32 位 → 1K 行,Tag 18 → 每行 128 + 18 + 2 = 148 → 148K 位);2010-44(8 行 × 64B 直接映射,不计一致性和替换位 → 每行 512 + 19 + 1,共 532B);2018-44(2 路 8 组、块 32B、Tag 20、LRU + 回写 → 每行 20 + 1 + 1 + 1 + 256 = 279 位,16 行共 4464 位 = 558B)。

替换算法与写策略

替换算法(组满了踢谁)
直接映射不需要(只有一个位置)。组相联/全相联:随机;FIFO(最早进来的);LRU 近期最少使用(最久没用的,最常考);LFU(用得最少的)。都由硬件实现。
写策略(CPU 要写数据)
写命中:直写(全写法)同时写 Cache 和主存(可加写缓冲,不需要脏位);回写只写 Cache,设脏位,被替换时再写回主存。写不命中:写分配(先把块调入 Cache 再写,常配回写);非写分配(直接写主存,常配直写)。
2015-16计组融合
假定编译器将赋值语句 “x = x + 3;” 转换为指令 “add xaddr, 3”,其中 xaddr 是 x 对应的存储单元地址。若执行该指令的计算机采用页式虚拟存储管理方式,并配有相应的 TLB,且 Cache 使用直写(Write Through)方式,则完成该指令功能需要访问主存的次数至少是( )。
  • A. 0
  • B. 1
  • C. 2
  • D. 3
展开解析

考点8.3 Cache 写策略8.8 访存过程

B问“至少”,就假设一切都命中:取指令在 Cache 命中;xaddr 的地址转换在 TLB 命中(不用去主存查页表);读 x 在 Cache 命中。但写回 x 时,直写要求同时写主存——这一次省不掉。

  • A 错0 次:只有回写(Write Back)策略下写操作才可能不访问主存。
  • B 对至少 1 次(直写引起的写主存)。
  • C 错2 次:把读 x 也算成访问主存了,但读可以在 Cache 命中。
  • D 错3 次:取指、读、写全部算进来,是最坏情况之一,不是“至少”。

“至少访存几次”:TLB、Cache 都命中;直写的写操作仍要写主存。

替换算法跟做一遍:FIFO、LRU、LFU、随机

问题引入:组满了要踢一块出去,踢谁?四种算法的规则一句话就能说完,但同一个访问序列下它们的命中次数可以差很多——跟着做一遍才知道差在哪。

设定:全相联 Cache 共 3 行(相当于“一组 3 路”),初始为空,访问的主存块号依次为 4、2、3、2、2、4、3、1、4、1。LFU 中若几块的使用次数相同,先踢最早进来的那块。

访问4232243141命中
FIFO缺缺缺中中中中缺·换 4缺·换 2中5
LRU缺缺缺中中中中缺·换 2中中6
LFU缺缺缺中中中中缺·换 4缺·换 1缺·换 44
  1. 前 7 次三种算法一样:4、2、3 装满,之后 2、2、4、3 都命中。此时使用次数 2 号 3 次、4 号 2 次、3 号 2 次;最近一次使用:4 号在第 6 次、2 号在第 5 次、3 号在第 7 次。
  2. 访问 1 缺失,要踢一块:FIFO 踢最早进来的 4;LRU 踢最久没用的 2;LFU 踢使用次数最少的(4、3 都是 2 次,按约定踢先进来的 4)。
  3. FIFO、LFU 刚踢掉 4,紧接着又要用 4,再缺一次;LFU 还因为 2 号“历史上用得多”而一直把它留着,最后连续缺失。LRU 留下了最近用过的 4,后面两次都命中。

已用 verify/08/p08_calc.py 模拟验算。随机替换没有固定结果,它不看历史、硬件最简单,实际命中率并不比 FIFO 差多少。LRU 最符合局部性,最常用也最常考;LFU 会被“很久以前频繁使用、现在已经不用”的块拖累(例子里的 2 号);FIFO 不看使用情况,可能把常用的块踢出去。直接映射不需要替换算法(只有一个位置可放)。

写策略的四种组合与写缓冲

问题引入:读操作只关心命中率;写操作还多了一个麻烦——Cache 里的副本改了,主存里的原件怎么办?于是有了“写命中”和“写不命中”两组选择,再加一个减少等待的写缓冲。

① 全写法(直写)+ 写缓冲 CPU Cache同时写 写缓冲(FIFO 队列) 主存 慢慢写 CPU 写进写缓冲就继续执行,不用等慢速的主存写得太频繁,缓冲满了 CPU 也得等(饱和) ② 回写法(写回) CPU Cache只写这里,脏位 = 1 主存 被替换且脏位 = 1时才写回 多次写同一块只在最后写回一次,访存次数少主存里的副本可能是旧的(一致性要靠脏位)
图 8-9 写命中时的两种策略。全写法保证主存总是最新的,不需要脏位;写缓冲让 CPU 不必等主存写完。回写法减少了写主存的次数,但每行要加一个脏位(修改位),2021-16、2018-44 算 Cache 行位数时要记得它。
写命中(块在 Cache 里)写不命中(块不在 Cache 里)常见搭配
选项 1全写法(直写):同时写 Cache 和主存非写分配:只写主存,不把块调入 Cache全写 + 非写分配
选项 2回写法:只写 Cache,置脏位,替换时再写回写分配:先把块调入 Cache,再在 Cache 里写回写 + 写分配

为什么这样搭配:回写法希望后面对同一块的写都在 Cache 里完成,所以写不命中时把块调进来(写分配)才划算;全写法反正每次都要写主存,把块调进 Cache 也省不了写主存的那一次,干脆不调(非写分配)。2015-16“直写方式下至少访问主存 1 次”就是全写法的直接后果。

多级 Cache,以及 Cache 行、TLB 项、页表项各存什么

问题引入:现代 CPU 有 L1、L2 甚至 L3 三级 Cache;第 8 篇后半还有 TLB 和页表。这几张“表”的每一项都存了标记、有效位之类的字段,选择题最爱问“哪个字段在哪里有、在哪里没有”。

多级 Cache:L1 小而快(常分成指令 Cache 和数据 Cache,避免流水线取指和访存冲突,2014-16),L2 大一些、慢一些,最后才访问主存。例:L1 命中时间 1 个周期、缺失率 5%;L2 命中时间 10 个周期、在 L1 缺失的访问中缺失率 20%;主存 100 个周期。

平均访问时间 = L1 命中时间 + L1 缺失率 × (L2 命中时间 + L2 缺失率 × 主存时间) = 1 + 0.05 × (10 + 0.2 × 100) = 1 + 0.05 × 30 = 2.5 个周期;只有 L1 时 = 1 + 0.05 × 100 = 6 个周期
字段Cache 行TLB 表项页表项(请求分页)
标记 / 查找用的键Tag(主存块号的高位)标记 = 虚页号(全相联)或虚页号高位(组相联)无,直接用页号作下标
有效位 / 存在位有效位有效位存在位(是否在内存)
修改位脏位(回写时才有)可以有(与页表项同步)修改位(换出时要不要写回磁盘)
替换信息LRU 计数位LRU 位访问位(给 CLOCK 等置换算法用)
内容数据块的副本物理页框号物理页框号、外存地址、保护位
谁维护硬件硬件(MMU)操作系统(硬件查)

平均访问时间已用 verify/08/p08_calc.py 验算。页表项各字段的作用详见第 8 篇后半 8.7“请求分页的页表项”。

程序怎么写,命中率差很多:数组遍历

分析方法(四步):① 元素多大、一块能放几个元素;② 数组起始地址是否对齐到块;③ 按程序的访问顺序,每一块第一次被访问时缺失,块内后续访问命中(顺序访问时);④ 检查会不会还没用完就被替换掉(同一行/组被别的块抢走)。

2010-44计组综合题 12 分
主存地址空间 256MB,按字节编址。数据 Cache 采用直接映射,共 8 行,每行 64B。int a[256][256] 按行优先存放,首地址为 320(十进制),int 占 4B,i、j、sum 在寄存器中。程序 A:for(i) for(j) sum += a[i][j];(按行访问);程序 B:for(j) for(i) sum += a[i][j];(按列访问)。(1)不考虑一致性和替换控制位,数据 Cache 总容量?(2)a[0][31] 和 a[1][1] 所在主存块对应的 Cache 行号?(3)程序 A、B 的数据访问命中率各是多少?哪个执行时间更短?
展开解析
  1. 地址切分:256MB = 228 → 28 位;块内 6 位,行号 3 位,Tag 19 位。每行 512 + 19 + 1 = 532 位,8 行 = 4256 位 = 532B。
  2. 行号 = 块号 mod 8:a[0][31] 地址 = 320 + 31×4 = 444,块号 444/64 = 6 → 行 6。a[1][1] 地址 = 320 + (256+1)×4 = 1348,块号 21 → 21 mod 8 = 行 5。
  3. 程序 A(按行):顺序访问,320 是 64 的倍数(块对齐),一块放 16 个 int,每块第一个元素缺失、后 15 个命中 → 命中率 15/16 = 93.75%。
  4. 程序 B(按列):相邻两次访问 a[i][j] 与 a[i+1][j] 相差一行 = 1024B = 16 块,而 16 mod 8 = 0 → 映射到同一个 Cache 行。每次访问都把上一次调进来的块挤掉,等回头访问 a[0][j+1] 时它早被替换了 → 命中率 0。
  5. A 快得多。
2023-43计组融合综合题
32 位虚拟地址、页 4KB、请求调页;数据 Cache 4 路组相联,数据区 8KB,块 32B。程序 for(i=0;i<24;i++) for(j=0;j<64;j++) a[i][j]=10;,int a[24][64] 按行优先,起始虚拟地址 0042 2000H,执行前 a 不在主存,执行中不发生页面置换。(1)a 分几个页面?会发生几次缺页?页故障地址?(2)数据访问有无时间局部性?(3)虚拟地址哪几位是块内地址、哪几位是 Cache 组号?a[1][0] 的虚拟地址及其组号?(4)a 占多少主存块?命中率?若把循环改成先 j 后 i,命中率又是多少?
展开解析
  1. (1)a 大小 24×64×4 = 6144B = 6KB。起点 00422000H 是页的开头(低 12 位为 0),占 00422000H~004237FFH → 2 页,2 次缺页,页故障地址就是第一次访问每页时的地址:0042 2000H、0042 3000H。
  2. (2)每个元素只写一次 → 没有时间局部性(有空间局部性)。
  3. (3)块 32B → 块内 A4~A0;组数 8KB/(32B×4) = 64 → 组号 A10~A5。它们都在页内偏移(低 12 位)里,所以虚拟地址和物理地址的这几位相同,可以直接用虚拟地址算。a[1][0] = 00422000H + 64×4 = 00422100H,低 12 位 100H = 0001 0000 0000,A10~A5 = 001000 = 组 8。
  4. (4)6144/32 = 192 块。按行顺序写:每块 8 个 int,首次缺失、后 7 次命中 → 7/8 = 87.5%。改成按列:a[i][j] 与 a[i+1][j] 相差 256B = 8 块;对同一组,i、i+8、i+16 三行会落在同一组(192 块 ÷ 64 组 = 3 块/组),4 路装得下,不会被替换,所以每块仍然只缺失 1 次 → 仍是 87.5%。

对比 2010-44:差别在于 2023 的 Cache 能装下“一列要用到的所有块”(组相联 + 数组小),2010 的直接映射装不下。

2025-43计组融合综合题
字长 32 位,按字节编址;数据 Cache 32KB,8 路组相联,块 64B,命中时间 2 个时钟周期,缺失损失 200 个时钟周期;页式虚拟存储,页 4KB。int x, d[2048], i; for(i=0;i<2048;i++) d[i] = d[i]/x; 数组 d 起始虚拟地址 0180 0020H。(1)Cache 组号、块内地址各几位?VA 中哪些位可以作 Cache 索引?(2)d[100] 的 VA 及其组号?(3)代码已在 Cache,i、x 在内存但不在 Cache,d[0] 在块内的偏移?访问 d 的缺失率?(4)d 分布在几个页中?若 d 不在主存,访问 d 引起几次缺页?
展开解析
  1. (1)512 行 / 8 路 = 64 组 → 组号 6 位;块内 6 位。两者共 12 位,恰好在页内偏移里 → VA 的 第 6~11 位就是组号,可直接用虚拟地址索引 Cache。
  2. (2)d[100] = 01800020H + 400 = 01800020H + 190H = 0180 01B0H;低 12 位 1B0H = 0001 1011 0000,第 11~6 位 = 000110 = 组 6。
  3. (3)d[0] 块内偏移 = 20H = 32(没有对齐到块开头)。d 共 8KB,从某块中间开始,会占 8192/64 + 1 = 129 块。每个元素先读后写,共 2048×2 = 4096 次访问,每块只在第一次访问时缺失 → 缺失率 = 129/4096 ≈ 3.15%。平均访问时间按“命中时间 + 缺失率 × 缺失损失”= 2 + 0.0315×200 ≈ 8.30 个周期(也有资料按“缺失时共 200 周期”算得 ≈ 8.24,写清所用公式即可)。
  4. (4)8KB 从页内偏移 020H 开始,跨过 01800、01801、01802 三页 → 3 页,3 次缺页。

同类:2016-15 a[k] = a[k] + 32(每块 4 个 int,每个元素读一次写一次,8 次访问只第 1 次缺失 → 缺失率 12.5%);2020-44(s[1024] 从 008000C0H 开始,块 64B 对齐,读写各一次,1024×4B/64B = 64 块 → 缺失 64 次);2014-45 第 2 问(程序段 6 条指令 24 字节都在一个 32B 块内,1000 次循环取指 6000 次只第 1 次缺失 → 99.98%)。

2021-44计组融合综合题 8 分
主存地址 24 位,按字节编址;分页存储,虚拟地址 30 位,页大小 4KB;TLB 采用 2 路组相联和 LRU 替换,共 8 组。(1)虚拟地址中哪几位是虚页号、哪几位是页内地址?(2)虚页号高位作 TLB 标记,低位作 TLB 组号,虚拟地址中哪几位是 TLB 标记、哪几位是组号?(3)TLB 初始为空,访问的虚页号依次为 10、12、16、7、26、4、12、20,哪个虚页号对应的 TLB 表项被替换?(4)若虚拟地址增加到 32 位,TLB 表项的位数增加几位?
展开解析
  1. 页内 12 位(A11~A0),虚页号 18 位(A29~A12)。
  2. 8 组 → 组号 3 位 = 虚页号最低 3 位(A14~A12);标记 15 位(A29~A15)。TLB 的组相联和 Cache 的组相联完全一样,只是“地址”换成了虚页号。
  3. 组号 = 虚页号 mod 8:10→2,12→4,16→0,7→7,26→2,4→4,12→4(命中,12 变成最近使用),20→4(组 4 已满:12 和 4,LRU 踢 4)。上面的工具已填好这组数据(16 行、2 路 = 8 组)。
  4. 虚页号多 2 位 → 标记多 2 位 → 每个表项多 2 位。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2009-14、2026-18 改
某计算机按字节编址,Cache 共 64 行,采用 4 路组相联映射,主存块大小为 64B。主存地址 3000(十进制)所在的主存块应装入的 Cache 组号是( )。
  • A. 8
  • B. 14
  • C. 46
  • D. 11
展开解析

B块号 = ⌊3000 ÷ 64⌋ = 46。组数 = 64 ÷ 4 = 16,组号 = 46 mod 16 = 14。

变化点:两步不能省:先算块号,再对组数取模。A 是直接拿地址对 16 取模,C 是只算了块号。

变式计组由 2021-16、2015-15 改
主存地址 32 位,按字节编址。Cache 数据区 16KB,块大小 32B,采用 2 路组相联映射、LRU 替换和回写策略。Cache 每行至少需要( )位。
  • A. 276
  • B. 277
  • C. 278
  • D. 286
展开解析

C行数 = 16KB ÷ 32B = 512,组数 = 256,组号 8 位,块内地址 5 位,Tag = 32 − 8 − 5 = 19 位。每行 = 数据 256 + Tag 19 + 有效位 1 + 脏位 1 + LRU 位 1 = 278 位。

变化点:2021-16 是直接映射(Tag 17 位、没有 LRU 位,共 275 位)。改成组相联后,Tag 变长,还要加 LRU 位(2 路时每行 1 位)。

变式计组由 2022-16 改
主存地址 32 位,按字节编址。Cache 数据区 64KB,块大小 32B,采用 4 路组相联映射。该 Cache 中比较器的个数和每个比较器的位数分别为( )。
  • A. 4,18
  • B. 4,21
  • C. 512,18
  • D. 2048,18
展开解析

A组数 = 64KB ÷ 32B ÷ 4 = 512,组号 9 位,Tag = 32 − 9 − 5 = 18 位。查找时只在选中的那一组里同时比较 4 行,所以比较器 4 个,每个 18 位。

变化点:2022-16 是 8 路、块 64B(8 个,20 位)。比较器个数 = 路数,与组数无关;位数 = Tag 位数。

变式计组由 2016-15 改
程序段 for (k = 0; k < 1000; k++) a[k] = a[k] + 32; 中,数组 a 为 int 型(4B)。数据 Cache 采用直接映射,数据区 1KB,块大小 32B,程序执行前 Cache 为空。访问数组 a 的缺失率是( )。
  • A. 3.125%
  • B. 6.25%
  • C. 12.5%
  • D. 25%
展开解析

B每块放 8 个元素,每个元素读 1 次、写 1 次,一块共访问 16 次,只有第一次读缺失。缺失率 = 1/16 = 6.25%。

变化点:2016-15 的块大小是 16B(4 个元素,缺失率 1/8 = 12.5%)。块大小翻倍,缺失率减半。每个元素访问两次(读 + 写)这一点不能漏:只算读会得到 12.5%。

变式计组由 2012-17 改
某计算机按字编址,Cache 有 4 行,块大小为 1 个字,采用直接映射。Cache 初始为空,依次访问的主存地址为 0, 4, 8, 2, 0, 6, 8, 6, 4, 8,命中 Cache 的次数是( )。
  • A. 1
  • B. 2
  • C. 3
  • D. 4
展开解析

A行号 = 地址 mod 4。0、4、8 都映射到第 0 行,2、6 都映射到第 2 行。逐个看:0 缺、4 缺(换出 0)、8 缺、2 缺、0 缺、6 缺(换出 2)、8 缺、6 命中、4 缺、8 缺,只命中 1 次。

变化点:2012-17 是 2 路组相联 + LRU(命中 3 次)。同样的访问序列,直接映射下 0、4、8 挤在同一行互相替换,命中次数大大减少。

变式计组考纲补充(尚无真题):替换算法与写策略
下列关于 Cache 替换算法和写策略的叙述中,正确的是( )。
  • A. 直接映射方式也需要使用 LRU 替换算法
  • B. 采用全写法时,每个 Cache 行都需要一个脏位
  • C. 写缓冲可以减少 CPU 因等待写主存而停顿的时间
  • D. 回写法通常与非写分配法配合使用
展开解析

C写缓冲让 CPU 把要写主存的数据放进缓冲区就继续执行,由缓冲区慢慢写入主存。A 错:直接映射只有一个位置,不需要替换算法。B 错:全写法主存总是最新的,不需要脏位,脏位是回写法才要的。D 错:回写法常配写分配法,全写法常配非写分配法。

考点:写策略的四种组合与写缓冲、替换算法。

8.4操作系统内存怎么分给进程:连续分配

★★ 常考选择题
先搞清楚:内存管理要管哪几件事
  • 逻辑地址与物理地址:程序里用的地址是从 0 开始编的逻辑地址(也叫虚拟地址、相对地址);内存条上存储单元真正的编号是物理地址。两者之间的转换叫地址变换(重定位),第 3 篇 3.5 讲了静态和动态两种。
  • 内存分配与回收:连续分配(本节)或离散分配(分页 8.5、分段 8.6)。
  • 内存保护:进程只能访问自己的内存,不能碰操作系统和别的进程。两种做法:① 设一对上、下限寄存器,存进程所在区域的起止物理地址,每次访存比较;② 重定位寄存器 + 界地址寄存器:先检查逻辑地址 < 界地址(越界就产生异常),再加上重定位寄存器得到物理地址(图 3-13)。分页系统里靠页表长度寄存器和页表项里的保护位。
  • 内存共享:多个进程共用同一块物理内存(同一个程序的代码、共享库、共享内存通信)。分页/分段中让各进程的页表项/段表项指向同一个页框/段即可(8.6)。被共享的代码必须是可重入代码(纯代码:执行过程中不修改自己,每个进程的数据放在各自的数据区)。
  • 内存扩充了解:早期的两种办法——覆盖:程序员把不会同时运行的模块安排在同一块内存区轮流使用(要程序员自己设计,对用户不透明);交换(对换):把暂时不能运行的进程整个换出到磁盘的对换区,腾出内存给别的进程,需要时再换入(就是中级调度,6.6)。虚拟内存(8.7)可以看成它们的“自动化升级版”。

现在切到操作系统的视角:内存里要同时放多个进程,操作系统得决定每个进程放在哪、占多少。最早的办法是给每个进程一块连续的区域:

  • 单一连续分配:内存只放一道用户程序。
  • 固定分区:预先切成若干固定大小的分区,一个分区放一个进程——分区里用不完的部分浪费了,叫内部碎片。
  • 动态分区:进程来了按需切一块。进程走了留下的空洞越来越小、越来越碎,每块都放不下新进程,叫外部碎片(可以“紧凑”:把进程挪到一起,需要动态重定位,第 3 篇)。
动态分区算法从空闲分区里选特点
首次适应 FF按地址递增找第一个够大的简单、效果通常最好;低地址会留很多小碎片
邻近适应 NF从上次找到的位置接着找空闲分区分布均匀,但大分区也被切碎
最佳适应 BF按容量递增找第一个够大的(最小的够用的)留下的碎片最小但最多、最容易产生外部碎片(2019-32)
最坏适应 WF找最大的大分区很快用完
伙伴算法分区大小都是 2 的幂,回收时只和大小相等的“伙伴”合并2024-27;Linux 回收页框用它

四种基本算法的区别其实就是空闲分区链怎么排序:首次适应按起始地址递增;最佳适应按容量递增;最坏适应按容量递减;邻近适应(循环首次适应)按地址递增、但从上次停下的位置接着找。分配时都是“从链首往后找第一个够大的”。

例操作系统同一组请求,三种算法
按地址顺序有 5 个空闲分区:100K、500K、200K、300K、600K。依次到来 4 个请求:212K、417K、112K、426K。分别用首次适应、最佳适应、最坏适应分配,结果如何?
展开解析
请求首次适应 FF最佳适应 BF最坏适应 WF
212K500K 分区(剩 288K)300K 分区(剩 88K)600K 分区(剩 388K)
417K600K 分区(剩 183K)500K 分区(剩 83K)500K 分区(剩 83K)
112K剩下的 288K(剩 176K)200K 分区(剩 88K)剩下的 388K(剩 276K)
426K没有够大的,等待600K 分区(剩 174K)没有够大的,等待

这组数据里只有最佳适应把 4 个请求都满足了:它总挑“刚好够”的,把大分区(600K)留给了后来的大请求。但它的代价是留下了 88K、83K、88K 这样的小碎片。所以“哪个算法最好”没有绝对答案,要看请求序列。(结果已用程序模拟验算。)

回收分区:四种合并情况

进程释放一个分区时,要看它和前后的空闲分区是否地址相邻,相邻就合并(2017-25):

情况怎么处理空闲分区数
只和前面一个空闲区相邻并入前面那块:前块的起始地址不变,大小加上回收区不变
只和后面一个空闲区相邻和后块合并:起始地址改成回收区的起始地址,大小相加不变
和前后两块都相邻三块合成一块:用前块的起始地址,大小是三者之和,删去后块的表项减 1
和前后都不相邻新建一个表项,填回收区的起始地址和大小加 1
2010-28操作系统
某基于动态分区存储管理的计算机,其主存容量为 55MB(初始为空闲),采用最佳适配算法,分配和释放的顺序为:分配 15MB、分配 30MB、释放 15MB、分配 8MB、分配 6MB,此时主存中最大空闲分区的大小是( )。
  • A. 7MB
  • B. 9MB
  • C. 10MB
  • D. 15MB
展开解析

考点8.4 动态分区分配算法8.4 回收分区

B最佳适配 = 每次选“够用的里面最小的”空闲分区。按顺序把内存画出来(单位 MB)。

  1. 分配 15、30:[15 用][30 用][10 空]。
  2. 释放 15:[15 空][30 用][10 空]。
  3. 分配 8:空闲区 15 和 10 都够,选更小的 10 → [15 空][30 用][8 用][2 空]。
  4. 分配 6:空闲区 15 和 2,只有 15 够 → [6 用][9 空][30 用][8 用][2 空]。空闲区为 9 和 2,最大 9MB。
  • A 错7MB 是最坏适配的结果:分配 8 时选最大的 15(剩 7),分配 6 时再选最大的 10(剩 4),最大空闲区 7。
  • B 对最佳适配后空闲区为 9MB 和 2MB,最大 9MB。
  • C 错10MB 是首次适配的结果:8 和 6 都从地址最低的 15 里切(剩 1),后面的 10MB 没动。
  • D 错15MB 是“释放 15MB 之后、还没分配 8MB 和 6MB”时的最大空闲区,后两次分配没做。

同一组请求,首次适配(10)、最佳适配(9)、最坏适配(7)结果不同,考场上三种都可能被拿来做干扰项。同类:2017-25(回收 60K 起 140KB 的分区,与前后两块都相邻 → 合成 20K 起 380KB;最佳适应按容量递增排序,链首是 500K 起 80KB)。

保护:每个进程只能访问自己的分区,用重定位寄存器 + 界地址寄存器检查越界(2009-26,图 3-13)。连续分配的根本问题是碎片和“必须一整块”,于是有了下面的分页。

单一连续与固定分区,以及内存保护的两种做法

问题引入:动态分区之前还有两种更简单的分配方式;不管哪种方式,都得防止一个进程越界去改别人的内存。保护的两种硬件做法,选择题常让你“看图判越界”。

① 单一连续 操作系统 一道用户程序 没用上 只能单道;有内部碎片 ② 固定分区(大小不等) OS(20K) 分区 1 12K 分区 2 32K 分区 3 64K 分区说明表 号 大小 起址 状态 1 12K 20K 已分 2 32K 32K 已分 3 64K 64K 空闲 分区里用不完的部分 = 内部碎片 ③ 内存保护的两种硬件做法 上、下限寄存器(比较物理地址) 下限 1000 ≤ 物理地址 < 上限 2000 才允许访问 访问 2100 → 越界,产生异常 重定位寄存器 + 界地址寄存器(先比较逻辑地址) 界地址 3000:逻辑地址 < 3000 才合法 逻辑 2500 → 物理 = 10000 + 2500 = 12500 逻辑 3200 ≥ 3000 → 越界,不再相加 两组寄存器都只能由操作系统(内核态)设置
图 8-10 两种早期分配方式与两种保护方式。固定分区用一张分区说明表记录每个分区的大小、起始地址和是否已分配;分配时找一个“够大且空闲”的分区。保护的第二种做法就是第 3 篇图 3-13 的动态重定位,进程切换时操作系统要重新装入这两个寄存器。
单一连续分配固定分区分配动态分区分配(上文)
做法内存分为系统区和用户区,用户区只放一道程序事先把用户区划成若干个固定的分区(大小可相等也可不等),每个分区装一道作业按作业的需要临时划分
多道不支持支持,道数 = 分区数支持
碎片内部碎片内部碎片外部碎片
缺点只能单用户单任务,内存利用率低程序太大可能哪个分区都放不下(只能用覆盖);小程序占大分区浪费碎片越来越多,要紧凑

真题怎么考:2009-26(分区分配内存管理方式的主要保护措施是界地址保护)、碎片类型对照(本节下面的表)。判别法:上下限寄存器比的是物理地址;界地址寄存器比的是逻辑地址(比之前不加重定位值)。

选择题常考:哪种方式产生哪种碎片
分配方式内部碎片(分给进程但用不完)外部碎片(空闲但太小、太散,谁都用不上)
单一连续分配有(整个用户区给一道程序)无
固定分区有无
动态分区无(按需切)有(用“紧凑”解决)
分页(8.5)有,但很小(只在每个进程的最后一页,平均半页)无
分段(8.6)无有(段长不一,像动态分区)
段页式(8.6)有(每段的最后一页)无

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2010-28 改
主存 64MB(初始全空闲),采用动态分区分配和首次适应算法。依次执行:分配 20MB、分配 30MB、释放 20MB、分配 12MB、分配 10MB。此时最大空闲分区的大小是( )。
  • A. 4MB
  • B. 8MB
  • C. 10MB
  • D. 14MB
展开解析

B分配 20、30 后,空闲区为 50~64(14MB)。释放 20MB 后,空闲区为 0~20(20MB)和 50~64(14MB)。首次适应分配 12MB:从 0 开始,剩 12~20(8MB)。分配 10MB:8MB 不够,用 50~60,剩 60~64(4MB)。最大空闲分区为 8MB。

变化点:2010-28 用最佳适应。同一组操作用最佳适应:12MB 放进 14MB 的分区,10MB 放进 20MB 的分区,最大空闲分区是 10MB(C)。算法一换,结果就变。

变式操作系统由 2019-32 改
在下列动态分区分配算法中,总是先把最大的空闲分区分割掉,导致以后到达的大作业难以得到满足的是( )。
  • A. 首次适应
  • B. 最佳适应
  • C. 最坏适应
  • D. 邻近适应
展开解析

C最坏适应每次都从最大的空闲分区中切出一块,很快就没有大的空闲分区了。它的好处是切剩的部分还比较大,不容易留下很小的碎片。

变化点:2019-32 问哪种算法最容易产生碎片(最佳适应,切剩的部分最小)。最佳适应和最坏适应的优缺点正好相反。

8.5融合分页:把进程切成等大的“页”

★★★ 高频选择题综合题

分页的思路:把进程的逻辑地址空间切成大小相等的页(如 4KB),把物理内存也切成同样大小的页框(也叫页帧、物理块),每一页可以放进任意一个空闲页框,不必连续。于是没有外部碎片,只有每个进程最后一页可能有一点内部碎片。

代价:操作系统要为每个进程维护一张页表,记录“第几页放在第几个页框”;CPU 每次访存都要查页表把逻辑地址变成物理地址——这由硬件 MMU(地址变换机构)完成。

逻辑地址(CPU 给出) 页号 P2 页内偏移 W3A4H 页表寄存器 PTR页表起始地址 · 页表长度 页表(在主存) 页号页框号 05 19 27 … ① P 与页表长度比较(越界?) ② 页表始址 + P × 页表项大小 页框号7 页内偏移3A4H 物理地址 ③ 取出页框号 ④ 页内偏移原样照抄(不参与计算) 物理地址 = 页框号 × 页面大小 + 页内偏移= 7 × 4KB + 3A4H = 73A4H
图 8-11 分页的地址变换(MMU 硬件完成)。页面大小是 2 的幂,所以“×页面大小 + 偏移”就是把页框号和偏移拼接起来,根本不用做加法。页内偏移在变换中原样传递——这是所有综合题最常用的一条性质(虚拟地址和物理地址的低位相同)。页表放在主存里,所以不用快表时,一次访存要访问主存两次(一次查页表、一次取数据)。
页内偏移位数 = log2(页面大小)  页号位数 = 逻辑地址位数 − 页内偏移位数 页框号位数 = 物理地址位数 − 页内偏移位数 页表项数 = 页数 = 2页号位数  页表大小 = 页表项数 × 页表项大小 页表项地址 = 页表起始地址 + 页号 × 页表项大小
  1. 页表寄存器 PTR 里放着当前进程页表的起始地址和页表长度(进程切换时由 OS 装入,第 6 篇)。
  2. 把逻辑地址切成页号 P 和页内偏移 W(二进制地址直接按位切;题目给十进制地址 A 时,P = ⌊A ÷ L⌋、W = A mod L,L 为页面大小。例:页 1KB,A = 5000 → P = 4、W = 904,因为 4 × 1024 + 904 = 5000);若 P ≥ 页表长度,产生越界中断。
  3. 页表项地址 = 页表起始地址 + P × 页表项长度,读出页表项,得到页框号 b。
  4. 物理地址 = b 拼接 W(= b × 页面大小 + W)。
分页的几个易错点

① 页表项里不存页号:页表是按页号顺序排的数组,页号就是下标,只存页框号(和标志位)。
② 页表项的长度至少要能放下页框号:物理地址 24 位、页 4KB → 页框号 12 位 → 至少 2 字节(实际常取 4 字节,多出的位放存在位、保护位等)。
③ 页面大小的取舍:页小 → 页内碎片小,但页数多、页表大,换页次数多;页大 → 页表小、换页少,但页内碎片大。页面大小由硬件(地址结构)决定,必须是 2 的幂。
④ 分页对用户透明,逻辑地址是一维的(给出一个地址就够,系统自己切页号、偏移)。

2022-15操作系统
某计算机主存地址为 24 位,采用分页虚拟存储管理方式,虚拟地址空间大小为 4GB,页大小为 4KB,按字节编址。某进程的页表部分内容:虚页号 82 → 页框号 024H、存在位 0;虚页号 129 → 页框号 180H、存在位 1;虚页号 130 → 页框号 018H、存在位 1(虚页号为十进制)。当 CPU 访问虚拟地址 00082840H,虚—实地址转换的结果是( )。
  • A. 得到主存地址 02 4840H
  • B. 得到主存地址 18 0840H
  • C. 得到主存地址 01 8840H
  • D. 检测到缺页异常
展开解析

考点8.5 分页的地址变换

C陷阱在进制:地址里的页号是十六进制,表里的页号是十进制。

  1. 页 4KB → 低 12 位 840H 是偏移,高 20 位 00082H 是页号。
  2. 00082H = 8 × 16 + 2 = 十进制 130。
  3. 查表:页 130 存在位 1,页框号 018H。
  4. 拼接:018H 拼 840H = 01 8840H(主存 24 位 = 6 个十六进制位)。
  • A 错把 82H 当成十进制 82,查到页框 024H(何况页 82 存在位为 0)。
  • B 错用了页 129 的页框号 180H。
  • C 对01 8840H。
  • D 错同样把页号当成 82,看到存在位 0 就以为缺页。

先统一进制再查表:页号要从十六进制换算成表中所用的进制。

2013-46操作系统综合题
主存按字节编址,逻辑地址和物理地址都是 32 位,页表项 4 字节。(1)一级页表,逻辑地址为 页号 20 位 | 页内偏移 12 位:页大小?页表最大占多少字节?(2)二级页表,逻辑地址为 页目录号 10 位 | 页表索引 10 位 | 页内偏移 12 位:给出页目录号和页表索引的表达式(逻辑地址 LA)。(3)采用(1)的方式,一个代码段起始逻辑地址 0000 8000H,长 8KB,被装载到从物理地址 0090 0000H 开始的连续主存;页表从主存 0020 0000H 开始连续存放。求该代码段两个页表项的物理地址、页框号,以及代码页面 2 的起始物理地址。
展开解析
  1. 页大小 212 = 4KB;页表最大 220 项 × 4B = 4MB。
  2. 页目录号 = (LA >> 22) & 0x3FF;页表索引 = (LA >> 12) & 0x3FF(第 0 篇的“右移去掉低位、与掩码留下几位”)。
  3. 0000 8000H 的页号 = 8,8KB 占第 8、9 两页。页表项地址 = 0020 0000H + 8×4 = 0020 0020H,下一项 0020 0024H。代码装在 0090 0000H 起连续两个页框 → 页框号 00900H、00901H;页面 2 起始物理地址 = 0090 1000H。

快表 TLB:页表的“Cache”

每次访存都多访问一次主存查页表,太慢。办法同 Cache:把最近用过的页表项放在 CPU 里一个很小很快的相联存储器里,叫快表 TLB(页表本身叫慢表)。先查 TLB,命中就不用访问主存里的页表。

设 TLB 查找时间 tT,主存访问时间 tm,TLB 命中率 h(先查 TLB,未命中再查页表): 有效访问时间 EAT = h(tT + tm) + (1 − h)(tT + tm + tm) 再考虑缺页(缺页率 p,缺页处理时间 tf):EAT ≈ 上式 + p × tf 若 TLB 和主存页表同时开始查找(TLB 命中就停止查页表):EAT = h(tT + tm) + (1 − h)(tm + tm)

例:tT = 20ns、tm = 100ns、h = 90%:先查 TLB 时 EAT = 0.9 × 120 + 0.1 × 220 = 130ns;同时查时 EAT = 0.9 × 120 + 0.1 × 200 = 128ns;不用快表是 200ns。TLB 的命中率高,是因为程序有局部性,最近用过的几页会被反复访问。

2009-46操作系统融合综合题 8 分
请求分页,页表:页号 0 → 页框 101H,有效位 1;页号 1 → 有效位 0;页号 2 → 页框 254H,有效位 1。页面 4KB,一次内存访问 100ns,一次快表(TLB)访问 10ns,处理一次缺页平均 108ns(已含更新 TLB 和页表的时间),进程驻留集大小固定为 2,采用 LRU 置换和局部淘汰策略。① TLB 初始为空;② 地址转换时先访问 TLB,未命中再访问页表(忽略访问页表之后的 TLB 更新时间);③ 有效位为 0 表示页面不在内存,产生缺页中断,处理后返回到产生缺页中断的指令处重新执行。访问序列 2362H、1565H、25A5H。(1)依次访问各需多少时间?(2)1565H 的物理地址是多少?
展开解析
  1. 页 4KB → 低 12 位是偏移,最高的十六进制位是页号:2362H 页 2,1565H 页 1,25A5H 页 2。
  2. 2362H:查 TLB 未命中 10 + 查页表 100(有效)+ 访存取数 100 = 210ns(此后页 2 进入 TLB)。
  3. 1565H:TLB 10 + 页表 100 发现有效位 0 → 缺页处理 108(含更新 TLB、页表)→ 重新执行这条指令:TLB 10(命中)+ 访存 100 → 10 + 100 + 108 + 10 + 100 = 100000220ns。
  4. 25A5H:页 2 已在 TLB → 10 + 100 = 110ns。
  5. (2)驻留集为 2,内存里已有页 0、页 2,页 1 缺页必须淘汰一页。LRU:页 2 刚访问过,页 0 最久没用 → 淘汰页 0,页 1 装入页框 101H → 物理地址 = 101565H。

多级页表:页表太大怎么办

32 位地址、4KB 页 → 220 个页表项 × 4B = 4MB,而且必须连续存放(要用“起始地址 + 页号×4”定位)。办法:给页表再分页。把页号再切成两段:页目录号 | 二级页表索引。页目录(一级页表)只有一页,记录每张二级页表放在哪个页框;二级页表也各占一页,用不到的二级页表可以不建、不调入内存。

页目录号 10 位 页表索引 10 位 页内偏移 12 位 PDBR / PTBR页目录物理地址 页目录(1 页)二级页表所在页框 某张二级页表(1 页)页框号 页框号偏移物理地址 找到二级页表 不用快表时:查页目录 1 次 + 查二级页表 1 次 + 取数据 1 次 = 访存 3 次
图 8-12 二级页表。多级页表的好处是减少页表占用的连续内存空间、可以只调入需要的部分(2014-32 选 D);坏处是查表次数变多、地址变换变慢(TLB 可以弥补)。页目录基址寄存器(PDBR/PTBR)里放的是一级页表的物理地址(2021-29 选 B),进程切换时要换(第 6 篇)。
设计多级页表的原则

每一级页表都正好占一页:一页能放 页面大小 ÷ 页表项大小 个表项,所以每级索引位数 = log2(页面大小 / 页表项大小)。4KB 页、4B 表项 → 每级 10 位;再加 12 位偏移,32 位地址正好两级。2010-29:页 210B、表项 2B → 一页放 29 项;共 216 页 → 需要 216/29 = 128 页来放页表 → 页目录至少 128 项。2026-28:64 位系统三级页表各 9 位、偏移 12 位(每页 512 个 8B 表项),第三级页表有 29×29 = 256K 张,各占一个页框。

2020-46操作系统融合综合题 8 分
32 位系统采用二级页表的请求分页,按字节编址,页目录项和页表项均 4 字节,虚拟地址 = 页目录号 10 位 | 页号 10 位 | 页内偏移 12 位。C 程序中数组 a[1024][1024](元素 4 字节)的起始虚拟地址为 1080 0000H,进程的页目录起始物理地址为 0020 1000H。(1)a[1][2] 的虚拟地址?对应的页目录号和页号?页目录项的物理地址?若该页目录项中存放的页框号为 00301H,a[1][2] 所在页的页表项的物理地址?(2)a 在虚拟地址空间、物理地址空间中是否必须连续?(3)按行遍历和按列遍历,哪种局部性更好?
展开解析
  1. 一行 1024 × 4B = 4KB = 恰好一页。a[1][2] = 1080 0000H + 1×4096 + 2×4 = 1080 1008H。
  2. 二进制 0001 0000 10|00 0000 0001|0000 0000 1000:页目录号 = 00 0100 0010 = 042H,页号 = 001H。
  3. 页目录项地址 = 0020 1000H + 42H × 4 = 0020 1000H + 108H = 0020 1108H。
  4. 二级页表在页框 00301H,起始物理地址 0030 1000H;页表项地址 = 0030 1000H + 1 × 4 = 0030 1004H。
  5. (2)虚拟地址空间必须连续(数组下标靠“首地址 + 偏移”计算);物理地址空间不必连续,每页可以在任意页框。
  6. (3)按行:每行正好一页,顺序访问 → 局部性好。按列:相邻两次访问在不同页,按行遍历好。

同类:2015-46(01000000H 和 01112048H 的最高 10 位都是 4 → 只访问 1 张二级页表;全部页表共 1 + 1024 = 1025 页);2018-45(页目录号 6、页号 6、偏移 8 → 0180 6008H);2019-31(2050 1225H → 页目录号 081H、页号 101H);2024-45(4MB 大页、页号 10 位、偏移 22 位:12345678H 页号 048H,其页表项虚拟地址 B8C00000H + 48H×4 = B8C00120H、物理地址 65400120H,缺页后映射到 BAB45678H → 页框号 2EAH)。

反置页表:按页框建表,全系统只要一张 拓展

问题:普通页表按虚页号建表,每个进程一张。32 位地址、4KB 页、页表项 4B 时,每个进程的一级页表就是 4MB,100 个进程就是 400MB;64 位地址更不用说。能不能让页表的大小只和物理内存有关?

大白话:普通页表像“每个学生一张清单,写着自己的书放在哪个柜子”;反置页表(也叫倒排页表)反过来,像“图书馆每个柜子贴一张条,写着这里放的是谁的哪本书”。柜子数是固定的,条子数也就固定。

定义:反置页表为每个物理页框设一个表项,内容是(进程标识 PID,虚页号),按页框号顺序排列,整个系统只有一张。表的大小只与物理内存容量有关,与虚拟地址空间多大、进程有多少无关。

逻辑地址(加上当前进程号) PID7 虚页号5 页内偏移1A4H 反置页表(每个页框一项) 页框号PID虚页号 032 179 275 315 … 项数 = 物理页框数 ① 按(PID,虚页号)搜索(常用哈希表加速) 页框号2 偏移1A4H 物理地址 21A4H ② 匹配项的下标就是页框号 ③ 偏移原样照抄 注意第 3 项也是“虚页号 5”,但属于进程 1:不同进程的虚页号会重复,所以必须连 PID 一起比较。
图 8-13 反置页表的地址变换。普通页表“用页号当下标、取出页框号”;反置页表反过来,“用(PID,页号)去搜、搜到的位置就是页框号”。

跟我做一遍:物理内存 1GB、页 4KB → 230 ÷ 212 = 218 = 262144 个页框;每项 8B(PID + 虚页号 + 哈希链指针)→ 反置页表 2MB,全系统只要这一张。普通一级页表 100 个进程要 400MB(已用程序验算)。

代价:① 不能用页号直接当下标,只能搜索,通常用哈希表加速,再配 TLB;② 一个表项只能记一个(PID,页号),多个进程共享同一页框不好表示;③ 不在内存的页在表里没有记录,缺页时还要另外的外部页表告诉 OS 它在磁盘的哪里。

真题怎么考:408 真题还没有考过反置页表。记住“按页框建表、全系统一张、大小只与物理内存有关、查找要搜索”即可,下面变式卡有一道判断题。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2019-31 改
某计算机采用二级分页,虚拟地址结构为页目录号(10 位)| 页号(10 位)| 页内偏移(12 位)。虚拟地址 1234 5678H 对应的页目录号和页号分别是( )。
  • A. 048H、345H
  • B. 123H、456H
  • C. 048H、145H
  • D. 091H、345H
展开解析

A1234 5678H = 0001 0010 00 | 11 0100 0101 | 0110 0111 1000B。页目录号 = 00 0100 1000B = 048H,页号 = 11 0100 0101B = 345H。

变化点:10 位的字段不是 4 的倍数,不能直接按十六进制位切(B 就是这样切错的)。展开成二进制再按 10、10、12 切。

变式操作系统由 2024-17、2026-19 改
虚拟地址 32 位,页大小 4KB。TLB 共 64 项,采用 8 路组相联映射。TLB 表项中标记字段至少为( )位。
  • A. 14
  • B. 17
  • C. 20
  • D. 18
展开解析

B虚页号 32 − 12 = 20 位。TLB 组数 = 64 ÷ 8 = 8,组号 3 位。标记 = 20 − 3 = 17 位。

变化点:2024-17 是 1KB 页、32 项 4 路(22 − 3 = 19)。TLB 的“地址”是虚页号,组号取虚页号的低位,其余是标记。C 是全相联时的答案。

变式操作系统由 2010-29、2026-28 改
某系统虚拟地址 48 位,页大小 4KB,页表项 8B。采用多级页表,要求每一级页表都恰好占一页,则至少需要( )级页表。
  • A. 2
  • B. 3
  • C. 4
  • D. 5
展开解析

C一页能放 4KB ÷ 8B = 512 个页表项,每级索引 9 位。虚页号 = 48 − 12 = 36 位,36 ÷ 9 = 4 级。

变化点:公式:每级索引位数 = log2(页大小 ÷ 页表项大小),级数 = ⌈虚页号位数 ÷ 每级位数⌉。

变式操作系统考纲补充(尚无真题):反置页表
下列关于反置页表(倒排页表)的叙述中,正确的是( )。
  • A. 每个进程各有一张反置页表
  • B. 反置页表的表项数由虚拟地址空间的大小决定
  • C. 反置页表按虚页号顺序排列,可以直接用虚页号作下标查找
  • D. 反置页表的大小只与物理内存容量有关
展开解析

D反置页表每个物理页框一项,整个系统一张,项数 = 页框数,只与物理内存有关。A 错:全系统只有一张。B 错:那是普通页表。C 错:它按页框号排列,要用(进程号,虚页号)去搜索,常用哈希表加速。

考点:反置页表。

8.6分段与段页式

★★ 常考选择题

分页是给机器看的(等长,方便管理),分段是给程序员看的:按程序的逻辑把地址空间分成代码段、数据段、栈段……每段长度不同,段内地址从 0 开始。逻辑地址 = 段号 | 段内地址,要用段表(段号 → 段基址、段长)转换。分段存储管理也叫段式存储管理(分页也叫页式);计组教材讲的“段式虚拟存储器”就是分段再加上请求调入,见本节末尾 段式虚拟存储器。

段号 S2 段内地址 W400 逻辑地址(二维:程序员自己分段) 段表寄存器段表始址 · 段表长度 段表 段号段长基址权限 01006000只读 1200(外存)读写 23004000读写 ① S ≥ 段表长度?→ 越界 W ≥段长? ② 取段长 300 400 ≥ 300→ 越界中断! + ③ 基址 4000 若合法:物理地址= 基址 + W(真要做加法) 与分页的区别:段长不固定,要检查段内地址是否越界,物理地址 = 基址 + 段内地址;分页只要拼接。
图 8-14 分段的地址变换(数据就是 2016-28)。先查段号越界,再查段内地址越界,最后检查权限、算物理地址。检测到“段不在内存”是缺段中断(类似缺页),检测到写只读段是越权。
分段的地址变换
① 段号 ≥ 段表长度 → 越界中断;② 查段表得到基址和段长;③ 段内地址 ≥ 段长 → 越界中断;④ 物理地址 = 基址 + 段内地址(真的要做加法,不是拼接);⑤ 检查权限。
要点
段号 8 位、地址 32 位 → 段内地址 24 位 → 最大段长 224 字节(2009-27)。便于按逻辑模块共享和保护(如只共享代码段)。有外部碎片。
2016-28操作系统
某进程的段表:段 0 段长 100、内存起始地址 6000、只读、在内存;段 1 段长 200、读写、不在内存;段 2 段长 300、起始 4000、读写、在内存。当访问段号为 2、段内地址为 400 的逻辑地址时,进行地址转换的结果是( )。
  • A. 段缺失异常
  • B. 得到内存地址 4400
  • C. 越权异常
  • D. 越界异常
展开解析

考点8.6 分段的地址变换

D分段地址变换的检查顺序:段号是否越界 → 查段表 → 段内地址是否 ≥ 段长(越界) → 检查状态和权限 → 物理地址 = 基址 + 段内地址。段 2 长 300,段内地址 400 ≥ 300 → 越界异常。

  • A 错段 2 的状态是“在内存”,不会段缺失。
  • B 错4000 + 400 = 4400 是没做越界检查的结果。
  • C 错段 2 可读写,访问不会越权(而且越界检查先发生)。
  • D 对越界异常。

分段必须检查段内地址 < 段长(分页不需要,页内偏移天然不会越界)。

分页分段
单位页是物理单位,为了提高内存利用率、消除外部碎片而划分段是逻辑单位,按程序模块(代码、数据、栈)划分,为了方便编程、共享和保护
大小固定,由系统(硬件)决定不固定,由程序决定
地址空间一维:只需给出一个地址二维:要同时给出段号和段内地址
对用户透明可见(程序员 / 编译器要知道分段)
碎片无外部碎片,有少量内部碎片无内部碎片,有外部碎片
共享与保护不方便(一页里可能混着能共享和不能共享的内容)方便(按段整体共享,按段设置读写权限)
地址变换页框号 拼接 偏移段基址 加 段内地址,并检查段长

段页式:先分段,每段再分页。逻辑地址 = 段号 | 页号 | 页内偏移;每个进程一张段表,每段一张页表。地址变换:① 段号与段表长度比较,越界则中断;② 查段表项,得到该段页表的起始地址和页表长度;③ 页号与页表长度比较,越界则中断;④ 查页表项得到页框号;⑤ 页框号拼接页内偏移。不用快表时一次访存要访问主存 3 次(段表、页表、数据)。段页式兼有分段便于共享保护、分页没有外部碎片的优点,地址空间仍是二维的。

共享

两个进程共享同一个段/页:在各自的段表/页表里,各写一个表项指向同一块物理内存。共享的东西在两个进程里的段号/页号不一定相同,但物理页框号一定相同(2023-30 选 C;2019-28 “段 S 在 P1 和 P2 中应该具有相同的段号”错)。2026-30:两进程共享文件 R,映射的虚拟地址不一定相同,但映射到的物理地址相同,一方修改另一方可见。

段式虚拟存储器:计组和操作系统说的是同一件事

问题:计组教材讲虚拟存储器时分“页式、段式、段页式”三种,操作系统讲的是“请求分页、请求分段”。它们是什么关系?

大白话:把 8.5、8.6 的分页、分段、段页式再加上“不在内存就去磁盘调、内存满了就换出”的能力,就是对应的虚拟存储器。计组关心硬件怎么查表,操作系统关心缺了怎么调、满了换谁。所以 页式虚拟存储器 = 请求分页,段式虚拟存储器 = 请求分段。

定义:段式虚拟存储器的段表项在 8.6 段表(段号、段长、基址、权限)的基础上增加 存在位(装入位)、访问位、修改位、外存始址,以及增补位(这一段运行中能否动态增长)。访问的段不在内存,就产生缺段中断。

缺段中断 有一块连续空闲区≥ 段长? 是 从外存调入该段,修改段表(存在位 = 1,填基址) 返回,重新执行指令 否 空闲区总和≥ 段长? 是 紧凑(拼接)空闲区 否 淘汰一个或几个段(被修改过的先写回) 再判断一次
图 8-15 缺段中断的处理。段长不固定,调入一段要找一块足够大的连续空闲区,这是它和缺页最大的不同:缺页只要随便找一个页框,缺段可能要先紧凑,甚至淘汰好几段。

跟我做一遍:内存中有 3 块互不相邻的空闲区 30K、20K、40K,要调入长 50K 的段。① 最大的一块 40K < 50K,没有能直接放下的;② 总和 90K ≥ 50K → 先紧凑成一块 90K,再调入,剩 40K。若要调入 100K 的段:总和 90K 也不够 → 只能淘汰若干段,再回到第 ① 步。

页式虚拟存储器段式虚拟存储器段页式虚拟存储器
虚拟地址页号 | 页内地址段号 | 段内地址段号 | 页号 | 页内地址
优点页长固定,页表简单,调入方便;没有外部碎片按程序逻辑分段,便于编译、修改、保护和共享兼有两者的优点
缺点页不是逻辑单位,共享和保护不方便;有页内碎片段长不定,主存分配麻烦,段间留下外部碎片要查段表和页表两次,地址变换慢
不用快表时一次访存访问主存2 次2 次3 次

真题怎么考:段表越界、越权(2016-28);共享段的段号(2019-28);段页式访存次数和优缺点比较。

易错

① “段式”“分段”是同一个意思,“页式”“分页”也是;题目说“段式虚拟存储器”就是请求分段。② 段页式的地址空间仍是二维的(程序员看到的是段)。③ 分段消除了内部碎片,却带来外部碎片,所以缺段处理里会出现“紧凑”这一步,缺页处理里没有。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2016-28 改
某进程的段表中,段 0 的段长为 200、内存起始地址为 3000、权限为只读、在内存中。进程对段号 0、段内地址 150 的单元执行写操作,地址转换的结果是( )。
  • A. 段缺失异常
  • B. 得到内存地址 3150
  • C. 越权异常
  • D. 越界异常
展开解析

C先查段长:150 < 200,不越界。再查权限:该段只读,而这次是写操作,产生越权异常。

变化点:2016-28 是段内地址超过段长(越界)。这里地址没超,但操作类型不允许。做这类题时先查段内地址是否超过段长,再查这次操作是否符合该段的权限。

变式操作系统由 2023-30、2026-30 改
进程 P1、P2 通过分页机制共享同一个物理页框 f。下列叙述中,正确的是( )。
  • A. 该页在两个进程中的页号一定相同
  • B. 两个进程页表中对应的页表项都指向页框 f
  • C. 一个进程修改该页的内容,另一个进程看不到
  • D. 被共享的页只能设为只读
展开解析

B共享的本质是两个进程的页表项指向同一个物理页框。页号可以不同(2023-30)。它们访问的是同一块物理内存,一方修改,另一方马上能看到。共享页可以可写,例如共享内存通信。

变化点:2023-30 从“页号、页框号是否相等”的角度考;这里从“页表项和数据可见性”的角度考同一件事。

变式操作系统考纲补充(尚无真题):缺段中断
段式虚拟存储系统中发生缺段中断。内存中有若干互不相邻的空闲区,每一块都比该段短,但空闲区的总和大于该段长度。此时操作系统应( )。
  • A. 先紧凑(拼接)空闲区,再调入该段
  • B. 把该段拆成几部分分别装入各空闲区
  • C. 终止该进程
  • D. 只能淘汰内存中的若干段
展开解析

A一段必须放在连续的内存里,所以 B 不行。空闲区总和够,把它们紧凑成一块就能放下,不必淘汰别的段(D 是总和也不够时才做的)。缺段是正常的调入请求,不是错误,C 错。

考点:段式虚拟存储器的缺段处理(已用程序验算例题)。

8.7融合虚拟内存:内存不够也能跑大程序

★★★ 高频选择题综合题

根据局部性原理,程序运行时并不需要全部装入内存。虚拟内存:只装入当前要用的几页就开始运行;访问到不在内存的页时,由硬件发出缺页异常,操作系统把它从磁盘调入(请求调页);内存满了就选一页换出(页面置换)。于是进程看到的“内存”可以比物理内存大得多——虚拟内存的大小由地址位数决定,和内存、磁盘多大无关(2012-25、2023-28)。它只能建立在离散分配(分页/分段)的基础上。

虚拟存储器的三个特征
多次性:一个作业分成多次调入内存运行,不必一次全部装入;对换性:作业运行期间可以换入、换出;虚拟性:从逻辑上扩充了内存容量,用户看到的容量远大于实际容量。实际能用的最大容量 = min(地址位数决定的范围,内存 + 外存容量)。
缺页中断和一般中断的区别
① 缺页是在指令执行期间产生并处理的(一般中断在一条指令执行完才检查);② 一条指令执行期间可能发生多次缺页(例如指令本身跨两页、要访问的数据又跨两页);③ 处理完返回本条指令重新执行,而不是下一条。它属于内中断(故障)。

请求分页的页表项

页框号在内存时有效存在位 P是否在内存访问位 A最近访问过?修改位 M脏?外存地址在磁盘哪里保护位读/写/执行

存在位给 MMU 判断缺页;访问位给置换算法(CLOCK)用;修改位决定换出时要不要写回磁盘;外存地址告诉 OS 去哪调入。改进型 CLOCK 需要访问位和修改位(2018-45 第 3 问)。

缺页处理:一次完整的“硬件—操作系统—磁盘”接力

硬件(CPU / MMU)操作系统(内核态)磁盘 ① 执行访存指令,MMU 查页表:存在位 P = 0 ② 产生缺页异常(故障)保存 PC/PSW,陷入内核 ③ 检查地址合法(越界/越权 → 终止进程) ④ 找空闲页框;没有 → 按置换算法选一页淘汰若它被修改过(M=1),先写回磁盘;改它的页表项 P=0 ⑤ 按外存地址启动磁盘 I/O 读入该页当前进程 → 阻塞,调度别的进程运行 读磁盘(ms 级)DMA 把页送入页框 ⑥ 完成,发 I/O 中断 ⑦ 中断处理:修改页表项(P=1,填页框号),(更新 TLB);把该进程 阻塞 → 就绪 ⑧ 该进程再次被调度,中断返回 ⑨ 重新执行这条指令这次 P=1,得到物理地址
图 8-16 缺页处理。缺页是故障类异常(第 5 篇),处理完回到发生缺页的那条指令重新执行(2019-14、2021-21)。缺页处理中 OS 可能做:修改页表、磁盘 I/O、分配页框(2011-28 全选)、置换页面(2013-30);但不一定要淘汰页(有空闲页框就不用,2022-29)。检测 TLB 缺失、缺页、越权是 MMU 在地址转换中做的,Cache 缺失不是(2024-18);异常最终由操作系统处理(2026-24)。
2014-45(3)计组融合
接第 3 篇 2014-44 的循环(1000 次,每次 load 一个 A[i]):页式虚拟存储;P 的代码已调入主存;数组 A 未调入主存,且所有元素在同一页、存在磁盘同一个扇区。P 执行过程中,哪条指令可能产生缺页异常?对数组 A 的访问,需要读磁盘和查 TLB 至少各多少次?
展开解析

只有访问数据的 load 指令(指令 3)可能缺页(代码已在内存)。A 在同一页 → 读磁盘 1 次。查 TLB:1000 次访问各查一次,再加上第一次访问 A[0] 缺页后重新执行那条指令又查一次 → 1001 次。

何时调入、从哪调入:请求调页与预调页

问题:缺页了才去磁盘读一页,一次只读一页,进程刚启动时要连续缺页好多次,能不能一次多读几页?

大白话:请求调页像“饿了才去买一个包子”;预调页像“顺手把旁边几个也买了,猜你待会儿也要吃”——猜中了省跑腿,猜错了白占地方。

策略什么时候调入优点缺点 / 用在哪
请求调页访问到不在内存的页(缺页)时,只调这一页调入的页一定会被访问每次一页,I/O 次数多、开销大;进程运行期间主要用它
预调页根据局部性,一次调入若干相邻的页相邻页在磁盘上连续存放时,一次 I/O 读入多页,省寻道和旋转时间要靠预测,调入的页不一定用得上;主要用在进程首次调入时(由程序员或系统指出先调哪些页)

跟我做一遍:进程启动后马上要用页 0~3,它们在磁盘上连续存放。请求调页:缺页 4 次,启动磁盘 4 次,每次都有寻道和旋转延迟。预调页:启动时一次把页 0~3 读入,只启动磁盘 1 次,之后访问这 4 页都不缺页。如果预调入的页 3 其实没用到,它就白白占了一个页框。

从哪调入:磁盘分为对换区(连续分配,读写快)和文件区(离散分配,读写慢)。① 对换区空间足够时:运行前把进程的相关页从文件区复制到对换区,之后全部从对换区调入;② 对换区不够时:不会被修改的页(代码)直接从文件区调入,换出时也不用写回;可能被修改的页换出到对换区,以后从对换区调回;③ UNIX 方式:没运行过的页从文件区调入,换出过的页从对换区调入;共享页可能已被别的进程调入内存,就不用再调。

页面置换算法

算法淘汰谁说明
OPT 最佳以后最长时间不会被访问的页缺页最少,但要预知未来,实际不能实现,用来作比较标准
FIFO 先进先出最早调入的页实现简单;会出现 Belady 异常:分配的页框多了缺页反而更多(只有 FIFO 会,2014-30)
LRU 最近最久未使用最长时间没被访问过的页效果接近 OPT;需要硬件支持(寄存器或栈),开销大。从当前位置往回数,最后一个“出现”的就是它
CLOCK(也叫 NRU 最近未用)指针循环扫描:访问位 A=1 → 清 0 跳过;A=0 → 淘汰LRU 的近似,便宜
改进型 CLOCK同时看 (A, M):优先淘汰 (0,0) → 再找 (0,1)(扫描时把 A 清 0)→ 重复(详见 四轮规则)淘汰顺序 (0,0)、(0,1)、(1,0)、(1,1)(2016-26);没修改过的页不用写回,代价小
例操作系统同一序列,三种算法
分配 3 个页框,初始为空,访问序列 7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3, 2, 1, 2, 0, 1, 7, 0, 1。分别用 OPT、FIFO、LRU,缺页各几次?
展开解析

OPT 的过程(“缺”表示缺页,“换 x”表示淘汰页 x;表中页框内容按页框位置列出):

访问70120304230321201701
页框 177722222222222222777
页框 20000004440000000000
页框 3111333333331111111
结果缺缺缺换7换1换0换4换3换2

OPT 每次淘汰“往后看最晚才会用到(或再也不用)”的页:例如访问 2 时,内存里有 7、0、1,往后看 0 最先用到、1 其次、7 最晚 → 淘汰 7。结果:OPT 缺页 9 次(其中置换 6 次),LRU 12 次,FIFO 15 次(已用程序验算;可以把序列和 3 个页框填进下面的工具逐步对照)。LRU 淘汰“往前看最久没用过的”,和 OPT 恰好是时间上反过来的镜像,所以效果最接近 OPT。

做大题时:题目问“缺页次数”要把一开始装入空页框的那几次算进去;问“置换次数”则不算(2019-29)。

2019-29操作系统
某系统采用 LRU 页置换算法和局部置换策略,若系统为进程 P 预分配了 4 个页框,进程 P 访问页号的序列为 0,1,2,7,0,5,3,5,0,2,7,6,则进程访问上述页的过程中,产生页置换的总次数是( )。
  • A. 3
  • B. 4
  • C. 5
  • D. 6
展开解析

考点8.7 页面置换算法

C先分清“缺页”和“置换”:页框还没满时缺页只是装入空页框,不算置换;页框满了再缺页才要换出一页。

  1. 0、1、2、7:装入 4 个空页框,缺页 4 次,置换 0 次。
  2. 0 命中;5 缺页,最久未用的是 1 → 换出 1;3 缺页 → 换出 2;5、0 命中。
  3. 2 缺页 → 换出 7;7 缺页 → 换出 3;6 缺页 → 换出 5。
  4. 置换共 5 次(缺页共 9 次)。
  • A 错3 次比 OPT(最佳置换,理论最少)的 4 次还少;LRU 不可能比 OPT 置换得更少。
  • B 错4 次是 OPT 的置换次数(每次换出将来最久才用到的页);题目用的是 LRU,看的是过去。
  • C 对LRU 置换 5 次。
  • D 错6 次是 FIFO 的置换次数(总换出最早装入的页);LRU 每次换出最久没被访问的页,0、5 命中会刷新它们的“最近使用”时间。

问“置换次数”时要减去开头装入空页框的那几次缺页。同类:2025-26(3 个页框、0/1/2 已在内存,序列 0,1,2,0,5,1,4,3,0,2,3,2,0 → LRU 缺页 6 次);2015-27(4 页框,序列 2,0,2,9,3,4,2,8,2,4,8,4,5,下一页 7 → LRU 淘汰页 2)。

2010-46操作系统综合题 8 分
逻辑地址空间和物理地址空间均 64KB,按字节编址;页大小 1KB;固定分配局部置换,为进程分配 4 个页框。时刻 260 前:页 0 在页框 7(装入时刻 130,访问位 1);页 1 在页框 4(230,1);页 2 在页框 2(200,1);页 3 在页框 9(260,1)。时刻 260 要访问逻辑地址 17CAH。(1)页号?(2)FIFO 置换,物理地址?(3)CLOCK 置换(指针按顺时针 2号页框→4→7→9 移动,当前指向 2 号页框),物理地址?
展开解析
  1. 页 1KB → 偏移 10 位。17CAH = 0001 01|11 1100 1010 → 页号 = 000101 = 5,偏移 3CAH。页 5 不在内存 → 缺页。
  2. FIFO:最早装入的是页 0(时刻 130),在页框 7 → 页 5 放入页框 7 → 物理地址 = 7 × 1024 + 3CAH = 000111|11 1100 1010 = 1FCAH。
  3. CLOCK:从页框 2 开始,四个页框访问位都是 1 → 依次清 0(2→4→7→9),转一圈回到页框 2,此时访问位 0 → 淘汰页 2,页 5 放入页框 2 → 物理地址 = 000010|11 1100 1010 = 0BCAH。
2021-28操作系统
页大小 4KB,按字节编址。为进程 P 分配 2 个固定页框,采用改进型 Clock 置换。P 的页表部分内容:页 2 存在位 0;页 3 在页框 60H,存在位 1、访问位 1、修改位 0;页 4 在页框 80H,存在位 1、访问位 1、修改位 1。P 访问虚拟地址 02A01H,经地址变换后得到的物理地址是( )。
  • A. 00A01H
  • B. 20A01H
  • C. 60A01H
  • D. 80A01H
展开解析

考点8.7 改进型 CLOCK 的扫描规则

C先判断缺页,再按改进型 CLOCK 一轮一轮扫描(从页 3 开始,依次页 3 → 页 4)。

  1. 02A01H:页 4KB → 偏移 A01H,页号 2。页 2 存在位 0 → 缺页;2 个页框都满了 → 要置换。
  2. 第一轮:找 (A=0, M=0),不改任何位。页 3 是 (1,0),页 4 是 (1,1),都不是。
  3. 第二轮:找 (0,1),扫过而不符合的,把 A 清 0。页 3 → (0,0),页 4 → (0,1)。这一轮也没找到。
  4. 第三轮:重新找 (0,0),页 3 符合 → 淘汰页 3,页 2 装入页框 60H。
  5. 物理地址 = 60H 拼 A01H = 60A01H。
  • A 错00A01H 是把页框号当成 0,没有做置换。
  • B 错20A01H 是把页号 2 当成页框号直接拼接(页 2 不在内存)。
  • C 对60A01H。
  • D 错淘汰了页 4:它被修改过,换出要写回,在两页都刚被访问过时不会先淘汰它。

改进型 CLOCK 最多四轮:找 00(不改位)→ 找 01(清 A)→ 再找 00 → 再找 01。

改进型 CLOCK:四轮扫描规则和一个完整例子

问题:简单 CLOCK 只看访问位。可是换出一个被修改过的页要先写回磁盘,比换出没改过的页多一次 I/O。能不能在“最近都没用过”的页里,优先淘汰没改过的?

大白话:清理冰箱时,先扔“好久没碰、也没拆封”的;其次扔“好久没碰、但拆过封”的(要多收拾一下);最近刚吃过的尽量留着。“最近用没用”比“好不好收拾”更重要。

规则(每页有访问位 A、修改位 M,指针循环扫描):

  1. 第一轮:从指针处扫一圈,找 (A=0, M=0),找到就淘汰;不修改任何位。
  2. 第二轮:再扫一圈,找 (0,1),找到就淘汰;扫过而不符合的页,把 A 清 0。
  3. 第三轮:重复第一轮(这时所有 A 都已是 0)。
  4. 第四轮:重复第二轮,这一轮一定能找到。所以最多扫四轮。
置换前(访问页 7,缺页) 页框0:页 3(1,1) 页框1:页 5(0,1) 页框2:页 8(1,0) 页框3:页 9(1,1) 指针 括号内为 (A, M);指针顺时针 0→1→2→3 移动 扫两轮 置换后 页框0:页 3(1,1)→(0,1) 页框1:页 7新装入 页框2:页 8(1,0) 页框3:页 9(1,1) 指针 页 5 是脏页,已先写回磁盘
图 8-17 改进型 CLOCK 一次置换的前后。页 8 是 (1,0),没改过、换出最省事,却没被选中——因为它最近刚被访问过。改进型 CLOCK 先看 A,再看 M。

跟我做一遍(已用程序验算):进程有 4 个页框,指针指向页框 0,页框 0~3 依次是页 3 (1,1)、页 5 (0,1)、页 8 (1,0)、页 9 (1,1)。访问页 7 缺页,淘汰谁?

  1. 第一轮找 (0,0):一圈下来没有,不改任何位。
  2. 第二轮找 (0,1):页框 0 是 (1,1),不符合,A 清 0 → (0,1);页框 1 是 (0,1),符合 → 淘汰页 5(它被修改过,先写回磁盘),页 7 装入页框 1,指针移到页框 2。
  3. 页框 2、3 没被扫到,位不变。

如果 4 页都是 (1,1):第一轮没有 (0,0);第二轮也没有 (0,1),但把 4 页的 A 全部清 0,都变成 (0,1);第三轮找 (0,0) 仍没有;第四轮在指针处就找到 (0,1) → 淘汰页框 0 的页。这就是“最多四轮”的情形。

真题怎么考:淘汰次序(2016-26);给页表算置换后的物理地址(2021-28,走到第三轮);页表项要有访问位和修改位(2018-45)。

易错

① 第一轮不清 A,第二轮才清;把顺序记反会选错页。② 第二轮清掉 A 的页,本轮不会马上回头再检查,要等下一轮。③ 淘汰次序是 (0,0) → (0,1) → (1,0) → (1,1),把 (A, M) 当成二进制数从小到大即可。

LFU、NRU 与页面缓冲算法 PBA

问题:LRU 效果好但要记录每次访问的时间,硬件开销大;CLOCK 便宜但粗糙。教材还介绍了几种算法,它们分别解决什么问题?

LFU 最少使用
淘汰访问次数最少的页:每页一个计数器,访问一次加 1(实际常定期右移,让旧的次数逐渐“衰减”)。和 8.3 Cache 的 LFU 是同一个思路。缺点:只看次数、不看最近,过去用得多的页会赖着不走。
NRU 最近未用
就是简单 CLOCK:每页一个访问位,指针循环扫描,A = 1 就清 0 跳过,A = 0 就淘汰。它只把页分成“最近用过 / 没用过”两类,所以叫“最近未用”。改进型 CLOCK 再加上修改位,分成四类。
例操作系统LFU 和 LRU 的差别(已用程序验算)
3 个页框,初始为空,访问序列 1, 1, 1, 2, 3, 4, 2, 3, 4, 2, 3, 4。LFU(次数相同时淘汰最早进入的)和 LRU 各缺页几次?
展开解析

LRU:4 次。1、2、3 装入;访问 4 时淘汰最久没用的页 1;之后 2、3、4 轮流访问,全部命中。

LFU:10 次。页 1 一开始被访问了 3 次,计数为 3;2、3、4 每次进来计数都只有 1,总是被彼此挤掉:访问 4 淘汰 2,访问 2 淘汰 3,访问 3 淘汰 4……从第 6 次访问起每次都缺页,而页 1 再也不用了,却一直占着一个页框。

结论:LFU 不是 LRU 的近似;程序的“热点”变了以后,它反应很慢。

页面缓冲算法 PBA:问题是“刚被换出去的页,很可能马上又要用”,每次都重新读盘很亏;被修改的页一页一页写回磁盘也很亏。做法(可变分配、局部置换,置换算法用简单的 FIFO):被淘汰的页不马上清除,而是按是否修改过挂到两个链表上:

进程的驻留集FIFO 选出被淘汰的页 M = ? M=0 M=1 空闲页链表:新淘汰的挂链尾(左),从链首(右)取页框 页框 页框 页框 分给新调入的页 又被访问:直接取回,不用读盘 脏页 脏页 修改页链表(也挂链尾,也可直接取回) 磁盘攒够一批一起写回写完转入空闲页链表
图 8-18 页面缓冲算法 PBA。链表中的页内容还在内存里,只是不再属于驻留集。被再次访问时,只要它的页框还没被分配出去,摘下来就能用,省一次读盘;修改过的页攒成一批再写,省多次写盘。

跟我做一遍:页 6(没修改过)被 FIFO 淘汰,挂到空闲页链表尾。过一会儿进程又访问页 6——这时它的页框还没轮到链首被分配出去,内容还在 → 从链表摘下来放回驻留集,不用读盘。如果这期间它已被分配给别的页、内容被覆盖了,就只能重新读盘。

真题怎么考:页缓冲队列的长度不影响缺页率(再访问照样算缺页,只是处理得快,2022-30);页面缓冲能降低平均访存时间(2026-29)。

页框分配、抖动与工作集

  • 驻留集:给一个进程分配的页框集合。固定分配(页框数不变)/ 可变分配;局部置换(只换自己的页)/ 全局置换(可以抢别的进程的页框)。组合:固定+局部、可变+全局、可变+局部;“固定分配 + 全局置换”不存在(全局置换会改变页框数,2015-30)。分配多少:平均分配、按比例分配(按进程大小)、考虑优先级的分配;每个进程至少要有能执行任何一条指令所需的页框数,它由指令的寻址方式决定(2025-27)。
  • 抖动(颠簸):页框太少,刚换出的页马上又要用,系统大部分时间都在换页。原因是进程太多、每个分得太少;办法是撤销/挂起部分进程(2011-29 选 A;增大对换区、提高优先级都没用)。
  • 工作集:最近 Δ 次访问(窗口)里访问过的不同页面的集合,应保证驻留集 ≥ 工作集。2016-29:窗口 6,最近 6 次访问是 6,0,3,2,3,2 → 工作集 {6, 0, 3, 2}。计算方法和例题见下。
  • 影响缺页率的因素:页面大小、分配的页框数、置换算法、程序的局部性(2022-30:“页缓冲队列的长度”不影响缺页率,它只影响置换的速度)。影响有效访存时间的:缺页率、磁盘读写时间、内存访问时间、缺页处理程序的 CPU 时间(2020-28 全选)。能加快地址转换的:增大 TLB、让页表常驻内存(2014-28)。

工作集怎么算:t 时刻、窗口 Δ 的工作集 W(t, Δ) = 从 t 往回数 Δ 次访问(含 t 时刻这次)里出现过的不同页面。窗口数的是访问次数,同一页出现几次都只算一个。

时刻 t访问的页 123456789101112 1 2 1 3 2 4 4 4 5 1 2 1 t = 6 的窗口(Δ = 4):1 3 2 4工作集 {1, 2, 3, 4},大小 4 t = 9 的窗口:4 4 4 5 → 工作集 {4, 5},大小 2 t = 7~9 一直访问页 4:局部性好,工作集就小
图 8-19 工作集是一个滑动窗口。窗口每个时刻右移一格,工作集随之变化。按工作集分配页框:驻留集至少要装得下工作集,否则就会抖动。
例操作系统拓展工作集计算(预测题型,已用程序验算)
某进程的页面访问序列为 1, 2, 1, 3, 2, 4, 4, 4, 5, 1, 2, 1(第 1~12 次访问),工作集窗口 Δ = 4。(1)t = 11 时的工作集是什么?(2)t = 1~12 中,工作集最大是多少?若系统按工作集给进程分配页框,至少应分配几个页框才能保证这段时间内驻留集不小于工作集?
展开解析

(1)t = 11 往回数 4 次:第 8~11 次访问是 4, 5, 1, 2 → 工作集 {1, 2, 4, 5}。

t123456789101112
访问121324445121
工作集大小122334322343

(2)工作集最大为 4(t = 6 时 {1, 2, 3, 4},t = 11 时 {1, 2, 4, 5}),所以至少分配 4 个页框。

易错:把窗口当成“往回数 4 个不同的页”;或者忘了 t 时刻这次访问本身也在窗口里。

内存映射文件

问题:用 read / write 读写文件,每次都要发系统调用,数据还要在内核缓冲区和用户缓冲区之间复制一遍。能不能像访问数组一样直接访问文件?

大白话:把文件“贴”到进程的虚拟地址空间里一段地址上,之后读写这段地址就是读写文件。哪一页第一次被访问,就靠缺页机制把文件的那一页调进来,和请求调页一模一样,只是页的来源是这个文件。

定义:内存映射文件(memory-mapped file)用一个系统调用把磁盘文件映射到进程的虚拟地址空间,之后通过访存指令访问文件内容;调入由缺页机制完成,被修改的页由系统在适当时候(解除映射、同步或换出时)写回文件。多个进程映射同一个文件时,各自的页表项指向同一批物理页框,因此可以共享内存、实现进程间通信。

进程 A 虚拟地址空间 映射区 页 0 映射区 页 1 映射区 页 2 4000 0000H 起 进程 B 虚拟地址空间 映射区 页 0 映射区 页 1 映射区 页 2 7000 0000H 起 物理内存 页框 12:文件页 0 页框 40:文件页 1 磁盘文件 data.bin(10KB)文件页 0 | 文件页 1 | 文件页 2 页 2 还没访问过:访问时缺页,从文件调入 脏页写回文件
图 8-20 两个进程映射同一个文件。映射区在两个进程中的虚拟地址可以不同(4000 0000H 和 7000 0000H),但页表项指向同一批物理页框,一方写入,另一方马上看得到(2026-30)。

跟我做一遍(已用程序验算):文件 data.bin 长 10KB,页 4KB → 占 3 页;进程 A 把它映射到 4000 0000H 起。程序要读文件的第 5000 字节:5000 = 1388H → 虚拟地址 4000 1388H → 在映射区的页 1、偏移 388H。若页 1 不在内存 → 缺页 → OS 从文件读入第 4096~8191 字节放进一个页框,改页表 → 重新执行这条指令。整个过程程序里没有一个 read 调用。

真题怎么考:映射到进程的虚拟地址空间,不是物理地址空间;可以实现进程间通信(2025-30);共享时虚拟地址可以不同、物理地址相同(2026-30)。

写时复制(copy-on-write)

问题:fork 要给子进程一个和父进程一模一样的地址空间。如果马上把父进程的几百页全部复制一份,又慢又费内存——何况子进程常常紧接着就调用 exec 换成别的程序,复制的页根本用不上。

大白话:两个人先合看一本书,谁要在书上写字,才给他复印那一页。

定义:fork 时不复制页面,而是让父、子进程的页表项指向同一批物理页框,并把这些页都设为只读。以后任何一方要写某一页,就会触发保护异常;操作系统发现这是写时复制页(不是真的越权),就为写的一方复制这一页、把它的页表项改为指向新页框并设为可写,然后重新执行那条写指令。没人写过的页始终共享。

① fork 之后 父页表页 0 只读页 1 只读页 2 只读 子页表页 0 只读页 1 只读页 2 只读 页框 A 页框 B 页框 C 一页都没复制,fork 很快 ② 子进程写页 1 之后 父页表页 0 只读页 1 只读页 2 只读 子页表页 0 只读页 1 可写页 2 只读 页框 A 页框 B 页框 C 页框 B′(副本) 子进程写页 1 → 保护异常 → OS 把页框 B 复制成 B′ → 改子页表(指向 B′、可写)→ 重新执行写指令
图 8-21 写时复制。谁写哪页,才给谁复制哪页;没写过的页 A、C 仍然共享。

跟我做一遍:父进程有 100 页。fork 后子进程写了其中 3 页,父进程写了另外 1 页 → 一共只复制 4 页;不用写时复制,fork 时就要立刻复制 100 页(已用程序验算)。

真题怎么考:408 选择题尚未单独考过写时复制。它把 8.6 的“共享”、8.7 的“保护异常 → OS 处理 → 重新执行指令”串在一起,理解了这两点就不会错。映射文件时若用“私有映射”,也是用写时复制让修改只对本进程可见。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2019-29、2015-27、2025-26 改
系统为进程 P 分配了 4 个页框(初始为空),采用 LRU 置换算法和局部置换策略。P 访问的页号序列为 1, 2, 3, 4, 1, 5, 2, 6, 1, 3,访问过程中发生页面置换的次数是( )。
  • A. 3
  • B. 4
  • C. 5
  • D. 8
展开解析

B前 4 次缺页装入 1、2、3、4,页框未满,不需要置换。之后:1 命中;5 缺页,换出 2;2 缺页,换出 3;6 缺页,换出 4;1 命中;3 缺页,换出 5。置换共 4 次(缺页共 8 次)。

变化点:置换次数 = 缺页次数 − 开始时用来填空页框的次数。D 是缺页次数。题目问“缺页”还是“置换”,答案差了 4。

变式操作系统由 2016-29 改
某进程访问页面的序列为 …, 2, 5, 3, 4, 1, 4, 1, 3 ↑t, …(t 为当前时刻,3 是 t 时刻访问的页)。若工作集窗口大小为 5,则 t 时刻的工作集为( )。
  • A. {1, 3, 4}
  • B. {1, 3, 4, 5}
  • C. {1, 2, 3, 4, 5}
  • D. {3, 4}
展开解析

A窗口 5 是指最近的 5 次访问:4, 1, 4, 1, 3。其中不同的页是 {1, 3, 4}。

变化点:窗口大小数的是“访问次数”,不是“页数”。重复访问的页在集合中只算一个,所以工作集的大小可以比窗口小。C 是往回数了 5 个不同的页。

变式操作系统由 2020-28 改
请求分页系统中,访问一次内存需 100ns,缺页处理(含读盘和重新访问)需 10ms,不考虑 TLB。若缺页率为十万分之一,则有效访存时间约为( )。
  • A. 110ns
  • B. 200ns
  • C. 1100ns
  • D. 10ms
展开解析

BEAT = (1 − p) × 100ns + p × 10ms = 0.99999 × 100 + 10−5 × 107 ≈ 100 + 100 = 200ns。

变化点:2020-28 只问哪些因素影响访存时间。这里真的算一次:缺页率只有十万分之一,平均访存时间就变成了原来的 2 倍,所以缺页率必须非常低。

变式操作系统由 2011-29、2022-30 改
系统发生抖动时,下列措施中能有效缓解的是( )。
Ⅰ. 减少并发运行的进程数 Ⅱ. 在内存允许时为进程增加页框 Ⅲ. 增大磁盘交换区的容量
  • A. 仅Ⅰ
  • B. 仅Ⅰ、Ⅱ
  • C. 仅Ⅱ、Ⅲ
  • D. Ⅰ、Ⅱ、Ⅲ
展开解析

B抖动是因为分给进程的页框太少,装不下它的工作集。减少进程数、给进程更多页框都能让每个进程的工作集装进内存。交换区大小与缺页多少无关。

变化点:2011-29 的正确项只有“撤销部分进程”,这里又加了“增加页框”。两者本质相同:让每个进程分到的页框不少于它的工作集。

变式操作系统由 2021-28、2016-26 改
进程有 4 个页框,采用改进型 CLOCK 置换算法,指针当前指向页框 0。页框 0~3 中页面的(访问位 A,修改位 M)依次为 (1,0)、(1,1)、(0,1)、(1,1)。发生缺页时,被淘汰的页面所在的页框是( )。
  • A. 页框 0
  • B. 页框 1
  • C. 页框 3
  • D. 页框 2
展开解析

D第一轮找 (0,0),一个都没有,不改位。第二轮找 (0,1):页框 0 (1,0) 不符合,A 清 0 变成 (0,0);页框 1 (1,1) 变成 (0,1);页框 2 是 (0,1),符合 → 淘汰页框 2 中的页(已用程序验算)。

变化点:A 是陷阱:页框 0 在第二轮被清成了 (0,0),但第二轮只找 (0,1),不会回头选它。2021-28 要走到第三轮,这里第二轮就找到了。考点:改进型 CLOCK 的四轮规则。

变式操作系统由 2022-30、2026-29 改
下列关于页面缓冲算法(PBA)的叙述中,正确的是( )。
  • A. 被淘汰的页面立即写回磁盘并清除
  • B. 页面缓冲算法能降低进程的缺页率
  • C. 未被修改的淘汰页挂到空闲页链表上,被再次访问时可以不经磁盘 I/O 直接取回
  • D. 修改页链表中的页面每换出一页就单独写一次磁盘
展开解析

C淘汰页的内容暂时还留在内存的链表里,再次访问时只要它的页框还没被分配出去,就能直接取回。A 错:不立即清除,脏页也是攒一批再写。B 错:再次访问仍然算缺页,PBA 降低的是缺页的处理代价(2022-30)。D 错:修改页成批写回,减少 I/O 次数。

变化点:2022-30 从“影不影响缺页率”考,这里考 PBA 的两个链表各起什么作用。考点:页面缓冲算法。

变式操作系统考纲补充(尚无真题):写时复制
父进程有 50 个页面,用 fork 创建子进程,系统采用写时复制。之后子进程修改了其中 2 个页面,父进程修改了另外 3 个页面。因写时复制而新分配的页框数是( )。
  • A. 5
  • B. 2
  • C. 3
  • D. 50
展开解析

Afork 时不复制任何页。之后谁写哪一页(且这一页仍被共享),就为写的一方复制那一页:子进程 2 页 + 父进程 3 页 = 5 页(已用程序验算)。B、C 只算了一方。D 是不用写时复制、fork 时全部复制的情形。

考点:写时复制。

8.8融合一次访存的全过程(可以一步步点)

★★★ 高频选择题综合题

现在把 8.3 的 Cache 和 8.5~8.7 的分页、TLB、缺页拼起来。设:虚拟地址 32 位、页 4KB;物理地址 24 位;TLB 全相联;Cache 2 路组相联、64 组、块 64B(块内 6 位 + 组号 6 位 = 12 位,正好等于页内偏移,于是 Cache 的 Tag 恰好就是页框号)。CPU 要读虚拟地址 0040 1A3CH,页 00401H 在页框 2F3H。

CPU 虚页号 VPN00401H 页内偏移A3CH 虚拟地址 0040 1A3CH TLB(快表,全相联) 标记  V 页框号1F2C0H 1  0A7H00401H 1  2F3H所有项同时比较 ① 用 VPN 查 TLB 页表(在主存) 页表项地址 = PTBR + VPN×4P=1 → 页框号 2F3HP=0 → 缺页异常!(多访问一次主存) ② TLB 未命中才查页表 页框号 2F3H= Cache 的 Tag 组号40 块内3CH 物理地址 2F3A3CH(偏移 A3CH 原样照抄) ③ 得到页框号,拼接偏移 Cache(2 路 64 组) 用组号 40 选出一组,两行 Tag 同时与 2F3H 比较且有效位 = 1 → 命中 ④ 主存未命中:读整块(64B)装入 Cache ⑤ 操作系统缺页处理:找页框(必要时置换)改页表,进程阻塞→就绪 磁盘把缺的页读进来 数据送回 CPU
图 8-22 一次访存的完整路径。在下面选择情形,点“下一步”看每一步发生了什么(亮的部件是当前这一步用到的)。
2010-17计组融合
下列命中组合情况中,一次访存过程中不可能发生的是( )。
  • A. TLB 未命中,Cache 未命中,Page 未命中
  • B. TLB 未命中,Cache 命中,Page 命中
  • C. TLB 命中,Cache 未命中,Page 命中
  • D. TLB 命中,Cache 命中,Page 未命中
展开解析

考点8.8 TLB、页表、Cache 的 8 种组合

D三者是“层层包含”的:TLB 里的页表项一定对应已在内存的页;Cache 里的块一定是内存里某页的副本。

  • A 错可能发生:第一次访问一个还在外存的页——TLB 没它的表项、页不在内存(缺页)、Cache 里自然也没有它的块,三个都未命中。
  • B 错可能发生:TLB 里刚好没有这页的表项,查内存页表发现页在内存(Page 命中),得到物理地址后数据块正好在 Cache 里。
  • C 错可能发生:TLB 命中说明页在内存,Page 命中;但该页里要访问的那一块还没调进 Cache,Cache 未命中。
  • D 对不可能:页不在内存(Page 未命中)时,操作系统不会让它的页表项留在 TLB 里,它的块也不可能在 Cache 里,TLB、Cache 都不会命中。

Page 未命中 ⇒ TLB 和 Cache 都未命中;TLB 命中 ⇒ Page 一定命中。8 种组合里只有 3 种可能、5 种不可能,见 8.8 的表。

2011-44计组融合综合题 12 分
按字节编址,虚拟地址空间 16MB,主存(物理)地址空间 1MB,页 4KB;Cache 直接映射,共 8 行,块 32B。页表部分内容(虚页号:有效位,页框号):0:1,06;1:1,04;2:1,15;3:1,02;4:0;5:1,2B;6:0;7:1,32。Cache 部分内容(行号:有效位,标记):0:1,020;1:0;2:1,01D;3:1,105;4:1,064;……(十六进制)。(1)虚拟地址几位?哪几位是虚页号?物理地址几位?哪几位是页框号?(2)用物理地址访问 Cache,物理地址分哪几个字段、各几位、在什么位置?(3)虚拟地址 001C60H 所在页面在主存吗?对应物理地址?Cache 命中吗?(4)若配一个 4 路组相联的 TLB,共 8 个表项,当前内容为:组 0:[V0]、[V1,标记 001,页框 15]、[V0]、[V1,标记 012,页框 1F];组 1:[V1,013,2D]、[V0]、[V1,008,7E]、[V0]。虚拟地址 024BACH 所在页面在主存吗?
展开解析
  1. (1)16MB = 224 → 虚拟地址 24 位,偏移 12 位,虚页号 高 12 位(23~12)。1MB → 物理地址 20 位,页框号 高 8 位(19~12)。
  2. (2)块 32B → 块内 5 位(4~0);8 行 → 行号 3 位(7~5);Tag = 20 − 8 = 12 位(19~8)。
  3. (3)001C60H:虚页号 001H,页表 1 号项有效,页框号 04H → 物理地址 = 04C60H。二进制 0000 0100 1100 0110 0000:Tag = 0000 0100 1100 = 04CH,行号 = 011 = 3。Cache 第 3 行有效但 Tag = 105H ≠ 04CH → 不命中。
  4. (4)TLB 8 项、4 路 → 2 组 → 虚页号最低 1 位是组号,其余 11 位是标记。024BACH 的虚页号 024H = 0000 0010 0100:组号 = 0,标记 = 0000 0010 010 = 012H。组 0 里有有效且标记为 012H 的项(页框 1FH)→ TLB 命中 → 该页在主存。
2018-44计组融合综合题
页式虚拟存储,按字节编址。题图:虚拟地址 = 虚页号 20 位 | 页内地址 12 位;页目录、页表各用 10 位索引(PDBR 指向页目录);TLB 每项为 Tag | 有效位 | 实页号,内容:(00324H,1,F800H)、(0008CH,1,0040H)、(8C040H,1,0160H)、(04320H,0,0E80H),查 TLB 时每项都有一个比较器;物理地址 = 实页号 | 页内地址,又被数据 Cache 看作 Tag 20 位 | 组号 3 位 | 块内 5 位;Cache 每组 2 行,组 2 的两行为 (Tag 00400H,有效位 0) 和 (Tag 01600H,有效位 1)。(1)主存物理地址多少位?(2)TLB 采用什么映射方式?用 SRAM 还是 DRAM?(3)Cache 采用什么映射方式?若采用 LRU 和回写,每行除数据、Tag、有效位外还应有哪些附加位?Cache 总容量?有效位的作用?(4)虚拟地址 0008C040H 对应的物理地址?是否 Cache 命中?虚拟地址 0007C260H 所在主存块映射到的 Cache 组号?
展开解析
  1. (1)20 + 3 + 5 = 28 位(也可由实页号 16 位 + 页内 12 位得到)。
  2. (2)每项都有比较器、同时比较整个虚页号 → 全相联;TLB 要快,用 SRAM。
  3. (3)每组 2 行 → 2 路组相联;附加位:LRU 位(1 位)和修改位(脏位,1 位)。8 组 × 2 行 × (20 + 1 + 1 + 1 + 32×8) = 16 × 279 = 4464 位 = 558B。有效位表示该行内容是否有效。
  4. (4)0008C040H:虚页号 0008CH,TLB 有有效项 → 实页号 0040H → 物理地址 = 004 0040H(28 位 = 7 个十六进制位)。Cache:Tag = 高 20 位 = 00400H,组号 = 040H 的第 7~5 位 = 010 = 组 2,块内 0。组 2 有 Tag 相同的一行但有效位为 0,另一行 Tag 不同 → 不命中。
  5. 0007C260H:Cache 组号在页内 12 位里,直接用 260H = 0010 0110 0000,第 7~5 位 = 011 → 组 3。

TLB、页表、Cache 三者命中的 8 种组合:哪些可能、要访存几次

问题:一次访存要过三道关——TLB 命中没有?页在不在主存?Cache 命中没有?每道关“是 / 否”两种结果,一共 2 × 2 × 2 = 8 种组合。2010-17 只问了其中一种“不可能”,换个问法(“可能的是”“访问主存几次”“哪些组合需要操作系统介入”)照样能出题。把 8 种一次列全,就不怕换问法。

判断的依据只有一条“包含关系”:TLB 里的有效页表项、Cache 里的有效块,都是已经在主存里的页的副本(操作系统换出一页时,会把它在 TLB 里的表项作废,把它在 Cache 里的块写回并作废,正是为了维持这一点)。所以:

  • 页不在主存(缺页)⇒ TLB 一定不命中,Cache 也一定不命中;
  • 反过来,TLB 命中 ⇒ 页一定在主存;Cache 命中(物理地址 Cache)⇒ 该块所在的页一定在主存;
  • 而 TLB 命中与 Cache 命中互不决定:TLB 只缓存“页号 → 页框号”,Cache 缓存的是数据块,一个命中另一个可能不命中。
进程的全部虚页 主存之外 = 缺页TLB、Cache 都不可能命中 在主存里的页(页表项 P = 1) TLB 里有表项的页 Cache 里有块的页 两个都中
图 8-23 三者的包含关系。两个椭圆都在“主存”这个框里,但彼此可以只重叠一部分——这就是“TLB 命中不一定 Cache 命中、反之亦然”;任何落在主存框外的访问(缺页)都不可能在 TLB 或 Cache 中命中。

8 种组合一张表(单级页表、物理地址 Cache、先查 TLB 再查 Cache;时间按 TLB 2 ns、Cache 5 ns、主存 50 ns 串行计算,由 verify/08/p13/a_combo.py 枚举):

TLB页在主存?Cache可能吗访问主存次数时间说明
命中在命中✓07 ns最快的情况
命中在缺失✓157 ns从主存调一块进 Cache
缺失在命中✓157 ns访存查页表一次,数据在 Cache(2010-17 B)
缺失在缺失✓2107 ns查页表一次 + 调块一次
缺失缺页缺失✓—ms 级缺页异常 → 操作系统从磁盘调页 → 重新执行这条指令(2010-17 A)
命中缺页命中✗——TLB 不会有不在主存的页(2010-17 D)
命中缺页缺失✗——同上
缺失缺页命中✗——Cache 不会有不在主存的块

跟我做一遍:TLB 命中率 95%、Cache 命中率 90%(两者独立),不考虑缺页,按上表的时间求平均访存时间。

  1. 每次都要查 TLB、查 Cache:2 + 5 = 7 ns。
  2. TLB 缺失(5%)时多访问一次主存查页表:0.05 × 50 = 2.5 ns。
  3. Cache 缺失(10%)时多访问一次主存调块:0.10 × 50 = 5 ns。
  4. 平均 = 7 + 2.5 + 5 = 14.5 ns。用上表四种“可能”的组合按概率加权(0.95 × 0.9 × 7 + 0.95 × 0.1 × 57 + 0.05 × 0.9 × 57 + 0.05 × 0.1 × 107)结果相同,程序已核对。
易错
  • “TLB 未命中就一定缺页”——错。TLB 只放一小部分页表项,没放进去的页照样可能在主存(上表第 3、4 行)。
  • “TLB 命中就不用访问主存”——不一定。TLB 命中只省掉查页表的那次访存,数据若不在 Cache 里还要访问主存(第 2 行)。
  • 多级页表时“查页表”要访问主存的次数等于级数;题目说页表项可以被 Cache 缓存时另算——看清题目给的条件。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2010-17 改
在有 TLB 和 Cache(用物理地址访问)的页式虚拟存储系统中,一次访存可能出现的是( )。
  • A. TLB 命中,但页不在内存中
  • B. TLB 未命中,页在内存中,Cache 命中
  • C. 页不在内存中,但 Cache 命中
  • D. TLB 命中,Cache 命中,但页不在内存中
展开解析

BTLB 未命中只是没有缓存这个页表项,查页表后照样能得到页框号,数据也可能正在 Cache 中。A、C、D 都不可能:TLB 和 Cache 中的内容都是主存中已有页的副本,页不在内存,它们里面也不会有。

变化点:2010-17 问“不可能”的组合(TLB 命中而页缺失)。这里问“可能”的,其余三项都含“页不在内存却命中”这类矛盾。

8.9虚拟存储器的性能:影响因素与改进方式

★★ 常考选择题

考纲在“虚拟内存管理”下单列了一条:虚拟存储器性能的影响因素及改进方式。前面几节分别讲了页面大小、置换算法、页框分配、工作集,这一节把它们放到同一个问题下面:同一个程序,为什么换一种写法、多给几个页框、换一个置换算法,运行时间会差几十倍甚至几百倍?该从哪几个方向改进?

打个比方主存是书桌,磁盘是图书馆。书桌上找一页纸要 1 秒,跑一趟图书馆借书要一天(缺页的代价大约是访存的十万倍)。看书快不快,几乎只取决于你多久跑一次图书馆:书桌太小(页框太少)、每次只借一页纸(页面太小)、还书的策略太蠢(置换算法差)、看书时东翻一页西翻一页(程序没有局部性),都会让你天天往图书馆跑。哪里不像:借书时你本人得去,而缺页时 CPU 不用等——操作系统会把 CPU 交给别的进程,只是这个进程自己被拖慢。

一切从一个公式开始:缺页率决定有效访存时间

设主存访问时间为 tm,缺页时的处理时间(缺页中断 + 必要时写回旧页 + 从磁盘读入新页 + 重新执行指令)为 tf,缺页率为 p(不考虑 TLB 和 Cache):

有效访存时间 EAT = (1 − p) × tm + p × tf

跟我做一遍:tm = 100 ns,tf = 8 ms = 8 000 000 ns。

缺页率 p010−610−510−410−3
EAT100 ns108 ns180 ns900 ns8099.9 ns
是不缺页时的1 倍1.08 倍1.8 倍9 倍81 倍
  1. p = 10−3:EAT = 0.999 × 100 + 0.001 × 8 000 000 = 99.9 + 8000 = 8099.9 ns——一千次访存里缺一次页,速度就只剩 1/81。
  2. 反过来问:要让 EAT 不超过 110 ns(只慢 10%),p 最大多少?100 + p × (8 000 000 − 100) ≤ 110 → p ≤ 10 ÷ 7 999 900 ≈ 1.25 × 10−6,也就是大约每 80 万次访存才允许缺一次页。
  3. 若被换出的页是“脏”的,要先写回磁盘再读入,tf 约翻倍:同样 p = 10−5,EAT 从 180 ns 变成 260 ns。这就是置换时优先淘汰未修改页(改进型 CLOCK)的理由。

数字由 verify/08/p13/b_perf.py 计算。结论:虚存性能的关键是把缺页率压到极低,其次是降低每次缺页的代价。下面的每个因素,都能归到这两条之一。2020-28 问“影响有效访存时间的因素”,缺页率、磁盘读写时间、内存访问时间、执行缺页处理程序的 CPU 时间四项都选——它们分别是公式里的 p、tf 的两部分和 tm。

因素一:页面大小

页面小页面大
页内碎片小(平均每个进程浪费半页)大
页表页数多 → 页表大、可能要更多级页表小
缺页次数每次只调入一小块,顺序访问大数组时缺页多一次调入多,缺页少;但可能调入用不到的内容
磁盘 I/O每次传得少,寻道和旋转的开销占比高一次传得多,寻道和旋转的开销被摊薄
TLB 覆盖范围64 项 × 4KB = 256KB64 项 × 2MB = 128MB(“大页”)

所以页面大小是个折中:页表开销希望页大,碎片希望页小。拓展 设进程平均大小为 s、页表项 e 字节、页面 p 字节,则每个进程的额外开销 ≈ 页表 s·e/p + 平均碎片 p/2,求导得最优 p = √(2se)。取 s = 1MB、e = 8B:√(2 × 220 × 8) = √(224) = 4096B = 4KB,此时页表 2KB + 碎片 2KB = 4KB,是所有取值里最小的(页 2KB 或 8KB 时都是 5KB)。这正好解释了为什么 4KB 页这么常见。页面大小由硬件(地址结构)决定,必须是 2 的幂,操作系统只能在硬件支持的几种里选。

因素二:分给进程的页框数(驻留集)与抖动

给一个进程的页框越多,缺页越少——但不是均匀地少。下图的访问序列模拟一个主循环:页 1、2、3、4 反复用 5 遍,最后转到页 5、6(共 24 次访问)。

0 6 12 18 24 1 2 3 4 5 6 7 分给进程的页框数 缺页次数 循环用到 4 页 1 个页框:OPT 缺页 24 次 2 个页框:OPT 缺页 16 次 3 个页框:OPT 缺页 11 次 4 个页框:OPT 缺页 6 次 5 个页框:OPT 缺页 6 次 6 个页框:OPT 缺页 6 次 7 个页框:OPT 缺页 6 次 1 个页框:LRU = FIFO 缺页 24 次 2 个页框:LRU = FIFO 缺页 22 次 3 个页框:LRU = FIFO 缺页 22 次 4 个页框:LRU = FIFO 缺页 6 次 5 个页框:LRU = FIFO 缺页 6 次 6 个页框:LRU = FIFO 缺页 6 次 7 个页框:LRU = FIFO 缺页 6 次 22 6 11 OPT LRU、FIFO(两条重合) OPTLRU = FIFO
图 8-24 页框数一旦少于“正在反复用的页数”,缺页次数陡增。3 个页框时,LRU 和 FIFO 每次都恰好把下一步要用的页淘汰掉,24 次访问缺页 22 次;多给 1 个页框,就只剩 6 次(6 个页各第一次进来)。OPT 也只是缓和一些(11 次)。把鼠标放在点上可以看到具体数值;数据由 verify/08/p13/b_perf.py 模拟得到。

图中“循环用到的 4 页”就是这一阶段的工作集(8.7)。页框数 ≥ 工作集,缺页只剩每页第一次的“冷启动”;页框数 < 工作集,进程大部分时间都在等页面——这就是抖动(颠簸)。放到整个系统看:

并发进程数(多道程序度)CPU 利用率 最佳点 进程多一些,CPU 空闲少 抖动区进程都在等页面
图 8-25 抖动(示意,无具体数值)。内存总量不变,进程越多每个进程分到的页框越少;一旦低于各自的工作集,所有进程都忙着缺页、换页,磁盘排长队,CPU 反而闲下来。此时如果调度程序看到“CPU 利用率低”再调入更多进程,会雪上加霜。
  • 抖动的原因:同时运行的进程太多,分给每个进程的页框太少(少于工作集);置换算法选择不当也会加剧。
  • 对策:① 降低多道程序度:挂起或撤销部分进程,把它们的页框让给其他进程(中级调度,6.2)——2011-29 选“撤销部分进程”;② 按工作集分配页框,工作集之和超过内存就不再调入新进程;③ 拓展 缺页率(PFF)策略:给每个进程的缺页率定上下限,高于上限就多给页框,低于下限就收回一些。无效的做法:增加磁盘对换区(缺页照样多,2011-29 Ⅱ)、提高某个进程的优先级(不改变页框数,2011-29 Ⅲ)。
  • 页框数的下限由指令系统决定:一条指令执行时可能同时要用到的页都必须能装下(指令本身可能跨页,操作数也可能跨页、间接寻址还要多一页),否则这条指令永远执行不完——2025-27“确定进程运行所需的最少页框数要考虑指令系统支持的寻址方式”。

因素三:置换算法与页面缓冲

置换算法决定“淘汰谁”,直接影响缺页率:一般 OPT 最少、LRU 接近 OPT、CLOCK 近似 LRU、FIFO 最差,且 FIFO 可能出现 Belady 异常(8.7)。上图的序列说明:页框不够时,再好的近似算法也救不了(LRU 和 FIFO 一样差),这时该做的是增加页框或减少进程。

页面缓冲算法(PBA,8.7)和改进型 CLOCK 优先淘汰未修改页,降低的是每次缺页的代价(被淘汰的页暂留在空闲 / 修改链表里,短时间内再访问不用读盘;修改页成批写回)——缺页次数不变。2022-30 正是考这一点:页缓冲队列的长度不影响缺页率;2026-29 则问“能降低平均访存时间”,页面缓冲就要选上。

因素四:程序的局部性——程序员能做的事

同一份数据,访问的顺序不同,缺页次数可以差上千倍。设 int a[512][1024](int 4B),页面 4KB,C 语言按行存放,所以一行正好一页,整个数组 512 页;给进程分 100 个页框,用 LRU 置换:

按行:for i → for j按列:for j → for i 行 0(页 0)行 1(页 1)行 2(页 2)… 一页里的 1024 个元素连续用完才换页缺页 512 次 … 每走一步换一行 = 换一页,100 个页框放不下 512 页缺页 512 × 1024 = 524288 次
图 8-26 访问顺序决定局部性。按列访问时,访问 a[0][j]、a[1][j]……a[511][j] 要轮流用到全部 512 页,而 100 个页框只能留住最近的 100 页,等回到第 0 行时它早被换出,于是每一次访问都缺页。数字由 verify/08/p13/b_perf.py 用 LRU 模拟得到。
  1. 按行(外层 i、内层 j):第 i 行的 1024 个元素都在页 i 里,第一次访问缺页,后面 1023 次命中 → 512 页各缺 1 次,512 次。
  2. 按列(外层 j、内层 i):对每个 j,依次访问 512 个不同的页;LRU 只保留最近 100 页,而下一次再访问页 i 要隔 511 次其他页的访问 → 每次都缺页,512 × 1024 = 524288 次,是按行的 1024 倍。
  3. 条件一变:若给 600 个页框(≥ 512 页),按列访问也只缺 512 次——页框够装下所有页时,访问顺序就无所谓了。所以这类题要先看“页框够不够”。

同样的道理对 Cache、TLB 也成立:按列访问的 Cache 命中率也低(11.6 按行 vs 按列),11.15 卷一第 44 题把 Cache、TLB、缺页放在一道题里算。程序员能做的:数组按存放顺序访问、把一起用的数据放在一起、少用指针在内存里乱跳的结构。

改进方式总表:硬件、操作系统、程序员各能做什么

谁来做措施降低的是
硬件TLB(加快地址转换);更大的页 / 大页(扩大 TLB 覆盖范围);更快的外存(SSD);由硬件自动置访问位、修改位地址转换时间、缺页代价
操作系统选好置换算法(LRU 的近似 CLOCK、改进型 CLOCK);按工作集或缺页率分配页框;控制多道程序度防抖动;预调页(利用空间局部性,一次调入多页)缺页率
页面缓冲;修改页成批写回;对换区连续分配(读写比文件区快,8.7);写时复制(少做无用的复制,8.7)每次缺页的代价
程序员 / 编译器提高局部性:按存放顺序访问数组、循环交换、把常一起用的数据放在相邻位置缺页率(也提高 Cache、TLB 命中率)

不能降低缺页率的常见干扰项:多级页表(只省页表占的内存,反而多一次访存,2026-29 Ⅱ 不选);增大对换区;提高进程优先级;页面缓冲队列加长(只降代价)。

真题怎么考

真题问法抓手
2020-28影响有效访存时间的因素对着 EAT 公式找:p、tf(磁盘时间 + 缺页处理的 CPU 时间)、tm——四项全选
2022-30不影响缺页率的是页缓冲只降代价,不改缺页次数;置换算法、工作集大小、进程数量都影响
2026-29能降低平均访存时间的是TLB、工作集、页缓冲都能;多级页表不能
2011-29抖动时的有效措施只有“撤销(挂起)部分进程”,把页框还给别人
2025-27最少页框数由什么决定指令系统的寻址方式(一条指令最多同时涉及几页)
2014-30、2016-29Belady 异常、工作集见 8.7 置换算法、工作集

秒杀思路:先判断选项改的是 p(缺页率)还是 tf(每次缺页的代价)还是 地址转换时间,再对照题目问的是“缺页率”还是“访存时间”。失效条件:题目若把 TLB、Cache 也放进来,平均访存时间还要加上地址转换和 Cache 的部分(8.8 组合表),不能只看缺页率。

自测
  1. 主存访问 100 ns,缺页处理 8 ms,缺页率 10−4,有效访存时间是多少?是不缺页时的几倍?
    答案
    0.9999 × 100 + 0.0001 × 8 000 000 = 99.99 + 800 ≈ 900 ns,约 9 倍。
  2. 系统抖动时,管理员把磁盘对换区从 2GB 加到 8GB,有用吗?
    答案
    没用。对换区大小只决定“能换出多少”,抖动是因为每个进程分到的页框少于工作集,缺页次数不变。应当减少并发进程数(2011-29)。
  3. 上面的 int a[512][1024] 改成 int a[1024][512](每页 4KB 装 2 行),100 个页框、LRU,按列访问缺页多少次?
    答案
    数组仍是 512 页。按列访问时,对固定的 j 依次访问 1024 行,相邻两行在同一页,所以一列要连续用到 512 个不同的页、每页用 2 次,而 100 个页框留不住 → 每页的第一次都缺页:每列 512 次,共 512 × 512 = 262144 次(程序模拟核对)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2022-30、2026-29 改
某进程采用 LRU 置换算法。下列措施中,可能降低该进程缺页率的是( )。
Ⅰ. 在总页框数允许的范围内增加分配给它的页框数 Ⅱ. 采用页面缓冲算法 Ⅲ. 把按列遍历二维数组的代码改为按行遍历 Ⅳ. 增加 TLB 的表项数
  • A. 仅Ⅰ、Ⅲ
  • B. 仅Ⅰ、Ⅱ、Ⅲ
  • C. 仅Ⅲ、Ⅳ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

A先分清每项改的是“缺页次数”还是“缺页代价 / 地址转换时间”。

  • Ⅰ 对页框数从少于工作集增加到不少于工作集,缺页会骤降(本节图中 3 → 4 个页框,22 → 6 次);LRU 是栈算法,页框增加不会使缺页增加。
  • Ⅱ 错页面缓冲把被淘汰的页暂留在链表里,再访问时“缺页”照样发生,只是不用读盘——降的是代价,不是缺页率(2022-30)。
  • Ⅲ 对按行遍历符合存放顺序,同一页的元素连续用完才换页(本节例子 524288 → 512 次)。
  • Ⅳ 错TLB 只缓存页表项,命中省的是查页表的那次访存;页在不在内存与 TLB 无关。

干扰项来源:B 是把页面缓冲误当成减少缺页(2022-30 的陷阱);C 是把“TLB 命中”误当成“页面命中”,同时漏了Ⅰ;D 把四项都当成“性能优化”一股脑选上。变化点:2022-30 问“不影响缺页率”,2026-29 问“降低平均访存时间”(那里页面缓冲、TLB 都要选),这里问“降低缺页率”,同一组措施答案不同。考点:8.9 改进方式总表。

变式操作系统由 2020-28 改(考纲补充:缺页率的上限)
某请求分页系统的主存访问时间为 100 ns,处理一次缺页平均需要 10 ms(不考虑 TLB 和 Cache)。若要求有效访存时间不超过 120 ns,则缺页率最大约为( )。
  • A. 2 × 10−5
  • B. 1.2 × 10−5
  • C. 2 × 10−6
  • D. 2 × 10−4
展开解析

CEAT = (1 − p) × 100 + p × 107 ≤ 120。

  1. 单位统一:10 ms = 10 × 106 ns = 107 ns。
  2. 100 + p × (107 − 100) ≤ 120 → p ≤ 20 ÷ 9 999 900 ≈ 2 × 10−6。
  • A 错2 × 10−5 = 20 ÷ 106:把 10 ms 当成了 1 ms(106 ns)。代回检验:p = 2 × 10−5 时 EAT ≈ 100 + 200 = 300 ns,超了。
  • B 错1.2 × 10−5 = 120 ÷ 107:直接拿“允许的总时间 120 ns”去除,忘了不缺页时本来就要 100 ns,能留给缺页的只有 20 ns。
  • C 对约 2 × 10−6,即平均 50 万次访存才允许缺一次页。
  • D 错2 × 10−4 = 20 ÷ 105:把 10 ms 换算成了 105 ns(ms 与 ns 之间是 106,不是 104)。

变化点:2020-28 只问“哪些因素影响 EAT”,这里把公式真正算一遍。数字由 verify/08/p13/b_perf.py 断言核对。考点:8.9 缺页率与 EAT。

8.10本篇对照表与公式

★ 考过选择题
Cache—主存主存—辅存(虚拟存储)TLB—页表
目的提高速度扩大容量加快地址转换
交换单位块(几十 B)页(KB 级)页表项
谁管理全硬件硬件(MMU)+ 操作系统硬件(有的系统由 OS 填)
映射直接/全相联/组相联页表(相当于全相联,任意页框;不用直接映射,2024-16)全相联/组相联
缺失代价访主存,几十 ns访磁盘,ms 级,要 OS 介入访主存查页表
写策略直写或回写只能回写—
替换硬件实现软件(OS)实现硬件

2020-15:TLB 和 Cache 的命中率都和局部性有关、缺失后都要访问主存、缺失处理都可以由硬件完成;“都由 DRAM 组成”错(都用 SRAM/相联存储器)。

地址位数 = log2(空间大小 / 编址单位);页内偏移 = log2(页大小);块内 = log2(块大小) Cache 组数 = 行数 / 路数;Tag = 地址位数 − 组号位数 − 块内位数(全相联没有组号) Cache 每行位数 = 数据 + Tag + 有效位 [+ 脏位(回写)] [+ 替换位] 页表项地址 = 页表基址 + 页号 × 页表项大小;物理地址 = 页框号 ∥ 页内偏移 多级页表每级索引位数 = log2(页大小 / 页表项大小) EAT(快表)= tTLB + tm + (1 − h)·tm  平均访存(Cache)= tc + (1 − H)·tm

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2020-15、2024-16 改
下列关于 Cache—主存层次和主存—辅存层次的叙述中,正确的是( )。
  • A. 两者的地址映射都由操作系统完成
  • B. Cache 缺失和缺页的处理都完全由硬件完成
  • C. 主存—辅存层次的写策略一般采用回写(写回)法
  • D. 两者交换信息的单位都是字
展开解析

C访问磁盘太慢,每次写都写回磁盘无法接受,所以虚拟存储器总是采用回写法(2016-45)。A、B 错:Cache 的映射和缺失处理由硬件完成,缺页由操作系统处理。D 错:分别以块和页为单位。

变化点:2024-16 找错误叙述。这里把“谁来管、写策略、交换单位”三个方面放在一起比较两个层次。

第 8 篇自测
  1. 32 位地址、按字节编址,Cache 64KB、4 路组相联、块 32B、回写、LRU:Tag、组号、块内各几位?每行共多少位?
    答案
    行数 2048,组数 512 → 组号 9,块内 5,Tag 18;每行 256 + 18 + 1 + 1 + 2 = 278 位(LRU 按每行 log24 = 2 位)。
  2. 虚拟地址 32 位、页 16KB、页表项 4B,页表最多多大?如果用多级页表且每级页表恰好一页,需要几级?
    答案
    偏移 14 位,页号 18 位 → 218 × 4B = 1MB。一页放 16KB/4B = 4096 项 → 每级 12 位;18 位页号需要 ⌈18/12⌉ = 2 级。
  3. TLB 访问 5ns,主存 100ns,TLB 命中率 95%,不考虑缺页和 Cache,EAT 是多少?
    答案
    5 + 100 + 0.05 × 100 = 110 ns。
  4. 3 个页框,序列 1 2 3 4 1 2 5 1 2 3 4 5,FIFO 缺页几次?4 个页框呢?(用上面工具验证)
    答案
    9 次;10 次——Belady 异常。
  5. 为什么“页内偏移原样传递”这个性质能让 Cache 在地址翻译完成前就开始查组?
    答案
    若 Cache 的组号 + 块内地址的位数 ≤ 页内偏移位数,组号就完全落在页内偏移里,虚拟地址和物理地址这几位相同,可以一边查 TLB 一边用虚拟地址选组,等页框号出来再比 Tag(2019-46、2023-43、2025-43 都利用了这一点)。
  6. DRAM 刷新周期 2ms、共 256 行,采用异步刷新,每隔多久刷新一行?
    答案
    2ms / 256 ≈ 7.8μs 刷新一行。
  7. 用 4K×4 位的 RAM 芯片组成 16K×8 位的存储器,需要几片?片内地址几位?片选要用几位高位地址?
    答案
    16K×8 / (4K×4) = 8 片(每 2 片一组位扩展,共 4 组);片内地址 12 位;4 组要 2 位高位地址经 2-4 译码产生片选。
  8. 动态分区中回收一个分区,它与前后两个空闲分区都相邻,空闲分区表的表项数怎么变?
    答案
    三块合成一块,表项数减 1。
  9. 分页和分段,哪个的地址空间是二维的?哪个有外部碎片?
    答案
    分段(以及段页式)的地址空间是二维的;分段有外部碎片,分页只有少量内部碎片。
  10. 一条指令执行过程中最多可能发生几次缺页?举例说明。
    答案
    不止一次。例如一条指令本身跨两页,它要访问的操作数又跨两页,最坏情况下这 4 页都不在内存,要发生多次缺页,每次处理完都重新执行这条指令。

8.✎本篇真题闯关(89 题)

本篇各小节挂的真题全部在这里,按小节排好。可以展开后一道道做,也可以用弹窗模式连续做。做题记录(对/错)只保存在你这台电脑的浏览器里,小节标题下的题号会随之变绿或变红。

第 9 篇 · 融合 · 计组第 6、7 章 × 操作系统第 5 章(5.1~5.2)

融合三:总线与 I/O —— 从接口电路到驱动程序

CPU 怎么和键盘、磁盘、网卡打交道?计组讲硬件:部件之间靠总线连接,设备通过 I/O 接口挂到总线上,数据传送有程序查询、中断、DMA 三种方式,常考“CPU 花在 I/O 上的时间占百分之几”。操作系统讲软件:一次 scanf 要经过库函数、系统调用、设备无关软件、驱动程序、中断处理程序好几层,进程在这期间阻塞又被唤醒,数据先进内核缓冲区再复制给用户。两边合起来,就是一次 I/O 的完整故事。

对应:王道计组第 6、7 章;王道操作系统 5.1、5.2难度:★★★本机题库计数:计组“中断 I/O”10 题、总线指标 8 题、DMA 5 题;操作系统 I/O 软件层次 4 题、缓冲区 5 题

9.1总线:部件之间的公共“马路”

★★★ 高频选择题

总线是多个部件分时共享的一组传输线:同一时刻只能有一个部件往上发送,但可以有多个部件同时接收。按传输内容,系统总线分三组:

地址总线
传地址,单向(CPU → 主存/接口)。位数决定寻址空间。
数据总线
传数据,双向。指令、操作数、I/O 接口的命令字/状态字、中断类型号都走它(2011-20、2012-21)。
控制总线
传控制和状态信号:读/写、中断请求、总线请求、握手(应答)信号等(2011-20:握手信号不在数据线上)。
CPU 主存 系统总线 / 存储器总线(快,支持突发传送) 桥接器 I/O 总线(如 PCIe,慢一些) 磁盘接口 网卡 显卡 USB 控制器
图 9-1 多总线结构。速度差别大的部件挂在不同总线上,靠近 CPU 的总线更快,总线之间用桥接器连接(2017-20)。常见标准:PCI、PCIe(串行)、USB(串行、即插即用、热插拔,用于连接设备和设备控制器)、SATA。2010-20 “均为总线标准”的一组是 ISA、EISA、PCI、PCI-Express(CRT 是显示器,CPI、MIPS 是性能指标,RAM 是存储器)。
按什么分分类
所在位置 / 功能片内总线(CPU 芯片内部,连寄存器、ALU);系统总线(连 CPU、主存、I/O 接口,又分数据、地址、控制三组);I/O 总线(连中低速 I/O 设备);通信总线(计算机与计算机、计算机与其他系统之间,也叫外部总线)
数据传输格式并行(多位同时传,线多、距离短);串行(逐位传,线少、适合远距离,现在高速总线多是串行:PCIe、USB、SATA)
时序控制同步、异步(9.2)
总线结构怎么连特点
单总线CPU、主存、I/O 设备都挂在一条系统总线上简单、便于扩充;但所有传送都抢这一条,带宽低,慢速设备拖累整体
双总线一条主存总线(CPU、主存、通道)+ 一条 I/O 总线(通道和各 I/O 设备)把慢速的 I/O 从主存总线上分出去,由通道负责 I/O;需要专门的通道硬件
三总线主存总线(CPU—主存)、I/O 总线(CPU—I/O 设备)、DMA 总线(高速 I/O 设备—主存)主存总线和 DMA 总线不能同时访问主存;I/O 总线只在 CPU 执行 I/O 指令时才用

常见总线标准:PCIe 与 USB 拓展

选择题考总线标准,只考“它是什么、连什么、并行还是串行”这几点,记住下表即可。

标准并行 / 串行连接什么要点
ISA、EISA、PCI并行主机内部插卡(I/O 接口)老式系统总线 / I/O 总线;PCI 曾是主流
AGP并行显卡显卡专用,已被 PCIe 取代
PCIe(PCI-Express)串行显卡、网卡、SSD 等插卡点对点、全双工;×1、×4、×16 指并用几条串行通道(lane),每条仍是串行(2017-20)
USB串行设备(键盘、U 盘、打印机)与主机的 USB 控制器即插即用、热插拔、可经集线器级联多台设备、可以为设备供电;一次传 1 位(2012-20、2013-19)
SATA串行硬盘、光驱取代并行的 IDE(PATA)

为什么高速总线反而都改成了串行:并行总线频率一高,几十根线上的信号到达时间对不齐(时钟偏移),互相还有串扰,频率上不去;串行只有一对差分线,可以跑到几 GHz 以上,还能用多条通道并起来提高带宽(2016-21)。

性能指标含义(选择题最容易混的几个)
总线时钟周期总线时钟信号的周期 = 1 / 总线时钟频率
总线传输周期(总线周期)完成一次总线操作所需的时间,通常包含若干个总线时钟周期(申请、寻址、传输、结束)
总线工作频率每秒传送数据的次数 = 1 / 总线传输周期;时钟周期里传多次时(DDR)可以比时钟频率高
总线宽度数据总线的根数(一次能并行传几位),通常 8、16、32、64 位
总线带宽单位时间内最多能传多少数据 = 总线宽度 × 总线工作频率(下面的公式)
总线复用同一组线分时传不同信号(如地址/数据复用),省引脚,不提高带宽

总线带宽(最大数据传输率)

总线带宽 = 总线宽度(字节)× 每秒传送次数 每秒传送次数 = 时钟频率 × 每个时钟周期传几次(DDR 传 2 次)÷ 传一次要几个时钟周期
2009-20计组
假设某系统总线在一个总线周期中并行传输 4 字节信息,一个总线周期占用 2 个时钟周期,总线时钟频率为 10MHz,则总线带宽是( )。
  • A. 10MB/s
  • B. 20MB/s
  • C. 40MB/s
  • D. 80MB/s
展开解析

考点9.1 总线带宽

B总线带宽 = 每秒总线周期数 × 每个总线周期传送的字节数。

  1. 每秒总线周期数 = 时钟频率 ÷ 每个总线周期的时钟数 = 10M ÷ 2 = 5M 个。
  2. 带宽 = 5M × 4B = 20MB/s。
  • A 错10MB/s ÷ 4B = 2.5M 次/秒,相当于每次传送要 4 个时钟周期,和题给“一个总线周期占 2 个时钟周期”对不上。
  • B 对10M ÷ 2 × 4B = 20MB/s。
  • C 错40MB/s = 10M × 4B,相当于每个时钟周期就传一次,漏看了“一个总线周期占 2 个时钟周期”。
  • D 错80MB/s = 10M × 2 × 4B,相当于每个时钟周期上升沿、下降沿各传一次;题目没说双沿传送,反而是 2 个时钟周期才传一次。

带宽 = 频率 ÷ 每次传送的时钟数 × 每次传的字节数。同类:2014-19(66MHz、上下沿各传一次、32 位 → 528MB/s);2019-19(3 通道 × 8B × 1333M ≈ 32GB/s);2020-19(QPI:2.4G × 2 次 × 2B × 2 个方向 = 19.2GB/s);2024-20(420MHz × 2 × 8B = 6.72GB/s);2025-20(1333MT/s 已是“每秒传送次数”,× 8B ≈ 10.66GB/s)。

能提高同步总线传输率的:增加总线宽度、提高工作频率、支持突发传输;地址/数据线复用只是省线,不提速(2018-21 选 B)。串行总线不一定比并行慢:频率高了以后并行线之间干扰大,串行反而能跑得更快(2016-21 “并行总线传输比串行快”错)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2009-20、2014-19 改
某同步总线宽 64 位,地址线和数据线分开,总线时钟频率为 200MHz,每个时钟周期传送两次数据(上升沿和下降沿各一次)。该总线的带宽是( )。
  • A. 1.6GB/s
  • B. 3.2GB/s
  • C. 6.4GB/s
  • D. 12.8GB/s
展开解析

B带宽 = 每秒传送次数 × 每次传送的字节数 = 200M × 2 × 8B = 3.2GB/s。

变化点:2014-19 的地址/数据线复用、32 位、66MHz。复用只影响一次事务要花几拍,不改变“每秒最多传多少数据”的带宽。

变式计组由 2024-20 改
某存储器总线时钟频率为 200MHz,宽 64 位,每个时钟周期传送一次数据。一次突发读事务:第 1 个时钟周期送地址和命令,第 2~3 个时钟周期等待,第 4~11 个时钟周期连续传送 8 个数据。该总线的带宽,以及一次突发读事务的平均数据传输率分别约为( )。
  • A. 1.6GB/s、1.16GB/s
  • B. 1.16GB/s、1.6GB/s
  • C. 1.6GB/s、1.6GB/s
  • D. 0.8GB/s、1.16GB/s
展开解析

A总线带宽只看峰值:200M × 8B = 1.6GB/s。一次突发事务共 11 个时钟周期(55ns)传 64B,平均 64B ÷ 55ns ≈ 1.16GB/s。

变化点:2024-20 给了一大段突发事务的细节,但问的是总线带宽,细节全是干扰。这里把两个量都问了,区分“带宽”(峰值)和“一次事务的实际传输率”。

变式计组由 2018-21、2016-21 改
下列措施中,不能提高同步总线带宽(单位时间内最多能传送的数据量)的是( )。
Ⅰ. 采用地址/数据线复用 Ⅱ. 采用分离事务通信方式 Ⅲ. 提高总线时钟频率 Ⅳ. 每个时钟周期传送两次数据
  • A. 仅Ⅰ
  • B. 仅Ⅰ、Ⅱ
  • C. 仅Ⅲ、Ⅳ
  • D. 仅Ⅱ
展开解析

B复用只是省了信号线,不提速(2018-21)。分离事务让总线在从设备准备数据期间可以给别人用,提高的是总线利用率(2016-21),峰值带宽不变。提高频率、一个周期传两次,都能提高带宽。

变化点:2018-21 问“能提高传输率的”。这里反过来问,并加入了“分离事务”:它提高利用率,但不提高带宽。

变式计组由 2011-20、2012-21 改
在系统总线的地址线上可能传输的信息是( )。
Ⅰ. 主存单元的地址 Ⅱ. I/O 端口的地址 Ⅲ. 中断类型号 Ⅳ. 握手(应答)信号
  • A. 仅Ⅰ
  • B. 仅Ⅰ、Ⅱ
  • C. 仅Ⅰ、Ⅱ、Ⅲ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

B地址线传主存地址和 I/O 端口地址。中断类型号由设备(中断控制器)通过数据线送给 CPU(2012-21)。握手信号走控制线(2011-20)。

变化点:两道真题都问数据线,这里改问地址线。三类线各传什么:地址线传地址,数据线传数据、指令、命令字、状态字、中断类型号,控制线传读写、握手、中断请求等控制信号。

9.2总线事务与定时

★★★ 高频选择题综合题

一次总线操作叫总线事务:请求 → 仲裁 → 寻址(送地址和命令)→ 传数据 → 释放。突发(猝发)传送:只送一次首地址,然后连续传送多个地址连续的数据(2014-20),大大节省了送地址的时间,读 Cache 块时特别有用。

2013-43:总线 200MHz(5ns/周期)、32 位;主存 8 体交叉,每体存取周期 40ns;突发传送一个 32B 的 Cache 块 总线 址 存储器准备数据 40ns(第 1 个体读出) 8 个数据,每个 5ns(后面几个体在交叉读出,和传送重叠) 5 + 40 + 8 × 5 = 85ns 若不支持突发(2023-20):每个数据都要“送地址 + 准备 +传数据”,32B 用 64 位总线分 4 次,每次 1+6+1 = 8ns → 32ns
图 9-2 读突发传送。2013-43 第 4 问:100 条指令、CPI 4、主频 800MHz(1.25ns)、每条指令访存 1.2 次、缺失率 5%,缺失时读一块用 85ns → 执行时间 = 100×4×1.25 + 100×1.2×5%×85 = 500 + 510 = 1010ns。2012-19:100MHz、32 位、地址数据复用、突发写 128 位 → 1 个地址周期 + 4 个数据周期 = 50ns。

总线仲裁:几个主设备同时要总线,给谁 拓展

问题:总线一次只能有一个主设备在用。CPU、DMA 控制器同时发请求,谁先用?

大白话:像一群人抢一个话筒。① 链式查询:话筒从主持人手里沿一排人往下递,第一个举手的人截住;② 计数器定时查询:主持人报号“0 号、1 号、2 号……”,报到谁、谁正好举手就给谁;③ 独立请求:每个人和主持人之间都有一根专线,主持人看着所有举手的人直接挑。

① 链式查询(3 根:BS、BR、BG) 总线控制器 BS 总线忙 BR 总线请求(各设备共用) 设备 0 设备 1 设备 2 设备 3 BG 总线允许:逐个往下传,有请求的设备截住它 → 离控制器越近,优先级越高 ② 计数器定时查询(2 + ⌈log₂n⌉ 根:BS、BR + 设备地址线) 总线控制器计数器0→1→2→… BS BR 设备地址线:送出计数值 设备 0 设备 1 设备 2 设备 3 计数值等于某个有请求的设备的地址 → 它获得总线;计数从 0 开始是固定优先级,从上次停下处继续就是轮流 ③ 独立请求(2n + 1 根:每个设备一对 BRᵢ、BGᵢ,再加一根 BS) 总线控制器(排队器) BR₃ BG₃ BR₂ BG₂ BR₁ BG₁ BR₀ BG₀ 设备 0 设备 1 设备 2 设备 3
图 9-3 三种集中式仲裁。每条线表示一根(或一组)控制线。③ 中每个设备和控制器之间的“一根”线代表一对:请求线 BRi 和允许线 BGi。
方式优先级控制线数(n 个设备)优点缺点
链式查询固定:离控制器越近越高3线最少,容易扩充设备对电路故障敏感(一个设备的 BG 电路坏了,后面的设备都用不了总线);远端设备可能长期得不到总线
计数器定时查询灵活:可固定,可轮流,也可由程序设置计数初值2 + ⌈log₂n⌉优先级可变,对故障不敏感控制较复杂,要等计数
独立请求由排队器决定,可编程设置2n + 1响应最快,优先级最灵活控制线最多,控制器复杂

跟我做一遍(已用程序验算):总线上挂 8 个设备。链式查询 3 根;计数器定时查询要 ⌈log₂8⌉ = 3 根设备地址线,加 BS、BR 共 5 根;独立请求 8 对 BRi、BGi 加 BS 共 17 根。

分布式仲裁:没有中央控制器,每个主设备有自己的仲裁号和仲裁器。要用总线时,各自把仲裁号送上共享的仲裁总线,一边送一边和总线上的号比较,号小的自动退出,最后留下仲裁号最大的设备获得总线。

真题怎么考:近年 408 真题没有考过仲裁,重点放在总线事务与定时上。会数控制线、知道三种方式各自的优缺点即可。

总线定时:同步、异步(三种互锁)、半同步、分离式

问题:一次总线事务里,主设备什么时候该送地址、从设备什么时候该把数据放上去,双方怎么对上节拍?

大白话:同步像全班跟着一个节拍器做操,简单快速,但得照顾最慢的同学;异步像两个人传东西,“给你”(请求)、“收到”(应答),谁快谁慢都能对上,就是多了来回确认的时间。

定时方式怎么协调特点
同步通信(同步定时)统一的时钟信号(不能由各设备自己提供,2015-19)快、简单;设备速度差别大时只能迁就最慢的
异步通信(异步定时)没有公共时钟,靠握手信号(请求/应答);不互锁、半互锁、全互锁全互锁最慢但最可靠,不互锁可靠性最差;一次握手完成的是一次数据交换,不一定只有 1 位(2021-19)
半同步同步时钟 + 等待信号握手信号的采样由时钟控制
分离式请求和应答分成两个事务,中间让出总线提高总线利用率
不互锁 请求应答 请求、应答都“定时”自己撤销最快,可靠性最差 半互锁 请求应答 请求等到应答才撤销;应答“定时”自己撤销 全互锁 请求应答 每一步都等对方回应最慢,最可靠
图 9-4 异步定时的三种握手方式。上面一条是主设备的“请求”,下面一条是从设备的“应答”;红色箭头表示“这一边的变化要等那一边的变化发生之后才做”。箭头越多,互相等待越多,越可靠也越慢。

跟我做一遍(已用程序验算):32 位异步总线采用全互锁,一个信号从发出到被对方检测到需 25ns。一次传送要依次经过“请求↑ → 应答↑ → 请求↓ → 应答↓”4 次信号变化,每次都要等前一次被对方看到,所以至少 4 × 25 = 100ns;最大数据传输率 = 4B ÷ 100ns = 40MB/s。

半同步
仍用同步时钟定时,但增加一根等待(WAIT)信号。从设备来不及时就让 WAIT 有效,主设备在时钟沿上采样到 WAIT,就插入等待周期,直到 WAIT 撤销再继续。既保留了同步的简单,又能照顾慢速设备(2015-19 D:握手信号的采样由同步时钟控制)。
分离式
把一次读操作拆成两个子周期:① 主设备送出地址和命令后马上释放总线;② 从设备准备好数据后,自己作为主设备申请总线,把数据送回。从设备准备数据期间,总线可以给别人用,提高了总线利用率(2016-21 D)。要求各模块都能申请总线,控制复杂,多用于多处理器系统。

真题怎么考:同步时钟由谁提供(2015-19);不互锁 / 全互锁哪个快、哪个可靠(2015-19 A、B);一次握手完成的是一次数据交换,而不是一位(2021-19);分离事务提高的是利用率而不是带宽(9.1 变式)。

易错

① “全互锁最慢”和“全互锁最可靠”都对。② 异步定时的握手信号走控制线。③ 半同步仍然有时钟;分离式提高的是总线利用率,不提高峰值带宽。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2012-19 改
某同步总线的时钟频率为 200MHz,宽 64 位,地址/数据线复用,传送一个地址或一个数据各占 1 个时钟周期。若支持突发传送,则一次“主存写”总线事务传送 256 位数据至少需要( )。
  • A. 20ns
  • B. 25ns
  • C. 40ns
  • D. 50ns
展开解析

B256 位 = 4 个 64 位数据。突发传送只送一次地址:1 个周期送地址 + 4 个周期送数据 = 5 个周期 × 5ns = 25ns。

变化点:2012-19 是 100MHz、32 位、128 位数据(5 个周期 × 10ns = 50ns)。不支持突发时,每个数据都要先送一次地址:4 × 2 = 8 个周期 = 40ns(C)。

变式计组由 2023-20 改
某存储器总线宽 64 位,时钟频率为 500MHz,传送一个地址或一个数据各需 1 个时钟周期,不支持突发传送。主存每准备一个 64 位数据需 10ns,主存块大小为 64B,则读取一个主存块需要( )。
  • A. 28ns
  • B. 84ns
  • C. 112ns
  • D. 128ns
展开解析

C每读一个 64 位数据:送地址 2ns + 主存准备 10ns + 传数据 2ns = 14ns。一块 64B 要读 8 次:8 × 14 = 112ns。

变化点:2023-20 是 1GHz、6ns、32B(4 × 8 = 32ns)。不支持突发时,每个数据都要重复“送地址 → 等待 → 传数据”三步。

变式计组由 2015-19、2021-19 改
下列关于总线定时方式的叙述中,正确的是( )。
  • A. 同步总线的时钟信号可以由各设备各自提供
  • B. 异步全互锁方式中,每个信号都要等对方回应后才撤销,可靠性最高
  • C. 半同步方式不使用时钟信号
  • D. 采用分离式通信时,从设备准备数据期间一直占用总线
展开解析

B全互锁:请求要等应答到来才撤销,应答要等请求撤销才撤销,最慢但最可靠。A 错:同步总线由统一的时钟定时(2015-19)。C 错:半同步 = 同步时钟 + 等待(WAIT)信号。D 错:分离式在准备数据期间会释放总线。

变化点:2015-19 问错误的一项(同步时钟由各设备提供)。这里把四种定时方式各出一句,问正确的一项。

变式计组考纲补充(尚无真题):总线仲裁
某总线上连接 16 个设备,采用独立请求方式进行集中式总线仲裁。仲裁所需的控制线至少为( )根。
  • A. 3
  • B. 6
  • C. 32
  • D. 33
展开解析

D独立请求方式中每个设备有一对请求线 BRi、允许线 BGi,16 个设备共 32 根,再加 1 根总线忙信号 BS,共 33 根(已用程序验算)。A 是链式查询;B 是计数器定时查询(2 + log216);C 漏了 BS。

考点:总线仲裁的控制线数。

变式计组考纲补充(尚无真题):全互锁传输率
某 16 位异步总线采用全互锁通信方式,一个握手信号从发出到被对方检测到需要 20ns。该总线的最大数据传输率约为( )。
  • A. 25MB/s
  • B. 50MB/s
  • C. 100MB/s
  • D. 12.5MB/s
展开解析

A全互锁一次传送依次经过请求↑、应答↑、请求↓、应答↓ 4 次信号变化,至少 4 × 20 = 80ns;每次传 16 位 = 2B → 2B ÷ 80ns = 25MB/s。B 只算了 2 次信号变化;C 只算了 1 次;D 把每次传送的数据当成了 1B。

考点:异步定时的三种握手方式(已用程序验算同类例题)。

9.3I/O 接口与端口

★★ 常考选择题

外设五花八门(速度、数据格式、电平都不一样),不能直接挂到总线上,中间要一块“翻译 + 缓冲”的电路:I/O 接口(设备控制器,如网卡、磁盘控制器、打印机适配器;磁盘驱动器是磁盘本身的机械部分,不是接口,2021-20)。

CPU(通过系统总线) I/O 接口(设备控制器) 数据缓冲寄存器(数据端口) 状态寄存器(状态端口) 控制寄存器(控制端口) I/O 控制逻辑、地址译码 外设键盘/磁盘… 数据线 状态 → CPU 命令 → 接口 地址线:选中哪个端口 设备侧
图 9-5 I/O 接口。CPU 能读写的那些寄存器叫 I/O 端口(2014-21:状态端口和控制端口可以合用一个寄存器)。命令字、状态字、数据都经过数据线传给 CPU;CPU 与打印机接口之间交换的是打印字符、设备状态、控制命令,不会是主存地址(2015-21,中断方式下数据由 CPU 搬)。
接口的功能做什么
选址识别总线上的地址是不是给自己的(地址译码),选中哪个端口
传送命令CPU 写到控制寄存器的命令(如“启动”)由接口转给设备
传送数据用数据缓冲寄存器暂存数据,缓和 CPU 与设备的速度差
反映设备状态设备的忙/闲/就绪/出错写在状态寄存器里供 CPU 查询;中断方式时由接口发出中断请求
格式、电平转换串行 ↔ 并行、设备电平 ↔ 总线电平

接口与端口:接口是整块电路(设备控制器),端口是接口里 CPU 能直接读写的寄存器(数据端口、状态端口、控制端口)。操作系统里说的“设备控制器”就是计组里的“I/O 接口”:它由 ① 与 CPU 的接口(数据线、地址线、控制线)、② I/O 逻辑(译码 CPU 发来的命令)、③ 与设备的接口 三部分组成。接口还可按数据传送方式分为并行接口和串行接口,按功能分为可编程/不可编程、中断接口/DMA 接口。

设备怎么分类分类(操作系统选择题)
信息交换单位块设备(磁盘:以块为单位、可寻址、传输快,常用 DMA);字符设备(键盘、打印机:以字符为单位、不可寻址、传输慢,常用中断)
使用特性人机交互类外设(键盘、显示器)、存储设备(磁盘)、网络通信设备(网卡)
传输速率低速(键盘、鼠标)、中速(打印机)、高速(磁盘、网卡)
共享属性独占设备、共享设备、虚拟设备(9.8)
统一编址(存储器映射 I/O)
端口占用一部分主存地址,用普通的访存指令(mov)访问端口。
独立编址(I/O 映射 I/O)
端口有自己的地址空间(端口地址可能和主存地址相同),用专门的 I/O 指令(in/out)访问;I/O 指令是在通用寄存器和 I/O 端口之间传数据(2017-21)。

2014-21:“采用统一编址方式时,CPU 不能用访存指令访问 I/O 端口”错。I/O 指令是特权指令,只能在内核态执行(第 5 篇),所以用户程序必须通过系统调用请 OS 做 I/O。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2014-21 改
采用统一编址(存储器映射 I/O)方式时,下列叙述中正确的是( )。
  • A. 需要设置专门的 I/O 指令
  • B. I/O 端口占用一部分主存地址空间,可以用访存指令访问
  • C. I/O 端口地址和主存单元地址可能相同
  • D. CPU 靠 I/O 指令的操作码区分访问的是主存还是 I/O 端口
展开解析

B统一编址把 I/O 端口看成主存的一部分,占用一段主存地址,用普通的访存指令访问,靠地址区分。A、C、D 都是独立编址的特点。

变化点:2014-21 的错误说法是“统一编址时 CPU 不能用访存指令访问 I/O 端口”。这里正面问统一编址的特点。

变式计组由 2015-21、2017-21 改
中断方式下,CPU 与 I/O 接口中的端口通过数据线交换的信息不包括( )。
  • A. 要打印的字符
  • B. 设备的状态
  • C. 控制命令
  • D. I/O 端口的地址
展开解析

D数据、状态、控制命令都经数据线在 CPU 的寄存器和接口的端口之间传送(2017-21)。端口地址由 CPU 通过地址线送出,用来选中端口。

变化点:2015-21 的答案是“主存地址”(中断方式下 CPU 不和接口交换主存地址,那是 DMA 的事)。这里换成“端口地址”:它要传,但走的是地址线。

9.4三种 I/O 方式:CPU 到底要花多少时间

★★★ 高频选择题综合题
程序查询 CPU 不停地读状态寄存器:“好了没?好了没?”……好了就读一个数据(忙等) 程序中断 跑别的程序中断 跑别的程序中断 跑别的程序中断 设备每准备好一个字/字符就中断一次,CPU 在中断服务程序里搬这一个数据 DMA 跑别的程序(DMA 控制器偶尔“挪用”一个存储周期) 预处理后处理 传完一整块才中断一次;数据在设备接口和主存之间直接传,不经过 CPU
图 9-6 三种方式的 CPU 时间线。查询:CPU 全程陪着。中断:CPU 和设备并行,但每个数据都要一次中断(开销:响应 + 服务程序)。DMA:CPU 只在开头设置参数(驱动程序做)和结尾处理“DMA 传送结束”中断,适合磁盘、网卡、SSD 等高速、成块的设备(2025-21);键盘、针式打印机这类慢速字符设备用中断。
CPU 占用率的万能公式
CPU 用于 I/O 的时间占比 = 每秒要做几次 × 每次花多少个时钟周期 ÷ 主频 查询:每秒次数 = 设备速率 ÷ 每次读的数据量(接口缓冲大小) 中断:每秒中断次数 = 设备速率 ÷ 每次中断传的数据量;每次开销 = (服务程序指令数 + 其他开销) × CPI DMA:每秒 DMA 次数 = 设备速率 ÷ 数据块大小;每次开销 = 预处理 + 后处理
2009-43计组综合题 8 分
CPU 主频 500MHz,CPI 为 5。某外设数据传输率 0.5MB/s,采用中断方式,以 32 位为传输单位,中断服务程序包含 18 条指令,中断服务的其他开销相当于 2 条指令的执行时间。(1)中断方式下,CPU 用于该外设 I/O 的时间占整个 CPU 时间的百分比?(2)当该外设的数据传输率达到 5MB/s 时,改用 DMA 方式,每次 DMA 传送块大小为 5000B,DMA 预处理和后处理的总开销为 500 个时钟周期,CPU 用于该外设 I/O 的时间占比?(假设 DMA 与 CPU 之间没有访存冲突)
展开解析
  1. 每次中断开销:(18 + 2) × 5 = 100 个时钟周期。
  2. 每秒中断次数:0.5MB/s ÷ 4B = 125000 次。
  3. 占比:100 × 125000 ÷ 500M = 12.5M / 500M = 2.5%。
  4. DMA:每秒 5MB/5000B = 1000 次,每次 500 周期 → 0.5M / 500M = 0.1%。数据量大了 10 倍,CPU 负担反而小了 25 倍——这就是 DMA 的意义。
2018-43计组综合题 8 分
主频 500MHz,CPI 为 4。设备 A、B 的数据传输率分别为 2MB/s 和 40MB/s,对应 I/O 接口中各有一个 32 位数据缓冲寄存器。(1)A 采用定时查询,每次输入输出都至少执行 10 条指令。A 最多间隔多长时间查询一次才能不丢数据?CPU 用于 A 的时间占比至少是多少?(2)中断方式下,若每次中断响应和处理的总时钟周期数至少为 400,B 能否采用中断方式?为什么?(3)若 B 采用 DMA,每次传送 1000B,CPU 用于 DMA 预处理和后处理的总时钟周期数为 500,CPU 用于 B 的时间占比最大是多少?
展开解析
  1. (1)缓冲寄存器只有 4B,设备每 4B/(2MB/s) = 2μs 就装满一次,必须在下一个数据到来之前读走 → 最多每 2μs 查询一次。每秒查询 5×105 次,每次 10×4 = 40 周期 → 2×107/500M = 4%。
  2. (2)一次中断要 400 周期 = 400/500MHz = 0.8μs;B 每 4B/(40MB/s) = 0.1μs 就来一个新数据。中断还没处理完,缓冲寄存器已经被覆盖好几次 → 会丢数据,不能用中断(这就是“外设准备数据的时间应大于中断处理时间”,2022-21 选 C 为错误项)。
  3. (3)每秒 DMA 次数最多 40MB/1000B = 40000,× 500 = 2×107 周期 → 4%。
2016-44计组综合题
CPU 主频 50MHz,CPI 为 4。设备 D 用异步串行通信向主机传送 7 位 ASCII 字符,通信规程有 1 位奇校验和 1 位停止位,从 D 接收启动命令到字符送入 I/O 端口需要 0.5ms。(1)每个字符在线上共需传输多少位?每秒最多向端口送入多少字符?(2)D 采用中断方式:端口每收到一个字符申请一次中断,中断响应需 10 个时钟周期,中断服务程序共 20 条指令,其中第 15 条指令启动 D 工作。若 CPU 需从 D 读取 1000 个字符,完成这一任务大约需要多少时钟周期?CPU 用于这一任务的时间大约多少时钟周期?中断响应阶段 CPU 进行了哪些操作?
展开解析
  1. 1 起始位 + 7 数据位 + 1 校验位 + 1 停止位 = 10 位;每 0.5ms 一个字符 → 每秒 2000 个。
  2. 时钟周期 20ns,0.5ms = 25000 个周期。一个字符的“周期”:设备准备 25000 + 中断响应 10 + 服务程序执行到第 15 条(启动下一个字符)15×4 = 60 → 25070。1000 个字符 ≈ 25070000 个周期(启动后剩下的 5 条指令和设备工作重叠)。
  3. CPU 真正花的:1000 × (10 + 20×4) = 90000 个周期——总时长两千五百万,CPU 只忙了九万,其余时间都在跑别的程序。
  4. 中断响应阶段(隐指令):关中断、保存断点和程序状态、识别中断源(转服务程序入口)(第 5 篇)。

DMA 细节

  1. 预处理(CPU/驱动程序):设置 DMA 控制器的主存起始地址、传送字数、方向,启动设备。
  2. 数据传送(DMA 控制器,硬件):设备准备好一个数据(接口缓冲寄存器满)→ DMA 控制器请求总线 → 获得后把数据直接写入主存 → 地址加 1、计数减 1。常用周期挪用:每次只“偷”一个存储周期,CPU 在其余时间照常访存。DMA 请求优先级高于 CPU(不及时传会丢数据,2012-43、2022-44)。
  3. 后处理:计数到 0,DMA 控制器发出“DMA 传送结束”中断(外部中断),CPU 执行中断服务程序做收尾(校验、唤醒等待的进程)。
DMA 与 CPU 怎么共用主存做法特点
停止 CPU 访存DMA 传送一整块数据期间,CPU 让出总线、不访存控制简单;CPU 可能长时间不能访存,主存利用率不高
周期挪用(周期窃取)设备每准备好一个字,DMA 就“偷”一个存储周期写入主存;CPU 其余时间照常访存。若恰好冲突,DMA 优先最常用,兼顾 CPU 和 I/O;每次挪用都要申请、释放总线
DMA 与 CPU 交替访存把一个存储周期分成两半,一半给 CPU、一半给 DMA不需要申请、释放总线;要求 CPU 周期比存储周期长很多,硬件复杂
每格 = 一个存储周期 停止 CPU 访存 CPU CPU DMA 连续占用,传完一整块(CPU 只能等) CPU CPU CPU CPU 周期挪用 CPU CPU DMA CPU 空闲 CPU DMA CPU CPU 空闲 DMA CPU 接口每准备好一个字,DMA 就“偷”一个周期;两者同时要时 DMA 优先,CPU 推迟一个周期 交替访存 蓝 = CPU 用的半个周期,橙 = DMA 用的半个周期 时间片固定分好,不用申请、释放总线
图 9-7 DMA 与 CPU 访存冲突的三种处理方式。停止 CPU 访存:简单,但 CPU 可能长时间不能访存;周期挪用:只偶尔占一个周期,最常用(2020-22:CPU 可以访存);交替访存:每个存储周期一分为二,要求存储器比 CPU 快很多。

跟我做一遍(已用程序验算):磁盘数据传输率 4MB/s,接口数据缓冲寄存器 32 位,主存存储周期 100ns,采用周期挪用。接口每装满 4B 就请求一次 → 每秒 4MB ÷ 4B = 106 次,即每 1μs 挪用一个 100ns 的存储周期 → 主存有 10% 的时间被 DMA 占用。CPU 只有恰好在这些周期也要访存时才需要等待,其余 90% 照常访存。

DMA 控制器里有什么:主存地址计数器(下一个数据写到主存哪里,每传一次加 1)、传送长度计数器(还剩几个字,每传一次减 1,减到 0 就结束)、数据缓冲寄存器、DMA 请求触发器(设备准备好数据时置 1)、控制/状态逻辑,以及发“DMA 传送结束”中断的中断机构。

程序中断方式DMA 方式
数据怎么传靠 CPU 执行中断服务程序,数据经过 CPU(寄存器)靠 DMA 控制器硬件直接在设备接口和主存之间传,不经过 CPU
请求的是CPU 的时间(要 CPU 来处理)总线的使用权(只要占用总线)
何时响应一条指令执行结束后一个存储周期(总线周期)结束后,可以在指令执行中间
对 CPU 现场要中断 CPU 正在执行的程序,保存现场不打断程序,只是挪用存储周期,不用保存现场
优先级低于 DMA高于中断(不及时传会丢数据)
多久干预一次每传一个字(字节)中断一次每传一整块才中断一次(结束时)
适合低速字符设备(键盘、打印机)高速块设备(磁盘、网卡)

四种 I/O 控制方式总表(操作系统角度)

程序直接控制(查询)中断驱动DMA通道
传送单位字字块一组数据块
数据流向设备 → CPU → 内存设备 → CPU → 内存设备 → 内存设备 → 内存
CPU 干预全程(忙等)每个字一次中断每块开始、结束各一次只在一组数据块的开始、结束时
CPU 与设备并行不能能能能,且并行度最高

通道是专门负责 I/O 的处理机:CPU 只要发一条 I/O 指令告诉通道“去执行内存里的哪段通道程序、控制哪台设备”,通道就独立执行通道程序完成一组数据块的传送,结束时向 CPU 发中断。它和一般处理机的区别是:指令类型单一(只做 I/O),没有自己的内存,与 CPU 共享主存。一个通道可以控制多台设备。

2017-32 顺序:初始化 DMA 控制器并启动磁盘 → 从磁盘传输一块数据到内存缓冲区 → DMA 控制器发出中断请求 → 执行“DMA 结束”中断服务程序(选 B)。2019-22 四句全对(驱动设置参数、DMA 控制器请求总线、直接控制总线传送、结束后中断服务程序处理)。2020-22:周期挪用期间 CPU 可以访问主存(只是偶尔被挪走一个周期)。2013-22:中断请求的是 CPU 时间、在指令结束时响应;DMA 请求的是总线使用权、在总线事务(存储周期)结束时响应;“中断方式适用于所有外设”错。2023-22:DMA 不是“CPU 执行 DMA 传送程序”。2024-22:DMA 的数据通路在设备接口和主存之间。

2012-43计组融合综合题 11 分
CPU 主频 80MHz,CPI 为 4,平均每条指令访存 1.5 次,块 16B,Cache 命中率 99%,存储器总线 32 位。(1)MIPS?平均每秒 Cache 缺失次数?不考虑 DMA 时主存带宽至少多少?(2)Cache 缺失时访问主存存在 0.0005% 的缺页率,CPU 平均每秒产生多少次缺页异常?页 4KB,每次缺页都访问磁盘,DMA 周期挪用,磁盘接口数据缓冲寄存器 32 位,磁盘接口每秒至少发出多少次 DMA 请求?(3)CPU 和 DMA 控制器同时要求使用存储器总线时,谁的优先级高?为什么?(4)主存采用四体低位交叉,每 1/4 存储周期启动一个体,每体存储周期 50ns,主存最大带宽?
展开解析
  1. 80M/4 = 20M 条/秒 → 20 MIPS;缺失 20M × 1.5 × 1% = 300000 次/秒;每次缺失从主存搬一块 16B → 至少 300k × 16B = 4.8MB/s。
  2. 缺页 = 300000 × 0.0005% = 1.5 次/秒;每次缺页 DMA 读 4KB,接口每满 4B 请求一次 → 1.5 × 4096/4 = 1536 次/秒(4KB 按 4096 算)。
  3. DMA 优先:磁盘一旦开始传送,数据按固定速率到来,不及时写走就会被下一个数据覆盖而丢失;CPU 晚一点访存只是慢一点。
  4. 4 × 4B / 50ns = 320MB/s。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2019-21 改
某设备以中断方式与 CPU 交换数据。CPU 主频为 500MHz,设备接口中的数据缓冲寄存器为 16 位,设备的数据传输率为 100KB/s(1KB = 1000B)。每次中断的开销为 500 个时钟周期,则 CPU 用于该设备 I/O 的时间占整个 CPU 时间的百分比是( )。
  • A. 1.25%
  • B. 2.5%
  • C. 5%
  • D. 10%
展开解析

C每次中断传 2B,每秒中断 100 × 103 ÷ 2 = 5 × 104 次。占比 = 5 × 104 × 500 ÷ (500 × 106) = 5%。

变化点:2019-21 是 32 位寄存器、1GHz(1.25%)。寄存器变窄,中断次数变多;主频降低,每秒的总周期数变少,占比上升。

变式计组由 2014-22 改
某设备每 500ns 发出一次中断请求,中断响应和处理共需 150ns,中断响应允许的最长延迟为 50ns。在该设备持续工作期间,CPU 用于该设备 I/O 的时间占整个 CPU 时间的百分比至少是( )。
  • A. 20%
  • B. 30%
  • C. 40%
  • D. 10%
展开解析

B每 500ns 中 CPU 要花 150ns 处理这个设备,占比 = 150 ÷ 500 = 30%。“允许的最长延迟”只说明响应不能太晚,不占用 CPU 时间。

变化点:2014-22 是 100 ÷ 400 = 25%。两道题都有“最长延迟”这个干扰条件:把它加进去会得到 C(40%)。

变式计组由 2019-22、2017-32 改
DMA 方式下,下列工作中需要 CPU 执行程序来完成的是( )。
Ⅰ. 设置 DMA 控制器的传送参数 Ⅱ. 在设备接口与主存之间传送数据
Ⅲ. 执行“DMA 传送结束”中断服务程序 Ⅳ. 向总线仲裁器申请总线使用权
  • A. 仅Ⅰ、Ⅲ
  • B. 仅Ⅱ、Ⅳ
  • C. 仅Ⅰ、Ⅱ、Ⅲ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

A预处理(设置主存地址、字数等参数,由驱动程序完成)和后处理(中断服务程序)都要 CPU 执行程序。数据传送、申请总线都由 DMA 控制器用硬件完成。

变化点:2019-22 问哪些叙述正确(全对)。这里把同样四件事按“谁来做”分开:CPU 只管开头和结尾,中间的传送是 DMA 控制器的事。

变式计组考纲补充(尚无真题):DMA 与 CPU 的访存冲突
下列关于 DMA 与 CPU 访存冲突处理方式的叙述中,正确的是( )。
  • A. 停止 CPU 访存方式下,主存的利用率最高
  • B. 周期挪用方式下,DMA 每次挪用存储周期都不需要申请总线
  • C. 周期挪用方式下,DMA 传送整个数据块期间 CPU 不能访问主存
  • D. 交替访存方式下,CPU 和 DMA 不需要申请、归还总线使用权
展开解析

D交替访存把每个存储周期分成两半,一半固定给 CPU、一半固定给 DMA,不需要申请和归还总线。A 错:停止 CPU 访存时,DMA 两次传送之间主存常常闲着,CPU 却不能用,利用率不高。B 错:周期挪用每次都要申请、释放总线。C 错:那是停止 CPU 访存方式(2020-22)。

考点:DMA 与 CPU 访存冲突的三种方式。

9.5操作系统I/O 软件的层次

★★ 常考选择题
用户层 I/O 软件库函数 printf / scanf,SPOOLing 设备独立性软件(系统调用处理程序)逻辑设备名→物理设备、权限检查、缓冲管理、设备分配、出错处理 设备驱动程序(厂家提供)把抽象请求翻译成具体命令:算柱面/磁头/扇区,写控制寄存器,启动设备 中断处理程序设备完成后被中断唤醒:读状态、搬数据、唤醒进程 硬件:设备控制器(I/O 接口)+ 设备 请求向下 结果向上 用户态内核态 ↓
图 9-8 I/O 软件四层(+硬件)。顺序:用户层 → 设备独立性软件 → 设备驱动程序 → 中断处理程序(2012-26 选 A);一次磁盘请求的处理流程是 用户程序 → 系统调用处理程序 → 设备驱动程序 → 中断处理程序(2011-26 选 B)。计算磁盘的柱面号、磁头号、扇区号是驱动程序的事(2013-25、2019-44);驱动程序和设备的 I/O 控制方式有关(2022-32);字符设备、块设备的 I/O 方式不同(2026-31)。
设备独立性

程序里用逻辑设备名(如“打印机”“/dev/sda”)申请设备,操作系统负责映射到具体的物理设备(2009-32)。好处:换了一台同类设备,只要换驱动程序,应用程序不用改(2020-30 选 D 为错误项)。

每一层到底干什么

层次主要工作打个比方(寄快递)
用户层 I/O 软件给程序员用的库函数(printf、scanf、fopen),把用户的请求整理好后发系统调用;SPOOLing 程序也在这一层你填好快递单
设备独立性软件① 逻辑设备名 → 物理设备(查逻辑设备表 LUT);② 缓冲区管理(9.7);③ 设备的分配与回收(9.8);④ 差错控制、权限检查;⑤ 向下提供统一的驱动程序接口快递公司的调度中心:不管你寄到哪,流程都一样
设备驱动程序把上层的抽象命令(“读第 100 块”)翻译成具体设备能懂的命令(柱面、磁头、扇区),写控制器的寄存器、启动设备、检查状态;每类设备一个驱动,通常由设备厂商提供懂某条线路的司机
中断处理程序设备完成后发中断:保存现场、判断中断原因、唤醒等待的进程、恢复现场送达后给你发的短信

记一条原则:越往下越和具体硬件有关。和设备寄存器打交道的只有驱动程序和中断处理程序;设备独立性软件负责所有设备都要做的“通用”工作。

I/O 应用程序接口:三类设备、三种等法

设备接口特点典型操作例子
字符设备接口以字符(字节)为单位,顺序存取,不可寻址get、put(读一个字符、写一个字符)键盘、打印机、鼠标
块设备接口以数据块为单位,可寻址、可随机访问read、write、seek;还可以把文件映射到内存(8.7 内存映射文件)磁盘、SSD
网络设备接口数据以报文、数据包的形式收发套接字(socket):创建、连接、发送、接收网卡
阻塞 I/O 运行 read() 没数据 → 进程阻塞,CPU 去跑别的进程 数据到 → 返回,继续运行 调用 read read 返回数据 非阻塞 I/O 运行 立刻返回“没数据”,干别的 再问一次,还没有,接着干 再问:有了 → 拿到数据 read read read 异步 I/O 运行 发出请求后立刻返回,进程一直在干别的,不再过问 收到通知,直接用 aio_read 内核已把数据放进用户缓冲区,发信号 / 回调 绿色 = 进程自己在运行;灰色 = 进程被阻塞;橙色 = 系统通知进程 I/O 已完成
图 9-9 阻塞、非阻塞、异步三种 I/O。阻塞:数据没好,进程就睡着(阻塞态),好了再被唤醒,编程简单(scanf 就是这样)。非阻塞:调用立即返回“现在有没有”,进程不睡,过一会儿再来问(轮询)。异步:进程只管发出请求,整个 I/O(包括把数据复制到用户缓冲区)由系统在后台做完,再通知进程。
阻塞 I/O非阻塞 I/O异步 I/O
调用后数据没好就阻塞,直到 I/O 完成才返回立即返回,告诉进程当前有没有数据立即返回,I/O 在后台进行
进程要不要自己反复问不用(睡着等唤醒)要,反复调用(轮询)不用,完成后由系统通知(信号、回调)
数据什么时候到用户缓冲区返回时已经复制好某次调用“有数据”时复制通知到达时已经复制好
适合一般程序,编程最简单要同时照看多个设备 / 连接的程序希望 I/O 和计算完全重叠的程序

非阻塞和异步都“立即返回”,区别在于后面:非阻塞要进程自己一次次回来问;异步发出请求后就不管了,等系统来通知。

设备驱动程序接口:操作系统为所有驱动程序规定了一套统一的接口(打开、关闭、读、写、控制等函数),新设备的厂商只要按这个接口写好驱动,操作系统的上层代码一行都不用改——这就是“设备独立性”能实现的原因。

I/O 核心子系统:调度、保护、缓冲、分配、假脱机

问题:考纲把“I/O 核心子系统”单独列为一条,它和上面的四个层次是什么关系?

大白话:I/O 核心子系统就是操作系统内核里“所有设备都要用到的那些公共服务”,主要在设备独立性软件这一层实现(假脱机程序在用户层)。驱动程序管“怎么让这台设备动起来”,核心子系统管“请求怎么排队、谁能用、数据先放哪儿”。

服务做什么本书哪里讲
I/O 调度给同一设备上的多个 I/O 请求重新排序,减少设备的平均等待时间、提高效率(如磁盘调度算法)下面的例子;第 10 篇磁盘调度
设备保护检查进程对设备有没有访问权限;用户程序不能直接执行 I/O 指令(特权指令),只能通过系统调用9.3、第 5 篇
缓冲区管理单缓冲、双缓冲、循环缓冲、缓冲池9.7
设备分配与回收按设备类型、状态、权限分配设备、控制器、通道;逻辑设备名到物理设备的映射9.8
假脱机(SPOOLing)把独占设备改造成共享的虚拟设备9.8
出错处理设备、传输出错时重试、报告错误—

跟我做一遍:I/O 调度(已用程序验算)。磁头在 50 号磁道,请求队列按到达顺序是 90、20、80、30。按到达顺序服务,磁头共移动 40 + 70 + 60 + 50 = 220 个磁道;I/O 调度把它们重排成 30、20、80、90,只要移动 20 + 10 + 60 + 10 = 100 个磁道。请求还是那四个,只是换了顺序,平均等待时间就少了一半多。

真题怎么考:设备分配要考虑的因素(2023-32);逻辑设备名映射属于设备独立性软件(2020-30、2009-32);SPOOLing(2016-31);磁盘调度在第 10 篇。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2013-25、2012-26 改
读磁盘文件时,下列工作与完成它的 I/O 软件层次对应正确的是( )。
  • A. 把逻辑块号转换成柱面、磁头、扇区号——设备独立性软件
  • B. 检查用户是否有权访问该文件——设备驱动程序
  • C. 向磁盘控制器写命令,启动磁盘——设备驱动程序
  • D. 唤醒等待该 I/O 的进程——用户层 I/O 软件
展开解析

C直接和设备控制器打交道(写命令寄存器、读状态)的是驱动程序。A 错:块号换算成物理地址也是驱动程序做的(2013-25)。B 是设备独立性软件(权限检查)。D 是中断处理程序。

变化点:2012-26 考四层的顺序,2013-25 考一件具体工作在哪一层。这里一次考四件工作。

变式操作系统由 2022-32、2026-31 改
下列关于设备驱动程序的叙述中,正确的是( )。
  • A. 驱动程序在用户态下运行
  • B. 驱动程序与设备的硬件特性密切相关
  • C. 所有设备可以共用同一个驱动程序
  • D. 执行驱动程序的进程不会进入阻塞态
展开解析

B驱动程序要读写具体控制器的寄存器,与设备硬件特性密切相关,一般由设备厂商提供,在内核态运行。不同的设备要不同的驱动程序。进程执行驱动程序启动 I/O 后,通常要阻塞等待 I/O 完成(2022-32)。

变化点:2022-32 的错误说法是“驱动程序与 I/O 控制方式无关”。这里从运行态、硬件相关性、阻塞三个方面考。

变式操作系统考纲补充(尚无真题):阻塞、非阻塞与异步 I/O
下列关于阻塞 I/O、非阻塞 I/O 和异步 I/O 的叙述中,正确的是( )。
  • A. 异步 I/O 发出请求后立即返回,I/O 完成后由系统通知进程
  • B. 非阻塞 I/O 发出请求后,进程进入阻塞态直到数据就绪
  • C. 阻塞 I/O 发出请求后立即返回,进程需要反复查询
  • D. 异步 I/O 要求进程不断轮询 I/O 是否完成
展开解析

A异步 I/O 只管发出请求,整个 I/O 在后台完成后,系统用信号或回调通知进程。B 说的是阻塞 I/O;C 说的是非阻塞 I/O;D 也是非阻塞 I/O 的做法。

考点:三种 I/O 等待方式。

变式操作系统由 2013-25、2020-30 改
下列功能中,不属于 I/O 核心子系统(设备独立性软件)所提供的服务的是( )。
  • A. I/O 调度
  • B. 缓冲区管理
  • C. 设备的分配与回收
  • D. 计算磁盘的柱面号、磁头号和扇区号
展开解析

D把逻辑块号换算成柱面、磁头、扇区要了解具体磁盘的结构,是设备驱动程序的工作(2013-25)。I/O 调度、缓冲区管理、设备分配与回收都是所有设备共用的服务,属于 I/O 核心子系统。

变化点:2013-25 直接问柱面号由哪一层计算。这里把它和核心子系统的几项服务放在一起,要分清“通用服务”和“具体设备相关”的工作。考点:I/O 核心子系统。

9.6融合一次 scanf 的全过程:进程状态 + CPU 模式 + 数据位置

★★★ 高频选择题综合题

这是把第 5、6、9 篇串起来的一张图,2017-45、2023-46、2024-31、2025-46 都在考它。以“进程 P 执行 scanf 从键盘读一个字符,采用中断方式”为例:

进程 P操作系统内核其他进程 Q键盘及控制器 scanf → read()运行态 · 用户态 trap 系统调用:内核缓冲区里没有字符驱动程序启动键盘(若需要) ② P 插入阻塞队列(P:运行 → 阻塞)调度程序选 Q,进程切换 阻塞态(在等) Q 运行用户态 ⑥ 用户按键字符进数据寄存器 中断请求(Q 被打断) ④ 键盘中断处理程序(内核态) ③ 从键盘控制器的数据寄存器读出字符,放进内核(系统)缓冲区(驱动完成)① 唤醒 P:P 插入就绪队列(阻塞 → 就绪) 就绪态 中断返回,Q 继续 某次调度选中 P(就绪 → 运行)把字符从内核缓冲区复制到 P 的用户缓冲区 ⑤ 从系统调用返回(内核态 → 用户态) scanf 拿到字符运行态 · 用户态
图 9-10 scanf 的一生(编号对应 2023-46)。要点:① 中断处理程序运行时,P 仍在阻塞态(它在等的事件正在被处理),CPU 处于内核态,被打断的是 Q;② 中断处理完不一定马上切回 P,P 只是变成就绪;③ 键盘中断服务例程结束时,数据在内核缓冲区(2024-31),之后才复制到用户缓冲区;④ 键盘输入首先由中断处理程序获得(2010-32)。
2023-46操作系统融合综合题 8 分
进程 P 通过执行系统调用从键盘接收一个字符的输入,与 P 相关的操作包括:① 将进程 P 插入就绪队列;② 将进程 P 插入阻塞队列;③ 将字符从键盘控制器读入系统缓冲区;④ 启动键盘中断处理程序;⑤ 进程 P 从系统调用返回;⑥ 用户在键盘上输入字符。(编号与先后无关)(1)按正确顺序,操作①的前一个和后一个分别是哪个?操作⑥的后一个是哪个?(2)在哪个操作之后 CPU 一定从 P 切换到其他进程?在哪个操作之后 CPU 调度程序才能选择 P 执行?(3)完成哪个操作的代码属于键盘驱动程序?(4)键盘中断处理程序执行时,P 处于什么状态?CPU 处于内核态还是用户态?
展开解析

完整顺序:② → ⑥ → ④ → ③ → ① → ⑤(P 先发现没数据而阻塞,然后用户按键、中断、读字符、唤醒 P,最后 P 被调度并从系统调用返回)。

  1. ① 前是 ③,后是 ⑤;⑥ 后是 ④。
  2. ② 之后 P 阻塞了,CPU 一定切换到别的进程;① 之后 P 进入就绪队列,调度程序才可能选它。
  3. ③(和具体硬件打交道、读控制器的数据寄存器)是键盘驱动程序的代码。
  4. P 处于阻塞态;CPU 处于内核态。
2017-45(3)操作系统
计算机 M 的 I/O 采用中断控制方式。若进程 P 在调用 f1 之前通过 scanf() 获取 n 的值,则在执行 scanf() 的过程中,进程 P 的状态会如何变化?CPU 是否会进入内核态?
展开解析

P:运行 → 阻塞(等待输入)→ 输入完成时被中断处理程序唤醒 → 就绪 → 被调度 → 运行。CPU 会进入内核态(系统调用、中断处理都在内核态)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2024-31、2026-32 改
进程调用 scanf 从键盘读入一个整数。下列工作中,由键盘中断处理程序(及它调用的驱动程序)完成的是( )。
  • A. 把字符从内核缓冲区复制到用户缓冲区
  • B. 把键盘控制器数据寄存器中的字符读入内核缓冲区
  • C. 把输入的字符串转换成整数
  • D. 发出读键盘的系统调用
展开解析

B键盘中断处理程序把字符从控制器的数据寄存器取到内核缓冲区,并唤醒等待的进程。之后进程被调度运行,由系统调用服务程序把数据复制到用户缓冲区;字符串到整数的转换由 scanf 在用户态完成。

变化点:2024-31 问中断服务例程结束时数据在哪(内核缓冲区),2026-32 问鼠标中断处理程序做什么。这里放进完整的 scanf 流程,把每一步分给不同的程序。

变式操作系统由 2010-32 改
用户在终端上输入一条命令并按回车,下列程序开始处理这条输入的先后顺序是( )。
① 命令解释程序分析并执行命令 ② 键盘中断处理程序 ③ 系统调用服务程序把输入交给命令解释程序
  • A. ②→③→①
  • B. ①→②→③
  • C. ③→②→①
  • D. ②→①→③
展开解析

A最先得到键盘输入的是中断处理程序(2010-32),它把字符放入内核缓冲区;命令解释程序通过读系统调用拿到这一行输入;最后命令解释程序分析并执行命令。

变化点:2010-32 只问“最先得到输入的程序”,这里要求排出完整的顺序。

9.7缓冲区:为什么要先放进内核缓冲区

★★ 常考选择题

缓冲的目的:缓和 CPU 与设备的速度差、减少中断次数、放宽对中断响应时间的要求、提高 CPU 与设备的并行性。磁盘缓冲区的主要目的是减少磁盘 I/O 次数(2015-28);提前读、延迟写、为文件分配连续簇、磁盘高速缓存都能提高文件访问速度(2018-31)。

设:设备把一块数据读入缓冲区用时 T,从缓冲区传到用户区用时 M,CPU 处理一块用时 C。

单缓冲:缓冲区只有一个,下一块要等上一块被传走(M)后才能读入 设备→缓冲缓冲→用户CPU 处理 T 块1 M T 块2 C M T 块3 C 每块用时 = max(C, T) + M(M 期间缓冲区被占用,设备和 CPU都不能和它并行) 双缓冲:两个缓冲区轮流用,设备可以一直往另一个里读 设备→缓冲传+处理 T 块1T 块2T 块3 每块用时 = max(T, M + C)
图 9-11 单缓冲与双缓冲。最后一块读完后还要加上它的传送和处理时间。
2011-31操作系统
某文件占 10 个磁盘块,现要把该文件磁盘块逐个读入主存缓冲区,并送用户区进行分析。假设一个缓冲区与一个磁盘块大小相同,把一个磁盘块读入缓冲区的时间为 100μs,将缓冲区的数据传送到用户区的时间是 50μs,CPU 对一块数据进行分析的时间为 50μs。在单缓冲区和双缓冲区结构下,读入并分析完该文件的时间分别是( )。
  • A. 1500μs、1000μs
  • B. 1550μs、1100μs
  • C. 1550μs、1550μs
  • D. 2000μs、2000μs
展开解析

考点9.7 单缓冲与双缓冲

B设 T = 读入缓冲区 100μs,M = 缓冲区送用户区 50μs,C = 分析 50μs。单缓冲每块 max(C, T) + M,双缓冲每块 max(T, M + C),最后再补上最后一块的尾巴。

  1. 单缓冲:每块 max(50, 100) + 50 = 150μs,10 块 1500μs;最后一块还要分析 50μs → 1550μs。
  2. 双缓冲:每块 max(100, 50 + 50) = 100μs,10 块 1000μs;最后一块还要传送 50、分析 50 → 1100μs。
  • A 错1500、1000 只算了 10 个“每块周期”,漏掉最后一块的收尾:单缓冲最后一块还要分析 50μs,双缓冲最后一块还要传送 + 分析 100μs。
  • B 对单缓冲 1550μs、双缓冲 1100μs。
  • C 错双缓冲也写成 1550μs,等于认为双缓冲没有带来并行。实际上两个缓冲区可以一个读盘、一个往用户区送,每块周期从 150 降到 100μs。
  • D 错2000μs = 10 × (100 + 50 + 50),把读盘、传送、分析完全串行,完全没有利用缓冲区让读盘和分析并行。

单缓冲:max(C, T) + M;双缓冲:max(T, M + C);n 块总时间 = n × 每块时间 + 最后一块的尾巴。同类:2013-27(系统缓冲区、用户工作区都单缓冲,T = 100、M = 5、C = 90,读入并分析 2 块最短 = 100 + 5 + 100 + 5 + 90 = 300)。

单缓冲:处理一块的平均时间 = max(C, T) + M 双缓冲:处理一块的平均时间 = max(C + M, T) 总时间 = 块数 × 平均时间,再按题目看开头、结尾是否要补上第一块的 T 或最后一块的 M、C(2011-31、自测第 4 题)

为什么单缓冲是 max(C, T) + M:缓冲区只有一个,下一块读入缓冲区(T)必须等上一块从缓冲区传走(M);而上一块的处理(C)可以和下一块的读入(T)同时进行。双缓冲时两个缓冲区交替使用,“传 + 处理”(M + C)可以和读入(T)完全重叠。

循环缓冲与缓冲池

问题:单缓冲、双缓冲只有一两个缓冲区,输入一阵快一阵慢时还是会互相等。缓冲区多一些,怎么管?

大白话:循环缓冲像回转寿司的转盘:厨师(输入进程)往空盘子里放寿司,客人(计算进程)从装满的盘子里拿,两人各有一个“下一个该处理的盘子”的指针,绕着圈走。缓冲池则像食堂的公用餐盘架,所有窗口共用,用完放回。

定义:循环缓冲由多个大小相同的缓冲区连成环,输入指针 in 指向下一个可以装数据的空缓冲区,输出指针 out 指向下一个可以取数据的满缓冲区,两个指针都沿同一方向前进。

满 满 满 空 空 空 顺时针前进↻ out:下一个可取数据的满缓冲区计算进程从这里取,取完变空,out 前进 in:下一个可装数据的空缓冲区输入进程往这里装,装满后 in 前进 in 追上 out(所有缓冲区都满了)→ 输入进程阻塞 out 追上 in(所有缓冲区都空了)→ 计算进程阻塞
图 9-12 循环缓冲。两个指针就是第 7 篇生产者—消费者问题里的 in、out:缓冲区就是那个“环形缓冲池”,满了生产者等,空了消费者等。

缓冲池:由系统统一管理、多个进程共享的一组缓冲区,比每个进程各设缓冲区省内存。池里的缓冲区按状态挂在三个队列上:空缓冲队列、输入队列(装满输入数据)、输出队列(装满输出数据)。工作时用四种工作缓冲区:

工作缓冲区从哪个队列取一个缓冲区做什么用完挂到哪
收容输入 hin空缓冲队列装入输入设备送来的数据输入队列
提取输入 sin输入队列把数据交给计算进程空缓冲队列
收容输出 hout空缓冲队列装入计算进程要输出的数据输出队列
提取输出 sout输出队列把数据送给输出设备空缓冲队列

记法:“收容”是往缓冲区里装,从空队列拿空的;“提取”是从缓冲区里取,从满的队列拿,取完还给空队列。高速缓存与缓冲区的区别:高速缓存存的是低速设备上某些数据的副本(设备上仍有);缓冲区存的是低速设备传给高速设备的数据,可能是唯一的一份。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2011-31 改
某文件占 8 个磁盘块,要逐块读入缓冲区并送用户区处理。把一块读入缓冲区需 80μs,从缓冲区传到用户区需 20μs,CPU 处理一块需 60μs。采用单缓冲和双缓冲时,读入并处理完该文件分别需要( )。
  • A. 860μs、720μs
  • B. 800μs、640μs
  • C. 860μs、860μs
  • D. 720μs、640μs
展开解析

A第一块要经过读入、传送、处理,共 160μs,之后每块增加一个周期。单缓冲周期 = max(C, T) + M = 100μs,总时间 = 160 + 7 × 100 = 860μs。双缓冲周期 = max(T, C + M) = 80μs,总时间 = 160 + 7 × 80 = 720μs。

变化点:2011-31 是 10 块、T = 100、M = 50、C = 50(1550μs、1100μs)。这里恰好 T = C + M,双缓冲时读入和“传送 + 处理”完全平衡。

变式操作系统由 2018-31、2015-28 改
下列措施中,可以减少磁盘平均寻道时间(磁头移动距离)的是( )。
Ⅰ. 采用 SCAN 等磁盘调度算法 Ⅱ. 为文件分配连续的簇 Ⅲ. 设置磁盘高速缓存 Ⅳ. 提高磁盘转速
  • A. 仅Ⅰ
  • B. 仅Ⅰ、Ⅱ
  • C. 仅Ⅲ、Ⅳ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

B调度算法和连续分配都能减少磁头的移动。磁盘缓存减少的是访问磁盘的次数(2015-28),提高转速减少的是旋转延迟,都不减少寻道时间。

变化点:2018-31 问能提高文件访问速度的措施(四项都能)。这里只问减少“寻道时间”的,要把访问时间的三部分分开看。

变式操作系统考纲补充(尚无真题):循环缓冲
采用循环缓冲时,若输入指针 in 追上了输出指针 out(in 所指的缓冲区已装满数据),则说明( )。
  • A. 缓冲区全空,计算进程应阻塞
  • B. 缓冲区全空,输入进程应阻塞
  • C. 缓冲区全满,输入进程应阻塞
  • D. 缓冲区全满,计算进程应阻塞
展开解析

Cin 指向下一个可装数据的空缓冲区。in 追上 out,说明转了一圈回来,所有缓冲区都装满了,输入进程没地方放数据,只能阻塞,等计算进程取走数据。反过来,out 追上 in 是全空,计算进程阻塞。

考点:循环缓冲;与第 7 篇生产者—消费者问题对照。

9.8设备分配与 SPOOLing

★★ 常考选择题
  • 设备分三类:独占设备(打印机:一段时间只给一个进程)、共享设备(磁盘:可以交替访问)、虚拟设备(用 SPOOLing 把独占设备变成“共享”的)。
  • 设备分配的数据结构:设备控制表 DCT、控制器控制表 COCT、通道控制表 CHCT、系统设备表 SDT。分配要考虑:设备的固有属性(类型)、分配算法、安全性、访问权限、占用状态、逻辑设备与物理设备的映射(2023-32 全选)。
  • 分配步骤(单通道结构):① 根据进程请求的物理设备名查 SDT,找到该设备的 DCT,设备忙就把进程的 PCB 挂到该设备的等待队列,不忙就分配设备;② 由 DCT 找到它连着的控制器的 COCT,控制器忙就等待,不忙就分配控制器;③ 由 COCT 找到通道的 CHCT,通道忙就等待,不忙就分配通道。设备、控制器、通道三者都分配成功,才算分配成功,才能启动 I/O。改进:用逻辑设备名请求,系统从 SDT 里找一台空闲的同类设备(设备独立性)。
  • 分配方式与算法:静态分配(作业开始前一次分配全部所需设备,不会死锁,但设备利用率低);动态分配(运行中按需申请);分配算法有先来先服务、优先级高者优先。安全分配(进程发出 I/O 请求后就阻塞,I/O 完成才唤醒,破坏了“请求并保持”,不会死锁,但 CPU 与 I/O 串行);不安全分配(发出请求后继续运行,还能再请求别的设备,效率高,但可能死锁)。

逻辑设备表 LUT 与设备分配的四张表

问题:进程说“我要打印机”,操作系统怎样一步步找到一台空闲的打印机,以及它连着的控制器和通道?

大白话:先查“通讯录”(LUT:你说的“打印机”是哪一台),再查“设备总表”(SDT),然后顺着设备 → 控制器 → 通道一路往下问“忙不忙”,三关都过了才能开始干活。

逻辑设备表 LUT 逻辑名 → 物理设备printer → 打印机 3disk → 磁盘 1(+ 驱动程序入口) 系统设备表 SDT全系统一张每台设备一项:类型、DCT 指针 设备控制表 DCT每台设备一张状态(忙 / 闲)→ COCT 指针 控制器表 COCT每个控制器一张状态(忙 / 闲)→ CHCT 指针 通道表 CHCT每个通道一张状态(忙 / 闲) 设备等待队列 控制器等待队列 通道等待队列 哪一关忙,进程的 PCB 就挂到哪一关的等待队列上
图 9-13 设备分配用到的表。设备、控制器、通道三者都分配成功,才能启动 I/O(9.8 分配步骤)。LUT 把用户用的逻辑设备名映射到物理设备,是实现设备独立性的关键。

跟我做一遍:进程请求逻辑设备 printer。① 查 LUT:printer 对应打印机 3;② 查 SDT 找到打印机 3 的 DCT:空闲 → 分配;③ 由 DCT 找到控制器 1 的 COCT:正忙 → 进程挂到控制器 1 的等待队列,I/O 还不能启动;④ 控制器 1 空出来后再分配它,接着查 CHCT 分配通道,三样都到手才启动打印。

LUT 怎么设置:① 整个系统一张 LUT:所有用户共用,不同用户不能用相同的逻辑设备名,适合单用户系统;② 每个用户一张 LUT:用户登录时建立,放在该用户的进程控制块(或用户管理进程的 PCB)里,不同用户可以用同样的逻辑设备名,适合多用户系统。

真题怎么考:设备独立性:用逻辑设备名、更换设备不改程序(2009-32、2020-30);分配要考虑的因素(2023-32)。

进程 A 进程 B 进程 C 输出缓冲区(内存) 输出井(磁盘)A 的文件B 的文件 C 的文件 输出进程(系统) 打印机 每个进程都觉得“打印机是我的”,马上写完就走;真正的打印由系统的输出进程排队完成
图 9-14 SPOOLing(假脱机)。用磁盘上的输入井/输出井模拟外围设备,把独占设备改造成共享设备(虚拟设备)。需要外存(磁盘)和多道程序支持;设备与输入/输出井之间的数据传送由系统(输入/输出进程)控制,而不是用户作业(2016-31 选 D 为错误项)。
SPOOLing 的组成作用
输入井、输出井在磁盘上开辟的两块区域:输入井模拟脱机输入时的磁盘,存放输入设备送来的数据;输出井存放用户进程要输出的数据
输入缓冲区、输出缓冲区在内存中开辟:暂存输入设备送来的数据再送输入井;暂存从输出井送来的数据再送输出设备
输入进程、输出进程模拟脱机输入/输出时的外围控制机:输入进程把输入设备的数据经输入缓冲区送到输入井;输出进程把输出井的数据经输出缓冲区送到输出设备
井管理程序控制作业与磁盘井之间的信息交换

共享打印机的过程:用户进程要打印时,SPOOLing 系统并不真的把打印机分给它,而是 ① 由输出进程在输出井中为它申请一块空闲区,把要打印的数据送进去;② 为它申请一张空白的用户请求打印表,填好打印要求,挂到打印请求队列上。之后用户进程就可以继续运行,好像已经打印完了一样。打印机空闲时,输出进程从队首取一张请求表,把输出井里对应的数据送打印机打印。每个进程都觉得自己独占了一台打印机——这就是“把独占设备改造成共享设备(虚拟设备)”。

SPOOLing 的特点:提高了 I/O 速度(进程对低速设备的 I/O 变成了对高速磁盘的读写);把独占设备改造成共享设备;实现了虚拟设备功能。代价是要占用磁盘空间、需要多道程序(输入/输出进程和用户进程并发)支持。

系统最大并发 I/O 速率怎么算

问题:一台机器挂了好几个控制器、几十台设备,它们同时工作时,系统每秒最多能传多少数据?

大白话:数一数“同一时刻真正能一起传数据的有几台”,把它们的速率加起来;最后再看公共的总线吃不吃得下,吃不下就以总线为准(短板)。

系统总线(带宽 500MB/s) 磁盘控制器 1 磁盘控制器 2 磁盘控制器 3 终端控制器 1 终端控制器 2 每个控制器 4 台磁盘(各 200MB/s),同一时刻只有 1 台在传 → 磁盘部分最多 3 × 200 = 600MB/s 每个控制器 8 台终端(各 2MB/s),可以同时传 → 16 × 2 = 32MB/s 设备能并发 600 + 32 = 632MB/s,但总线只有 500MB/s → 系统最大 I/O 速率 = min(632, 500) = 500MB/s
图 9-15 最大并发 I/O 速率。红色是某一时刻正在传输的磁盘,绿色是正在传输的终端。关键是读懂“控制器能不能让几台设备同时传”。
  1. 按控制器分组:同一个控制器下“一次只允许一台传输”的,这一组只算 1 台的速率;“可以同时处理所有设备”的,这一组全部相加。
  2. 各组相加:不同控制器可以并行工作。本例 3 × 200 + 2 × 8 × 2 = 600 + 32 = 632MB/s(已用程序验算)。
  3. 和公共通路比:若题目给了总线(或通道)带宽,取较小者。本例总线 500MB/s < 632MB/s → 最大 500MB/s。没给就不用比。

真题怎么考:408 真题还没有考过这种题,它是教材习题里的经典算例。会了“分组相加、再取瓶颈”这两步就够了。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2016-31 改
下列关于 SPOOLing 技术的叙述中,正确的是( )。
  • A. SPOOLing 把共享设备改造成独占设备
  • B. 输入井和输出井设在内存中
  • C. 由输入进程和输出进程负责在设备与井之间传送数据
  • D. 不需要多道程序设计技术的支持
展开解析

CSPOOLing 用输入进程、输出进程模拟脱机输入输出,在设备和磁盘上的输入井、输出井之间传送数据(不是用户作业自己传,2016-31)。它把独占设备改造成“共享”的虚拟设备,井在外存(磁盘)上,需要多道程序设计的支持。

变化点:2016-31 的错误说法是“由用户作业控制设备与井之间的数据传送”。这里把 SPOOLing 的四个要点(改造方向、井的位置、谁来传送、依赖什么)都放进选项。

变式操作系统由 2020-30、2009-32 改
下列关于设备独立性的叙述中,正确的是( )。
  • A. 用户程序中使用物理设备名
  • B. 更换同类物理设备后,应用程序不必修改
  • C. 逻辑设备表由用户程序维护
  • D. 设备独立性要求所有设备使用同一个驱动程序
展开解析

B用户程序使用逻辑设备名,由系统通过逻辑设备表(由操作系统维护)映射到物理设备。更换物理设备只需修改映射,不必修改程序(2020-30)。

变化点:2020-30 的错误说法是“更换物理设备后必须修改应用程序”,这里给出正确的说法。

变式操作系统由 2009-32、2020-30 改
在多用户系统中,为了让不同用户可以使用相同的逻辑设备名,逻辑设备表 LUT 应( )。
  • A. 为每个用户设置一张
  • B. 整个系统只设置一张
  • C. 放在设备控制表 DCT 中
  • D. 由设备驱动程序维护
展开解析

A每个用户一张 LUT(用户登录时建立),不同用户的逻辑设备名互不冲突。B 适合单用户系统,所有用户共用,逻辑设备名不能重复。C、D 错:LUT 由设备独立性软件维护,DCT 记录的是一台物理设备的状态。

变化点:2009-32、2020-30 考设备独立性的含义,这里考实现它的 LUT 怎么设置。考点:逻辑设备表。

变式操作系统考纲补充(尚无真题):最大并发 I/O 速率
某系统有 2 个磁盘控制器,每个连接 3 台磁盘,同一时刻每个控制器只允许一台磁盘传输数据,磁盘的数据传输率为 300MB/s;还有 1 个终端控制器,连接 10 台终端,可以同时传输,每台终端 5MB/s。不考虑总线的限制,系统的最大 I/O 速率为( )。
  • A. 1850MB/s
  • B. 950MB/s
  • C. 350MB/s
  • D. 650MB/s
展开解析

D磁盘:每个控制器同一时刻只有 1 台在传,2 个控制器并行 → 2 × 300 = 600MB/s;终端:10 台同时传 → 50MB/s。合计 650MB/s。A 把 6 台磁盘都当成同时传;B 是把一个控制器下的 3 台磁盘当成同时传(900)再加终端;C 只算了一个磁盘控制器。

考点:最大并发 I/O 速率。

第 9 篇自测
  1. 主频 1GHz,某设备 100KB/s、以 16 位为单位用中断方式传送,每次中断开销 500 个时钟周期,CPU 用于它的时间占比?
    答案
    每秒 100KB/2B = 50000 次(按 K=1000 口径)× 500 = 2.5×107 → 2.5%。
  2. 中断方式和 DMA 方式各在什么时刻响应请求?
    答案
    中断:一条指令执行结束后;DMA:一个存储周期(总线事务)结束后,可以在指令执行过程中。
  3. 键盘中断处理程序在运行时,等待输入的进程处于什么状态?
    答案
    阻塞态(中断处理程序运行在内核态,它会把该进程唤醒成就绪)。
  4. 单缓冲(系统缓冲区和用户区各一个),T=80、M=20、C=100,读入并处理 5 块共需多少时间?
    答案
    画时间线:块1 读 0~80、传 80~100、处理 100~200;块2 读 100~180,但要等用户区空(块1 处理完)才能传:传 200~220、处理 220~320;之后每块都是“传 20 + 处理 100” = 120 一个周期:块3 处理到 440,块4 到 560,块5 传 560~580、处理 580~680。即 5 × max(C,T)+M = 5×120 = 600,再加上第一块开头的 T = 80。(和 2011-31 对比:那里 T > C,最后要加的是 C。)
  5. 总线时钟频率 100MHz,一个总线传输周期占 4 个时钟周期,总线宽度 32 位,总线带宽是多少?
    答案
    每秒 100M / 4 = 25M 次,× 4B = 100MB/s。
  6. DMA 方式下,数据在设备和主存之间传送时要经过 CPU 的寄存器吗?DMA 请求在什么时候被响应?
    答案
    不经过 CPU,由 DMA 控制器直接在设备接口和主存之间传;在一个存储周期(总线周期)结束时响应,可以在一条指令执行的中间。
  7. 设备分配时,设备、控制器、通道中哪些分到了才能启动 I/O?
    答案
    三者都分配成功才行,缺一个就要在相应队列上等待。
  8. SPOOLing 系统中,用户进程执行“打印”后立刻继续运行,打印机当时被分配给它了吗?
    答案
    没有。数据只是被放进了磁盘上的输出井,并挂了一张打印请求表;真正打印由输出进程在打印机空闲时完成。

9.✎本篇真题闯关(56 题)

本篇各小节挂的真题全部在这里,按小节排好。可以展开后一道道做,也可以用弹窗模式连续做。做题记录(对/错)只保存在你这台电脑的浏览器里,小节标题下的题号会随之变绿或变红。

第 10 篇 · 融合 · 计组 3.4(磁盘) × 操作系统第 4 章(文件)+ 5.3(磁盘管理与调度)

融合四:磁盘与文件系统 —— 数据怎么长期存在磁盘上

前面讲的都是“内存里的事”。程序、文件最终都存在磁盘上。这一篇自底向上:先看磁盘的机械结构和一次读写要多长时间(计组),再看操作系统怎么调度多个磁盘请求、怎么格式化和引导(OS 5.3),最后看文件系统怎么组织:目录、inode、文件的块放在哪、空闲块怎么记录、打开一个文件时内存里建了哪些表(OS 第 4 章)。文件系统的综合题几乎每两年一道。

对应:王道计组 3.4;王道操作系统第 4 章、5.3难度:★★★本机题库计数:inode 7 题、文件物理结构 5 题、外存空间管理 5 题、磁盘调度算法 5 题、位图法 3 题

10.1计组磁盘的结构和访问时间

★★★ 高频选择题综合题
侧面:盘片、磁头、柱面 主轴(旋转) 磁头臂(所有磁头一起移动) 虚线:各盘面同一半径的磁道 = 一个柱面 正面:磁道和扇区 一个扇区(最小读写单位) 同心圆 = 磁道
图 10-1 磁盘结构。地址由柱面号(磁道号)、磁头号(盘面号)、扇区号组成(CHS 地址)。磁盘容量 = 柱面数 × 磁头数(盘面数)× 每道扇区数 × 扇区大小。磁盘的最小读写单位是扇区,不是字节(2019-20)。格式化后要写入地址、校验等信息,所以格式化容量比非格式化容量小。
访问一个扇区的时间 = 寻道时间 + 旋转延迟 + 传输时间 (+ 控制器延迟) 平均旋转延迟 = 转半圈的时间 = (60 / 转速) / 2 (转速单位 rpm = 转/分) 传输一个扇区 = 转一圈的时间 / 每道扇区数 或 数据量 / 传输速率
2022-44计组融合综合题 8 分
某磁盘驱动器有 4 个双面盘片,每个盘面 20000 个磁道,每个磁道 500 个扇区,每个扇区 512 字节,转速 7200rpm,平均寻道时间 5ms。(1)每个扇区的地址信息分为 3 个字段,名称是什么?各至少占多少位?(2)一个扇区的平均访问时间约为多少?(3)若采用周期挪用 DMA 方式,磁盘控制器中的数据缓冲区为 64 位,则一个扇区读写过程中,DMA 控制器向 CPU 发送了多少次总线请求?若 CPU 检测到 DMA 总线请求时也要访问主存,DMA 能否获得总线使用权?为什么?
展开解析
  1. (1)柱面号(磁道号):20000 个 → ⌈log220000⌉ = 15 位;磁头号(盘面号):4 片 × 2 面 = 8 → 3 位;扇区号:500 → ⌈log2500⌉ = 9 位。
  2. (2)转一圈 60s/7200 ≈ 8.33ms;寻道 5 + 旋转半圈 4.17 + 传输 8.33/500 ≈ 0.017 → 约 9.18ms。
  3. (3)缓冲区 8 字节满一次就请求一次总线:512/8 = 64 次。可以:周期挪用 DMA 优先级高于 CPU,磁盘数据一旦开始传送就必须按时写走,否则会被后面的数据覆盖而丢失。

同类:2013-21(10000rpm、寻道 6ms、传输率 20MB/s、控制器延时 0.2ms,读 4KB:6 + 3 + 0.2 + 0.2 = 9.4ms);2015-20(7200rpm、寻道 8ms、每道 1000 扇区:8 + 4.17 + 0.008 ≈ 12.2ms)。

数据传输率 = 每秒转数 × 每道容量(例:7200rpm = 120 转/秒,每道 500 × 512B = 256000B → 120 × 256000B ≈ 30.72MB/s) 平均寻址时间 = 平均寻道时间 + 平均旋转延迟(不含传输时间)
磁盘管理里的三个“为什么”(选择题 / 大题都考过)

① 为什么地址按“柱面号—盘面号—扇区号”排,而不是“盘面—柱面—扇区”?读一段地址连续的数据时,地址增加先让扇区号变、再让盘面号变、最后才让柱面号变:同一柱面上换盘面只是电子地切换磁头,不用移动磁臂;若把盘面放在最高位,连续的数据就会分布在同一盘面的不同柱面上,每读一个磁道都要移动磁臂(寻道最慢)。2019-44 的簇号换算就是按这个顺序:簇号 ÷ 每柱面簇数 = 柱面号,余数 ÷ 每磁道簇数 = 盘面号,再余的就是磁道内的位置。
② 交替编号:读完一个扇区后,控制器要花一点时间把数据送走,这期间盘片还在转。如果逻辑上相邻的扇区在物理上也紧挨着,处理完时磁头已经转过了下一个扇区,只好再等一整圈。办法是让逻辑相邻的扇区在物理上隔开一个或几个扇区(例如逻辑 0、1、2、3 放在物理 0、2、4、6),处理的时间里正好转过中间的扇区。
③ 错位命名:不同盘面上相同编号的扇区彼此错开一点,读完一个盘面的某磁道、切换到下一盘面继续读时,也留出了磁头切换的时间。

RAID 磁盘阵列:条带、镜像、分布式校验

问题:一块磁盘太慢、坏了数据就全没了。把好几块磁盘组合起来,能不能又快又可靠?

大白话:抄作业的三种办法。条带化(RAID 0):一本书拆开,几个人同时抄不同的页,快,但谁丢了一页书就残了;镜像(RAID 1):两个人抄同样的内容,丢一份还有一份,但纸用了双倍;校验(RAID 5):几个人各抄一部分,再多抄一页“校验页”,谁丢了,用其余的几页就能把它推算回来。

RAID 0:条带化 A1A3A5A7 A2A4A6A8 盘 0盘 1 块轮流放在各盘,可以同时读写没有冗余,坏一块盘数据就丢 RAID 1:镜像 A1A2A3A4 A1A2A3A4 盘 0盘 1(副本) 每块存两份坏一块盘不丢数据,容量减半 RAID 5:条带 + 分布式校验 A1A2A3Ap B1B2BpB3 C1CpC2C3 DpD1D2D3 盘 0盘 1盘 2盘 3 橙色 = 校验块 Ap = A1 ⊕ A2 ⊕ A3,轮流放在各盘坏任意一块盘都能恢复,只损失一块盘的容量 校验块轮流放,是为了不让某一块盘成为每次写都要访问的“热点”
图 10-2 RAID 0、RAID 1、RAID 5。条带化提高速度;镜像和校验提供冗余,提高可靠性(2013-20)。
级别做法n 块容量为 C 的盘,可用容量最多坏几块不丢数据特点
RAID 0条带化nC0读写最快,可靠性比单盘还差
RAID 1镜像(两盘一组)nC / 2每组坏 1 块最简单可靠,空间利用率 50%
RAID 5条带 + 分布式奇偶校验(n − 1)C任意 1 块读快;写一块小数据要 4 次 I/O(读旧数据、读旧校验、写新数据、写新校验)
RAID 10 拓展先镜像再条带nC / 2每个镜像组坏 1 块又快又可靠,但空间利用率 50%

跟我做一遍(已用程序验算):4 块 1TB 的盘,RAID 0 可用 4TB,RAID 1(两组镜像)和 RAID 10 可用 2TB,RAID 5 可用 3TB。校验怎么恢复:一条带上 A1 = 1011、A2 = 0110、A3 = 1100,校验 Ap = A1 ⊕ A2 ⊕ A3 = 0001。存 A2 的盘坏了:A2 = A1 ⊕ A3 ⊕ Ap = 1011 ⊕ 1100 ⊕ 0001 = 0110,原样算回来了。

真题怎么考:提高可靠性的措施是镜像和校验,不是条带化和 Cache(2013-20)。

固态硬盘 SSD:页写块擦、闪存翻译层与磨损均衡

问题:SSD 用 Flash 做成,没有磁头和盘片。它和机械硬盘在读写上有什么根本不同?操作系统要为它改什么?

大白话:Flash 像一本“只能用铅笔写、而且只能整页撕下来重写”的本子:写之前要擦,但擦只能一大块一起擦。所以 SSD 改一页数据时不在原地改,而是写到一张新纸上,再记一笔“这个内容现在在新的位置”。

定义:Flash 以页(几 KB)为单位读写,以块(几十到几百页)为单位擦除,写之前必须先擦(页写块擦)。SSD 内部的控制器运行闪存翻译层 FTL:把操作系统看到的逻辑块号映射到 Flash 的物理页,让 SSD 在操作系统看来就是一个普通的块设备。

操作系统看到的 逻辑块 0逻辑块 1逻辑块 2 FTL 映射表(SSD 内部) 0 → 块0 页01 → 块0 页32 → 块0 页2 Flash 芯片 块 0(擦除单位) 页 0:逻辑块 0 页 1:旧内容(无效) 页 2:逻辑块 2 页 3:逻辑块 1(新) 块 1 空空空空 GC ① 改写逻辑块 1:不能原地改,写到空页 3,改映射,旧页 1 标为无效 ② 垃圾回收(GC):把块 0 的有效页(0、2、3)搬到块 1,再把块 0 整块擦除,回收无效页
图 10-3 SSD 内部:异地更新与垃圾回收。这些事全由 SSD 自己的控制器(FTL)完成,操作系统看到的仍是“读写逻辑块”。
  • 磨损均衡:每个块能承受的擦写次数有限(几千到几万次),某些块被反复擦写会先坏。动态磨损均衡:写新数据时,优先选擦除次数少的空闲块;静态磨损均衡:还会把长期不改的“冷”数据从年轻的块搬到老的块上,让年轻的块也参与写入,效果更好。磨损均衡由 SSD 的控制器完成,不是文件系统(2025-32)。
  • 和机械硬盘比:没有寻道和旋转延迟,随机读很快;写比读慢(可能要先擦除);对 SSD 做磁盘调度没有意义,FCFS 即可(2010-45 第 3 问)。
  • 拓展 写放大:垃圾回收要搬有效页,Flash 实际写入的数据比主机要求写的多。例(已用程序验算):一个块 64 页,回收时里面还有 48 个有效页,把它们搬走、擦除整块后只腾出 16 页给主机写 → 主机写 16 页,Flash 共写 48 + 16 = 64 页,写放大 = 64 ÷ 16 = 4。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式计组由 2013-21 改
某磁盘转速为 6000rpm,平均寻道时间为 4ms,数据传输率为 40MB/s,磁盘控制器延迟为 0.2ms。读取一个 4KB 扇区所需的平均时间约为( )。
  • A. 4.3ms
  • B. 9.3ms
  • C. 14.3ms
  • D. 9.1ms
展开解析

B平均旋转延迟 = (60 ÷ 6000) ÷ 2 = 5ms;传输 4KB ÷ 40MB/s ≈ 0.1ms。总时间 = 4 + 5 + 0.1 + 0.2 ≈ 9.3ms。

变化点:2013-21 是 10000rpm、6ms 寻道(9.4ms)。C 用了整圈旋转时间,A 漏了旋转延迟,D 漏了控制器延迟。

变式计组由 2015-20 改
某磁盘转速为 10000rpm,平均寻道时间为 4ms,每个磁道有 500 个扇区。访问一个扇区的平均存取时间约为( )。
  • A. 4.0ms
  • B. 7.0ms
  • C. 10.0ms
  • D. 7.5ms
展开解析

B转一圈 6ms,平均旋转延迟 3ms;读一个扇区 = 6ms ÷ 500 = 0.012ms。总时间 ≈ 4 + 3 + 0.012 ≈ 7.0ms。

变化点:2015-20 是 7200rpm、8ms、1000 个扇区(约 12.2ms)。扇区很多时传输时间很小,平均存取时间主要是寻道时间加半圈。

变式计组由 2013-20 改
用 4 块容量均为 2TB 的磁盘组成 RAID 5,可用容量和最多允许同时损坏而不丢数据的磁盘数分别是( )。
  • A. 8TB、0
  • B. 4TB、2
  • C. 6TB、1
  • D. 6TB、2
展开解析

CRAID 5 用相当于一块盘的空间存校验:可用 (4 − 1) × 2TB = 6TB;每个条带只有一个校验块,只能恢复 1 块坏盘。A 是 RAID 0;B 是 RAID 1 / RAID 10 的容量。

变化点:2013-20 问哪些措施提高可靠性(镜像、校验)。这里算 RAID 5 的容量和容错能力。考点:RAID。

变式计组由 2025-32 改
下列关于固态硬盘(SSD)的叙述中,正确的是( )。
  • A. 可以直接在原位置覆盖写某一页
  • B. Flash 的擦除以页为单位
  • C. 磨损均衡由操作系统的文件系统实现
  • D. 闪存翻译层(FTL)把逻辑块号映射到 Flash 的物理页
展开解析

DFTL 在 SSD 内部维护逻辑块号到物理页的映射。A 错:写前要先擦,改写只能写到新页(异地更新)。B 错:读写以页为单位,擦除以块为单位。C 错:磨损均衡由 SSD 的控制器完成(2025-32)。

变化点:2025-32 问文件系统能为两种硬盘做什么。这里考 SSD 内部自己做的事。考点:SSD。

10.2操作系统磁盘调度算法

★★★ 高频选择题综合题

寻道时间是大头,所以当有多个请求排队时,操作系统(设备驱动)按某种顺序服务,目标是让磁头少跑路。

算法规则特点
FCFS按请求到达顺序公平,不会“磁臂黏着”(2018-30);寻道长
SSTF 最短寻道优先每次选离磁头最近的平均寻道短;远处的请求可能饥饿
SCAN 扫描(电梯)沿一个方向服务到磁盘端点,再掉头无饥饿;两端的请求等得久
LOOK同 SCAN,但到最后一个请求就掉头SCAN 的改进
C-SCAN 循环扫描只沿一个方向服务,到端点后直接返回另一端重新扫描(返回途中不服务)各位置等待时间更均匀
C-LOOK同 C-SCAN,但到最后一个请求就返回,返回到另一侧最远的请求
例操作系统同一序列,六种算法
磁道号 0~199,磁头当前在 100 号磁道,正向磁道号增大的方向移动。请求序列 55、58、39、18、90、160、150、38、184。分别求 FCFS、SSTF、SCAN、LOOK、C-SCAN、C-LOOK 的服务顺序、磁头移动总距离和平均寻道长度。
展开解析
算法服务顺序移动总距离平均寻道长度
FCFS55 58 39 18 90 160 150 38 18449855.3
SSTF90 58 55 39 38 18 150 160 18424827.6
SCAN150 160 184 (199) 90 58 55 39 38 1828031.1
LOOK150 160 184 90 58 55 39 38 1825027.8
C-SCAN150 160 184 (199)(0) 18 38 39 55 58 9038843.1
C-LOOK150 160 184 18 38 39 55 58 9032235.8

括号里的 199、0 是到达端点(不是请求),距离要算上。平均寻道长度 = 移动总距离 ÷ 请求个数(这里 ÷ 9)。以上全部用程序验算;也可以把数据填进下面的工具逐个核对。

口径提醒:汤子瀛《计算机操作系统》里称为 SCAN、C-SCAN 的例题(平均 27.8、35.8)实际走的是“到最远的请求就掉头/返回”,即这里的 LOOK、C-LOOK。408 题目一般会说明“移动到磁盘端点”还是“到最远请求”;没说明时按题目给出的选项反推口径,C-SCAN 的返回距离是否计入也同理(2024-32 计入)。

2015-32操作系统
某硬盘有 200 个磁道(最外侧磁道号为 0),磁道访问请求序列为 130,42,180,15,199,当前磁头位于第 58 号磁道并从外侧向内侧移动。按照 SCAN 调度方法处理完上述请求后,磁头移过的磁道数是( )。
  • A. 208
  • B. 287
  • C. 325
  • D. 382
展开解析

考点10.2 磁盘调度算法

C“从外侧向内侧”= 磁道号增大。SCAN(电梯)沿当前方向走到头,再掉头处理另一边。

  1. 向增大方向:58 → 130 → 180 → 199(199 正好是最内侧磁道,也是请求):199 − 58 = 141。
  2. 掉头:199 → 42 → 15:199 − 15 = 184。
  3. 合计 141 + 184 = 325。
  • A 错208 比 325 还少。只要从 58 出发先向内走、又要服务 199 和 15,最少就要 (199 − 58) + (199 − 15) = 325,208 漏掉了一段移动。
  • B 错287 也小于 325 这个下限:路线必须先从 58 走到 199(141)、再掉头走到 15(184),少一道都服务不到全部请求。
  • C 对141 + 184 = 325。
  • D 错382 = 141 + 199 + 42,是 C-SCAN 的走法:到 199 后返回 0 号磁道(计返回距离 199)再向内处理 15、42。题目要求的是 SCAN,不做单向回零。

SCAN 走到端点再掉头;LOOK 只走到最远的请求;C-SCAN 单向,返回距离要不要算看题目。本题 199 正好是端点,SCAN = LOOK。同类:2009-29(105 向增大方向 SCAN:110, 170, 180, 195, 68, 45, 35, 12);2021-26(SSTF 从 184:184, 182, 187, 176, 199 → 0 + 2 + 5 + 11 + 23 = 41)。

2024-32操作系统
磁道数 400(0~399),采用 C-SCAN 调度,完成对 200 号磁道的请求后磁头向磁道号减小的方向移动,还有 7 个请求:300,120,110,0,160,210,399。完成上述请求后磁头移动的距离是( )。
  • A. 599
  • B. 619
  • C. 788
  • D. 799
展开解析

考点10.2 磁盘调度:C-SCAN

CC-SCAN(循环扫描)只朝一个方向服务:走到这一端后直接返回另一端,再继续同一方向。本书与 2010-45 的口径都把返回的距离计入。

  1. 向减小方向:200 → 160 → 120 → 110 → 0,移动 200。
  2. 返回另一端:0 → 399,移动 399。
  3. 继续向减小方向:399 → 300 → 210,移动 189(399 本身也是请求,返回时顺便完成)。
  4. 合计 200 + 399 + 189 = 788。
  • A 错599 = 200 + 399,是 SCAN 的走法:到 0 后掉头向增大方向,顺路服务 210、300、399。C-SCAN 不在回程服务,回到 399 后还要向下走到 210(再加 189)。
  • B 错619 少于 C-SCAN 的必经路程:向下到 0(200)、返回到 399(399)、再向下到 210(189),三段缺一段都服务不到全部请求。
  • C 对200 + 399 + 189 = 788。
  • D 错799 比 788 多了 11:最后一个请求是 210,磁头走到 210 就完成了,不用继续向下多走。

C-SCAN 的返回要不要计入看题目口径,408 真题(2010-45、2024-32)都计入。2010-45:从 100 向增大方向 C-SCAN,请求 50、90、30、120 → 100 → 120 → 30 → 50 → 90,移动 20 + 90 + 20 + 40 = 170。

2019-44操作系统融合综合题 7 分
磁盘有 300 个柱面,每柱面 10 个磁道,每磁道 200 个扇区,扇区 512B;文件系统每个簇包含 2 个扇区。(1)磁盘容量?(2)磁头在 85 号柱面,有 4 个访问请求,簇号分别为 100260、60005、101660、110560,采用 SSTF,访问簇的先后次序?(3)第 100530 簇在磁盘上的物理地址是什么?将簇号转换成物理地址的过程是由 I/O 系统的什么程序完成的?
展开解析
  1. 300 × 10 × 200 × 512B = 3×105 KB(≈ 293MB)。
  2. 每磁道 200/2 = 100 簇,每柱面 1000 簇 → 簇号 ÷ 1000 = 柱面号:100260 → 100,60005 → 60,101660 → 101,110560 → 110。从 85 出发 SSTF:100(距 15)→ 101(1)→ 110(9)→ 60(50)→ 次序 100260、101660、110560、60005。
  3. 100530:柱面 = 100530 ÷ 1000 = 100,余 530;磁道(磁头)= 530 ÷ 100 = 5,余 30;扇区 = 30 × 2 = 60。由磁盘驱动程序完成。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2021-26 改
磁盘有 200 个磁道(0 ~ 199),磁头当前在 100 号磁道。磁盘请求的磁道号依次为 90、115、60、130、70。采用 SSTF 调度,磁头移动的磁道数是( )。
  • A. 100
  • B. 110
  • C. 120
  • D. 130
展开解析

B从 100 出发,每次去最近的:90(10)→ 70(20)→ 60(10)→ 115(55)→ 130(15),共 110。

变化点:SSTF 不管方向。从 90 出发时,70(距离 20)比 115(距离 25)近,所以先去 70,最后再掉头。

变式操作系统由 2015-32、2009-29 改
磁盘有 200 个磁道(0 ~ 199),磁头当前在 100 号磁道,正向磁道号减小的方向移动。请求的磁道号为 150、30、180、90、60。按 SCAN 算法(到达磁盘端点才掉头),处理完这些请求后磁头移过的磁道数是( )。
  • A. 220
  • B. 250
  • C. 280
  • D. 330
展开解析

C先向减小方向:90、60、30,一直走到 0 号磁道才掉头,再去 150、180。移动 = (100 − 0) + (180 − 0) = 280。

变化点:按 LOOK(到最远的请求就掉头)是 (100 − 30) + (180 − 30) = 220(A)。题目写明“到达端点才掉头”,就要一直走到 0。

变式操作系统由 2024-32 改
磁盘有 400 个磁道(0 ~ 399),采用 C-SCAN 调度。刚完成 200 号磁道的请求,磁头向磁道号增大的方向移动,还有请求 300、120、110、0、160、210、399。完成这些请求后,磁头移动的磁道数是( )。
  • A. 359
  • B. 558
  • C. 758
  • D. 798
展开解析

C向增大方向:210、300、399,然后返回 0 号磁道(399 个磁道,计入),再向增大方向:0、110、120、160。共 (399 − 200) + 399 + 160 = 758。

变化点:2024-32 是向减小方向(788)。408 真题的口径是把返回的距离计入;不计入则是 A(359)。

10.3磁盘的初始化与引导

★★ 常考选择题综合题
  1. 物理格式化(低级格式化):把磁道划分成扇区,每个扇区写上头部(地址)、数据区、尾部(校验码),标记坏扇区。一般出厂时完成。
  2. 分区:把磁盘分成一个或多个分区(C 盘、D 盘),分区表放在第一个扇区的主引导记录 MBR 里。
  3. 逻辑格式化(高级格式化):在分区上建立文件系统——写入根目录、初始化空闲空间管理的数据结构(位示图/空闲链等)、确定盘块(簇)大小(2017-29 选“建立根目录、初始化空闲块结构”;2025-32 文件系统能为机械硬盘和 SSD 提供的是“确定盘块大小”)。
  4. 安装操作系统。

2021-46:制作启动盘的顺序是物理格式化 → 分区 → 逻辑格式化 → 安装操作系统;扇区的划分在物理格式化中完成,根目录的建立在逻辑格式化中完成;开机时执行顺序是 ROM 中的引导程序 → 磁盘引导程序(MBR)→ 分区引导程序 → 操作系统初始化程序(第 1 篇图 1-19)。

扇区、块、簇,以及坏块怎么处理

问题:题目里一会儿说扇区,一会儿说块、簇,它们是什么关系?磁盘用久了有扇区坏掉,数据会不会写进坏扇区?

大白话:扇区是磁盘这张“纸”上最小的格子,磁盘一次至少读写一个格子;块(Windows 里叫簇)是文件系统分配空间的单位,由几个相邻的扇区组成,像“按整行发纸”。坏了的格子要么由文件系统记下来不再用,要么由磁盘自己偷偷换成备用格子。

  • 扇区:磁盘的物理读写单位(常见 512B 或 4KB),物理格式化时划分(2019-20)。
  • 块 / 簇:文件系统分配空间的单位,= 2k 个连续扇区,逻辑格式化时确定(2025-32)。文件最后一块没用满也整块占用(内部碎片,2017-26)。
一个磁道(展开画) 扇区 0 扇区 1 扇区 2 扇区 3 ✗ 扇区 4 扇区 5 扇区 6 扇区 7 备用 S0 备用 S1 簇 0 = 扇区 0~3 簇 1 = 扇区 4~7 控制器重新映射:访问扇区 3 → 实际读写 S0 控制器的坏块表扇区 3 → 备用 S0 上层(文件系统)仍按“扇区 3”访问,完全感觉不到坏块; 坏块表在低级格式化时建立,使用中发现新的坏块再加进去
图 10-4 扇区备用(sector sparing)。每个磁道(或每个区)预留几个备用扇区,坏了的扇区由磁盘控制器换成备用扇区,对操作系统透明。
坏块处理方式谁来做怎么做
文件系统标记操作系统(简单磁盘,如早期 IDE)逻辑格式化时扫描全盘,把坏块在 FAT 或坏块表中标记出来,以后不再分配;对操作系统不透明
扇区备用磁盘控制器(复杂磁盘,如 SCSI、现代硬盘)控制器维护坏块链表,低级格式化时初始化,用备用扇区替换坏扇区;对操作系统透明

跟我做一遍(已用程序验算):扇区 512B、簇 4KB → 一簇 = 8 个扇区。一个 10000B 的文件要 ⌈10000 ÷ 4096⌉ = 3 个簇 = 24 个扇区,实际占 12288B,最后一簇浪费 2288B。

引导块:计算机开机后,ROM 里只有一个很小的自举程序,它把磁盘引导块(第一个扇区 MBR 中的引导程序)读进内存执行,再由它找到活动分区的分区引导记录,最终装入操作系统内核(第 1 篇图 1-19)。存有引导块和操作系统的磁盘叫启动盘(系统盘)。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2017-29、2025-32 改
下列工作中,属于磁盘物理格式化(低级格式化)的是( )。
  • A. 建立文件系统的根目录
  • B. 把磁道划分成扇区,确定每个扇区的头部、数据区和校验码
  • C. 初始化空闲块管理的数据结构
  • D. 建立分区表
展开解析

B物理格式化划分扇区,写入扇区头、尾(含校验码)。分区之后,逻辑格式化建立文件系统:根目录、空闲空间管理结构(2017-29)。分区表在分区时建立。

变化点:2017-29 问逻辑格式化做什么(建根目录、初始化空闲块结构),这里问物理格式化做什么。

变式操作系统考纲补充(尚无真题):坏块处理
对于较复杂的磁盘(如 SCSI 磁盘),处理坏扇区的常用方法是( )。
  • A. 由文件系统在 FAT 中把坏簇标记出来
  • B. 由用户程序自行跳过坏扇区
  • C. 每次访问前重新进行低级格式化
  • D. 由磁盘控制器用备用扇区替换坏扇区,对操作系统透明
展开解析

D复杂磁盘的控制器维护坏块链表,把坏扇区重新映射到备用扇区(扇区备用),操作系统感觉不到。A 是简单磁盘的做法,由操作系统处理;B、C 显然不对。

考点:坏块处理。

10.4操作系统文件、FCB 与 inode

★★★ 高频选择题

操作系统为每个文件建立一个文件控制块 FCB,存文件的名字、大小、类型、创建时间、存取权限(2009-30)、数据在磁盘上的位置等。一个目录就是一个特殊的文件,内容是很多条“目录项”。

为了让查目录更快,UNIX 把 FCB 拆成两部分:目录项里只放文件名 + inode 号(很短,一个盘块能放很多项),其余的描述信息都放在 inode(索引节点)里。

目录文件 /home 文件名  inode号 a.txt   100 b.txt   100 c.lnk   205 inode 100 大小、类型、权限、时间链接计数 = 2数据块地址:直接 / 间接…(没有文件名!) 数据块文件内容 inode 205(软链接文件)内容:“/home/a.txt” 按路径名再去找 硬链接:a.txt 和 b.txt指向同一个 inode
图 10-5 目录项、inode、硬链接与软链接。文件的访问权限等信息在 inode(FCB)里,不在目录项里(2025-29)。目录项里的 inode 号占 4 字节 → 最多 232 个 inode → 最多 232 个文件(2020-31)。
硬链接
新建一个目录项,指向同一个 inode,inode 的链接计数 +1。删除时计数 −1,减到 0 才真正删除文件。
软链接(符号链接、快捷方式)
新建一个独立的文件(有自己的 inode),内容是目标的路径名。目标被删了,软链接就“断了”。删除文件时内核不必去删它的快捷方式(2021-30)。
2009-31操作系统
设文件 F1 的当前引用计数值为 1,先建立 F1 的符号链接(软链接)文件 F2,再建立 F1 的硬链接文件 F3,然后删除 F1。此时,F2 和 F3 的引用计数值分别是( )。
  • A. 0、1
  • B. 1、1
  • C. 1、2
  • D. 2、1
展开解析

考点10.4 硬链接与软链接

B软链接是一个新文件(有自己的 inode,内容是 F1 的路径);硬链接是一个新目录项,和 F1 共享同一个 inode。

  1. 建 F2(软链接):F2 自己的计数为 1;F1 的计数不变,仍为 1。
  2. 建 F3(硬链接):F1、F3 共享的 inode 计数变为 2。
  3. 删除 F1:共享 inode 的计数减 1 → 1。F2 不受影响,仍为 1。
  • A 错删除 F1 不会把软链接 F2 的计数变成 0(F2 只是“断了”,指向的路径不存在了)。
  • B 对1、1。
  • C 错忘了删除 F1 时共享 inode 的计数要减 1。
  • D 错把建软链接也算成了 F1 计数加 1。

只有硬链接会增加 inode 的链接计数;删除一个名字就减 1,减到 0 才真正删除文件。

文件属性:FCB 里记了什么

FCB(inode)里记的就是文件的属性。不同系统各有增减,教材列出的常见属性如下:

属性含义
名称给人看的文件名(inode 方式下放在目录项里,不在 inode 里)
标识符系统内部唯一的编号(如 inode 号),人看不懂
类型普通文件、目录文件、设备文件……
位置文件存在哪个设备上、占了哪些盘块(10.6)
大小当前大小(字节数或块数),也可以有允许的最大值
保护访问控制信息:谁能读、写、执行(10.8)
时间、日期、用户标识创建、最后修改、最后访问的时间和用户,用于保护和监控

文件的基本操作:每个操作 OS 做了什么

操作操作系统做的事
创建 create① 为文件在外存上分配空间(需要时);② 在目录中为它建一个新的目录项(FCB/inode),记下文件名、位置等
删除 delete按文件名在目录中找到目录项;回收文件占的磁盘块(和 inode,若链接计数减到 0);删除目录项。不删除它所在的目录(2013-23)
打开 open按文件名逐级查目录找到 FCB/inode(按名查找只在 open 时做,2024-29),检查权限,把 FCB/inode 读入内存,在系统打开文件表、进程打开文件表中建表项,返回文件描述符 fd。不读文件的数据(2014-29)
关闭 close删除进程打开文件表中的表项;系统打开文件表项的打开计数减 1,减到 0 时删除该项(修改过的 FCB 写回磁盘,2023-31)
读 read / 写 write用 fd(不用文件名,2012-28)找到表项,按读写指针算出逻辑块号,查 FCB/inode 转换成物理块号,读写后移动读写指针
定位 seek只修改读写指针

有了 open,后面每次读写就不用再“按名查目录”——目录查找很慢(要逐级读盘),只做一次。系统打开文件表里一个文件只有一项(多个进程共享,记打开计数),进程打开文件表里各进程有自己的表项(记读写指针、打开方式等)。

文件的逻辑结构:用户眼里文件长什么样

“物理结构”(10.6 节)说的是文件的块在磁盘上怎么放;“逻辑结构”说的是用户把文件看成什么:

逻辑结构样子查找第 i 条 / 按关键字查找
无结构(流式)文件一串字节(源程序、图片、可执行文件),现代系统的普通文件都是这种按字节偏移定位
顺序文件记录一条接一条;可以按关键字排好序定长记录可以直接算出第 i 条的位置;按关键字只能顺序找(排好序可以折半)
索引文件另建一张索引表:每条记录一个表项(关键字、长度、地址),适合变长记录查索引表就能直接定位,但索引表本身可能很大
索引顺序文件记录分组,每组在索引表里只占一项(组内按顺序)先查索引找到组,再在组内顺序找;N 条记录分 √N 组时平均查找约 √N 次
直接文件(散列文件)由关键字经散列函数算出存放地址最快,但有冲突

按有没有结构分两大类:无结构文件(流式文件,一串字节)和有结构文件(记录式文件,由一条条记录组成,每条记录有关键字)。顺序、索引、索引顺序、散列都是记录式文件的组织方式。

顺序文件 03 | 张 07 | 李 12 | 王 18 | 赵 25 | 钱 按关键字一条接一条找一条:从头顺序比较 索引文件 索引表 03 长 2 →07 长 4 →12 长 1 →… 每条记录一个索引项,适合变长记录索引表可能和文件一样长 索引顺序文件 索引表 03 →25 →52 → 03 07 12 18 25 31 40 47 52 58 66 70 记录分组,每组只占一个索引项先查索引定组,再在组内顺序找 散列(直接)文件:关键字经散列函数直接算出存放地址,不需要索引,但可能冲突
图 10-6 三种记录式文件。索引文件“每条一项”,索引顺序文件“每组一项”,所以索引顺序文件的索引表小得多。

跟我做一遍:平均查找次数(已用程序验算,按“查找成功时平均比较一半”计算):

文件有多少条记录顺序文件一级索引顺序文件二级索引顺序文件
N = 1000010000 ÷ 2 = 5000分 100 组,每组 100 条:查索引 50 + 组内 50 = 100—
N = 106500000分 1000 组:500 + 500 = 1000100 × 100 × 100:50 + 50 + 50 = 150

规律:N 条记录分成 √N 组时,一级索引顺序文件平均约查 √N 次;记录再多,就给索引表再建一级索引。

打开文件时内存里建了什么

进程 A 的打开文件表 fd 0:键盘 fd 1:显示器 fd 3:读写指针 = 100   只读 → 系统表项 进程 B 的打开文件表 fd 5:读写指针 = 0   读写 → 系统表项 系统打开文件表文件 F 只有这一项:F 的 FCB / inode 副本打开计数 = 2(还有修改标志等) 磁盘上的FCB / inode open 读入关闭到 0 写回 open:按名查目录 → FCB 读入内存 → 建表项 → 返回 fd;read / write 只用 fd;close:计数减 1,减到 0 就释放系统表项
图 10-7 打开文件的两级表(教材的说法)。首次打开文件时,操作系统把 FCB/inode 读入内存(不是把文件内容读入,2014-29);按名查找是 open 做的(2024-29);关闭时计数减到 0 才释放内存中的 inode(2023-31)。多个进程打开同一个文件:系统打开文件表里只有一项包含 F 的属性,各进程表项(读写指针、打开方式)不一定相同(2020-23 选 B)。2017-31:f1 和它的硬链接 f2 被两个进程分别打开:共享同一个内存 inode,fd1、fd2 各指向自己的打开文件表项,读写指针不必相同。
教材(王道 / 汤小丹)的两级表UNIX / Linux 的实际实现(三级)
每个进程一张进程打开文件表:读写指针、访问权限(打开方式)、指向系统表项的指针;fd 就是它的下标文件描述符表:fd → 指向一个“文件表项”
中间层—文件表(file 表):读写偏移、打开方式、引用计数;每次 open 新建一项
全系统一份系统打开文件表:每个打开的文件一项,含 FCB 副本、打开计数内存 inode 表:每个打开的文件一项

两种说法只是分层不同:教材把“读写指针”放在进程打开文件表里,UNIX 把它放在中间的文件表里。做题按教材的两级说法即可:读写指针各进程各一份,FCB(inode)全系统一份。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2009-31 改
文件 F1 的当前引用计数为 1。先建立 F1 的硬链接 F2,再建立 F1 的符号链接(软链接)F3,然后删除 F1。此时 F2 和 F3 的引用计数分别是多少?通过 F3 还能否访问原文件内容?( )
  • A. 1、1;不能访问
  • B. 2、1;能访问
  • C. 1、1;能访问
  • D. 1、0;不能访问
展开解析

AF2 与 F1 共用一个 inode,建硬链接后计数为 2,删除 F1 后减为 1。F3 有自己的 inode,计数为 1。F3 里存的是路径“F1”,F1 删除后路径失效,不能访问。

变化点:2009-31 先建软链接、后建硬链接,计数结果相同(1、1)。这里多问一句能否访问:硬链接能,软链接不能。

变式操作系统由 2014-29、2023-31 改
进程首次打开一个文件时,操作系统要做的是( )。
Ⅰ. 按文件名查找目录 Ⅱ. 把文件的 FCB(inode)读入内存
Ⅲ. 把文件的全部内容读入内存 Ⅳ. 在打开文件表中建立表项,返回文件描述符
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅱ、Ⅳ
  • C. 仅Ⅰ、Ⅱ、Ⅳ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

Copen 只沿路径查目录,找到 FCB(inode)读入内存,在系统打开文件表和进程打开文件表中登记,返回文件描述符。文件的数据要等到 read 时才读。

变化点:2014-29 是单选(把 FCB 读到内存)。这里把 open 的完整步骤列出来,干扰项是“读入全部内容”。

变式操作系统由 2017-30 改
某文件系统采用 UNIX 的做法,把用户分为文件主、同组用户、其他用户 3 类,访问权限分为读、写、执行 3 种。若在 FCB 中用二进制位串表示各类用户对文件的访问权限,至少需要( )位。
  • A. 3
  • B. 6
  • C. 9
  • D. 27
展开解析

C每类用户对每种权限都要 1 位表示“有/无”,共 3 × 3 = 9 位,也就是 UNIX 的 rwxrwxrwx。

变化点:2017-30 是 4 类用户 × 5 种权限 = 20 位。D 是把 3 种权限的所有组合数当成了位数。

变式操作系统考纲补充(尚无真题):索引顺序文件
某记录式文件有 10000 条记录,按关键字排序。若组织成顺序文件,查找一条记录平均要比较约 5000 次;若组织成索引顺序文件,分成 100 组、每组 100 条,平均要比较约( )次。
  • A. 50
  • B. 100
  • C. 5000
  • D. 10000
展开解析

B先在 100 项的索引表中找到组,平均 50 次;再在 100 条的组内顺序找,平均 50 次,共约 100 次(已用程序验算)。A 只算了一步。

考点:文件的逻辑结构。

10.5目录结构

★★ 常考选择题综合题
/ bin home alice bob a.c x.c y.lnk 绝对路径(从根开始):/home/alice/a.c 相对路径(当前目录是 alice 时):a.c x.c 是硬链接:和 a.c 同一个 inode y.lnk 是软链接:内容是 “/home/alice/a.c”
图 10-8 树形目录与两种链接。设置“当前目录”后可以用相对路径,查找时不用每次从根一级一级找,加快检索(2010-31)。有了链接,目录结构就从“树”变成了“无环图”:a.c 和 x.c 是同一个文件的两个名字(inode 链接计数为 2,删掉其中一个名字文件还在);y.lnk 只记了一个路径,a.c 被删掉后它就失效了。

四种目录结构:单级、两级、树形、无环图

问题:目录要解决三件事:按名字快速找到文件、允许不同用户起同样的名字、允许多人共享同一个文件。不同的目录结构各解决了几件?

大白话:单级目录像全校一张花名册,重名就乱套;两级目录像每个班一张花名册(主目录记有哪些班),不同班可以重名,但班里不能再分组;树形目录像“年级 → 班 → 小组”一层层分下去;无环图目录再允许一个人同时出现在两个小组的名单上(共享)。

单级目录 全系统一张目录表 a.c (用户 A)b.c (用户 A)test (用户 B)test (用户 C)✗ 重名 不能重名,文件多了查找慢 两级目录 主文件目录 MFD 用户 A →用户 B →用户 C → UFD(A):a.c b.c UFD(B):test UFD(C):test ✓ 不同用户可以重名;但用户自己的文件不能再分类,用户之间也不便共享
图 10-9 单级目录与两级目录。两级目录查找时先在 MFD 中找到用户,再在该用户的 UFD 中找文件。
结构允许重名文件分类共享查找
单级✗✗—文件多时慢
两级不同用户间 ✓✗(每个用户只有一层)不便比单级快
树形不同目录间 ✓✓不便(只能用路径访问别人的文件)用当前目录 + 相对路径,查找更快
无环图✓✓✓(硬链接、软链接,见本节开头的树形目录图)同树形;删除要看链接计数

跟我做一遍(每个目录只占一个盘块、目录项就是 FCB、内存里只有根目录的位置):打开 /home/alice/a.c 要依次读根目录、home 目录、alice 目录 3 个盘块,才能在 alice 目录里找到 a.c 的 FCB;若当前目录是 /home/alice(内存里记着它的位置),用相对路径 a.c 只要读 alice 目录这 1 个盘块——这就是设置当前目录能加快检索的原因(2010-31)。

  • 单级目录:所有文件一个目录,不能重名。两级目录:每个用户一个目录。
  • 树形目录:现在都用它;绝对路径从根开始,相对路径从当前目录开始——设置当前工作目录是为了加快检索速度(2010-31,不用每次从根一级一级查)。
  • 无环图目录:允许多个目录项指向同一文件(共享),用链接计数决定何时真正删除。
  • 目录的实现(目录项在目录文件里怎么组织):线性列表——目录项一条接一条存放,查找时逐个比较文件名,实现简单但查找慢,创建文件前也要先查一遍有没有重名;哈希表——按文件名算出哈希值,直接定位目录项,查找快,但要处理冲突,表的大小也不好改。
  • 删除一个文件:删它在目录中的目录项、释放 FCB/inode、释放它占的磁盘块和内存缓冲区;不会删除文件所在的目录(2013-23)。
例操作系统为什么要把 FCB 拆成目录项 + inode
某目录文件中有 256 个目录项,磁盘块 1KB。(1)若目录项就是完整的 FCB,每个 64B,按文件名查找一个文件平均要读几个盘块?(2)若改用 inode 方式,目录项只含文件名 14B 和 inode 号 2B,其余信息放在 inode 中,查找一个文件并得到它的 FCB 信息,平均要读几个盘块?
展开解析
  1. (1)目录占 256 × 64B / 1KB = 16 个盘块,要找的目录项可能在第 1~16 块中任何一块,平均读 (1 + 16)/2 = 8.5 块。
  2. (2)目录项 16B,目录只占 256 × 16B / 1KB = 4 块,平均读 (1 + 4)/2 = 2.5 块找到目录项,再读 1 块得到 inode → 平均 3.5 块。

这就是 UNIX 用 inode 的原因:目录项越短,一个盘块能放的目录项越多,查目录时读盘越少。(按“查找成功时平均要比较一半”的口径计算。)

2022-45操作系统综合题 7 分
磁盘块 4KB,目录项由文件名和索引节点号构成,每个索引节点 256 字节,含直接地址项 10 个,一级、二级、三级间接地址项各 1 个,每个地址项 4 字节。stu 目录包含子目录 course 和文件 doc,course 包含文件 course1 和 course2。各文件的索引节点号和占用磁盘块:stu:1;course:2;course1:10,磁盘块 30;course2:100,磁盘块 …;doc:10,磁盘块 x。(1)目录文件 stu 中每个目录项的内容?(2)doc 占用的磁盘块号 x?(3)若目录文件 course 的内容已在内存,打开 course1 并读入内存需要读几个磁盘块?(4)若 course2 增长到 6MB,存取 course2 需要用到索引节点的哪几级间接地址项?
展开解析
  1. stu 的目录项:(course,2)、(doc,10)。
  2. doc 和 course1 的 inode 号都是 10 → 它们是同一个文件(硬链接),数据块相同 → x = 30。
  3. 目录项已在内存,知道 course1 的 inode 号 → 读 inode 所在的块(1 次)→ 从 inode 得知数据在块 30 → 读数据块(1 次),共 2 个。
  4. 6MB / 4KB = 1536 块。一块能放 4KB/4B = 1024 个地址:直接 10 块 + 一级 1024 块 = 1034 < 1536 → 还要二级 → 需要一级和二级间接地址项。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2020-31 改
某文件系统的目录项由文件名和索引结点号组成,每个目录项 32B,其中索引结点号占 2B。该文件系统能创建的文件数量上限为( )。
  • A. 216
  • B. 230
  • C. 232
  • D. 2256
展开解析

A每个文件都有一个索引结点,索引结点号只有 16 位,最多有 216 个不同的索引结点,所以最多 216 个文件。文件名的长度与文件数的上限无关。

变化点:2020-31 的索引结点号占 4B(232)。只看索引结点号的位数,不要被文件名的长度干扰。

变式操作系统由 2013-23、2021-30 改
删除一个文件时,操作系统需要做的是( )。
Ⅰ. 删除该文件在目录中的目录项 Ⅱ. 若链接计数减为 0,回收它的索引结点和磁盘块 Ⅲ. 删除该文件所在的目录
  • A. 仅Ⅰ
  • B. 仅Ⅰ、Ⅱ
  • C. 仅Ⅱ、Ⅲ
  • D. Ⅰ、Ⅱ、Ⅲ
展开解析

B删除文件要删它的目录项,并把链接计数减 1;减到 0 时才回收索引结点和磁盘块(还有硬链接时不能回收)。文件所在的目录不会被删除(2013-23)。

变化点:2013-23 问不可能执行的操作(删除所在目录)。这里加上了硬链接的情况:回收是有条件的。

变式操作系统由 2010-31 改
在两级目录结构中,不同用户的文件可以重名,这是因为( )。
  • A. 每个文件名的长度可以不同
  • B. 系统为每个用户设置了当前目录
  • C. 每个用户有自己的用户文件目录(UFD)
  • D. 系统允许建立硬链接
展开解析

C两级目录中,主文件目录 MFD 为每个用户指向一个用户文件目录 UFD,同名文件分别在不同的 UFD 里,互不冲突。当前目录是树形目录用来加快检索的(2010-31);硬链接用于共享。

变化点:2010-31 问树形目录中设置当前目录的作用。这里问两级目录解决了什么问题。考点:四种目录结构。

10.6文件的物理结构:文件的块放在磁盘哪里

★★★ 高频选择题综合题
连续分配 567 FCB 记:起始块号 5,长度 3 ✓ 顺序、随机访问都快✗ 不易扩展,有外部碎片 链接分配(隐式) 9→16 16→1 1→−1 每块末尾存下一块的块号;FCB 记首块、末块 ✓ 易扩展,无外部碎片✗ 只能顺序访问;指针占空间;一块坏全断 索引分配 索引块 199161 9161 ✓ 随机访问 + 易扩展✗ 索引块占空间 FAT(显式链接):把所有“下一块号”集中成一张表,开机调入内存 块号:01…9…16… FAT:…−1…16…1… 文件从块 9 开始:FAT[9]=16 → FAT[16]=1 → FAT[1]=−1(结束) 查链在内存里完成,所以支持随机访问;FAT 还能标记空闲块
图 10-10 三种分配方式。选择题口诀:要随机访问又要易扩展 → 索引分配(2009-28、2020-24);只读、一次写入不修改、要快速随机播放 → 连续分配(2013-24 CD-ROM 视频、2011-46);FAT 可以用来管理空闲块(2019-26、2025-31)。
2016-47操作系统综合题
磁盘文件系统使用链接分配,簇 4KB,目录项包括文件名和文件的第一个簇号,其他簇号存放在 FAT 中。目录树:dir(簇 1)下有 dir1(簇 48),dir1 下有 file1(簇 100、106、108)、file2(簇 200、201、202)。(1)写出所有目录文件的内容。(2)若 FAT 每个表项仅存放簇号,占 2 字节,则 FAT 最大长度是多少?文件最大长度?(3)file1 的 106、108 两个簇号分别存放在 FAT 的哪个表项中?(4)若仅 FAT 和 dir 已在内存,要将 dir/dir1/file1 的第 5000 个字节读入内存,要访问哪几个簇?
展开解析
  1. dir:(dir1,48);dir1:(file1,100)、(file2,200)。
  2. 簇号 16 位 → 最多 216 个表项 → FAT 最大 216×2B = 128KB;文件最大 216×4KB = 256MB。
  3. FAT 第 i 项存“簇 i 的下一个簇号”:106 存在 FAT[100],108 存在 FAT[106]。
  4. 从内存中的 dir 得知 dir1 在簇 48 → 读簇 48 得到 file1 首簇 100;第 5000 字节在第 2 簇(4096 ≤ 5000 < 8192),查内存中的 FAT[100] = 106 → 读簇 106。共访问 48、106 两个簇。
连续分配隐式链接显式链接(FAT)索引分配
目录项/FCB 里记起始块号、长度首块号、末块号首块号索引块号(或 inode 中的地址项)
访问方式顺序、随机都快只能顺序顺序、随机(查 FAT)顺序、随机
访问第 i 块读盘次数
(FCB 已在内存,块号从 0 起)
1i + 1(要沿指针从第 0 块读起)1(FAT 常驻内存,查表不读盘)2(索引块 + 数据块;索引块已在内存则 1)
碎片外部碎片无外部碎片无外部碎片无外部碎片
文件增长困难(后面可能被占)方便方便方便
额外开销无每块要存指针FAT 表要占内存索引块(小文件也要一整块)
缺点要预先知道文件长度一个指针坏了,后面全丢FAT 损坏影响全盘大文件索引块不够,要多级/混合索引

一个索引块不够用:链接方案、多层索引、混合索引

问题:索引分配时,一个索引块只能放 N = 块大小 ÷ 地址项大小 个块号。块 1KB、地址项 4B 时 N = 256,文件最多 256 块 = 256KB。更大的文件怎么办?

大白话:一本通讯录写满了,有三种办法:① 在最后一行写“续见下一本”(链接方案);② 做一本“目录的目录”,记各本通讯录放在哪(多层索引);③ 常用的几个号码直接写在封面上,不够再用续本、目录的目录(混合索引)。

链接方案 索引块 1255 个块号下一索引块 索引块 2255 个块号下一索引块 索引块 3255 个块号−1(结束) 文件多大都能装下找后面的块要沿链一块块读索引块 两层索引(多层索引) 第一层256 项,各指向一个二层索引块 二层索引块 0 二层索引块 1 … 二层索引块 255 256 个数据块 256 个数据块 256 个数据块 最多 256 × 256 = 65536 块;读任何一块都是 2 次索引 + 1 次数据
图 10-11 链接方案与两层索引(块 1KB、地址项 4B)。链接方案每个索引块留一项指向下一个索引块,所以只放 255 个块号。

跟我做一遍(块 1KB、地址项 4B,FCB 已在内存,已用程序验算):

  1. 链接方案:一个 1000 块的文件要 ⌈1000 ÷ 255⌉ = 4 个索引块。读第 800 块(从 0 编号):800 ÷ 255 = 3 余 35,在第 4 个索引块里,要沿链读 4 个索引块,再读 1 个数据块,共 5 次。
  2. 两层索引:最多 256 × 256 = 65536 块 = 64MB;读任何一块都是 3 次(第一层、第二层索引块各 1 次,数据块 1 次)。
  3. 混合索引:把少量直接地址、一个一级间接(单层索引)、一个二级间接(两层索引)……组合在一个 inode 里,小文件快、大文件也装得下(下面详讲)。

说法统一:“二级间接地址”也叫“二次间接地址”,“三级间接”也叫“三次间接”;“一级间接”也叫“一次间接”或“单级间接”。

混合索引(UNIX inode):最常考的计算

每个索引块能放的地址数 N = 块大小 ÷ 地址项大小 最大文件长度 = (直接地址数 + N + N² + N³) × 块大小(有几级间接就加到第几次方;某级有多个地址项就乘个数) 逻辑块号 i:i < 直接数 → 直接;再往后 N 块 → 一级间接;再往后 N² 块 → 二级间接…… 读盘次数 = 间接级数 + 1(inode 已在内存)
inode 直接地址 0 …(共 d 个) 一级间接地址 二级间接地址 三级间接地址 数据块 索引块N 个地址 N 个数据块 一级索引块N 个 N 个二级索引块 N² 个数据块 N = 盘块大小 / 地址项大小 直接:d 块一级:N 块二级:N² 块三级:N³ 块 最大文件 = (d + N + N² + N³) × 块大小 (inode 已在内存时)读一个数据块要读盘:直接 1 次,一级 2 次,二级 3 次
图 10-12 混合索引。小文件用直接地址访问快,大文件用间接地址也能装下。单个文件的最大长度和“索引节点总数”无关(2013-26)。
2015-29操作系统
文件的索引节点中存放直接索引指针 10 个,一级和二级索引指针各 1 个。磁盘块大小为 1KB,每个索引指针占 4 个字节。若某文件的索引节点已在内存中,则把该文件偏移量(按字节编址)为 1234 和 307400 处所在的磁盘块读入内存,需访问的磁盘块个数分别是( )。
  • A. 1、2
  • B. 1、3
  • C. 2、3
  • D. 2、4
展开解析

考点10.6 混合索引的计算

B先算一个索引块能放几个指针,确定直接、一级、二级各覆盖哪些逻辑块,再数要读几个盘块(索引节点已在内存,不算)。

  1. 每块指针数 N = 1KB ÷ 4B = 256。直接:逻辑块 0 ~ 9;一级:10 ~ 265;二级:266 起。
  2. 偏移 1234:逻辑块 1234 ÷ 1024 = 1(取整),在直接范围,直接读数据块 → 1 次。
  3. 偏移 307400:逻辑块 307400 ÷ 1024 = 300(取整),超过 265 → 二级:读二级索引块、一级索引块、数据块 → 3 次。
  • A 错第二个算成 2 次,是把逻辑块 300 当成在一级索引范围里;但一级只覆盖 10 ~ 10 + 256 − 1 = 265,300 必须走二级,要多读一个索引块。
  • B 对1 次、3 次。
  • C 错第一个算成 2 次,是把已在内存的索引节点也算成一次读盘;题目说索引节点已在内存,逻辑块 1 用直接指针,只读数据块。
  • D 错两个都多 1,同样是把已在内存的索引节点算成了一次读盘。

逻辑块号 = 偏移 ÷ 块大小(取整);读盘次数 = 用到的索引块层数 + 1(数据块),已在内存的 inode 不算。同类:2010-30(4 直接、2 一级、1 二级,块 256B、指针 4B → N = 64,最大 (4 + 2×64 + 64²) × 256B = 1057KB);2018-46 第 3 问(簇 4KB、8 个直接:6KB 的 F1 只用直接,40KB 的 F2 最后一簇要一级间接);2026-46(偏移 21460 在逻辑块 5,只有 5 个直接地址 → 读间接块 + 数据块 = 2 块)。

2018-46(1)(2)操作系统综合题
簇 4KB,每个索引节点 64B,有 11 个地址项:直接 8 个,一级、二级、三级间接各 1 个,每个地址项 4B。(1)最大文件长度?(2)文件系统用 1M 个簇存放索引节点,512M 个簇存放文件数据。若图像文件大小为 5600B,最多能存放多少个这样的文件?
展开解析

(1)N = 4KB/4B = 1024;最大 = (8 + 1024 + 10242 + 10243) × 4KB ≈ 4TB。(2)索引节点总数 = 1M × 4KB / 64B = 64M 个;5600B 占 2 个簇,数据区能放 512M/2 = 256M 个;但每个文件都要一个 inode,所以受限于 inode 数 → 最多 64M 个。

连续与链接的访盘次数:2014-46(200 条记录,每块一条,插入为第 30 条:连续分配最少把前 29 条各前移一块,读写 29×2 + 写新记录 1 = 59 次,FCB 的起始块号和长度改变;链接分配读前 29 块 + 写新块 1 + 改写第 29 块 1 = 31 次;块 1KB、指针 4B → 最大 232 × 1020B = 4080GB)。2017-26:簇 1KB,1026B 的文件要分配 2 个簇 = 2048B。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2010-30 改
文件的索引结点中有 8 个地址项:5 个直接地址项、2 个一级间接地址项、1 个二级间接地址项。每个地址项 4B,磁盘块大小为 512B。单个文件的最大长度是( )。
  • A. 130.5KB
  • B. 8322.5KB
  • C. 8448KB
  • D. 16645KB
展开解析

B每个索引块放 512 ÷ 4 = 128 个地址。最大块数 = 5 + 2 × 128 + 1282 = 16645 块,× 512B = 8322.5KB。

变化点:2010-30 的块是 256B(N = 64)、4 个直接地址项。一级间接有 2 个,要乘 2。D 是块数,没有乘块大小。

变式操作系统由 2015-29 改
文件的索引结点中有 10 个直接索引指针、一级和二级索引指针各 1 个,磁盘块 2KB,每个指针 4B,索引结点已在内存中。把文件偏移量 20000 和 500000 处所在的磁盘块读入内存,需要访问磁盘的次数分别是( )。
  • A. 1、2
  • B. 1、3
  • C. 2、3
  • D. 2、2
展开解析

AN = 2KB ÷ 4B = 512。20000 ÷ 2048 → 第 9 块,属于直接索引,读 1 次。500000 ÷ 2048 → 第 244 块,10 ≤ 244 < 522,属于一级间接,先读索引块再读数据块,共 2 次。

变化点:2015-29 的块是 1KB,307400 落在二级间接(3 次)。块变大后,每一级能管的块数变多,同样大小的偏移可能落在更低的一级。

变式操作系统由 2017-26 改
某文件系统的簇大小为 4KB,扇区大小为 512B。若一个文件的大小为 5000B,则系统分配给该文件的磁盘空间是( )。
  • A. 5000B
  • B. 5120B
  • C. 8192B
  • D. 4096B
展开解析

C空间按簇分配:5000B 需要 2 个簇,2 × 4KB = 8192B。

变化点:2017-26 是 1KB 簇、1026B 文件(2048B)。B 是按扇区分配的结果;分配单位是簇,最后一个簇没用满也整个分给它(内部碎片)。

变式操作系统考纲补充(尚无真题):链接方案
磁盘块 1KB,块号占 4B。索引分配采用链接方案:每个索引块的最后一项指向下一个索引块。若文件的 FCB 已在内存,读该文件的第 600 块(从 0 开始编号)需要读磁盘( )次。
  • A. 2
  • B. 3
  • C. 5
  • D. 4
展开解析

D每个索引块放 255 个块号:第 0~254 块在第 1 个索引块,255~509 在第 2 个,510~764 在第 3 个。读第 600 块要沿链读 3 个索引块,再读数据块,共 4 次(已用程序验算同类例题)。A 是只有一个索引块时的次数;B 少读了一个索引块(第 600 块在第 3 个索引块里,不在第 2 个)。

考点:链接方案与多层索引。

10.7空闲空间管理:位示图等

★★★ 高频选择题综合题
方法做法特点
空闲表法每个空闲区一项:起始块号、块数适合连续分配
空闲链表法空闲块(或空闲区)串成链简单,分配多块要多次操作
位示图每一位对应一个盘块:0 空闲、1 已分配(或相反)占用空间只和总块数有关、与当前空闲块多少无关(2024-26);容易找连续空闲块
成组链接法(UNIX)空闲块分组,每组第一块记下一组的块号适合大文件系统

空闲表法与空闲链表法

问题:文件系统要随时知道磁盘上哪些块是空的。最直接的办法就是把空闲的块记成一张表或串成一条链,具体怎么做?

大白话:空闲表像停车场门口的一块黑板,写着“3 号起连着 4 个空位”;空闲链表则是每个空车位里放一张纸条,写着“下一个空位在哪”。

空闲表 序号第一个空闲块块数 124 293 3155 4258 分配、回收和内存的动态分区一样(首次适应等) 空闲盘块链 块 2 块 3 块 4 … 以块为单位,每块记下一块分配 n 块要摘 n 次 空闲盘区链 区:2 起4 块 区:9 起3 块 区:15 起5 块 以盘区为单位每区记大小和下一区
图 10-13 空闲表与两种空闲链表。空闲盘块链简单,但分配、回收一块就要改一次链;空闲盘区链一次能分出一整段连续块,但分配回收要像动态分区那样合并相邻区。

跟我做一遍(用上图的空闲表,首次适应,已用程序验算):要分配 5 块:第 1 项(4 块)、第 2 项(3 块)都不够,第 3 项正好 5 块 → 分配块 15~19,删除这一项。之后回收块 12~14:它紧接在“9 起 3 块”(9~11)后面,合并成“9 起 6 块”;它后面的块 15 已被占用,不再合并。

方法额外占用的空间找连续空闲块适合
空闲表随空闲区个数变化容易连续分配、空闲区少
空闲链表指针放在空闲块自己身上;链长随空闲块数变化盘块链难,盘区链较易离散分配
位示图固定,只与总块数有关(2024-26)容易(找连续的 0)中小磁盘,位图可常驻内存
成组链接大部分信息放在空闲块里不考虑连续大型文件系统(UNIX)

位示图:一位管一块

位号 j → 0 1 2 3 4 5 6 7 字 0 1 0 1 1 0 2 1 3 0 4 0 5 1 6 1 7 字 1 1 8 0 9 1 10 1 11 1 12 1 13 0 14 0 15 字 2 0 16 1 17 1 18 0 19 1 20 0 21 1 22 1 23 字 3 1 24 1 25 1 26 0 27 0 28 0 29 0 30 0 31 ↑ 字号 i 格子上面的数字 = 这一位, 下面的小数字 = 它对应的盘块号 b 蓝 1 = 已分配,白 0 = 空闲 字长 8 位、编号都从 0 开始时: b = i × 8 + j i = b ÷ 8(取整),j = b mod 8 例:橙色格 i = 1、j = 5 → b = 13
图 10-14 位示图。一个盘块对应一位,所以位示图的大小只和盘块总数有关(2024-26)。分配:找一个值为 0 的位,算出盘块号,把这一位置 1;回收:由盘块号算出 (i, j),把这一位清 0。若题目规定字号、位号从 1 开始,公式要相应地加减 1(2015-31)。
位示图大小(位)= 盘块总数  字节数 = 总数 / 8 盘块号 b ↔ 第 ⌊b / 每块位数⌋ 块(加上起始块号),块内第 ⌊(b mod 每块位数) / 8⌋ 字节,字节内第 b mod 8 位
2015-31操作系统
文件系统用位图法表示磁盘空间的分配情况,位图存于磁盘的 32~127 号块中,每个盘块占 1024 个字节,盘块和块内字节均从 0 开始编号。假设要释放的盘块号为 409612,则位图中要修改的位所在的盘块号和块内字节序号分别是( )。
  • A. 81、1
  • B. 81、2
  • C. 82、1
  • D. 82、2
展开解析

考点10.7 位示图

C一个位图盘块有 1024 × 8 = 8192 位,盘块号 ÷ 8192 得到在位图的第几块(从 0 数),余数 ÷ 8 得到块内字节号。

  1. 409612 ÷ 8192 = 50 余 12 → 位图的第 50 块,盘块号 32 + 50 = 82。
  2. 块内第 12 位:12 ÷ 8 = 1 余 4 → 第 1 号字节(的第 4 位)。
  • A 错盘块 81 是把位图的“第 50 块”按从 1 开始数又减了 1;50 × 8192 = 409600 ≤ 409612,它就在从 0 数的第 50 块,即 32 + 50 = 82。
  • B 错盘块号同样少了 1;字节号 2 是把 12 ÷ 8 = 1.5 向上取整,或把字节从 1 开始数——题目明确块内字节从 0 编号。
  • C 对盘块 82、字节 1。
  • D 错盘块 82 对;但第 12 位落在 8 ~ 15 位,即第 1 号字节(0 号字节是第 0 ~ 7 位),不是 2 号。

位示图定位:块号 = 起始块 + ⌊b ÷ 每块位数⌋,字节号 = ⌊(b mod 每块位数) ÷ 8⌋,都从 0 数。同类:2014-27(10GB、簇 4KB → 2.5M 位 = 320KB → 80 个簇);2023-25(位图管 16GB 内存的 4KB 页框:4M 位 = 512KB);2010-45 第 1 问(2KB 记录 16384 块:16384 位 = 2KB,正好用位示图)。

成组链接法:分配和回收怎么做

UNIX 用的办法:把空闲盘块分组(比如每组 100 块);每一组的第一个空闲块里记录下一组的空闲块数和所有块号;第一组的空闲块数和块号记在内存里的超级块(空闲盘块号栈)中。于是只要超级块在内存,就能不断找到空闲块。

  1. 分配:从超级块(栈)里取一个块号分配出去,块数减 1。若超级块里只剩最后一个块号——这一块正是记着下一组信息的那块——就先把这块的内容(下一组的块数和块号)读入超级块,再把这一块分配出去。
  2. 回收:若超级块里的块号还没满(不到 100 个),把回收的块号压进去,块数加 1。若已满,就把超级块当前的内容(100 个块号和块数)写入这个回收的块,然后超级块清空、只记这一块(块数为 1)——回收的块成了新的一组的“记录块”。

好处:空闲块的信息大部分放在空闲块自己身上,不占额外空间;内存里只要一个超级块,分配回收大多数时候不用读盘,适合大型文件系统。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2014-27 改
某磁盘分区的容量为 20GB,以簇为单位分配空间,簇大小为 8KB。若用位图管理空闲空间(一位表示一个簇),则存放位图需要的簇数为( )。
  • A. 40
  • B. 80
  • C. 320
  • D. 40K
展开解析

A簇数 = 20GB ÷ 8KB = 2.5M 个,位图 2.5M 位 = 320KB,占 320KB ÷ 8KB = 40 个簇。

变化点:2014-27 是 10GB、4KB 簇(80 簇)。这里容量和簇大小都翻倍,簇的总数不变,位图的位数也不变;但每个簇能放的位数翻倍,所以位图占的簇数减半(80 → 40)。

变式操作系统由 2015-31 改
位图存放在磁盘的 50 号块开始的连续盘块中,每个盘块 2048B,盘块号、块内字节号和位号都从 0 开始编号。释放盘块 409612 时,位图中要修改的位所在的盘块号和块内字节号分别是( )。
  • A. 75、1
  • B. 75、12
  • C. 74、1
  • D. 25、1
展开解析

A每块位图有 2048 × 8 = 16384 位。409612 ÷ 16384 = 25 余 12,所以在位图的第 25 块,即盘块 50 + 25 = 75;块内第 12 位,在第 12 ÷ 8 = 1 个字节(第 4 位)。

变化点:2015-31 是 1024B 的块、从 32 号块开始(82、1)。步骤:先求在位图的第几块(除以每块位数),加上起始块号;余数再除以 8 得到字节号。D 忘了加起始块号。

变式操作系统由 2023-25 改
某系统采用页式存储管理,用位图管理空闲页框。若页大小为 4KB,物理内存大小为 64GB,则位图所占空间为( )。
  • A. 256KB
  • B. 512KB
  • C. 2MB
  • D. 16MB
展开解析

C页框数 = 64GB ÷ 4KB = 16M,位图 16M 位 = 2MB。

变化点:2023-25 是 16GB(512KB)。位图大小只与页框(块)的总数有关,与当前有多少空闲无关(2024-26)。

变式操作系统考纲补充(尚无真题):空闲表法
空闲表按起始块号排序,内容为(起始块号,块数):(2, 4)、(9, 3)、(15, 5)、(25, 8)。采用首次适应算法为文件分配 5 个连续块,分配到的起始块号是( )。
  • A. 2
  • B. 15
  • C. 25
  • D. 9
展开解析

B首次适应从表头找第一个够大的空闲区:(2, 4)、(9, 3) 都不够 5 块,(15, 5) 正好够 → 从 15 开始分配(已用程序验算)。C 是最坏适应(最大的区)的结果。

考点:空闲表法;分配方法与第 8 篇动态分区相同。

10.8文件系统的全局结构、VFS 与挂载

★★ 常考选择题综合题
MBR含分区表 分区 1 分区 2 … 引导块 超级块分区信息 空闲空间管理位示图等 i 节点区 根目录 文件和目录
图 10-15 文件系统在磁盘上的布局。内存里则有:挂载表、目录缓存、系统打开文件表、每个进程的打开文件表(图 10-7)。2026-46:inode 表从盘块 100 开始,每个 inode 128B、每块 4KB 放 32 个 → inode 1000 在盘块 100 + 1000/32 = 131;inode 表占 4096 块 → 最多 4096×32 = 131072 个文件;删除目录要释放数据块并清磁盘位示图、清 inode 位示图、从父目录中删除目录项。
  • 虚拟文件系统 VFS:在各种具体文件系统(ext4、NTFS、FAT32、网络文件系统…)之上定义统一的接口(open/read/write…),上层程序不用管底层是哪种(2025-28 选 C);它不是“运行在虚拟内存的文件系统”,也不是为了加速。
  • 挂载(mount):把一个文件系统(如 U 盘)接到目录树的某个目录(挂载点)上,之后访问这个目录就是访问那个文件系统。
  • 文件保护:口令、加密、访问控制(访问控制表/访问权限位)。2017-30:4 类用户 × 5 种权限,用位串表示每类用户对每种权限的允许与否 → 4×5 = 20 位。

文件保护:访问控制矩阵、访问控制表与权限表

问题:系统里有很多用户、很多文件,每个用户对每个文件能做什么(读、写、执行)都可能不同,怎么记?

大白话:画一张大表,行是用户,列是文件,格子里写权限——这就是访问控制矩阵。这张表太大而且大部分格子是空的,所以实际只存它的一部分:按列存,每个文件带一张“谁能用我”的名单(访问控制表 ACL);按行存,每个用户带一张“我能用哪些文件”的清单(权限表,也叫能力表)。

访问控制矩阵 用户\文件 file1 file2 file3 Alice rw rw — Bob r r rx Carol — — rwx 按列:file2 的访问控制表 ACLAlice: rw Bob: r 按行:Bob 的权限表(能力表)file1: r file2: r file3: rx 空格子(—)不用存:ACL 和权限表都只记有权限的项,比整张矩阵省得多 UNIX 进一步把用户分成文件主、同组、其他 3 类,每类 rwx 3 位,共 9 位(rwxrwxrwx)
图 10-16 访问控制矩阵的两种存法。按文件存(ACL)方便回答“这个文件谁能用”,删除文件时一起删;按用户存(权限表)方便回答“这个用户能用哪些文件”。

跟我做一遍:系统有 100 个用户、1000 个文件,完整的访问控制矩阵有 100 × 1000 = 105 格,其中绝大多数是空的。若平均每个文件只有 3 个用户能访问,ACL 一共只需 3 × 1000 = 3000 项。用户分 3 类、权限 3 种时,每个文件只需 3 × 3 = 9 位(同理,2017-30:4 类用户 × 5 种权限 = 20 位)。

真题怎么考:访问权限存放在 FCB(inode)里,不在目录项里(2025-29、2009-30);用位串表示权限要几位(2017-30)。

保护方式做法特点
口令用户访问文件时要提供口令,与 FCB 中存的口令比较简单、开销小;但口令存在系统里,不够安全
加密用密钥对文件编码,读时解码保密性强;编码解码要花时间
访问控制为每个文件设一张访问控制表 ACL,列出每个用户(或用户组)允许的操作(读、写、执行、追加、删除、列表……);为了精简,常把用户分成拥有者、组、其他三类(UNIX 的 rwxrwxrwx 共 9 位)最常用;口令和加密都是防止别人偷看,访问控制还能区分不同用户的不同权限

文件系统的层次结构

层次(自上而下)负责什么
应用程序通过系统调用(open、read…)使用文件
逻辑文件系统管理元数据(除文件数据以外的所有结构):目录结构、用 FCB/inode 维护文件结构,负责文件保护(检查权限)
文件组织模块知道文件的逻辑块和物理块,把逻辑块号转换成物理块号(按连续/链接/索引结构);还包括空闲空间管理
基本文件系统向设备驱动程序发出一般的读写命令(“读第 x 个物理块”),管理内存中的缓冲区和各种缓存
I/O 控制层设备驱动程序和中断处理程序,在内存和磁盘之间真正传送数据(第 9 篇)

这张表把本篇串了起来:权限、目录在最上层;10.6 的物理结构、10.7 的空闲空间管理在文件组织模块;10.1、10.2 的磁盘访问和调度由最下面的驱动程序完成。

选择题变式:真题换个条件再做一遍

每道题都由一道真题改动一两个条件而来。先做本节的真题,再做这里的变式,最后看“变化点”:改了什么、答案为什么跟着变。

变式操作系统由 2025-28 改
下列关于文件系统挂载(mount)的叙述中,正确的是( )。
  • A. 挂载后,访问挂载点目录就是访问被挂载文件系统的根目录
  • B. 挂载会把被挂载的整个文件系统复制到挂载点所在的磁盘上
  • C. 一个文件系统只能挂载在根目录下
  • D. 挂载前后,被挂载文件系统中文件的访问路径不变
展开解析

A挂载把另一个文件系统接到当前目录树的某个目录(挂载点)上,访问挂载点就进入了那个文件系统的根目录,不复制任何数据。挂载点可以是任意一个目录,挂载后文件的路径以挂载点开头。

变化点:2025-28 考虚拟文件系统(VFS 定义统一接口)。这里考同一节里的挂载。

变式操作系统由 2017-30 改
把访问控制矩阵按列(每个文件一列)分开存放,得到的是( )。
  • A. 权限表(能力表)
  • B. 访问控制表(ACL)
  • C. 位示图
  • D. 文件分配表(FAT)
展开解析

B访问控制矩阵的行是用户、列是文件。按列存:每个文件一张表,记录哪些用户有什么权限,就是访问控制表 ACL。按行存:每个用户一张表,就是权限表(A)。C、D 是空闲空间管理用的。

变化点:2017-30 算用位串表示权限要几位。这里考访问控制矩阵的两种存法。考点:文件保护。

10.9融合一次 open + read 的全过程

★ 考过选择题

把全书串起来:程序执行 fd = open("/home/a.txt", O_RDONLY); read(fd, buf, 100); 时发生了什么?

  1. open 陷入内核(系统调用,第 5 篇)。
  2. 按路径逐级查目录:从根目录的 inode 找到根目录文件的数据块 → 在里面找 “home” 的目录项 → 读 home 的 inode → 读 home 目录的数据块 → 找到 “a.txt” 的 inode 号 → 把 a.txt 的 inode 读入内存。每一步不在缓冲区里就要读磁盘。
  3. 检查权限;在系统打开文件表和进程打开文件表中各建一项,返回 fd(10.4 节)。
  4. read(fd, …) 陷入内核:通过 fd 找到表项 → 读写指针 = 0 → 逻辑块号 0 → 查 inode 的直接地址得到物理块号(10.6 节)。
  5. 查磁盘高速缓存/缓冲区,命中就直接复制;否则:驱动程序把块号换算成柱面/磁头/扇区,请求进入队列,按磁盘调度算法排序(10.1~10.2 节),写磁盘控制器的寄存器启动传送,进程阻塞(第 6 篇)。
  6. 磁盘控制器用 DMA 把数据送进内存的内核缓冲区,传完发出中断(第 9 篇);中断处理程序唤醒进程。
  7. 进程被调度后,内核把数据从内核缓冲区复制到用户的 buf(buf 是虚拟地址,写它的时候又要经过页表、TLB、Cache,第 8 篇),读写指针后移,返回用户态。
另一条路

也可以用内存映射文件(第 8 篇):把文件映射进虚拟地址空间,之后直接读内存数组,缺页时由缺页处理程序去读文件的块——文件 I/O 变成了“缺页 + 页面调入”。

第 10 篇自测
  1. 磁盘 6000rpm,每道 100 扇区,平均寻道 5ms,读一个扇区平均要多久?
    答案
    一圈 10ms:5 + 5 + 10/100 = 10.1 ms。
  2. 磁头在 100 号磁道正向磁道号增大方向移动,请求 55、58、39、18、90、160、150、38、184,SSTF 和 SCAN(到 199)的移动距离?(用工具验证)
    答案
    SSTF:100→90→58→55→39→38→18→150→160→184 = 10+32+3+16+1+20+132+10+24 = 248。SCAN:100→150→160→184→199→90→58→55→39→38→18 = 99 + 181 = 280。
  3. 块 4KB、地址项 4B,inode 有 12 个直接地址和一、二、三级间接各 1 个,最大文件约多大?
    答案
    N = 1024:(12 + 1024 + 1024² + 1024³) × 4KB ≈ 4TB + 4GB + 4MB + 48KB。
  4. 硬链接和软链接,哪个在目标文件被删除后还能访问到原数据?
    答案
    硬链接(共享 inode,计数没到 0 就不删数据);软链接只存了路径,目标被删就找不到了。
  5. 磁盘转速 6000rpm,每道 200 个扇区、每扇区 512B,数据传输率是多少?
    答案
    100 转/秒 × 200 × 512B = 10240000B/s ≈ 10.24MB/s。
  6. 隐式链接分配下,FCB 已在内存,读文件的第 10 块(从 0 编号)要读几次盘?若改为 FAT 且 FAT 在内存呢?
    答案
    隐式链接要从第 0 块沿指针读到第 10 块,共 11 次;FAT 在内存时查表即可得到块号,只读 1 次。
  7. 为什么打开文件后,读写时不再需要文件名?
    答案
    open 时已经按名查到了 FCB/inode 并放进内存的打开文件表,返回了 fd;之后用 fd 直接找到表项,避免每次都逐级查目录读盘。
  8. 成组链接法中,超级块里只剩一个空闲块号时又来了一个分配请求,要做什么?
    答案
    这个块记录着下一组空闲块的信息,先把它的内容读入超级块,再把这个块分配出去。

10.✎本篇真题闯关(50 题)

本篇各小节挂的真题全部在这里,按小节排好。可以展开后一道道做,也可以用弹窗模式连续做。做题记录(对/错)只保存在你这台电脑的浏览器里,小节标题下的题号会随之变绿或变红。

第 11 篇 · 综合题专项

大题满分训练营:13 类综合题逐类击破

前十篇按知识讲,这一篇按题型讲。2009—2026 年计组和操作系统一共考了 72 道综合题,拆开看只有 13 类。每一类都按同一个顺序教:这类题长什么样 → 做它必须会的几件事(从零讲) → 解题模板 → 一道真题完整示范(连答题卡怎么写都给出) → 常见丢分点 → 本类全部真题。72 道题每一道都配了“满分答案”式的分步解答:先把条件翻译成大白话,再逐问写思路、答题卡写法和易错点。每类后面还有一道自编模拟大题和一张“举一反三”变式表;最后的 11.15 是两套按真实考试格式出的整卷模拟,附评分细则。

建议:学完对应的知识篇再来每类先自己在纸上做,再点开解答对照计组 7 类 · 操作系统 6 类

11.1先弄清楚:大题怎么考、怎么给分

408 的综合题是第 41~47 题,共 7 道,一般是数据结构 2 道、计组 2 道、操作系统 2 道、计算机网络 1 道。计组两道一般排在 43、44 题,操作系统两道在 45、46 题(2014、2016 年计组是 44、45,操作系统是 46、47;2019 年操作系统在前,是 43、44,计组是 45、46)。分值每年略有不同,2026 年卷面标的是:43 题 10 分、44 题 15 分、45 题 7 分、46 题 8 分——这四道题合计 40 分,差不多是整张卷子的四分之一。

打个比方

综合题像一套“连环选择题”:出题人先编一个场景(一台机器、一段程序、一个进程),然后在这个场景里问 3~5 个小问题。每个小问题单拿出来,难度和一道选择题差不多。所以大题难的不是知识,而是两件事:① 读懂场景,把一大段文字翻译成你熟悉的量(“页大小 4KB”→“页内偏移 12 位”);② 知道每一问在考哪个知识点,按步骤写出来。这一篇就是专门练这两件事的。

阅卷是怎么给分的

  • 按步骤给分。每一问会拆成几个“得分点”(例如:算出每秒中断次数、算出每次中断的开销、算出占比)。最后结果算错了,前面写对的步骤仍然有分;反过来,只写一个结果、不写过程,往往只能拿一部分分,题目里写着“要求给出计算过程”“说明理由”的更是如此。
  • 理由要写到“点子”上。“为什么会死循环”这类问题,阅卷看的是关键词:比如“n 是无符号数,n−1 = 232−1,i ≤ n−1 恒成立”。写一大段但没说到关键原因,照样没分。
  • PV 题看三样东西:信号量的定义和初值、每个进程的完整代码、同步/互斥关系是否正确。代码能跑但会死锁、或者没有实现题目要求的并发度,都要扣分。

答题卡上的五条规矩

  1. 先翻译条件,再动笔算。在草稿纸上把题目里的数字全部“翻译”一遍:页 4KB → 偏移 12 位;按字节编址 → 地址单位是字节;主频 500MHz → 每秒 5×108 个周期、周期 2ns。翻译错一个数,后面全错。
  2. 公式先写成文字或符号,再代数字。例如写“CPU 占比 = 每秒中断次数 × 每次开销 ÷ 每秒总周期数 = 1.25×105 × 100 ÷ 5×108 = 2.5%”。阅卷老师一眼能看到你的思路,数字算错也能给步骤分。
  3. 地址题一律展开成二进制,在二进制上画竖线分字段,再换回十六进制。十六进制直接心算切字段最容易出错(字段边界往往不在 4 位的整数倍上)。
  4. 单位和进制写全。十六进制结尾加 H(或写 0x);B(字节)和 b(位)分清;存储容量的 K、M 是 210、220,速率和频率的 K、M 是 103、106。
  5. 题目条件优先于教材默认。流水线有没有转发、Cache 是直写还是回写、“优先数小的优先”还是“大的优先”、页号给的是十进制还是十六进制——一律以题目为准,并且在答案里写一句“由题意……”。
✗ 容易丢分的写法

(2009-43 第 1 问)
0.5M/4 = 125000,125000×100/500M = 2.5%。

问题:100 从哪来没交代;“125000 是什么”没说;读卷人要自己猜你的思路。

✓ 步骤分拿满的写法

每次中断传送 32 位 = 4B,每秒中断次数 = 0.5MB/s ÷ 4B = 1.25×105 次;
每次中断的开销 = (18 + 2) 条指令 × CPI 5 = 100 个时钟周期;
CPU 用于该外设的时间占比 = 1.25×105 × 100 ÷ (500×106) = 2.5%。

13 类题型 × 18 年分布

下表每一格是那一年考的那道题(题号),点一下直接弹出题目和满分解答。一道题跨两类时两格都标出(虚线框的是“次要归类”)。看一眼就知道:计组的 ②指令/机器代码、⑥虚存+Cache 综合、⑦I/O 性能,操作系统的 ⑧PV、⑩分页、⑪文件系统是年年轮着来的主力。

这一篇怎么用

  1. 先把对应的知识篇读完(每类开头都写了要先读哪几节)。
  2. 读“必须会的几件事”和“解题模板”,把模板抄在自己的本子上。
  3. 跟着“完整示范”走一遍,重点看绿色的“答题卡这样写”——这是考场上真正要写下来的内容。
  4. 点小节标题下的红色题号,先在纸上自己做完,再点“看解答”对照。做完自评“基本做对了 / 没做出来”,题号会变绿或变红,方便二刷。
  5. 每一类在“完整示范”后面都有一道自编模拟大题(虚线框),问法照真题出,但换了场景和数字。真题做完再做它,用来检验这一类是不是真的会做了。

11.2计组 ① 数据的表示与运算:看一段 C 程序,算机器数、判溢出

★★★ 高频综合题

先读:2.1 补码 2.2 类型转换 2.4 标志位 2.5 乘除法 2.6 IEEE 754

基本功:知识篇里对应的例题和总结表(先做这些,再做本类真题)

这类题长什么样

题目给一小段 C 代码,变量有 unsigned、int、short、float 等类型,然后问“执行完之后某个寄存器/变量里是什么”。它有时单独成题(2011-43、2017-43、2020-43、2025-44),更多时候作为别的大题的一两个小问出现。

常见问法考过的地方
某变量/寄存器的机器数是多少(十六进制)2011-43(1),2017-43(2),2020-43(4),2021-43(3),2025-44(1)
某变量的值是多少(十进制)2011-43(2)
运算是否溢出?硬件怎么判断溢出?2011-43(4),2017-43(4),2020-43(4),2021-43(3),2019-45(4)(5),2022-43(1)
为什么死循环 / 为什么结果变成 −12017-43(1)(4)
float 为什么不精确、不溢出/不舍入的最大 n2017-43(3)(5)
加减乘除能否共用加法器、乘法怎么用加法和移位实现、哪种乘法最快2011-43(3),2020-43(1)~(3)
除法器初值、除法异常2025-44
地址计算时偏移量做零扩展还是符号扩展2021-43(4)

做这类题必须会的 6 件事

① 一个位串,两种读法。计算机里存的只是一串 0 和 1,本身没有正负。C 语言的类型决定怎么读它:unsigned 就按普通二进制读;int 按补码读。最高位是 0 时两种读法结果一样;最高位是 1 时,按有符号读出的值 = 按无符号读出的值 − 2n(n 是位数)。下图是 8 位的情况。

位串(8 位) 当 unsigned 读 当 int(补码)读 最高位 0:00H ~ 7FH最高位 1:80H ~ FFH 0 → 127128 → 255 0 → 127(一样)−128 → −1(减 256) 86H 134−122 F6H 246−10 n 位时:最高位为 1 的位串,有符号值 = 无符号值 − 2ⁿ(8 位减 256,16 位减 65536,32 位减 2³²)
图 11-1 同一个位串的两种读法。2011-43 的 x = 134(86H)交给 int 型的 m 以后,位串没变,读出来却是 −122。

② 快速写出 n 位机器数。正数:直接转二进制,高位补 0 补够 n 位。负数:先算 2n − |x|,再转二进制。例:8 位的 −10 = 256 − 10 = 246 = F6H;16 位的 −1 = FFFFH;32 位的 −2AH = 1 0000 0000H − 2AH = FFFF FFD6H。十六进制要写满位数(32 位的 255 写成 0000 00FFH)。

③ 赋值、类型转换只有三条规则。等长(int ↔ unsigned):位串不变,换个读法。短变长:看原来的类型,原来有符号就高位补符号位(符号扩展),原来无符号就补 0(零扩展)。长变短:直接砍掉高位。另外,有符号数和无符号数一起比较或运算时,按无符号数处理——unsigned 的 0 − 1 是 232 − 1,这就是 2017-43 死循环的原因。

④ 加减法只有一个加法器,按模 2n 算。不管有没有符号,位串的加法都一样,超出 n 位的进位直接丢掉;减法 A − B 就是 A + (B 取反 + 1)。有没有溢出要分开看:

无符号数带符号数(补码)
表示范围(n 位)0 ~ 2n − 1−2n−1 ~ 2n−1 − 1
“结果不对”的标志CF(加法有进位 / 减法有借位)OF(溢出)
考场判断法(最稳)用十进制真值算,看是否 < 0 或 ≥ 2n用十进制真值算,看是否超出范围
硬件判断法(问“如何判断”时写)加法:Cout = 1;减法:Cout = 0(CF = Cout ⊕ 减法信号)OF = 最高位进位 ⊕ 次高位进位;或“两个同号数相加,结果符号相反”

⑤ 乘法:2n 位乘积,只留低 n 位。n 位 × n 位的完整乘积是 2n 位,C 语言只取低 n 位。无符号乘法:高 n 位全为 0 才不溢出;带符号乘法:高 n 位每一位都等于低 n 位的最高位才不溢出。没有乘法器时,乘法 = 循环 n 次“看乘数一位、决定加不加、移位”。整数除法有两种异常:除数为 0;−2n−1 ÷ (−1)(商放不下)。

⑥ float:1 + 8 + 23,有效位 24 位。

S符号 1 位阶码 E(8 位)存的是 指数 + 127尾数 M(23 位)前面还藏着一个 1,所以有效位 = 24 位

值 = (−1)S × 1.M × 2E − 127;E 全 1、M 全 0 表示 ∞;E 的有效范围 1 ~ 254(指数 −126 ~ 127)。

整数转 float 的步骤:写成二进制 → 写成 1.xxx × 2k → E = k + 127 → 把小数点后的位填进 M(不够补 0)。从最高位的 1 到最低位的 1 超过 24 位,就必须舍入,结果不再精确。例:224 − 1(24 个 1)能精确存;225 − 1(25 个 1)会被舍入成 225。

解题模板

  1. 列变量表。每个变量写:类型、位数、有无符号。8 位机、16 位机的 int 就是 8 位、16 位,别按 32 位算。
  2. 常量先变成机器数。每个初值写成 n 位二进制,再写成十六进制。
  3. 一句一句“执行”。运算在位串上做,按模 2n 丢掉进位;赋值按“等长不变、扩展看原类型、截断砍高位”。
  4. 按目标变量的类型读值。问“值”时,看结果存进了什么类型的变量。
  5. 判溢出。先看这句运算是有符号还是无符号(看参与运算的变量类型),用真值法判断;题目问“硬件如何判断”,再写 OF/CF 的规则。
  6. 浮点数算有效位。写成 1.xxx × 2k,数有效位是否超过 24,阶码是否超过 254。
  7. 写答案。机器数带 H、写满位数;每个“是/否”后面跟一句理由。
完整示范2011 年第 43 题计组

假定在一个 8 位字长的计算机中运行如下 C 程序段:

unsigned int x=134;
unsigned int y=246;
int m=x;
int n=y;
unsigned int z1=x-y;
unsigned int z2=x+y;
int k1=m-n;
int k2=m+n;

若编译器编译时将 8 个 8 位寄存器 R1~R8 分别分配给变量 x、y、m、n、z1、z2、k1 和 k2。请回答下列问题。(提示:带符号整数用补码表示)

(1) 执行上述程序段后,寄存器 R1、R5 和 R6 的内容分别是什么(用十六进制表示)?

(2) 执行上述程序段后,变量 m 和 k1 的值分别是多少(用十进制表示)?

(3) 上述程序段涉及带符号整数加/减、无符号整数加/减运算,这四种运算能否利用同一个加法器及辅助电路实现?简述理由。

(4) 计算机内部如何判断带符号整数加/减运算的结果是否发生溢出?上述程序段中,哪些带符号整数运算语句的执行结果会发生溢出?

先在纸上自己做一遍,再点这里展开满分解答

考点2.1 补码2.2 类型转换2.4 加减法与溢出训练营 ①

先把条件翻译成大白话
  • “8 位字长”:题里的 unsigned int 和 int 都只有 8 位。无符号能表示 0 ~ 255,有符号(补码)能表示 −128 ~ 127。
  • x = 134、y = 246 都比 127 大——这是本题的“机关”:把它们原样交给 int 型的 m、n 时,位串不变,但会被读成负数。
  • R1 ~ R8 依次放 x、y、m、n、z1、z2、k1、k2,所以问的 R1 = x、R5 = z1、R6 = z2。
  • 寄存器里放的永远是位串;“值”是按变量类型去读这个位串得到的。
(1) R1、R5、R6 的内容(十六进制)

先把 x、y 写成 8 位二进制,再在 8 位里做加减:结果超出 8 位就把第 9 位扔掉(相当于对 256 取余)。

答题卡这样写

x = 134 = 1000 0110B,所以 R1 = 86H;y = 246 = 1111 0110B = F6H。

z1 = x − y:134 − 246 = −112,在 8 位中按模 256 得 −112 + 256 = 144 = 1001 0000B,所以 R5 = 90H。
(用补码加法验证:86H − F6H = 86H + [−F6H]补 = 86H + 0AH = 90H。)

z2 = x + y:86H + F6H = 1 7CH,只保留低 8 位,所以 R6 = 7CH。

z1 是 unsigned,它的“值”是 144 而不是 −112;但本问只问寄存器内容,写位串 90H 即可。结果写成十进制或忘了丢掉第 9 位(写成 17CH)都会丢分。

(2) m 和 k1 的值(十进制)

m = x 是“等长的无符号 → 有符号”,位串原样复制(仍是 86H),只是改成按补码读。k1 = m − n 在位串上和 z1 的运算完全一样,结果位串也是 90H,同样按补码读。

答题卡这样写

m 的机器数与 x 相同,为 86H,最高位为 1,按补码解释:m = 134 − 256 = −122。

n 的机器数为 F6H,n = 246 − 256 = −10。k1 = m − n = −122 − (−10) = −112(机器数 90H,−112 在 −128 ~ 127 之内,结果正确)。

有符号 8 位数“最高位为 1 时,值 = 无符号读出的值 − 256”,这一条要熟到不用想。

(3) 四种运算能否用同一个加法器及辅助电路实现?

从“位串”的角度看:无符号加法、补码加法都是“两串 8 位二进制相加、丢掉第 9 位”,一模一样;减法都能变成“加上减数的相反数”。不同的只是结果怎么解释、看哪个标志。

答题卡这样写

能。n 位加法器实现的是模 2n 的加法。无符号整数和带符号整数(补码)的加法在位串层面完全相同;减法 A − B 都可以化为 A + [−B]补,即把 B 各位取反、最低位进位输入为 1 后与 A 相加,只需在加法器的一个输入端加一个受“减法”信号控制的取反电路。两者的区别只在于结果的解释和溢出判断方法不同(无符号看进位/借位 CF,带符号看溢出标志 OF),这些标志由辅助电路同时产生。

(4) 硬件如何判断带符号加减溢出?哪些语句溢出?

先找出“带符号运算”的语句:只有 k1 = m − n 和 k2 = m + n(z1、z2 是无符号运算,不讨论 OF)。判断是否溢出最稳的方法是用十进制真值算一遍,看在不在 −128 ~ 127 之内;然后再用硬件规则验证一次。

答题卡这样写

判断方法:最高数值位产生的进位 Cn−1 与符号位产生的进位 Cn 不同时溢出,即 OF = Cn ⊕ Cn−1;等价地,两个符号相同的数相加(或符号不同的数相减),结果的符号与被加数(被减数)不同时溢出。

k1 = m − n = −122 − (−10) = −112,在范围内,不溢出。

k2 = m + n = −122 + (−10) = −132 < −128,溢出:86H + F6H = (1)7CH,两个负数相加得到正数 7CH,符号位进位为 1、次高位进位为 0。

所以只有 k2 = m + n 发生溢出。

不要拿 z2 的“最高位进位丢了”说事——那是无符号加法的进位(CF = 1),不是带符号溢出。

  • 寄存器里只有位串;同一个位串,按 unsigned 读和按 int 读可以差 2n。
  • 加减法永远在 n 位里按模 2n 做,一个加法器搞定有符号和无符号。
  • 判溢出:先确定是有符号还是无符号运算,再用真值法算范围,最后写出硬件判断规则。

模拟大题:一段 C 程序把本类问法走一遍(按 408 风格自编)

这一类单独成题的真题有 2011-43、2017-43、2020-43、2025-44。下面这道自编题把最常见的四种问法放进同一段程序:写机器数、类型转换、标志位与溢出、float 编码与舍入。所有结果都用 gcc 实际运行核对过,加法按补码回绕,编译时用了 -fwrapv。

模拟题机器数、标志位与 float计组 · 约 12 分

在 32 位计算机上运行下列 C 程序段。int 型占 32 位,short 型占 16 位,带符号整数用补码表示,float 型采用 IEEE 754 单精度格式。

short          s  = -20000;
unsigned short us = s;
unsigned int   u  = us;
int            i  = s;
short          t  = s * 2;
int            m  = 0x7FFFFFF0;
int            n  = m + 0x20;
float          f  = -20000.75;
float          h  = 16777217;

(1) s、us、u、i 的机器数各是多少(用十六进制表示)?us 和 u 的值(十进制)各是多少?u 和 i 都是由 −20000 扩展得到的,为什么它们的高 16 位不同?(3 分)

(2) 执行“t = s * 2”后,t 的机器数和值各是多少?若在一台 16 位机器上用算术左移 1 位来实现 s × 2,硬件怎样判断结果是否溢出?本例是否溢出?(3 分)

(3) 执行“n = m + 0x20”对应的加法指令后,OF、SF、ZF、CF 各是多少?n 的值是多少?若 m、n 都声明为 unsigned int,这次加法的结果是否正确?为什么?(3 分)

(4) f 和 h 的机器数各是多少(用十六进制表示)?h 中实际存储的值是多少?它为什么不等于 16777217?(3 分)

先在纸上自己做一遍,再点这里展开满分解答

考点2.1 写机器数2.2 类型转换2.3 算术移位2.4 溢出判断2.6 IEEE 7542.6 舍入

先把条件翻译成大白话
  • 先列变量表:s 是 16 位有符号数,us 是 16 位无符号数,u 是 32 位无符号数,i 是 32 位有符号数,t 是 16 位有符号数,m、n 是 32 位有符号数。
  • 赋值只有三条规则:等长赋值位串不变;短变长看原来的类型,有符号就符号扩展,无符号就零扩展;长变短直接截断高位。
  • float 是 1 位符号 + 8 位阶码(指数 + 127)+ 23 位尾数,有效位数是 24 位(含隐藏的 1)。
(1) s、us、u、i 的机器数

负数的 n 位补码 = 2n − |x|,即 65536 − 20000 = 45536 = B1E0H。然后按赋值规则逐个推。

答题卡这样写

s = −20000,16 位补码 = 65536 − 20000 = 45536 = B1E0H。

us = s:等长赋值,位串不变,us = B1E0H,按无符号数读值为 45536。

u = us:us 是无符号数,短变长做零扩展,u = 0000B1E0H,值为 45536。

i = s:s 是有符号数,短变长做符号扩展,i = FFFFB1E0H,值仍为 −20000。

u 和 i 的高 16 位不同,是因为扩展方式由原来的类型决定:u 从无符号的 us 扩展,高位补 0;i 从有符号的 s 扩展,高位补符号位 1。

(2) t = s * 2,以及算术左移的溢出判断

C 语言会先把 s 提升为 int 再乘:得 −40000 = FFFF63C0H,赋给 16 位的 t 时截断高 16 位。

答题卡这样写

s × 2 = −40000,32 位机器数为 FFFF63C0H。赋给 short 型的 t 时截去高 16 位,t = 63C0H,值为 25536。结果变成了正数,是错误的。

用 16 位算术左移实现:1011 0001 1110 0000 → 0110 0011 1100 0000 = 63C0H。

判断方法:补码算术左移 1 位时,若移位前的符号位与最高数值位不同,也就是移出的位与移位后的符号位不同,结果就溢出。本例移位前最高两位是 1、0,所以溢出。从数值上看,−40000 < −32768,超出了 16 位补码的表示范围。

算术左移的溢出不看“移出的位是不是 1”,而看移位后符号有没有变。例如 FFFFH(−1)左移后得 FFFEH(−2),移出的是 1,却没有溢出。

(3) n = m + 0x20 的四个标志
答题卡这样写

7FFFFFF0H + 00000020H = 80000010H。

OF = 1:两个正数相加得到负数。也可以用进位判断:最高位的进位 Cn = 0,次高位向最高位的进位 Cn−1 = 1,OF = Cn ⊕ Cn−1 = 1。

SF = 1(结果最高位为 1);ZF = 0(结果不为 0);CF = 0(最高位没有向外进位)。

n 的值:80000010H 按补码读为 −231 + 16 = −2147483632,发生溢出,结果错误。

若 m、n 都是 unsigned int:2147483632 + 32 = 2147483664 = 80000010H,没有超过 232 − 1,CF = 0,所以结果正确。同一个位串、同一次加法,有符号数要看 OF(溢出了),无符号数要看 CF(没溢出)。

硬件做加法时,四个标志位全都会生成,不管变量是什么类型。题目问“是否溢出”时,要按变量的类型选看 OF 还是 CF。

(4) f、h 的机器数,h 的实际值

写成二进制 → 规格化成 1.xxx × 2k → 阶码 = k + 127 → 把小数点后的位填入尾数。h 要数一数有效位数有没有超过 24 位。

答题卡这样写

f:20000.75 = 100 1110 0010 0000.11B = 1.0011 1000 1000 0011B × 214。符号 S = 1,阶码 E = 14 + 127 = 141 = 1000 1101B,尾数 M = 0011 1000 1000 0011 000 0000(共 23 位)。

拼起来:1 10001101 00111000100000110000000 = 1100 0110 1001 1100 0100 0001 1000 0000B = C69C4180H。

h:16777217 = 224 + 1 = 1 0000 0000 0000 0000 0000 0001B(25 位有效位)= 1.000…0001B × 224,小数点后有 24 位。

float 的尾数只有 23 位,最后那个 1 要舍掉。它恰好是“一半”,按默认的就近舍入(恰为一半时取偶数)舍去,尾数全为 0。所以 h 实际存储的值是 224 = 16777216,阶码 E = 24 + 127 = 151 = 1001 0111B,h = 0 10010111 000…0 = 4B800000H。

h ≠ 16777217 的原因:float 的有效位只有 24 位(含隐藏的 1),而 16777217 需要 25 位有效位,无法精确表示。

判断 int 转 float 是否精确,要数从最高位的 1 到最低位的 1 一共几位,超过 24 位就要舍入。224 + 1 是最小的不能被 float 精确表示的正整数。

  • 写机器数的三板斧:负数用 2n − |x|;扩展看原类型;截断砍高位。
  • 同一次加法,OF 给有符号数看,CF 给无符号数看。
  • 算术左移溢出 ⇔ 符号位变了。
  • float 看有效位数,超过 24 位就要舍入;恰为一半时就近取偶。

举一反三:条件一变,答案怎么变

以上面的模拟题为基准,每行只改一个条件。先自己想出新答案和理由,再点开核对。

条件怎么变新答案和理由
s 改为 20000(正数)
想好了再点s = us = 4E20H;u = i = 0000 4E20H。正数的符号扩展和零扩展结果相同,u 和 i 的高 16 位都是 0。
s 改为 −16000,再执行 t = s * 2
想好了再点−32000 没有超出 16 位补码的范围(≥ −32768),t = 8300H,值为 −32000,不溢出。用算术左移看:C180H 的最高两位是 11,相同,所以不溢出,结果是 8300H。
n = m + 0x20 改为 n = m − 0x20
想好了再点n = 7FFF FFD0H。正数减正数不会溢出,OF = 0;按无符号数看够减,CF = 0;SF = 0,ZF = 0。
m 改为 0x80000000,执行 n = m − 1
想好了再点n = 7FFF FFFFH。有符号:−231 − 1 超出范围(负数减正数得到正数),OF = 1。无符号:231 − 1 够减,CF = 0。SF = 0。
h 改为 16777218(224 + 2)
想好了再点从最高位的 1 到最低位的 1 只有 24 位,能精确表示,h = 4B80 0001H。
h 改为 16777219(224 + 3)
想好了再点需要 25 位有效位,舍掉的最低位恰好是一半,要就近取偶。16777218 的尾数末位是 1,16777220 的尾数末位是 0,所以 h = 16777220 = 4B80 0002H。
float f = 0.1
想好了再点0.1 化成二进制是无限循环小数 0.000110011…,只能舍入。实际存的是 3DCC CCCDH ≈ 0.100000001,所以浮点数不能用 == 判断相等。
h 改为 16777219,舍入方式改为向零舍入(截断)
想好了再点只保留 24 位有效位,多出的直接丢掉:224 + 3 → 224 + 2 = 16777218,h = 4B80 0001H(默认的就近舍入得到 16777220)。四种舍入方式见 2.6(程序核对)。
float f = 2−149
想好了再点这是单精度能表示的最小正数,是非规格化数:阶码全 0,尾数只有最低位为 1,机器数 0000 0001H,值 = 2−23 × 2−126。比它还小就只能下溢为 0(2.6 非规格化数)。

丢分点清单

  • 无符号变量的“值”写成了负数(2011-43 的 z1 是 144,不是 −112)。
  • 十六进制没写满位数,或者忘了写 H。
  • 把无符号加法的进位(CF)当成溢出(OF),或者反过来。
  • “能否共用一个加法器”只答“能”,不给理由(要说出“模 2n 加法相同、减法变加补码”)。
  • float 有效位写成 23 位(忘了隐藏的 1);阶码忘了加 127。
  • 乘法溢出只看“有没有进位”——要看 2n 位乘积的高 n 位。
  • 符号扩展和零扩展用反:看原来的类型,不看目标类型。

本类其他真题(先做再看解答)

  • 2017-43:f(n) = 2n+1 − 1 的 int 版和 float 版——死循环、溢出成 −1、float 舍入、∞。本类最经典的一道,必做。
  • 2020-43:没有乘法指令怎么做乘法、三种乘法实现谁快、乘法溢出判断。
  • 2025-44:补码除法器的初值、除法异常的两种情况、异常响应。
  • 夹在别的大题里的:2021-43(3)(4)(16 位减法和乘法是否溢出、偏移量符号扩展)、2019-45(4)(5)(阶乘溢出、imul 的 OF)、2022-43(1)(OF 的逻辑表达式)、2013-44(3)(无符号比较用哪些标志)。

11.3计组 ② 指令格式、寻址方式与机器级代码

★★★ 高频综合题

先读:3.1 指令格式 3.2 寻址方式 3.3 看懂汇编 3.4 CISC 与 RISC 2.7 大小端

基本功:知识篇里对应的例题和总结表(先做这些,再做本类真题)

这类题长什么样

计组大题里出现最多的一类(18 年里有 10 年考)。题目给一张指令格式图或一段带地址和机器码的汇编代码,然后问“能有几条指令、几个寄存器”“这条指令的机器码是什么”“跳到哪里”“是大端还是小端”“这个寄存器里放的是哪个变量”。

常见问法考过的地方
最多多少条指令 / 多少个寄存器 / 多少种操作2010-43(1),2015-44(1),2021-43(2),2022-43(3),2024-43(1)
MAR、MDR、IR、ALU、寄存器、存储单元各多少位2010-43(1),2021-43(1),2026-43(1)
按字节还是按字编址 / 数组元素多大2013-44(1),2014-44(1)(2)
写出机器码 / 由机器码说出功能2010-43(3),2015-44(2),2021-43(3),2024-43(5)(6),2024-44(3),2026-43(3)
相对寻址:目标地址、偏移量、跳转范围、推断公式2010-43(2),2013-44(1)(2),2014-44(3),2019-45(3),2023-44(2)
是什么寻址方式2023-44(2)(3)
大端还是小端 / 从内存表读数2019-45(3),2023-44(3),2024-44(2)
下一条指令地址、代码总长度2017-44(2),2019-45(3),2023-44(1)
读汇编:寄存器里是哪个变量、哪条是条件转移、调用几次2014-44(2),2019-45(1)(2),2023-44(3),2024-44(1)
RISC 还是 CISC2017-44(1)
执行后寄存器/存储单元的内容 / 写一段指令2010-43(3),2026-43(3)(4)

做这类题必须会的 7 件事

① 位数 → 个数。k 位的字段能区分 2k 种东西:操作码 k 位 → 最多 2k 条指令;寄存器号 r 位 → 最多 2r 个寄存器。几种格式共用同一个操作码字段时,被别的格式占掉的编码要减掉(2021-43:6 位操作码本来 64 种,000000 被 R 型占了,I、J 型只剩 63 种)。扩展操作码也是同一个道理:短操作码留出几个编码当“扩展标志”,剩下的才能直接用。

② 各个寄存器的位数。

部件位数等于例
MAR、PC地址的位数 = log2(存储单元个数),或地址线条数128KB 按字(2B)编址 → 216 个单元 → 16 位
MDR一次读写的数据宽度(存储字长 / 数据线宽度)数据线 8 位 → MDR 8 位(2021-43)
IR一条指令的长度16 位定长指令 → 16 位
ALU、通用寄存器机器字长16 位机 → 16 位

③ 编址单位决定 PC 怎么加。按字节编址时,下一条指令地址 = 本条地址 + 本条指令的字节数;按字编址(单字长指令)时 + 1。反过来,从“相邻两条指令地址差几”就能判断编址单位:32 位指令地址差 4 → 按字节编址。

④ 寻址方式速查。A 是指令里的形式地址,EA 是真正的操作数地址。

寻址方式操作数在哪x86 / RISC 里长什么样
立即就在指令里mov [..], 0Ah 的 0Ah;addi 的 imm
直接EA = Amov eax, [00422000h]
寄存器在寄存器 R 里add eax, ecx
寄存器间接EA = (R)(R4)、[eax]、0(r4)
相对EA = (PC) + A,PC 已指向下一条jmp、jxx、call、bne 的偏移
基址EA = (基址寄存器) + A[ebp−8]、lw r5, 0(r4)
变址EA = (变址寄存器) × 比例 + A[ecx+edx*4+00422000h](基址 + 比例变址 + 位移)

⑤ 相对寻址的三个公式(最常考)。下一条地址 = 本条地址 + 本条长度;目标地址 = 下一条地址 + 偏移 × 单位;偏移 = (目标 − 下一条地址) ÷ 单位,写成补码。“单位”是 1(字节)还是指令长度,由题目公式或反推决定。

00401000Hf1 入口(转移目标) …… 第 2 ~ 15 行指令 …… E8 D6 FF FF FF 操作码 偏移量字段(4 字节) 内存里依次是 D6 FF FF FF,拼成的值却是 FFFF FFD6H 低字节放在低地址 → 小端 00401025Hcall 本条地址 0040102AH下一条 = 执行时的 PC 本条长 5 字节:00401025H + 5 = 0040102AH 偏移 = 目标 − 下一条 = 00401000H − 0040102AH = −2AH → 32 位补码 FFFF FFD6H
图 11-2 相对寻址和小端,一张图看完(2019-45 第 16 行的 call)。三步:先用“本条地址 + 本条长度”求下一条地址;再用“目标 − 下一条”求偏移;最后在机器码里找到这个偏移,看字节顺序判断大小端。

⑥ 大端还是小端。在机器码或内存表里找一个值已知的多字节字段(偏移量、立即数、数组首地址),看它的最低字节是不是排在最前面(最低地址):是 → 小端(x86 都是小端);否 → 大端。从内存表读一个 int 时,小端要把 4 个字节倒过来拼。

⑦ 读汇编的套路。不用会写汇编,只要会“认”:

看到意思
mov、lw/load、sw/store传送 / 从内存取 / 存到内存
shl、sll、slli x, k左移 k 位 = × 2k;数组下标 × 元素大小时常见(左移 2 位 → 元素 4 字节)
cmp / test + jle/jge/jne/bne…比较后按标志条件转移 → if 或循环条件
jmp无条件转移
call / ret调用函数(压入返回地址再转移)/ 返回(弹出返回地址)
push ebp; mov ebp, esp函数开头建立栈帧;[ebp+8] 是第一个参数,[ebp−4] 等是局部变量

RISC 还是 CISC:指令长度不一(1、2、3、5、11 字节都有)、运算指令能直接访问内存 → CISC;定长指令、只有 load/store 访存 → RISC。

解题模板

  1. 画格式。把格式图抄下来,每个字段标上位数;题目给的机器码全部写成二进制,按字段竖线切开。
  2. 数量和位宽。个数 = 2位数(共享编码记得减);MAR/PC 看地址,MDR 看数据线,IR 看指令长,ALU 看字长。
  3. 地址计算先求“下一条”。下一条 = 本条 + 本条长度;再加偏移(补码、注意单位)。
  4. 反推类问题用已知的一对地址验算。题目里一般有“某条跳转指令 → 它的目标”,用它反推或验证公式,再用在别的指令上。
  5. 读汇编先给寄存器起名字。对照 C 代码写出“R2 = i,R3 = A 的首地址……”,地址表达式和 C 的数组地址公式逐项对齐。
  6. 执行结果画小表。寄存器一列、内存一列,逐条指令改表,最后回答“谁变了、变成什么”。
  7. 和虚存/Cache 连着考时,把地址切成页号 + 页内偏移(4KB 页 → 去掉最后 3 个十六进制位就是页号),这一部分见 ⑥。
完整示范2019 年第 45 题计组

已知 f(n)=n!=n×(n−1)×(n−2)×⋯×2×1 ,计算 f(n) 的 C 语言函数 f1 的源程序(阴影部分)及其在 32 位计算机 M 上的部分机器级代码如下:

int f1(int n) {
1   00401000   55             push ebp
      ...           ...            ...
   if(n>1)
11  00401018  83 7D 08 01    cmp   dword ptr [ebp+8],1
12  0040101C  7E 17          jle   f1+35h (00401035)
   return n*f1(n-1);
13  0040101E  8B 45 08       mov   eax, dword ptr [ebp+8]
14  00401021  83 E8 01       sub   eax, 1
15  00401024  50             push  eax
16  00401025  E8 D6 FF FF FF call  f1 ( 00401000)
      ...           ...            ...
19  00401030  0F AF C1       imul  eax, ecx
20  00401033  EB 05          jmp   f1+3Ah (0040103a)
   else return 1;
}
21  00401035  B8 01 00 00 00 mov   eax,1
      ...           ...            ...
26  00401040  3B EC          cmp   ebp, esp
      ...           ...            ...
30  0040104A  C3             ret

其中,机器级代码行包括行号、虚拟地址、机器指令和汇编指令,计算机 M 按字节编址,int 型数据占 32 位。请回答下列问题:

(1) 计算 f(10) 需要调用函数 f1 多少次?执行哪条指令会递归调用 f1?

(2) 上述代码中,哪条指令是条件转移指令?哪几条指令一定会使程序跳转执行?

(3) 根据第 16 行的 call 指令,第 17 行指令的虚拟地址应是多少?已知第 16 行的 call 指令采用相对寻址方式,该指令中的偏移量应是多少(给出计算过程)?已知第 16 行的 call 指令的后 4 字节为偏移量,M 是采用大端方式还是采用小端方式?

(4) f(13)=6227020800,但 f1(13) 的返回值为 1932053504,为什么两者不相等?要使 f1(13) 能返回正确的结果,应如何修改 f1 的源程序?

(5) 第 19 行的 imul 指令(带符号整数乘)的功能是 R[eax]←R[eax]×R[ecx],当乘法器输出的高、低 32 位乘积之间满足什么条件时,溢出标志 OF=1?要使 CPU 在发生溢出时转异常处理,编译器应在 imul 指令后应加一条什么指令?

先在纸上自己做一遍,再点这里展开满分解答

考点3.3 过程调用、转移指令3.2 相对寻址2.7 大小端2.5 乘法溢出训练营 ②

先把条件翻译成大白话
  • f1 是递归求阶乘:n > 1 时返回 n × f1(n − 1),否则返回 1。
  • 每行代码:行号 | 虚拟地址 | 机器码 | 汇编。机器码有几个字节,指令就多长;下一条指令的地址 = 本条地址 + 本条长度。
  • x86 的 call、jmp、jle 用相对寻址:偏移量是相对于“下一条指令的地址”算的(执行时 PC 已经指向下一条)。
  • int 是 32 位,最大 231 − 1 = 2147483647。
(1) 计算 f(10) 调用 f1 几次?哪条指令递归调用?
答题卡这样写

计算 f(10) 依次调用 f1(10)、f1(9)、……、f1(1),共 10 次(f1(1) 时 n > 1 不成立,直接返回 1,不再调用)。递归调用由第 16 行的 call 指令完成。

(2) 哪条是条件转移?哪几条一定跳转?
答题卡这样写

条件转移指令:第 12 行 jle(小于等于时转移)。

一定会使程序跳转的指令:第 16 行 call、第 20 行 jmp、第 30 行 ret(调用、无条件转移、返回都一定改变执行顺序)。

ret 也是“一定跳转”(跳回调用处),常被漏掉。

(3) 第 17 行的地址?call 的偏移量?大端还是小端?

call 在 00401025H,机器码 E8 D6 FF FF FF 共 5 字节 → 第 17 行在 0040102AH。call 的目标是 f1 的入口 00401000H,偏移 = 目标 − 下一条指令地址。偏移量的 4 个字节在机器码里是 D6 FF FF FF,而偏移的值是 FFFF FFD6H:最低字节 D6 排在最前 → 小端。

答题卡这样写

call 指令地址 00401025H,长 5 字节,第 17 行指令的虚拟地址 = 00401025H + 5 = 0040102AH。

执行 call 时 PC 已指向下一条指令 0040102AH,转移目标为 f1 的首地址 00401000H,偏移量 = 00401000H − 0040102AH = −2AH,32 位补码为 FFFF FFD6H。

机器码中偏移量字段依次为 D6 FF FF FF,低位字节 D6H 存放在低地址,所以 M 采用小端方式。

可以用第 12 行验算公式:jle 在 0040101CH,长 2 字节,偏移 17H,0040101EH + 17H = 00401035H,正是 else 分支第 21 行的地址 ✓。

(4) f(13) = 6227020800,f1(13) 却是 1932053504,为什么?怎么改?
答题卡这样写

f(13) = 6227020800 > 231 − 1 = 2147483647,超出了 int 型(32 位)的表示范围,乘法运算发生了溢出,只保留了乘积的低 32 位,所以结果不对。

修改:将 f1 的返回值类型(以及参与乘法的变量)改为 long long(64 位整数)或 double 型。

(5) imul 何时 OF = 1?要在溢出时转异常处理,编译器应加什么指令?

32 位 × 32 位得到 64 位乘积,结果放回 32 位的 eax。只有当高 32 位只是低 32 位的“符号扩展”时,截断才不丢信息。

答题卡这样写

当乘积的高 32 位中每一位都与低 32 位的最高位相同(即 64 位乘积的高 33 位全 0 或全 1)时不溢出;否则乘积不能用 32 位表示,OF = 1。

编译器应在 imul 后加一条溢出自陷指令(如 x86 的 INTO),OF = 1 时触发异常,转入操作系统的溢出处理程序。

  • 下一条指令地址 = 本条地址 + 机器码字节数;相对偏移 = 目标 − 下一条地址。
  • 从机器码看大小端:找一个已知值的多字节字段(偏移、立即数、地址),看低字节是否在前。
  • 有符号乘法溢出:高半部分不是低半部分的符号扩展。

模拟大题:一个循环函数的机器级代码(按 408 风格自编)

机器级代码题的真题(2017-44、2019-45、2021-43 等)都给一段反汇编,问 C 语句对应哪几条指令、转移目标怎么算、栈里放了什么、某条指令访存几次。下面这段代码按 gcc(-m32 -O0)的编译结果整理,每条指令的机器码都用汇编器重新核对过,偏移量都是真的,只把起始地址放到了 0040 1000H。不同版本的 gcc 给 s、i 分配的栈位置可能对调,传参也可能改用 push 指令——考场上一律以题目给出的代码为准。

模拟题for 循环 + 函数调用的反汇编计组 · 约 12 分

在 IA-32 机器上,C 函数如下(int 型占 4B,小端方式):

static int sum(int a[], int n) {
    int s = 0;
    for (int i = 0; i < n; i++)
        s += a[i];
    return s;
}
int g(int *p) { return sum(p, 10); }

反汇编结果如下(Intel 格式,每行依次为:地址、机器码、汇编指令):

sum:
00401000  55                       push  ebp
00401001  89 E5                    mov   ebp, esp
00401003  83 EC 10                 sub   esp, 0x10
00401006  C7 45 FC 00 00 00 00     mov   dword ptr [ebp-0x4], 0
0040100D  C7 45 F8 00 00 00 00     mov   dword ptr [ebp-0x8], 0
00401014  EB 18                    jmp   0040102E
00401016  8B 45 F8                 mov   eax, dword ptr [ebp-0x8]
00401019  8D 14 85 00 00 00 00     lea   edx, [eax*4+0x0]
00401020  8B 45 08                 mov   eax, dword ptr [ebp+0x8]
00401023  01 D0                    add   eax, edx
00401025  8B 00                    mov   eax, dword ptr [eax]
00401027  01 45 FC                 add   dword ptr [ebp-0x4], eax
0040102A  83 45 F8 01              add   dword ptr [ebp-0x8], 1
0040102E  8B 45 F8                 mov   eax, dword ptr [ebp-0x8]
00401031  3B 45 0C                 cmp   eax, dword ptr [ebp+0xc]
00401034  7C E0                    jl    00401016
00401036  8B 45 FC                 mov   eax, dword ptr [ebp-0x4]
00401039  C9                       leave
0040103A  C3                       ret
g:
0040103B  55                       push  ebp
0040103C  89 E5                    mov   ebp, esp
0040103E  83 EC 08                 sub   esp, 0x8
00401041  C7 44 24 04 0A 00 00 00  mov   dword ptr [esp+0x4], 0xa
00401049  8B 45 08                 mov   eax, dword ptr [ebp+0x8]
0040104C  89 04 24                 mov   dword ptr [esp], eax
0040104F  E8 AC FF FF FF           call  sum
00401054  C9                       leave
00401055  C3                       ret

(1) 局部变量 s、i 和参数 a、n 分别存放在哪里(用相对 ebp 的形式表示)?“s += a[i]”“i++”“i < n”分别对应哪几条指令?(3 分)

(2) 调用 sum(p, 10) 时,地址为 0040 1034H 的 jl 指令共执行几次?其中几次发生跳转?jl 的机器码是 7C E0,转移目标 0040 1016H 是怎样算出来的?(3 分)

(3) 若把 i 和 n 都声明为 unsigned int,cmp 指令和 jl 指令分别要不要改?若要改,改成什么?(2 分)

(4) call 指令的机器码是 E8 AC FF FF FF,它的转移目标是怎样算出来的?执行 call 时 CPU 做了哪两件事?设 sum 执行完“mov ebp, esp”后 ebp = 0019 FF00H,写出返回地址、g 的 ebp 旧值、参数 n 所在单元的地址,以及执行完“sub esp, 0x10”后 esp 的值。(2 分)

(5) 地址为 0040 1027H 的“add dword ptr [ebp-0x4], eax”执行时(不含取指令)要访问主存几次?地址为 0040 1019H 的 lea 指令呢?从本段代码看,IA-32 属于 CISC 还是 RISC?举出两个依据。(2 分)

先在纸上自己做一遍,再点这里展开满分解答

考点3.3 x86 汇编3.3 过程调用与栈帧3.2 寻址方式全表3.4 CISC 与 RISC

先把条件翻译成大白话
  • 栈帧的固定格局:[ebp] 是调用者 ebp 的旧值,[ebp+4] 是返回地址,[ebp+8] 是第 1 个参数,[ebp+0xC] 是第 2 个参数,[ebp−4]、[ebp−8]…… 是局部变量。
  • 相对转移:目标地址 = 下一条指令的地址 + 偏移量(偏移量是补码,机器码按小端存放)。
  • -O0 下 for 循环的典型形状:先 jmp 到条件判断处;循环体在上面,条件判断在下面;条件成立就往回跳。
(1) 变量位置与语句对应
答题卡这样写

s 在 [ebp−4],i 在 [ebp−8](从两条赋 0 的 mov 可以看出),a 在 [ebp+8],n 在 [ebp+0xC]。

“s += a[i]”对应 0040 1016H ~ 0040 1027H 的 6 条指令:取 i,算 i × 4,取 a,相加得 &a[i],读 a[i],加到 s 上。

“i++”对应 0040 102AH 的 add dword ptr [ebp−8], 1。

“i < n”对应 0040 102EH ~ 0040 1034H 的 mov、cmp、jl 三条指令。

(2) jl 的执行次数与转移目标
答题卡这样写

n = 10,i 从 0 取到 10,每次都要判断一次,所以 jl 共执行 11 次。i = 0 ~ 9 时条件成立,跳转 10 次;i = 10 时不跳,顺序执行 0040 1036H。

jl 是相对转移,偏移量 E0H 是 8 位补码,值为 −20H(−32)。下一条指令地址 = 0040 1034H + 2 = 0040 1036H,目标地址 = 0040 1036H − 20H = 0040 1016H。

偏移量相对的是“下一条指令的地址”(执行 jl 时 PC 已经指向下一条),不是 jl 自己的地址。同样,0040 1014H 的 jmp(EB 18):0040 1016H + 18H = 0040 102EH。

(3) 改成 unsigned 之后
答题卡这样写

cmp 不用改:比较就是做减法并设置标志位,有符号数和无符号数的减法电路相同。

jl 要改为 jb(低于则转移)。jl 按有符号数比较,根据 SF ⊕ OF = 1 转移;jb 按无符号数比较,根据 CF = 1 转移。

(4) call 的目标与栈帧里的地址
答题卡这样写

偏移量按小端读作 FFFF FFACH,即 −54H(−84)。下一条指令地址 = 0040 104FH + 5 = 0040 1054H,目标 = 0040 1054H − 54H = 0040 1000H,即 sum 的入口。

执行 call 时,CPU 先把返回地址 0040 1054H 压栈,再把 EIP 改为 0040 1000H。

ebp = 0019 FF00H 时:g 的 ebp 旧值在 [ebp] = 0019 FF00H 单元;返回地址 0040 1054H 在 0019 FF04H;参数 n(= 10)在 [ebp+0xC] = 0019 FF0CH。执行完 sub esp, 0x10 后,esp = 0019 FF00H − 10H = 0019 FEF0H。

g 里的两条 mov 把 10 放到 [esp+4]、把 p 放到 [esp]:参数按从右到左的顺序放,第 1 个参数离返回地址最近。

(5) 访存次数与 CISC 的依据
答题卡这样写

add dword ptr [ebp−4], eax 的目的操作数在主存中:先读 [ebp−4],相加后再写回 [ebp−4],共访存 2 次。

lea 只计算地址 eax × 4 + 0 并送入 edx,不访存(0 次)。

IA-32 属于 CISC。依据:① 指令长度不固定,本段中有 1、2、3、4、5、7、8 字节的指令;② 运算指令可以直接对主存操作数运算(如 add [ebp−4], eax),不只有 load/store 能访存;③ 寻址方式复杂,如比例变址 [eax*4+0x0]。

判断访存次数时只看“数据”:取指令本身也访存,但题目一般说明“不含取指令”。mov eax, [eax] 访存 1 次,lea edx, [eax*4] 不访存。

  • 看 -O0 代码先认栈帧:ebp 往上是参数和返回地址,往下是局部变量。
  • 相对转移一律用“下一条指令地址 + 补码偏移”算,机器码按小端读。
  • cmp 与有无符号无关,区分有无符号靠条件转移:jl/jg 看 SF、OF,jb/ja 看 CF。

举一反三:条件一变,答案怎么变

以上面的模拟题为基准,每行只改一个条件。先自己想出新答案和理由,再点开核对。

条件怎么变新答案和理由
调用 sum(p, 0)
想好了再点开头的 jmp 直接跳到条件判断处,jl 执行 1 次、不跳转,循环体一次也不执行。
循环体里多了 10 字节的指令,jl 仍紧跟在循环体后面
想好了再点开头的 jmp 要多往前跳 10 字节:18H + 0AH = 22H。jl 要多往回跳 10 字节:E0H − 0AH = D6H(−42)。
for 循环改成 do-while 循环
想好了再点开头没有那条 jmp,先执行一次循环体,再判断条件是否往回跳。
问 call 指令中偏移量字段的值
想好了再点FFFF FFACH,即 −84。机器码中按小端存放,字节顺序是 AC FF FF FF。
sum 放在 g 的后面
想好了再点转移目标在 call 的下一条指令之后,call 的偏移量变成正数。计算方法不变:目标 = 下一条指令地址 + 偏移量。
用 -O1 优化编译
想好了再点局部变量都放在寄存器里,不再访问栈;循环改成用指针遍历数组,条件判断变成 cmp eax, ecx 和 jne,访存次数大大减少。

丢分点清单

  • 相对寻址的偏移从“本条指令地址”算——应该从下一条指令地址算(除非题目公式另有规定)。
  • 机器码字节数数错,导致下一条地址全错(一个字节 = 两个十六进制数字)。
  • 按字编址和按字节编址混用:按字编址时自增 1、PC + 1。
  • 负的偏移没有写成补码,或写成 8 位/16 位补码但题目要 32 位。
  • 代码总长度忘了加上最后一条指令的长度。
  • 共用操作码字段时忘了减去被占用的编码。
  • MDR 按字长算,但题目给了数据线宽度(按数据线算)。
  • 读小端内存时忘了倒序,或者数错了内存表的列。

本类其他真题(先做再看解答)

  • 2010-43:格式图 → 指令条数、寄存器数、MAR/MDR;相对寻址范围;写机器码并执行(寄存器间接自增)。
  • 2013-44:条件转移指令,检测位 C/Z/N;目标地址 (PC) + 2 + 2 × OFFSET;无符号比较用哪些标志。
  • 2014-44:一段循环的机器码:编址单位、元素大小、由地址反推 bne 的偏移公式、流水线阻塞。
  • 2017-44:x86 代码:CISC 判断、代码长度、cmp 后的 CF、float 能不能用 shl。
  • 2021-43:R/I/J 三种格式:ALU/IR/MAR/MDR 位数、操作种类(63)、解码 01B2H 并判溢出、符号扩展。
  • 2023-44:x86 代码:下一条地址、相对寻址计算、立即寻址、ecx 是什么、小端、取指是否缺页。
  • 2024-43:RISC-V 风格的 add/slli/lw:寄存器数、标志位、Ext 与 ALUctr、解码 lw 并算地址。
  • 2024-44:sum += a[i] 的 4 条指令:寄存器分配、小端内存表读数、页号、写 slli 的机器码。
  • 2026-43:R/I/M 三种 16 位格式:操作码能否重码、执行一条加法、自己写出 y = 16x − 5 的 4 条指令。

11.4计组 ③ 数据通路与控制信号:一拍一拍写微操作

★★★ 高频综合题

先读:1.3 一条指令怎么执行 4.1 几种周期 4.2 数据通路 4.3 控制器

基本功:知识篇里对应的例题和总结表(先做这些,再做本类真题)

这类题长什么样

题目画一张 CPU 内部的连线图(数据通路),图上每根虚线是一个控制信号。然后让你:按节拍写出某条指令要做的事和要发的控制信号;解释某个部件为什么存在;补连线;算控制信号的位数。看上去图很吓人,其实每一问都只用到几条固定规则。

常见问法考过的地方
按节拍写出执行阶段的功能和控制信号2009-44,2015-44(3)(4),2022-43(4)
多路选择器式通路:控制信号取几2024-43(2)(3)(4),2026-44(3)(4)
为什么要设暂存器(T / Y / Z)、哪些寄存器程序员可见2015-43(1),2022-43(2)
控制信号至少几位2015-43(2),2026-44(2)
部件名称、补连线、哪些端点接控制部件2013-44(4),2015-43(3)~(6),2026-44(1)
寄存器编号接译码器还是多路选择器、控制部件的输入是什么2022-43(3)(5)
标志位的逻辑表达式2022-43(1)

做这类题必须会的 6 件事

① 认识图上的“角色”。

部件干什么程序员能看见吗
PC下一条指令的地址能
IR正在执行的指令;操作码送控制器,寄存器号、立即数等字段送通路不能
MAR / MDR访存时放地址 / 放数据,是 CPU 和主存之间的“收发室”不能
通用寄存器 R0 ~ Rn放操作数和结果能
暂存器(Y、Z、T、A、AC)单总线时给 ALU 临时存输入或输出不能
标志寄存器(PSW / FR)存 ZF、SF、CF、OF能(条件转移要用)
多路选择器 MUX多个输入选一个输出,选择信号 = 输入端编号—
三态门控制一个部件的输出接不接到总线上—
译码器把 k 位编号变成 2k 根线中的一根(写寄存器时选中目标)—
扩展器把指令里的短立即数/偏移扩成字长(零扩展或符号扩展)—

② 控制信号的名字就是“开哪个门”。Xin:总线上的数打入 X;Xout:X 的内容送上总线;MemR / Read:读主存;MemW / Write:写主存;Add、ALUop = …:ALU 做什么运算。写“某件事”的信号,就是“源 out,目的 in”。

③ 单总线的三条规则。一条总线像一条单车道,每个时钟周期只能过一辆车:

内总线 PC PCout/in IR IRin 操作码 → 控制器 MAR MARin MDR MDRout/in 主存 地址 数据 MemR / MemW R0 R1 R2 Y Yin ALU AB Z Zin Zout 1 每个节拍总线上只能有一个 out(一辆车) in 可以有多个(多个人同时抄) 2 ALU 两个输入不能同时从总线来: 先把一个存进 Y 3 算的时候总线被 B 端占着:结果先进 Z,下一拍再 Zout
图 11-3 单总线 CPU 的三条规则。做加法 R0 + R1 → R2 至少 3 拍:① R0out, Yin;② R1out, Add, Zin;③ Zout, R2in。有了这三条规则,“为什么要设暂存器 Y、Z / T”(2015-43、2022-43)的答案也就有了。多路选择器式(多总线)通路没有这个限制,一拍里可以同时做好几件不冲突的事。

④ 取指令三拍,几乎每题都一样。

节拍功能控制信号
1MAR ← (PC)PCout,MARin
2MDR ← M(MAR);PC ← (PC) + 1(或 + 2、+ 4)MemR(Read),MDRinE;PC+1
3IR ← (MDR)MDRout,IRin

PC 增量有两种做法:PC 自己带计数功能(信号 PC+1);或者借 ALU 算 (PC) + 常数(2015-43 的 MUX 输入端 2、2026-44 的 ALUBSrc 选常数 2),这时 PC 增量会占用 ALU 和总线,要单独排节拍。

⑤ 访存固定两拍。读:MAR ← 地址(Xout, MARin);MDR ← M(MAR)(MemR, MDRinE)。写:MDR ← 数据(Xout, MDRin);M(MAR) ← (MDR)(MemW, MDRoutE)。只要中间没改过 MAR,写回同一地址时不用再送一次地址。

⑥ 控制器从哪来、控制信号几位。控制信号都由控制器 CU 产生,CU 的输入是:IR 中的操作码(做什么)、标志寄存器(条件是否满足)、时钟和节拍(什么时候做)。一个部件有 n 种操作,控制它的信号至少 ⌈log2n⌉ 位。微程序控制器里,同一字段内互斥的 n 个信号编码成 ⌈log2(n + 1)⌉ 位(多出的 1 个编码表示“一个都不发”)——这一点选择题常考(详见 4.3)。

解题模板

  1. 读图。找出总线、每个寄存器的 in/out 信号、暂存器接在 ALU 哪一端、每个 MUX 各输入端的编号、MDR 连内总线和外部总线的信号名各是什么。
  2. 把指令拆成几件事。一般是:取操作数(要访存的先送地址、再读)→ 运算 → 送结果(写寄存器,或写回主存)。先确认结果写到哪里:“(R1)”是寄存器 R1,“((R1))”是 R1 指向的主存单元。
  3. 每件事写成微操作。“目的 ← 源”,旁边写信号“源out,目的in”,运算再加 ALU 的操作信号。
  4. 排节拍。单总线每拍一个 out;访存按题目给的周期数;读主存时总线空闲,可以顺手做一次寄存器传送。
  5. 检查。MDRinE 和 MDRin、MDRout 和 MDRoutE 有没有用对;ALU 的一端是否已经在暂存器里;结果是否先进了 Z/AC。
  6. 按题目的表格格式写。节拍 | 功能 | 有效控制信号,一行一拍。
完整示范2009 年第 44 题计组

某计算机字长 16 位,采用 16 位定长指令字结构,部分数据通路结构如下图所示,图中所有控制信号为 1 时表示有效、为 0 时表示无效。例如控制信号 MDRinE 为 1 表示允许数据从 DB 打入 MDR,MDRin 为 1 表示允许数据从内总线打入 MDR。假设 MAR 的输出一直处于使能状态。加法指令“ADD (R1), R0”的功能为 (R0) + ((R1)) → (R1),即将 R0 中的数据与 R1 的内容所指主存单元的数据相加,并将结果送入 R1 的内容所指主存单元中保存。

内总线 R0 R1 R0in/out R1in/out A Ain ALU ADD AC ACin ACout PCPC+1 PCin/out IR IRin MAR MARin MDR MDRin/out 存储器 M 地址总线 AB 数据总线 DBMDRinE / MDRoutE 控制总线:MemR / MemW
(按原卷图重绘)

下表给出了上述指令取指和译码阶段每个节拍(时钟周期)的功能和有效控制信号,请按表中描述方式用表格列出指令执行阶段每个节拍的功能和有效控制信号。

时钟功能有效控制信号
C1MAR ← (PC)PCout, MARin
C2MDR ← M(MAR),PC ← (PC) + 1MemR, MDRinE, PC+1
C3IR ← (MDR)MDRout, IRin
C4指令译码无
先在纸上自己做一遍,再点这里展开满分解答

考点4.2 单总线数据通路1.3 指令执行过程训练营 ③

先把条件翻译成大白话
  • 单总线:CPU 内部只有一条内总线,每个节拍总线上只能有一个部件往外送数(一个 out)。
  • ALU 有两个输入:一个来自暂存器 A,另一个直接来自内总线;结果先打入 AC,再由 AC 送回总线。
  • MDR 有两个入口:MDRinE 从外部数据总线 DB 打入(读内存时用),MDRin 从内总线打入(写内存前用);出口也有两个:MDRout 送内总线,MDRoutE 送 DB(写内存时用)。
  • “ADD (R1), R0”:(R0) + ((R1)) → (R1),即 结果写回 R1 所指的主存单元,不是写进 R1。
列出执行阶段每个节拍的功能和有效控制信号

先把指令拆成三件事:① 以 (R1) 为地址读出操作数;② 与 (R0) 相加;③ 把和写回同一个地址。再按“每拍总线上只有一个 out”排节拍:ALU 的一个输入要提前放进 A,结果要先进 AC。

答题卡这样写
时钟功能有效控制信号
C5MAR ← (R1)R1out,MARin
C6MDR ← M(MAR)MemR,MDRinE
C7A ← (R0)R0out,Ain
C8AC ← (MDR) + (A)MDRout,Add,ACin
C9MDR ← (AC)ACout,MDRin
C10M(MAR) ← (MDR)MDRoutE,MemW

① 写回时 MAR 中仍是 (R1)(中间没有改过 MAR),不必再送一次地址。② 从 DB 进 MDR 用 MDRinE,从内总线进 MDR 用 MDRin,出去同理——这两对信号不能写混。③ C6 读主存时内总线空闲,也可以把 C7 的 A ← (R0) 并到 C6 里做,少一个节拍,同样正确。

  • 写控制信号的格式:“目的 ← 源”对应“源out,目的in”。
  • 读内存两拍:送地址(MARin)、读(MemR + MDRinE);写内存两拍:数据进 MDR、写(MemW + MDRoutE)。
  • 单总线:每拍一个 out;ALU 一端先进暂存器,结果先进 AC/Z。

模拟大题:自增寻址、条件转移与微指令格式(按 408 风格自编)

数据通路题的真题大多问一条“运算 + 访存”指令的执行节拍(2009-44、2015-44、2022-43)。下面这道题用本节上面“单总线 CPU 的三条规则”图里那种单总线 CPU,考三件真题问得少、但最能检验是否真懂的事:节拍数的下界怎么论证、条件转移指令怎样由标志位控制、微指令每个字段几位。

模拟题单总线 CPU 的节拍与控制计组 · 约 12 分

某 16 位计算机的 CPU 采用单总线结构,内部有 PC、IR、MAR、MDR、通用寄存器 R0~R3、暂存器 Y 和 Z。

  • ALU:A 端固定接 Y,B 端接内总线,结果送 Z。ALU 能做 Add(A + B)、Sub(A − B)、Inc(B + 1)、And、Or 五种运算。
  • 主存:按字编址,读、写各用 1 个节拍。MDR 与内总线之间的控制信号为 MDRin / MDRout,与外部数据总线之间的为 MDRinE / MDRoutE。
  • IR:指令中的 8 位偏移量 disp 经符号扩展后,可由信号 IRout 送上内总线。
  • 取指:取指阶段需 3 个节拍,并已由 PC 自带的计数功能完成 PC + 1。

(1) 指令“ADD R0, (R1)+”的功能是:((R1)) + (R0) → R0,然后 (R1) + 1 → R1。按“节拍 | 功能 | 有效控制信号”的格式写出它的执行阶段。(4 分)

(2) 这条指令的执行阶段最少需要几个节拍?说明理由,并写出节拍最少的方案。(3 分)

(3) 指令“BZ disp”的功能是:若 ZF = 1,则 (PC) + disp → PC,否则什么也不做。写出 ZF = 1 时执行阶段的微操作和控制信号。分别说明硬布线控制器和微程序控制器是怎样根据 ZF 决定是否执行这些微操作的。(2 分)

(4) 采用字段直接编码的水平型微指令,控制字段分组如下:内总线输出信号 8 个(PCout、IRout、MDRout、Zout、R0out~R3out),任一时刻最多一个有效;寄存器输入信号 10 个(PCin、IRin、MARin、MDRin、Yin、Zin、R0in~R3in),可以同时有效;ALU 操作 5 个,互斥;主存读、写 2 个,互斥;PC+1 信号 1 个。顺序控制部分:控存共 100 条微指令,用断定法给出下址;条件测试有“无条件、测 ZF、按操作码转移”3 种。问微指令至少多少位?(3 分)

先在纸上自己做一遍,再点这里展开满分解答

考点4.2 单总线四类基本操作4.3 硬布线控制器4.3 微程序控制器训练营 ③ 单总线三条规则

先把条件翻译成大白话
  • 单总线:每拍只能有一个 out,in 可以有多个;ALU 的一端必须先放进 Y,结果先进 Z。
  • 读主存的那一拍用的是外部总线(MemR、MDRinE),内总线空着,可以顺便做一次寄存器传送。
  • (R1)+ 是自增寻址:先用 R1 的内容作地址取操作数,再把 R1 加 1(按字编址,加 1 就是下一个字)。
(1) ADD R0, (R1)+ 的执行阶段
答题卡这样写
节拍功能有效控制信号
C1MAR ← (R1)R1out,MARin
C2MDR ← M(MAR);Y ← (R0)MemR,MDRinE;R0out,Yin
C3Z ← (Y) + (MDR)MDRout,Add,Zin
C4R0 ← (Z)Zout,R0in
C5Z ← (R1) + 1R1out,Inc,Zin
C6R1 ← (Z)Zout,R1in

C2 里读主存用的是外部数据总线,内总线空闲,所以能同时做 R0out、Yin。不要写成 MDRin,从外部总线进 MDR 的信号是 MDRinE。

(2) 最少几个节拍

单总线每拍只能有一个 out,所以“至少要用几次内总线”就是节拍数的下界。数一数哪些数据必须经过内总线。

答题卡这样写

必须经过内总线的传送有 5 次:① R1 送出(给 MAR,同时可作为 ALU 的 B 输入算 R1 + 1);② R0 送 Y;③ MDR 送 ALU;④ Z 送 R0;⑤ Z 送 R1。每拍只能有一个 out,所以至少 5 个节拍。

节拍功能有效控制信号
C1MAR ← (R1);Z ← (R1) + 1R1out,MARin,Inc,Zin
C2MDR ← M(MAR);R1 ← (Z)MemR,MDRinE;Zout,R1in
C3Y ← (R0)R0out,Yin
C4Z ← (Y) + (MDR)MDRout,Add,Zin
C5R0 ← (Z)Zout,R0in

C1 中 R1 的内容只上一次总线,同时送进 MAR 和 ALU(一个 out,多个 in,不冲突);C2 利用读主存时内总线空闲,把 R1 + 1 写回。

C2 之后 Z 被 R1 + 1 用掉了,所以加法要在 C4 重新写 Zin;只要不在同一拍出现两个 out、不在用完 Z 之前覆盖它,顺序可以灵活安排。

(3) BZ disp 与 ZF
答题卡这样写
节拍功能有效控制信号
C1Y ← (PC)PCout,Yin
C2Z ← (Y) + dispIRout,Add,Zin
C3PC ← (Z)Zout,PCin

取指时 PC 已经加 1,所以转移目标 = 本指令地址 + 1 + disp。ZF = 0 时执行阶段不做任何操作,直接去取下一条指令。

硬布线控制器:ZF 是组合逻辑电路的一个输入,这几个控制信号的逻辑表达式中都含有 ZF 因子,例如 PCin = … + BZ · C3 · ZF。ZF = 0 时这些信号不会有效。

微程序控制器:按操作码转到 BZ 的微程序时,先用一条条件测试字段为“测 ZF”的微指令做判断(也可以把这个测试放在前一条微指令里)。ZF = 1 时转去执行上面三拍对应的微指令,ZF = 0 时转回取指微程序。

(4) 微指令字长

互斥的一组信号编码成一个字段:n 个信号要 ⌈log2(n + 1)⌉ 位,“+1”留给“都不发”。可以同时有效的信号不能编码,只能每个 1 位(直接控制)。

答题卡这样写

总线输出:8 个互斥,⌈log29⌉ = 4 位。寄存器输入:10 个可以同时有效,直接控制,10 位。ALU:5 个互斥,⌈log26⌉ = 3 位。主存读写:2 个互斥,⌈log23⌉ = 2 位。PC+1:1 位。

下址字段:100 条微指令,⌈log2100⌉ = 7 位。条件测试字段:3 种,⌈log23⌉ = 2 位。

微指令至少 4 + 10 + 3 + 2 + 1 + 7 + 2 = 29 位。

编码时一定要给“都不发”留一个码,所以 8 个信号是 ⌈log29⌉ = 4 位,不是 3 位。寄存器输入信号能同时有效,所以不能编进一个字段。

  • 单总线下,节拍数的下界 = 必须经过内总线的传送次数。
  • 读主存的那一拍内总线空闲,可以顺手做一次寄存器传送。
  • 条件转移的“条件”,在硬布线中是逻辑表达式里的一个因子,在微程序中是微指令里的一个条件测试字段。

举一反三:条件一变,答案怎么变

以上面的模拟题为基准,每行只改一个条件。先自己想出新答案和理由,再点开核对。

条件怎么变新答案和理由
ADD R0, (R1)(没有自增)
想好了再点最少 4 拍:C1 R1out, MARin;C2 MemR, MDRinE, R0out, Yin;C3 MDRout, Add, Zin;C4 Zout, R0in。
ADD (R1), R0(结果写回 R1 指向的主存单元)
想好了再点共 5 拍:C1 R1out, MARin;C2 MemR, MDRinE, R0out, Yin;C3 MDRout, Add, Zin;C4 Zout, MDRin;C5 MemW, MDRoutE。MAR 没有改过,写回时不用再送一次地址。
PC 没有自增功能,要借 ALU 算 PC + 1
想好了再点取指仍可以 3 拍:C1 PCout, MARin, Inc, Zin;C2 MemR, MDRinE, Zout, PCin;C3 MDRout, IRin。读主存那一拍的内总线正好用来写回 PC。
改成三总线或多路选择器式的通路
想好了再点不再受“每拍只能一个 out”的限制,一拍里可以同时做几件不冲突的事,节拍数可以减少,但连线和多路选择器更多。
内总线输出信号改为 7 个
想好了再点⌈log28⌉ = 3 位,比原来少 1 位,微指令为 28 位。
控存改为 200 条微指令
想好了再点下址字段要 ⌈log2200⌉ = 8 位。

丢分点清单

  • 一个节拍里写了两个 out(单总线冲突)。
  • ALU 两个输入都“从总线来”,忘了先送暂存器。
  • 把 ((R1)) 的结果写进了 R1,而不是写回 R1 指向的主存单元。
  • MDR 的内/外两对信号用混(从外部总线进是 MDRinE,从内总线进是 MDRin)。
  • 多路选择器的取值写成信号名而不是输入端编号。
  • “程序员可见”把 MAR、MDR、IR 也算进去。
  • 控制信号位数按操作种数直接写(7 种操作写 7 位),忘了编码只需 ⌈log27⌉ = 3 位。

本类其他真题(先做再看解答)

  • 2015-43:单总线 + 移位寄存器 SR:程序员可见寄存器、暂存器 T 的作用、ALUop/SRop 位数、三态门、补连线、MUX 为什么有常数 2。
  • 2015-44:接上题:指令编码、按样板填 ① ~ ⑧ 的控制信号、数执行阶段的节拍数。
  • 2022-43:单总线 + Y、Z:SF/OF 的逻辑表达式、暂存器作用、rd 接译码器、取指至少几拍、控制器的输入。
  • 2026-44:多路选择器式通路:部件名称、ExtOp 位数、取指周期和左移指令的控制信号取值。
  • 2024-43(2)~(4):ALUBsrc、Ext、ALUctr 的取值(多路选择器式)。

11.5计组 ④ 指令流水线:画时空图、找冒险、数阻塞

★★ 常考综合题

先读:4.4 指令流水线 4.1 几种周期 1.8 性能指标

基本功:知识篇里对应的例题和总结表(先做这些,再做本类真题)

这类题长什么样

流水线整道大题只考过一次(2012-44),但作为大题的一问(2014-44(4))和选择题(几乎每两年一道)出现得很频繁。题型非常固定:算时间/吞吐率、找数据冒险、按题目约定画时空图数周期、说出阻塞原因和解决办法。

常见问法考过的地方
无阻塞时 n 条指令要几个周期 / 吞吐率 / 时钟周期取多长2012-44(2),2009-18,2013-18,2018-20
哪些指令之间有数据冒险 / 哪些会阻塞2014-44(4),2016-19,2019-18,2023-19
画出执行过程,数总周期2012-44(4)
某条指令为什么阻塞 / 为什么不阻塞2012-44(3),2014-44(4)
哪条指令引起控制冒险2014-44(4)
冒险的解决办法、什么有利于流水线、超标量2010-19,2011-18,2014-16,2017-17,2024-19

做这类题必须会的 5 件事

① 五个段各干什么、用什么部件。IF 用 PC 和指令存储器取指令;ID 译码并读寄存器;EX 用 ALU 运算或算地址;M 访问数据存储器(只有 load/store 真正用);WB 把结果写回寄存器(store 和转移指令没有要写的)。记住“ID 读、WB 写”,数据冒险就全靠这两个位置判断。

② 三个公式。k 段流水线、n 条指令、时钟周期 Δt,无阻塞时:

总时间 T = (k + n − 1) × Δt  (第 1 条要 k 拍,之后每拍出 1 条) 吞吐率 TP = n / T  加速比 S = n·k·Δt / T  效率 E = n·k·Δt / (k·T) 时钟周期 Δt = 最慢那一段的时间(+ 段间寄存器的延迟)

③ 三种冒险。

冒险大白话怎么办
结构冒险两条指令同一拍要用同一个部件(典型:一条在 M 段访存,另一条在 IF 取指,都要用存储器)后一条等;指令 Cache 和数据 Cache 分开(2014-16)
数据冒险后一条要读的寄存器,前一条还没写回(写后读 RAW)阻塞(插气泡);转发/旁路;编译器调整顺序或插 nop
控制冒险遇到转移指令,要等几拍才知道下一条从哪取阻塞;分支预测;延迟转移

④ 阻塞几拍,全看题目约定。同样是“I1 写 R1、I2 紧接着读 R1”,约定不同,结果不同:

时钟周期 → 1 2 3 4 5 6 7 8 9 ① 没有转发,同一寄存器同一拍不能又写又读(408 最常见,2012-44) I1 IF ID EX M WB I2 IF 停 停 停 ID EX M WB I2 的 ID 排在 I1 的 WB 下一拍:停 3 拍 ② 没有转发,但“前半拍写、后半拍读” I1 IF ID EX M WB I2 IF 停 停 ID EX M WB I2 的 ID 可以和 I1 的 WB 同一拍:停 2 拍 ③ 有转发(旁路),I1 是运算指令 I1 IF ID EX M WB I2 IF ID EX M WB I1 的 EX 一结束就把结果直接送给 I2 的 EX:不停 ④ 有转发,但 I1 是 load(load-use) I1 IF ID EX M WB I2 IF ID 停 EX M WB 数据到 M 段末才有:停 1 拍再转发
图 11-4 同一对指令,四种约定,四种停法。408 大题默认的是 ①(2012-44 原话:“没有采用转发技术”“同一个寄存器的读和写操作不能在同一个时钟周期内进行”)。所以最实用的一句口诀:用到结果的指令,它的 ID 排在产生者 WB 的下一拍。有转发时只有 load-use 还要停 1 拍(2024-19:“所有数据冒险都能靠转发解决”是错的)。

⑤ 按序发射的连锁反应。前一条卡在 ID,后一条就只能卡在 IF(IF 段被占着,再后面的也进不来)。所以画图时,每条指令的 IF 最早排在“前一条的 ID 那一拍”。

拓展 · 超标量和动态流水线

超标量 = 同时开好几条流水线,一个时钟周期能发射多条指令(每段的时间并没有变短,2017-17);动态流水线 = 允许后面不相关的指令“插队”先执行,等前面卡住的指令数据到齐再继续,结果再按原顺序提交。408 只考概念,知道“超标量靠多发射提高并行度、不缩短单段时间”就够了。

解题模板

  1. 抄约定。在草稿纸上写下:有没有转发?同一拍能否先写后读?分支阻塞几拍?是不是按序发射?
  2. 列“读写表”。每条指令一行:写哪个寄存器、读哪些寄存器(store 读它要存的寄存器;load 的地址寄存器也是读)。
  3. 连依赖。对每条指令,找它读的寄存器是前面哪一条最后写的(只看写后读)。
  4. 从上往下排时空图。每条指令:IF = max(上一条 IF + 1, 上一条 ID);ID = max(IF + 1, 所依赖指令的 WB + 1(按约定 ①));之后 EX、M、WB 各 + 1。
  5. 数周期、写原因。总周期 = 最后一条指令 WB 所在的拍。解释阻塞时写清“与哪条指令有什么相关、为什么要等”。
完整示范2012 年第 44 题计组

某 16 位计算机中,带符号整数用补码表示,数据 Cache 和指令 Cache 分离。下表给出了指令系统中部分指令格式,其中 Rs 和 Rd 表示寄存器,mem 表示存储单元地址,(x) 表示寄存器 x 或存储单元 x 的内容。

名称指令的汇编格式指令功能
加法指令ADD Rs, Rd(Rs) + (Rd) → Rd
算术/逻辑左移SHL Rd2 × (Rd) → Rd
算术右移SHR Rd(Rd) / 2 → Rd
取数指令LOAD Rd, mem(mem) → Rd
存数指令STORE Rs, mem(Rs) → mem

该计算机采用 5 段流水方式执行指令,各流水段分别是取指(IF)、译码/读寄存器(ID)、执行/计算有效地址(EX)、访问存储器(M)和结果写回寄存器(WB),流水线采用“按序发射,按序完成”方式,没有采用转发技术处理数据相关,并且同一个寄存器的读和写操作不能在同一个时钟周期内进行。请回答下列问题:

(1) 若 int 型变量 x 的值为 −513,存放在寄存器 R1 中,则执行指令“SHR R1”后,R1 的内容是多少(用十六进制表示)?

(2) 若某个时间段中,有连续的 4 条指令进入流水线,在其执行过程中没有发生任何阻塞,则执行这 4 条指令所需的时钟周期数为多少?

(3) 若高级语言程序中某赋值语句为 x = a + b,x、a 和 b 均为 int 型变量,它们的存储单元地址分别表示为 [x]、[a] 和 [b]。该语句对应的指令序列及其在指令流水线中的执行过程如下所示。

I1  LOAD  R1, [a]
I2  LOAD  R2, [b]
I3  ADD   R1, R2
I4  STORE R2, [x]
指令1234567891011121314
I1IFIDEXMWB
I2IFIDEXMWB
I3IFIDEXMWB
I4IFIDEXMWB

则这 4 条指令执行过程中,I3 的 ID 段和 I4 的 IF 段被阻塞的原因各是什么?

(4) 若高级语言程序中某赋值语句为 x = x*2 + a,x 和 a 均为 unsigned int 类型变量,它们的存储单元地址分别表示为 [x]、[a],则执行这条语句至少需要多少个时钟周期?要求模仿上图画出这条语句对应的指令序列及其在流水线中的执行过程示意图。

先在纸上自己做一遍,再点这里展开满分解答

考点2.3 算术右移4.4 流水线与数据冒险训练营 ④

先把条件翻译成大白话
  • 16 位机,补码;指令 Cache 和数据 Cache 分离(取指和访存不会抢存储器,没有这类结构冒险)。
  • 5 段流水:IF → ID(译码并读寄存器)→ EX → M → WB(写寄存器)。
  • 三个关键约定:按序发射、按序完成;没有转发;同一寄存器不能在同一周期既写又读。合起来就是:要用某条指令结果的指令,它的 ID 只能排在那条指令 WB 的下一拍。
(1) x = −513 在 R1 中,执行 SHR R1 后 R1 的内容

先写 −513 的 16 位补码,再算术右移一位:整体右移,最高位补原来的符号位。

答题卡这样写

513 = 0000 0010 0000 0001B,−513 的补码 = 1111 1101 1111 1111B = FDFFH。

SHR 为算术右移,高位补符号位 1:1111 1110 1111 1111B,R1 = FEFFH。

算术右移补符号位、逻辑右移补 0;−513 右移后是 −257(向下取整),不是 −256。

(2) 连续 4 条指令无阻塞,需要多少个时钟周期?
答题卡这样写

第 1 条指令需要 5 个时钟周期,之后每个周期完成一条,共需 5 + (4 − 1) = 8 个时钟周期。

(3) I3 的 ID 段、I4 的 IF 段被阻塞的原因

I3(ADD R1, R2)要读 R1、R2,它们分别是 I1、I2 刚 LOAD 进来的,还没写回。I4 被卡住是因为前面的 I3 没往前走。

答题卡这样写

I3 的 ID 段被阻塞:I3 要读取的 R1、R2 分别由 I1、I2 写入,存在数据相关(写后读)。没有转发,且同一寄存器不能在同一周期读写,I3 必须等 I2 在第 6 周期写回 R2 后,才能在第 7 周期译码并读寄存器。

I4 的 IF 段被阻塞:流水线按序发射,I3 阻塞在 ID 段,前一条指令没有离开 ID 段,I4 就不能进入 ID,只能停在 IF 段等待。

(4) x = x × 2 + a 至少需要多少个时钟周期?画出流水线执行过程

x × 2 用左移实现(SHL)。指令序列:取 x、取 a、x 左移、相加、存回 x。然后一条一条排:每条指令的 ID 放在它所依赖的指令 WB 的下一拍;它的 IF 放在前一条指令 ID 的同一拍(前一条离开 IF 后才能进)。

答题卡这样写
I1  LOAD  R1, [x]
I2  LOAD  R2, [a]
I3  SHL   R1         // 2×(R1) → R1
I4  ADD   R1, R2     // (R1)+(R2) → R2
I5  STORE R2, [x]
指令1234567891011121314151617
I1IFIDEXMWB
I2IFIDEXMWB
I3IFIDEXMWB
I4IFIDEXMWB
I5IFIDEXMWB

I3 读 R1,等 I1 第 5 周期写回,第 6 周期 ID;I4 读 R1、R2,等 I3 第 9 周期写回,第 10 周期 ID;I5 要存 R2,等 I4 第 13 周期写回,第 14 周期 ID。执行这条语句至少需要 17 个时钟周期。

STORE 也“读”寄存器(要把 R2 的值读出来存进内存),它和前面写 R2 的 ADD 之间同样有数据相关,不要漏。

原卷参考答案的评分标注
小问得分点分值
(1)执行前 R1 = FDFFH,算术右移后 R1 = FEFFH2
(2)至少 4 + (5 − 1) = 8 个时钟周期2
(3)I3 的 ID 段阻塞:与 I1、I2 有数据相关,要等它们写回1
I4 的 IF 段阻塞:前一条 I3 在 ID 段被阻塞1

原卷只给了这些得分点的分值,没有给出本题总分,表中不列合计。

评分说明(原卷参考答案):

  • 原卷参考答案第 (4) 问(指令序列与流水线图,至少 17 个时钟周期)没有标注分值。
  • 无转发 + 同周期不能读写:依赖者的 ID = 被依赖者的 WB + 1。
  • 按序发射:前一条卡在 ID,后一条卡在 IF。
  • 画图时一行一条指令、一列一个周期,每行 5 段一个不能少,停顿的格子空着。

模拟大题:换一段循环再练一遍(按 408 风格自编)

这一类整道大题只考过 2012-44 一次。下面这道题是照 2012-44、2014-44(4) 的问法自编的,把本类常考的问法都放进去了:时钟周期和加速比、找相关、按约定画图数周期、转发与 load-use、调整指令顺序。所有周期数都用程序逐条模拟核对过。

模拟题流水线 · 数组求和循环计组 · 约 10 分

某 32 位 RISC 计算机采用 5 段指令流水线:取指(IF)、译码/读寄存器(ID)、执行/计算地址(EX)、访存(M)、写回(WB),各段延迟依次为 300ps、200ps、250ps、300ps、150ps,每个流水段寄存器的延迟为 20ps。指令 Cache 与数据 Cache 分离,流水线按序发射、按序完成。部分指令如下(R0 的内容恒为 0):

指令功能
LW Rd, imm(Rs)M[(Rs) + imm] → Rd
ADD Rd, Rs, Rt(Rs) + (Rt) → Rd
ADDI Rd, Rs, imm(Rs) + imm → Rd
BNE Rs, Rt, L若 (Rs) ≠ (Rt),则转到 L

下面的程序段对 int 型数组 a 的 100 个元素求和。开始时 R1 = 数组首地址,R3 = 0,R4 = 100。

loop: I1  LW    R2, 0(R1)      // R2 ← a[i]
      I2  ADD   R3, R3, R2     // sum += a[i]
      I3  ADDI  R1, R1, 4      // R1 指向下一个元素
      I4  ADDI  R4, R4, -1     // 计数减 1
      I5  BNE   R4, R0, loop   // 没加完就回到 loop

约定 A:没有转发;同一个寄存器的读和写不能在同一个时钟周期内进行;转移指令在 EX 段结束时才能确定是否转移,在此之前不取后续指令。
约定 B:在约定 A 的基础上增加转发。ALU 的运算结果在 EX 段结束时可以直接送给后续指令的 EX 段;LW 读出的数据在 M 段结束时才能送给后续指令的 EX 段。转移指令的处理方式不变。

(1) 该流水线的时钟周期最短是多少?主频是多少?不考虑阻塞时,执行 100 条指令需要多少时间?非流水 CPU 不分段,每条指令的用时等于各段延迟之和。与它相比,执行这 100 条指令的加速比是多少?(2 分)

(2) 列出循环体中所有的写后读(RAW)相关,包括与下一次循环中指令的相关。按约定 A,哪几条指令会因数据相关被阻塞?(2 分)

(3) 按约定 A,模仿 2012-44 画出第 1 次循环的 I1~I5 以及第 2 次循环的 I1 在流水线中的执行过程。每次循环需要多少个时钟周期?循环 100 次共需多少个时钟周期?平均 CPI 是多少?第 2 次循环的 I1 要读 R1,而 R1 是第 1 次循环的 I3 写的,I1 为什么不会因此阻塞?(4 分)

(4) 按约定 B,每次循环需要多少个时钟周期?剩下的阻塞各是什么原因造成的?在不改变程序功能的前提下,怎样调整指令顺序,才能使每次循环再少 1 个时钟周期?(2 分)

先在纸上自己做一遍,再点这里展开满分解答

考点4.4 吞吐率、加速比4.4 三种约定下数阻塞4.4 有阻塞时的 CPI4.4 冒险的对策

先把条件翻译成大白话
  • 时钟周期要按最慢的一段取,再加上段寄存器的延迟。非流水 CPU 执行一条指令要 300 + 200 + 250 + 300 + 150 = 1200ps。
  • 先列读写表。store 和转移指令没有要写的寄存器;LW 的地址寄存器也算“读”。
    指令写读
    I1 LWR2R1
    I2 ADDR3R3、R2
    I3 ADDIR1R1
    I4 ADDIR4R4
    I5 BNE—R4、R0
  • 约定 A 翻译成一句话:要用某条指令结果的指令,ID 排在那条指令 WB 的下一拍;BNE 后面那条指令的 IF 排在 BNE 的 EX 下一拍。
  • 约定 B 翻译成一句话:用 ALU 结果的指令,EX 可以紧跟在产生者的 EX 后面,不用等;用 LW 结果的指令,EX 最早排在 LW 的 M 下一拍,也就是 load-use 仍要停 1 拍。
(1) 时钟周期、主频、100 条指令的时间和加速比
答题卡这样写

时钟周期 Δt = 最长段延迟 + 段寄存器延迟 = 300 + 20 = 320ps,主频 = 1 / 320ps = 3.125GHz。

无阻塞时 100 条指令的时间 = (5 + 100 − 1) × 320ps = 33 280ps = 33.28ns。

非流水 CPU 每条指令 1200ps,100 条共 120ns。加速比 = 120 ÷ 33.28 ≈ 3.61。

加速比达不到段数 5,有三个原因:Δt 按最慢的一段取,快的段要等;每段多了 20ps 的段寄存器延迟;流水线装入和排空还要多花 4 拍。

(2) 写后读相关,约定 A 下谁被阻塞

拿读写表逐条看:每个“读”的寄存器,是前面哪条指令最后写的?跨循环的相关要把下一次循环接在 I5 后面看。

答题卡这样写

循环内:I1 → I2(R2),I4 → I5(R4)。

跨循环:I2 → 下一次的 I2(R3),I3 → 下一次的 I1 和 I3(R1),I4 → 下一次的 I4(R4)。

按约定 A,I2 和 I5 会因数据相关被阻塞。I2 要等 I1 写回 R2,I5 要等 I4 写回 R4。跨循环的相关不会引起阻塞,理由见第 (3) 问。

I1 读 R1、I3 随后写 R1,这是读后写(WAR)。按序流水线里 I1 早在 ID 段就读完了 R1,所以既不出错也不阻塞。R0 的内容恒为 0,没有指令写它,不构成相关。

(3) 约定 A:画执行过程,数每次循环和总共的周期,算 CPI

从上往下一条一条排,三条规则:IF 排在前一条指令进入 ID 的那一拍,因为前一条离开 IF 后才能取下一条;ID 取 “IF + 1” 和 “所依赖指令的 WB + 1” 中较晚的一拍;BNE 后面那条指令的 IF 排在 BNE 的 EX + 1。

答题卡这样写
指令123456789101112131415161718
I1IFIDEXMWB
I2IFIDEXMWB
I3IFIDEXMWB
I4IFIDEXMWB
I5IFIDEXMWB
I1(第 2 次)IFIDEXMWB

着色的空格表示这条指令在该拍被阻塞,停在原来的段上。

I2 与 I1 有关于 R2 的数据相关,I2 的 ID 推迟到 I1 写回(第 5 周期)后的第 6 周期。I5 与 I4 有关于 R4 的数据相关,I5 的 ID 推迟到第 12 周期。I5 在第 13 周期末才确定转移,所以第 2 次循环的 I1 在第 14 周期取指。

每次循环 = 14 − 1 = 13 个时钟周期,即 5 条指令各 1 拍,加上 I2 阻塞 3 拍、I5 阻塞 3 拍、转移阻塞 2 拍。

前 99 次循环各 13 个周期;最后一次从 I1 的 IF 到 I5 的 WB 共 15 个周期。所以 100 次共 99 × 13 + 15 = 1302 个时钟周期,约 1302 × 320ps ≈ 416.6ns。

平均 CPI = 1302 ÷ 500 ≈ 2.6。也可以按公式算:1 + (3 + 3 + 2) ÷ 5 = 2.6。

第 2 次循环的 I1 在第 15 周期读 R1,而 I3 在第 10 周期就已经把 R1 写回了,所以不会阻塞。原因是 I3 和下一次的 I1 之间隔着 I4、I5,还有转移造成的等待。

“每次循环的周期数”要从本次 I1 的 IF 数到下一次 I1 的 IF。总周期数要数到最后一条指令 WB 所在的那一拍,不是 13 × 100 = 1300。

(4) 约定 B:每次循环多少周期,还剩什么阻塞,怎样调整顺序

有了转发,ALU 结果随算随用;只剩 LW 后面紧跟着使用它结果的指令(load-use)要停 1 拍,转移仍然停 2 拍。调整顺序的思路:在 LW 和用它结果的 ADD 之间,塞进一条与它们都无关的指令。

答题卡这样写
指令12345678910111213
I1IFIDEXMWB
I2IFIDEXMWB
I3IFIDEXMWB
I4IFIDEXMWB
I5IFIDEXMWB
I1(第 2 次)IFIDEXMWB

着色的空格表示这条指令在该拍被阻塞,停在原来的段上。

I2 紧跟在 LW 后面使用 R2,LW 的数据要到 M 段末(第 4 周期)才有,所以 I2 的 EX 只能在第 5 周期,阻塞 1 拍。I5 要用 I4 的结果 R4,I4 的 EX 一结束就能转发,所以 I5 不阻塞。转移仍在 EX 段末才确定,要阻塞 2 拍。

每次循环 = 5 + 1 + 2 = 8 个时钟周期,100 次共 99 × 8 + 10 = 802 个周期。

调整:把 I3 移到 I1 和 I2 之间,顺序变成 I1(LW)、I3(ADDI R1)、I2(ADD)、I4、I5。这样 I2 与 LW 之间隔了一条指令,I2 的 EX 正好在 LW 的 M 之后一拍,不再阻塞,每次循环 7 个时钟周期。程序功能不变:I1 先用旧的 R1 取数,I3 再修改 R1,而 I2 与 R1 无关。

如果把 I3 移到 I1 前面,I1 就要改成 LW R2, −4(R1),否则取到的是下一个元素。改成 I1、I4、I3、I2、I5 也是 7 个周期。剩下的 2 拍转移阻塞,要靠分支预测、延迟转移(把与转移无关的指令放到转移指令后面)或者把比较提前到 ID 段来解决。

  • 数阻塞前先把约定抄下来。三种阻塞分开数:数据相关、load-use、转移。
  • 每次循环的周期数 = 指令条数 + 各种阻塞的拍数;CPI = 1 + 阻塞拍数 ÷ 指令条数。
  • 在 load 和使用它结果的指令之间插一条无关指令,这是编译器消除 load-use 阻塞的标准做法(2024-19 考过“转发不能解决全部数据冒险”)。

举一反三:条件一变,答案怎么变

以上面的模拟题(约定 A 每次循环 13 拍,约定 B 每次循环 8 拍)为基准,每行只改一个条件。先自己想出新答案和理由,再点开核对。

条件怎么变新答案和理由
约定 A 改为“同一寄存器前半周期写、后半周期读”
想好了再点使用结果的指令,ID 可以和产生者的 WB 在同一拍,每处数据相关少停 1 拍。I2、I5 各停 2 拍,每次循环 5 + 2 + 2 + 2 = 11 拍,100 次共 1102 拍。
约定 A 中,转移改为在 ID 段结束时确定
想好了再点转移阻塞由 2 拍变为 1 拍,每次循环 12 拍,100 次共 1203 拍。
约定 B 中,转移改为在 ID 段结束时确定
想好了再点每次循环 5 + 1 + 1 = 7 拍。
约定 B 中采用分支预测,且每次都预测正确
想好了再点没有转移阻塞,只剩 load-use 的 1 拍,每次循环 6 拍;再把 I3 调到 I1 后面,每次 5 拍,CPI = 1。
IF 段的延迟从 300ps 降到 200ps
想好了再点Δt 仍由最慢的 M 段(300ps)决定,时钟周期不变,仍为 320ps。要把 IF 和 M 都降到 250ps,Δt 才变为 270ps。
指令条数由 100 条改为 n 条(无阻塞)
想好了再点T = (5 + n − 1) × Δt。n 很大时,吞吐率趋近于 1 / Δt,加速比趋近于 1200 ÷ 320 = 3.75。

丢分点清单

  • 没看约定就按“有转发”算,或者把“不能同一周期读写”当成可以。
  • 漏掉 store 对寄存器的“读”,漏掉 load 地址寄存器的“读”。
  • 把写后写(WAW)、读后写(WAR)也当成要阻塞(按序流水线里它们不引起阻塞)。
  • 阻塞的指令后面那条的 IF 画早了(按序发射时它要等前一条离开 IF)。
  • n 条指令写成 k × n 个周期(那是不用流水线的时间)。
  • 时钟周期取了各段的平均值,应该取最长的一段。

本类其他真题

  • 2014-44(4):一段循环:找出因数据相关阻塞的指令、哪条引起控制冒险、为什么指令 1 不因指令 5 阻塞。
  • 选择题:2009-18(时钟周期取最长段)、2013-18(吞吐率)、2016-19 / 2019-18 / 2023-19(找数据冒险)、2018-20(流水线时间)、2010-19 / 2024-19(冒险的解决)、2011-18(什么有利于流水线)、2014-16(Cache 分离)、2017-17(超标量)。

11.6计组 ⑤ Cache:切地址、算容量、算命中率

★★★ 高频综合题

先读:8.1 局部性 8.3 Cache

基本功:知识篇里对应的例题和总结表(先做这些,再做本类真题)

这类题长什么样

Cache 几乎每年都考,单独成题或者和虚拟存储合在一起(那种见 ⑥)。永远是这几问:地址怎么切、Cache 一共多少位、某个地址映射到哪一组、一段数组程序的命中率、写策略和附加位、访问过程。

常见问法考过的地方
地址中哪几位是 Tag / 组号(行号)/ 块内地址2011-44(2),2016-45(1),2019-46,2023-43(3),2025-43(1)
Cache 总容量(含 Tag、有效位、脏位、LRU 位)/ 每行有哪些附加位2010-44(1),2018-44(3),2020-44(1)
某地址 / 某块 / 某数组元素映射到哪一组(行)2010-44(2),2016-45(2),2018-44(4),2023-43(3),2025-43(2)
数组程序的命中率 / 缺失次数2010-44(3),2014-45(2),2020-44(2),2023-43(4),2025-43(3)
平均访问时间、存储带宽2025-43(3),2012-43,2013-43
是否命中(Tag 和有效位)2011-44(3),2018-44(4)
访问过程(含缺失处理)、写策略2020-44(3),2016-45(4)

做这类题必须会的 6 件事

① 一个地址切三段,查 Cache 三步走。Cache 按“块”从主存搬数据。一个主存地址被切成:Tag(标记)| 组号 | 块内地址。查的时候:① 用组号选中一组;② 拿 Tag 和这一组里每一行的 Tag 同时比较,相等且有效位为 1 才算命中;③ 用块内地址从块里取出要的那个字节/字。

主存地址(例:2020-44,32 位,8 路组相联,64 组,块 64B) Tag 20 位 组号 6 位 块内地址 6 位 第 0 组 第 k 组 ⋮ 第 63 组 有效Tag数据块(64B) 100010H 13A2C1H (8 路就是每组 8 行,这里只画 2 行) ①选组 = = ② 比 Tag相等 且 有效位=1→ 命中 ③ 块内地址取出那个字节
图 11-5 Cache 查找三步。直接映射 = 每组只有 1 行(这时“组号”叫“行号”);全相联 = 只有 1 组(没有组号,Tag = 整个块号,要和所有行比)。组数 = 行数 ÷ 路数,这一个公式把三种映射统一了。

② 位数怎么算(按字节编址)。

地址位数 = log₂(主存容量)  块内地址 = log₂(块大小) 行数 = 数据区容量 ÷ 块大小  组数 = 行数 ÷ 路数  组号位数 = log₂(组数) Tag = 地址位数 − 组号位数 − 块内地址位数 每行位数 = 数据(块大小 × 8)+ Tag + 有效位 1 +〔回写:脏位 1〕+〔LRU:log₂(路数)〕 Cache 总容量 = 行数 × 每行位数(“数据区容量”只算数据部分)

③ 某个地址落在哪一组。块号 = 地址 ÷ 块大小(取整),组号 = 块号 mod 组数,Tag = 块号 ÷ 组数(取整)。在二进制上就是:去掉低位的块内地址,再往上数“组号位数”位就是组号。

④ 数组程序的命中率——先想清楚四件事。

  • 元素多大?一个块放几个元素?(块 64B、int 4B → 16 个)
  • 首地址是不是块的开头?不是的话数组会多占一个块(2025-43 的 129 块)。
  • 每个元素访问几次?s[k] = 2*s[k] 是读 1 次 + 写 1 次,写的时候块已经在了,必命中。
  • 访问顺序是按地址顺序(按行)还是跳着走(按列)?跳着走时,要算“同一组要放几个块、放不放得下”。
程序 A:按行访问(顺着地址走) 程序 B:按列访问(每次跳一整行) 粗框 = 一个主存块(这里一块放 4 个元素) 缺 中 中 中 缺 中 中 中 第 0 行 缺 中 中 中 缺 中 中 中 第 1 行 缺 中 中 中 缺 中 中 中 第 2 行 缺 中 中 中 缺 中 中 中 第 3 行 缺 ? ? ? 缺 ? ? ? 第 0 行 缺 ? ? ? 缺 ? ? ? 第 1 行 缺 ? ? ? 缺 ? ? ? 第 2 行 缺 ? ? ? 缺 ? ? ? 第 3 行 每块:第 1 次缺失,后 3 次命中 命中率 = 3/4,和 Cache 大小无关 第 0 列:4 个元素在 4 个不同的块 → 全缺失 第 1~3 列(?):这 4 块还在 Cache 里 → 命中; 已被别的块挤掉 → 又缺失 关键:同一组要放的块数 ≤ 路数?
图 11-6 按行 vs 按列。按行访问的命中率只由“每块几个元素”决定。按列访问要看冲突:2010-44 直接映射、Cache 只有 8 行,相邻两次访问相差 16 块,16 mod 8 = 0,全挤在同一行 → 命中率 0;2023-43 4 路组相联,每组只要放 3 块 → 放得下 → 命中率和按行一样是 87.5%。

⑤ 写策略和附加位。写命中时:回写(只写 Cache,换出时才写回主存,要有脏位)或直写(同时写主存,不需要脏位,一般配写缓冲)。写不命中时:写分配(先把块调进来再写,常配回写)或非写分配(直接写主存,常配直写)。替换:直接映射不用选;组相联、全相联用 LRU、FIFO、随机,k 路 LRU 每行 log₂k 位计数。

⑥ 平均访问时间。“缺失损失”是缺失时多花的时间:平均访问时间 = 命中时间 + 缺失率 × 缺失损失。如果题目给的是“访问 Cache 的时间 tc、访问主存的时间 tm”:先访问 Cache、未命中再访问主存时 = h·tc + (1 − h)(tc + tm);同时访问 Cache 和主存时 = h·tc + (1 − h)·tm。

解题模板

  1. 写出三个基本量。地址位数、块大小、组数(= 数据区 ÷ 块 ÷ 路数)。
  2. 切地址。从低位往高位:块内地址 → 组号(行号)→ 剩下的是 Tag,标出每段是第几位到第几位。
  3. 算容量。把每行的附加位逐个列出来(有效位;脏位看写策略;LRU 位看路数),再乘行数。
  4. 求映射。地址写成二进制切段,或者用“块号 mod 组数”。
  5. 求命中率。列出“访问次数”和“缺失次数”:顺序访问时缺失次数 = 占用的块数;跳着访问时检查同组块数是否 ≤ 路数,不够就按替换算法模拟。
  6. 写结论并带理由:对齐了没有、每块几个元素、会不会被替换。
完整示范2010 年第 44 题计组

某计算机的主存地址空间大小为 256MB,按字节编址。指令 Cache 和数据 Cache 分离,均有 8 个 Cache 行,每个 Cache 行大小为 64B,数据 Cache 采用直接映射方式。现有两个功能相同的程序 A 和 B,其伪代码如下所示:


// 程序 A
int a[256][256];
.....
int sum_array1()
{
  int i, j, sum=0;
  for (i=0; i<256; i++)
    for (j=0; j<256; j++)
      sum += a[i][j];
  return sum;
}

// 程序 B
int a[256][256];
.....
int sum_array2()
{
  int i, j, sum=0;
  for (j=0; j<256; j++)
    for (i=0; i<256; i++)
      sum += a[i][j];
  return sum;
}

假定 int 类型数据用 32 位补码表示,程序编译时 i,j,sum 均分配在寄存器中,数组 a 按行优先方式存放,其首地址为 320(十进制数)。请回答下列问题,要求说明理由或给出计算过程。

(1) 若不考虑用于 cache 一致性维护和替换算法的控制位,则数据 Cache 的总容量为多少?

(2) 数组元素 a[0][31] 和 a[1][1] 各自所在的主存块对应的 Cache 行号分别是多少(Cache 行号从 0 开始)?

(3) 程序 A 和 B 的数据访问命中率各是多少?哪个程序的执行时间更短?

先在纸上自己做一遍,再点这里展开满分解答

考点8.3 Cache 地址划分、容量、命中率8.1 局部性训练营 ⑤

先把条件翻译成大白话
  • 主存 256MB = 228 B,按字节编址 → 主存地址 28 位。
  • 数据 Cache:8 行,每行(块)64B,直接映射 → 地址切成 Tag | 行号 3 位 | 块内地址 6 位,Tag = 28 − 3 − 6 = 19 位。
  • int 4 字节,一个块装 64 / 4 = 16 个 int;数组一行 256 个 int = 1024B = 16 个块。
  • 首地址 320 = 5 × 64,正好是块的开头(块对齐)。
(1) 数据 Cache 的总容量(不计一致性和替换控制位)
答题卡这样写

主存地址 28 位,块内地址 6 位,行号 3 位,Tag = 28 − 6 − 3 = 19 位。

每行 = 有效位 1 位 + Tag 19 位 + 数据 64 × 8 = 512 位,共 532 位。

总容量 = 8 × 532 = 4256 位 = 532 字节。

“总容量”要算上 Tag 和有效位,不能只答 8 × 64B = 512B(那是“数据区容量”)。

(2) a[0][31]、a[1][1] 所在主存块对应的 Cache 行号

直接映射:行号 = 块号 mod 行数;块号 = 地址 ÷ 块大小(取整)。

答题卡这样写

a[0][31] 的地址 = 320 + 31 × 4 = 444,块号 = ⌊444 / 64⌋ = 6,行号 = 6 mod 8 = 6。

a[1][1] 的地址 = 320 + (256 × 1 + 1) × 4 = 1348,块号 = ⌊1348 / 64⌋ = 21,行号 = 21 mod 8 = 5。

(3) 程序 A、B 的命中率?谁更快?

A 按行访问 = 按地址顺序访问:每个块第一次访问缺失,同一块剩下的元素都命中。B 按列访问:a[i][j] 和 a[i+1][j] 相差 1024B = 16 块,16 mod 8 = 0,落在同一个 Cache 行里,每次都把上一次调入的块挤掉。

答题卡这样写

程序 A:按行优先顺序访问,数组首地址按块对齐,每块 16 个元素,每块只在访问第 1 个元素时缺失,命中率 = 15 / 16 = 93.75%。

程序 B:按列访问,相邻两次访问的元素相差 256 × 4 = 1024B = 16 个主存块,而 16 mod 8 = 0,它们映射到同一 Cache 行,后一次访问总把前一次调入的块替换掉;等到访问下一列时,该块早已被替换,所以每次访问都缺失,命中率为 0。

程序 A 的执行时间更短。

算命中率前先检查“首地址是否块对齐”:不对齐时数组会多占一个块(见 2025-43)。

  • Cache 总容量 = 行数 × (数据 + Tag + 有效位 + 脏位 + 替换位),题目说不计的就不加。
  • 顺序访问的命中率 = (每块元素数 − 1) / 每块元素数。
  • 跨行访问时,看“相邻两次访问相差几个块”再 mod 组数:落进同一组且装不下就互相挤。

模拟大题:三个数组抢同一组(按 408 风格自编)

真题里的 Cache 程序题大多只有一个数组(2010-44、2020-44、2023-43、2025-43)。多个数组互相“挤”才是命中率题最能考出理解的地方,也最容易举一反三:换映射方式、换相联度、挪一下首地址,结果完全不同。下面的命中率都用 Cache 模拟程序逐次访问核对过。

模拟题Cache 冲突与相联度计组 · 约 12 分

某计算机主存容量 256MB,按字节编址。Cache 数据区容量 8KB,块大小 32B,采用 2 路组相联映射、LRU 替换算法、回写(write back)和写分配策略。int 型占 4B。数组 a、b、c 都是 int 型,各有 1024 个元素,首地址分别为 0800000H、0802000H、0804000H。变量 i 和 sum 都在寄存器中,不访存。Cache 初始为空。

// 程序 A
for (i = 0; i < 1024; i++)  sum += a[i] * b[i];
// 程序 B
for (i = 0; i < 1024; i++)  c[i] = a[i] + b[i];

(1) 主存地址中 Tag、组号、块内地址各占几位?Cache 每行包含哪些信息、共多少位?Cache 的总容量是多少字节(含所有附加位)?(3 分)

(2) 主存地址 1234567H 映射到 Cache 的哪一组?它的 Tag 是多少?(2 分)

(3) 程序 A 的 Cache 命中率是多少?若改为直接映射,数据区容量和块大小不变,命中率又是多少?设命中时的访问时间为 2ns,缺失损失为 60ns,求两种情况下的平均访问时间。(4 分)

(4) 仍用 2 路组相联,程序 B 的命中率是多少?为什么?请给出两种改进方法(一种改 Cache 的组织,一种不改硬件),并说明改进后的命中率。(3 分)

先在纸上自己做一遍,再点这里展开满分解答

考点8.3 映射方式与地址划分8.3 平均访问时间训练营 ⑤ 按行 vs 按列

先把条件翻译成大白话
  • 256MB = 228B,所以主存地址 28 位;块 32B,块内地址 5 位。
  • 行数 = 8KB ÷ 32B = 256 行,2 路组相联,组数 = 256 ÷ 2 = 128,组号 7 位。
  • “组号 + 块内地址”共 12 位,对应 4KB 的地址范围:地址每差 4KB 的整数倍,组号就相同。a、b、c 的首地址两两相差 2000H = 8KB,所以 a[i]、b[i]、c[i] 总是落在同一组,这是本题的关键。
  • 一个块放 32 ÷ 4 = 8 个 int;三个数组的首地址都是块的开头(低 5 位为 0)。
(1) 地址划分与 Cache 总容量
答题卡这样写

主存地址 28 位:块内地址 5 位(第 4~0 位),组号 7 位(第 11~5 位),Tag = 28 − 7 − 5 = 16 位(第 27~12 位)。

每行包含:数据 32B = 256 位,Tag 16 位,有效位 1 位,脏位 1 位(回写需要),LRU 位 1 位(2 路只要 1 位)。每行共 256 + 16 + 1 + 1 + 1 = 275 位。

总容量 = 256 行 × 275 位 = 70 400 位 = 8800B。

“数据区 8KB”只是数据部分。2 路组相联的 LRU 每行 1 位就够了(每组只要记住哪一行较久没用);如果是直写,就不需要脏位。

(2) 1234567H 映射到哪一组
答题卡这样写

1234567H 的低 12 位是 567H = 0101 0110 0111B。块内地址是低 5 位 00111B = 7;组号是第 11~5 位 0101011B = 43;Tag 是第 27~12 位 = 1234H。

即映射到 Cache 的第 43 组,可以放在这一组的两行中的任意一行。

(3) 程序 A:2 路组相联 vs 直接映射

每次循环访问 a[i] 和 b[i],它们在同一组。2 路组:一组放得下两个块;直接映射:每“组”只有一行,两个块只能轮流住。

答题卡这样写

程序 A 共访存 2 × 1024 = 2048 次。

2 路组相联:a[i] 所在的块和 b[i] 所在的块映射到同一组,但一组有 2 行,可以同时存放。每个块第一次访问时缺失,块内其余 7 个元素命中。a、b 各占 1024 × 4 ÷ 32 = 128 块,缺失 256 次,命中率 = (2048 − 256) ÷ 2048 = 87.5%。

直接映射:共 256 行,行号 8 位,“行号 + 块内地址”共 13 位,对应 8KB。a、b 相差 8KB,所以 a[i] 与 b[i] 映射到同一行。访问顺序为 a[0]、b[0]、a[1]、b[1]……,每次都把对方的块替换出去,每次访问都缺失,命中率 = 0。

平均访问时间:2 路组相联 = 2 + (1 − 87.5%) × 60 = 9.5ns;直接映射 = 2 + 1 × 60 = 62ns。

判断两个地址是否冲突,要看“组号(行号)+ 块内地址”共几位:2 路组相联时是 12 位(4KB),直接映射时是 13 位(8KB)。不要只看组号的位数。

(4) 程序 B:三个块抢一组

每次循环访问 a[i]、b[i]、c[i](读、读、写),三个块在同一组,而一组只有 2 行。LRU 下循环访问 3 个块、只有 2 个位置:每次要用的恰好是刚被换出的那个。

答题卡这样写

访存 3 × 1024 = 3072 次。以第 0 组为例:访问 a₀ 块缺失、b₀ 块缺失,组满;c₀ 块缺失,LRU 替换 a₀ 块;下一次访问 a[1] 时 a₀ 块已被换出,缺失并替换 b₀ 块;接着 b[1] 缺失……每次访问都缺失,命中率 = 0(c 的块写入后还会被换出、写回主存,开销更大)。

改进方法一(改 Cache):改为 4 路组相联(容量不变)。一组能同时放下 a、b、c 的三个块,每块只在第一次访问时缺失。缺失 3 × 128 = 384 次,命中率 = (3072 − 384) ÷ 3072 = 87.5%。

改进方法二(不改硬件):让 c 与 a、b 错开,例如把 c 的首地址改为 0804020H(错开一个块,32B)。这样 c[i] 所在的块落在下一组,每组最多同时需要 2 个块,放得下。命中率也是 87.5%。

写操作 c[i] = … 在写分配策略下,写不命中也要先把块调入 Cache,所以每个 c 的块也要缺失 1 次。另外,“错开首地址”是消除冲突缺失的通用办法,叫“数组填充(padding)”:例如在直接映射下把 b 的首地址改为 0802020H,程序 A 的命中率也能回到 87.5%。

  • 先算“组号 + 块内地址”覆盖多大的地址范围(本题 4KB)。首地址相差这个范围整数倍的数组,同下标元素一定在同一组。
  • 同时要用的块数 ≤ 路数,就只有第一次访问缺失;超过路数,LRU 下会反复互相替换,命中率可以降到 0。
  • 提高相联度、错开首地址(填充)都能消除这种冲突缺失。

举一反三:条件一变,答案怎么变

以上面的模拟题(2 路组相联,程序 A 命中率 87.5%)为基准,每行只改一个条件。先自己想出新答案和理由,再点开核对。

条件怎么变新答案和理由
块大小改为 64B(容量、2 路不变)
想好了再点组数 64,组号 6 位,块内地址 6 位,Tag 仍为 16 位。每块 16 个 int,程序 A 命中率 = 15/16 = 93.75%。
写策略改为直写
想好了再点不需要脏位,每行 256 + 16 + 1 + 1 = 274 位。
改为 4 路组相联(容量不变)
想好了再点组数 64,组号 6 位,Tag 17 位;LRU 每行 2 位,每行 256 + 17 + 1 + 1 + 2 = 277 位。程序 A 命中率不变(87.5%),程序 B 变为 87.5%。
改为全相联
想好了再点没有组号,Tag = 28 − 5 = 23 位。任何块都能放进任意一行,程序 A、B 都不会发生冲突缺失,命中率都是 87.5%。
数组改为 double 型(8B)
想好了再点每块只能放 4 个元素,程序 A 命中率 = 3/4 = 75%。
程序 A 改为 i 从 1023 递减到 0
想好了再点命中率不变,仍是 87.5%:每个块还是只在第一次被访问时缺失。
程序 B,替换算法改为 FIFO
想好了再点命中率仍为 0(程序模拟核对)。每组循环访问 a、b、c 三个块、只有 2 行,FIFO 每次换出的也恰好是下一个要用的块;这种循环访问下 FIFO 和 LRU 一样差(8.3 替换算法)。
程序 B,写策略改为直写 + 非写分配
想好了再点写 c[i] 不命中时不调入 Cache,直接写主存,c 的块从不进 Cache;a、b 两个块一组放得下。读 2048 次缺失 256 次,写 1024 次全部不命中,命中率 = (3072 − 256 − 1024) ÷ 3072 ≈ 58.3%(程序模拟核对;8.3 写策略)。
本题 Cache 作为 L1,再加一级 L2:L1 缺失时访问 L2(10ns),L2 命中率 90%,L2 缺失再访问主存(60ns)
想好了再点程序 A 的平均访问时间 = 2 + (1 − 87.5%) × (10 + 10% × 60) = 2 + 0.125 × 16 = 4ns,比没有 L2 时的 9.5ns 少了一半多(8.3 多级 Cache)。

预测大题:双核“伪共享”与 MESI 拓展

多核与 Cache 一致性在考纲里只是“了解”,真题还没出过大题。下面这道把它和本类题的地址划分、第 7 篇的竞争条件串在一起,作为预测。缺失和作废次数都用 MESI 模拟程序核对过。

模拟题多核 Cache 一致性计组 + 操作系统 · 约 11 分

某双核处理器的两个核各有一个私有的 L1 数据 Cache:容量 32KB,块大小 64B,8 路组相联,回写、写分配,用 MESI 协议保持一致性;两个核共享主存,物理地址 32 位,按字节编址。int 型占 4B。有全局变量:

struct { int x; int y; } s;     // s.x 的地址为 1000 0040H
// 线程 T1 在核 0 上运行:
for (i = 0; i < 1000; i++)  s.x++;
// 线程 T2 在核 1 上运行:
for (i = 0; i < 1000; i++)  s.y++;

i 在寄存器中。假设两个线程的 s.x++ 和 s.y++ 严格交替执行(T1 一次、T2 一次……),每次 ++ 都是先读一次、再写一次。开始时两个 Cache 中都没有 s。

(1) 物理地址中 Tag、组号、块内地址各占几位?s.x、s.y 分别映射到哪一组?它们在不在同一个 Cache 块中?(3 分)

(2) T1 第一次执行 s.x++ 后,核 0 中该块处于 MESI 的什么状态?接着 T2 第一次执行 s.y++:读的时候发生了什么?写的时候又发生了什么?之后两个核中该块各处于什么状态?(3 分)

(3) 按题设的交替顺序执行完两个循环,两个核一共发生多少次读缺失?一共发出多少次作废(invalidate)请求?(2 分)

(4) 两个线程访问的明明是不同的变量,为什么会这样?不改硬件,怎样修改程序消除这个问题?修改后读缺失和作废各多少次?(2 分)

(5) 若两个线程执行的都是 s.x++(同一个变量,共 2000 次),MESI 协议能否保证最后 s.x = 2000?为什么?(1 分)

先在纸上自己做一遍,再点这里展开满分解答

考点8.3 地址划分4.5 MESI 协议7.1 竞争条件

先把条件翻译成大白话
  • 32KB ÷ 64B = 512 行,8 路 → 64 组:组号 6 位,块内 6 位。
  • 一致性是按块维护的:一个核要写某个块,就得让其他核里这个块的副本作废,不管它们用的是块里的哪个字节。
  • MESI:M 已修改(只有我有、和主存不一致),E 独占(只有我有、和主存一致),S 共享(多个核都有),I 无效。
(1) 地址划分
答题卡这样写

块内地址 6 位(第 5~0 位),组号 6 位(第 11~6 位),Tag = 32 − 6 − 6 = 20 位(第 31~12 位)。

s.x = 1000 0040H:块内地址 00H,组号 = 000001B = 1,Tag = 10000H。s.y = 1000 0044H:块内地址 04H,组号也是 1,Tag 相同。两者都在 1000 0040H ~ 1000 007FH 这一个 64B 的块里,在同一个 Cache 块中。

(2) 前两次 ++ 的状态变化
答题卡这样写

T1 读 s.x:核 0 缺失,其他核没有这个块,从主存调入,状态为 E;写 s.x:E → M,不用通知别的核。

T2 读 s.y:核 1 缺失,发现核 0 中该块为 M,核 0 把块写回(提供数据),两个核的副本都变为 S。T2 写 s.y:核 1 发出作废请求,核 0 的副本变为 I,核 1 的变为 M。

(3) 读缺失和作废次数
答题卡这样写

从第 2 次开始,每次轮到一个核做 ++ 时,它的副本都刚被对方作废(I),所以每次读都缺失,对方把 M 块写回,两边变 S;接着写时再把对方作废。

读缺失共 2000 次(每次 ++ 一次);作废请求共 1999 次(只有 T1 的第一次写是 E → M,不用作废),同时 M 块被写回 1999 次(程序模拟核对)。

(4) 伪共享与改进
答题卡这样写

原因:x 和 y 落在同一个 Cache 块里,而一致性按块维护,两个核轮流写同一块,块在两个 Cache 之间来回“搬家”,这叫伪共享(false sharing)。

改进:在 x 和 y 之间填充,让它们落在不同的块里,例如 struct { int x; char pad[60]; int y; } s;(y 的偏移为 64)。修改后每个核只在第一次访问时读缺失,之后块一直在自己的 Cache 中处于 M 状态:读缺失共 2 次,作废 0 次。

这和 8.3 的“数组填充”是同一个思路:改数据的位置,让不该挤在一起的东西分开。

(5) 一致性 ≠ 互斥
答题卡这样写

不能保证。s.x++ 由“读、加 1、写”三步组成,MESI 只保证所有核看到的对同一块的写是一致的,不保证这三步不被打断。两个核可能先后读到同一个旧值,各自加 1 后写回,丢掉一次更新,结果小于 2000。要用锁或原子指令(如 TSL、xchg)实现互斥(第 7 篇)。

  • 一致性按块维护:不同变量只要在同一块里,多个核轮流写也会互相作废(伪共享)。
  • MESI 中写一个 S 状态的块要先作废其他副本;读一个别人处于 M 的块要先让对方写回。
  • Cache 一致性解决“看到的值是否一致”,互斥解决“读改写是否被打断”,两件事不能互相代替。

丢分点清单

  • “Cache 总容量”只算了数据区。
  • 回写忘了脏位、组相联忘了 LRU 位;直写却加了脏位。
  • 按字编址的主存按字节算地址位数(或反过来)。
  • 没检查首地址是否块对齐,块数少算一个。
  • 把“读 + 写”算成一次访问,或者把写也算成缺失。
  • 按列访问一律写命中率 0,没算同组能不能放下。
  • 判断命中只比 Tag 不看有效位(2018-44 的坑)。

本类其他真题

  • 2020-44:8 路组相联、直写、LRU:Tag 和 LRU 位数、是否要修改位、数组缺失次数、一次取指的完整 Cache 访问过程。
  • 2023-43:4 路组相联 + 分页:数组占几页、缺页地址、时间局部性、组号、行列两种循环的命中率。
  • 2025-43:8 路组相联 + 分页:虚拟地址哪几位可作索引、组号、不对齐时的缺失率、平均访问时间、缺页次数。
  • 和虚拟存储合考的(见 ⑥):2011-44、2014-45、2016-45、2018-44、2019-46。

11.7计组 ⑥ 虚拟存储 + TLB + Cache:一次访存走到底

★★★ 高频综合题

先读:8.5 分页 8.7 虚拟内存 8.8 一次访存的全过程 8.3 Cache

基本功:知识篇里对应的例题和总结表(先做这些,再做本类真题)

这类题长什么样

这是计组和操作系统“握手”最多的一类题:给出虚拟地址、页大小、TLB 和 Cache 的组织方式,有时还给页表、TLB、Cache 的部分内容,然后让你把一个虚拟地址一路翻译下去,判断每一步命中还是缺失。它和操作系统的分页大题(⑩)考的是同一件事,只是这里更关心硬件(TLB、Cache 的结构)。

常见问法考过的地方
虚拟地址 / 物理地址各几位,哪几位是虚页号、页框号2011-44(1),2016-45(1),2018-44(1),2021-44(1)
TLB 是什么映射、标记是什么、组号是哪几位、用 SRAM 还是 DRAM2011-44(4),2016-45(1),2018-44(2),2021-44(2)
给一个虚拟地址:在不在主存、物理地址、Cache 是否命中2011-44(3)(4),2018-44(4)
只可能在 Cache 的哪一组命中(组号在页内偏移中)2019-46,2018-44(4),2023-43(3),2025-43(1)
TLB 的 LRU 替换模拟、表项位数2021-44(3)(4)
哪条指令可能缺页、访问 TLB / 磁盘几次2014-45(3),2023-44(4)
Cache 缺失和缺页谁开销大、为什么页面总是回写2016-45(3)(4)

做这类题必须会的 5 件事

① 一次访存的完整流程。下面这张图要能默画出来:蓝色是硬件自动完成的,绿色是操作系统接手的。

① 虚拟地址 = 虚页号 | 页内偏移 ② 查 TLB(全相联 / 组相联,SRAM) 未命中 ③ 查页表(页表在主存 → 多访存一次) 有效位 = 1 得到页框号,并把这个页表项装入 TLB ④ 物理地址 = 页框号 ∥ 页内偏移 ⑤ 按 Tag | 组号 | 块内地址 查 Cache 命中:直接取数约几 ns 缺失:从主存调入整块几十 ~ 几百 ns,硬件完成 命中 操作系统:缺页处理(ms 级) a. 保存现场(这是一次“异常”) b. 找一个空闲页框;没有就按置换算法 选一页换出,修改过(脏)就先写回磁盘 c. 启动磁盘把需要的页读进来 (等待期间进程阻塞,CPU 去跑别人) d. 修改页表:填页框号、有效位 = 1 e. 返回后重新执行引起缺页的那条指令 (所以 TLB 会被再查一次) 有效位 = 0缺页异常 重新执行
图 11-7 一次访存走到底。TLB、页表、Cache 的查找都是硬件(MMU 和 Cache 控制器)自动做的;只有“缺页”要操作系统出手。三个量级要记住:Cache 命中几 ns,Cache 缺失几十到几百 ns,缺页要读磁盘,毫秒级——这就是 2016-45(3)(4) 的答案来源。

② 位数关系。虚页号 = 虚拟地址位数 − 页内偏移位数;页框号 = 物理地址位数 − 页内偏移位数。Cache 切的是物理地址(Tag 位数按物理地址算)。

③ TLB 就是“页表的 Cache”。它的“地址”是虚页号:全相联时标记 = 整个虚页号;k 路组相联时,虚页号的低位是组号,高位是标记(2011-44(4)、2021-44)。每个 TLB 表项 = 标记 + 页框号 + 有效位(+ 修改位、访问位等)。TLB 追求速度,用 SRAM;每一项一个比较器 = 全相联。

④ 什么时候可以直接用虚拟地址算 Cache 组号。当“组号 + 块内地址”的位数 ≤ 页内偏移位数时,组号整个落在页内偏移里,而页内偏移在地址转换时不变,所以虚拟地址和物理地址的组号相同(2019-46、2023-43、2025-43)。这样 CPU 可以一边查 TLB、一边用组号选 Cache 组,速度更快。

⑤ 各种“次数”。TLB 命中:只需访存取数据(Cache 未命中时)。TLB 未命中、页在主存:先访存读页表项,再访存取数据(两级页表再多一次)。缺页:处理完后重新执行这条指令,TLB 要再查一次(2014-45(3):TLB 至少 1001 次)。一段数据全在同一页时缺页 1 次;跨 n 页就是 n 次。

解题模板

  1. 画两根地址条。虚拟地址:虚页号 | 偏移;物理地址:页框号 | 偏移,同时在物理地址上再切 Tag | 组号 | 块内。
  2. 给的是十六进制虚拟地址时,先切出虚页号(4KB 页就是去掉最后 3 个十六进制位)。
  3. 查 TLB。全相联直接比虚页号;组相联先算组号和标记。命中 → 页框号;有效位为 0 或找不到 → 查页表。
  4. 查页表。有效位 1 → 页框号;0 → 缺页(页不在主存)。
  5. 拼物理地址,查 Cache。写成二进制切段,找组,比 Tag,看有效位。
  6. 每一步的结论都写理由:“TLB 第 0 组中有标记为 012H 且有效的项,所以命中”。
完整示范2011 年第 44 题计组

某计算机存储器按字节编址,虚拟(逻辑)地址空间大小为 16MB,主存(物理)地址空间大小为 1MB,页面大小为 4KB;Cache 采用直接映射方式,共 8 行;主存与 Cache 之间交换的块大小为 32B。系统运行到某一时刻时,页表的部分内容和 Cache 的部分内容分别如题 44-a 图、题 44-b 图所示,图中页框号及标记字段的内容为十六进制形式。

虚页号有效位页框号
0106
1104
2115
3102
40—
512B
60—
7132
44-a 图 页表的部分内容
行号有效位标记
01020
10—
2101D
31105
41064
5101D
60—
7127A
44-b 图 Cache 的部分内容

请回答下列问题:

(1) 虚拟地址共有几位,哪几位表示虚页号?物理地址共有几位,哪几位表示页框号(物理页号)?

(2) 使用物理地址访问 Cache 时,物理地址应划分成哪几个字段?要求说明每个字段的位数及在物理地址中的位置。

(3) 虚拟地址 001C60H 所在的页面是否在主存中?若在主存中,则该虚拟地址对应的物理地址是什么?访问该地址时是否 Cache 命中?要求说明理由。

(4) 假定为该机配置一个 4 路组相联的 TLB,该 TLB 共可存放 8 个页表项,若其当前内容(十六进制)如题 44-c 图所示,则此时虚拟地址 024BACH 所在的页面是否在主存中?要求说明理由。

组号有效位 / 标记 / 页框号(4 路)
00 / — / —0 / — / —1 / 001 / 151 / 012 / 1F
11 / 013 / 2D1 / 008 / 7E0 / — / —0 / — / —
44-c 图 TLB 的部分内容
先在纸上自己做一遍,再点这里展开满分解答

考点8.5 分页地址变换8.3 Cache 地址划分8.8 TLB → 页表 → Cache训练营 ⑥

先把条件翻译成大白话
  • 虚拟地址空间 16MB = 224 → 虚拟地址 24 位;主存 1MB = 220 → 物理地址 20 位。
  • 页 4KB → 页内偏移 12 位;虚页号 = 24 − 12 = 12 位,页框号 = 20 − 12 = 8 位。
  • Cache 直接映射 8 行,块 32B:物理地址切成 Tag | 行号 3 位 | 块内 5 位。
  • TLB 4 路组相联、共 8 项 → 8 / 4 = 2 组,虚页号的最低 1 位是 TLB 组号,其余 11 位是 TLB 标记。
(1) 虚拟地址几位、哪几位是虚页号?物理地址几位、哪几位是页框号?
答题卡这样写

虚拟地址空间 16MB = 224B,虚拟地址 24 位;页大小 4KB,页内地址 12 位,第 23 ~ 12 位为虚页号。

主存 1MB = 220B,物理地址 20 位,第 19 ~ 12 位为页框号。

(2) 用物理地址访问 Cache,应划分成哪几个字段?
答题卡这样写

块大小 32B → 块内地址 5 位,第 4 ~ 0 位;Cache 共 8 行,直接映射 → 行号 3 位,第 7 ~ 5 位;标记(Tag)= 20 − 3 − 5 = 12 位,第 19 ~ 8 位。

(3) 虚拟地址 001C60H:页在主存吗?物理地址?Cache 命中吗?

先切虚拟地址:高 12 位是虚页号,低 12 位偏移。查页表得页框号,拼上偏移得物理地址;再把物理地址按 (2) 的字段切开查 Cache。

答题卡这样写

001C60H 的虚页号 = 001H,页内地址 = C60H。页表中虚页 1 的有效位为 1,页面在主存中,页框号 04H。

物理地址 = 04H ∥ C60H = 04C60H。

04C60H = 0000 0100 1100 | 011 | 0 0000B:Tag = 04CH,行号 = 3。Cache 第 3 行有效位为 1,但标记为 105H ≠ 04CH,Cache 不命中。

(4) 4 路组相联 TLB:虚拟地址 024BACH 所在页在主存中吗?
答题卡这样写

TLB 共 8 项、4 路组相联,共 2 组,虚页号最低 1 位为组号,高 11 位为标记。

024BACH 的虚页号 = 024H = 0000 0010 0100B,组号 = 0,标记 = 0000 0010 010B = 012H。

TLB 第 0 组中有有效位为 1、标记为 012H 的表项(页框号 1FH),TLB 命中,所以该页面在主存中。

TLB 标记不是整个虚页号 024H——组相联时要去掉组号位,剩下的 11 位是 012H。直接拿 024H 去比较就会误判为不命中。

  • 地址变换两步走:虚页号查页表得页框号,物理地址 = 页框号 ∥ 页内偏移。
  • 组相联 TLB 和组相联 Cache 一样:虚页号低位是组号,高位是标记。
  • TLB 命中就说明页在主存(TLB 只存有效页的表项)。

模拟大题:两级页表 + 组相联 TLB + 物理 Cache(按 408 风格自编)

这类题的真题(2011-44、2016-45、2018-44、2021-44)都是单级页表。下面这道把两级页表也放进来,同一台机器上走两个虚拟地址:一个从 TLB 一路命中到 Cache,另一个 TLB 缺失、查两级页表后缺页。最后再问页表占多少空间,这是两级页表最常问的“为什么”。

模拟题一次访存走到底(两级页表)计组 · 约 12 分

某计算机的虚拟地址 32 位,物理地址 30 位,按字节编址,页大小 4KB。采用两级页表:虚拟地址 = 页目录号(10 位)| 页表号(10 位)| 页内偏移(12 位),页目录项和页表项各占 4B,页目录的起始物理地址放在 PDBR 中。TLB 共 16 项,采用 4 路组相联映射。Cache 数据区 32KB,采用 8 路组相联映射,块大小 64B,用物理地址访问。

当前 PDBR = 0020 0000H。TLB 第 1 组、第 3 组的内容如下(“—”表示与本题无关):

TLB 组号标记有效位页框号
100201H1—
100300H1—
300100H10A5F1H
300101H01B2C0H

Cache 第 41 组中有一行的 Tag 为 0A5F1H,有效位为 1。页目录第 2 项的内容是:存在位 1,页表所在页框号 00345H。该页表第 1 项的存在位为 0。

(1) TLB 标记、TLB 组号分别是虚拟地址的哪几位?Cache 的 Tag、组号、块内地址分别是物理地址的哪几位?TLB 每项至少多少位(只含标记、有效位和页框号)?(3 分)

(2) 为什么可以在查 TLB 的同时,直接用虚拟地址的低位去选 Cache 的组?(1 分)

(3) CPU 读虚拟地址 0040 3A6CH 中的数据。说明 TLB 和 Cache 是否命中,并给出物理地址。(3 分)

(4) CPU 读虚拟地址 0080 1234H 中的数据。说明访问过程,给出访问的页目录项和页表项的物理地址,以及最后的结果。缺页处理完后重新执行这条指令,这条指令一共要查几次 TLB、读几次页表项(页目录项也算)?设缺页处理程序不修改 TLB,页表项不在 Cache 中。(3 分)

(5) 某进程只用了两段虚拟地址:0000 0000H ~ 003F FFFFH(代码和数据),以及 BFC0 0000H ~ BFFF FFFFH(栈)。它的页目录和页表共占多少空间?如果改用单级页表(每项 4B)呢?(2 分)

先在纸上自己做一遍,再点这里展开满分解答

考点8.5 TLB8.5 分页与两级页表8.8 一次访存的全过程训练营 ⑩ 两级页表

先把条件翻译成大白话
  • 页 4KB,页内偏移 12 位;虚页号 20 位;页框号 = 30 − 12 = 18 位。
  • TLB 16 项、4 路,所以有 4 组。虚页号的低 2 位是 TLB 组号,高 18 位是 TLB 标记。
  • Cache 行数 = 32KB ÷ 64B = 512,8 路,所以有 64 组。块内地址 6 位,组号 6 位,Tag = 30 − 12 = 18 位,正好等于页框号。
(1) 各字段的位置和 TLB 表项位数
答题卡这样写

虚拟地址:虚页号为第 31~12 位。其中 TLB 组号为第 13~12 位(2 位),TLB 标记为第 31~14 位(18 位)。

物理地址:块内地址为第 5~0 位(6 位),Cache 组号为第 11~6 位(6 位),Tag 为第 29~12 位(18 位)。

TLB 每项至少 = 标记 18 + 有效位 1 + 页框号 18 = 37 位。

组相联 TLB 的标记要去掉组号位,不是整个虚页号(20 位)。Cache 的 Tag 按物理地址(30 位)算,不按虚拟地址算。

(2) 为什么能并行
答题卡这样写

Cache 的“组号 + 块内地址”共 6 + 6 = 12 位,恰好等于页内偏移的位数。页内偏移在地址转换时保持不变,所以虚拟地址和物理地址的低 12 位相同,用虚拟地址的第 11~6 位就能选出 Cache 组。查 TLB 与选 Cache 组可以同时进行,拿到页框号后再比较 Tag。

(3) 0040 3A6CH:TLB 命中、Cache 命中
答题卡这样写

虚页号 = 00403H,二进制最低两位为 11,所以 TLB 组号 = 3,TLB 标记 = 00403H >> 2 = 00100H。

TLB 第 3 组中有标记为 00100H、有效位为 1 的项,TLB 命中,页框号 = 0A5F1H。

物理地址 = 0A5F1H 拼接 A6CH = 0A5F 1A6CH。

A6CH = 1010 0110 1100B:Cache 组号 = 第 11~6 位 101001B = 41,Tag = 0A5F1H。Cache 第 41 组中有 Tag 为 0A5F1H 且有效的行,Cache 命中。

TLB 第 3 组中标记为 00101H 的项有效位为 0,就算标记相同也不能算命中。

(4) 0080 1234H:TLB 缺失 → 查两级页表 → 缺页

先切出页目录号和页表号,再按“基址 + 索引 × 4”走两次。

答题卡这样写

0080 1234H:页目录号 = 002H,页表号 = 001H,偏移 = 234H。虚页号 = 00801H,TLB 组号 = 1,标记 = 00200H。TLB 第 1 组中没有标记为 00200H 的项,TLB 缺失。

页目录项地址 = PDBR + 2 × 4 = 0020 0008H。该项存在位为 1,页表起始地址 = 00345H × 4KB = 0034 5000H。

页表项地址 = 0034 5000H + 1 × 4 = 0034 5004H。该项存在位为 0,发生缺页异常,由操作系统把该页调入内存并修改页表项。

次数:第一次执行时查 TLB 1 次(缺失)、读页目录项和页表项 2 次,然后缺页。处理完后重新执行这条指令,再查 TLB 1 次(缺失,因为处理程序没有装入 TLB)、再读页目录项和页表项 2 次(这次有效,并把该项装入 TLB)。共查 TLB 2 次,读页表项 4 次。

缺页属于故障,处理完返回后要重新执行这条指令,所以地址转换要再做一遍(2014-45(3) 的考法)。

(5) 两级页表省了多少空间
答题卡这样写

每张二级页表有 1024 项,管 1024 × 4KB = 4MB 的虚拟地址。0000 0000H ~ 003F FFFFH 正好是页目录第 0 项管的 4MB,BFC0 0000H ~ BFFF FFFFH 正好是第 767 项(BFC0 0000H >> 22 = 2FFH)管的 4MB。所以只需要 2 张二级页表,再加 1 张页目录,共 3 页 = 12KB。

单级页表需要 220 项 × 4B = 4MB,而且必须连续存放。

两级页表省空间,是因为没用到的 4MB 区域不需要分配二级页表。如果进程把整个 4GB 都用满,两级页表反而多出一个页目录(4MB + 4KB)。

  • TLB 的“地址”是虚页号,Cache 的“地址”是物理地址,两者分开切。
  • “组号 + 块内地址”的位数 ≤ 页内偏移时,可以用虚拟地址直接选 Cache 组。
  • 两级页表的每一步都是“基址 + 索引 × 表项大小”;任何一级存在位为 0,都会产生缺页异常。

举一反三:条件一变,答案怎么变

以上面的模拟题为基准,每行只改一个条件。先自己想出新答案和理由,再点开核对。

条件怎么变新答案和理由
Cache 改为 32KB、4 路组相联
想好了再点组数 128,组号 7 位,“组号 + 块内地址”共 13 位,比页内偏移多 1 位。组号的最高位来自页框号,不能只用虚拟地址选组,要等 TLB 给出页框号。
页大小改为 8KB(仍为 32 位虚拟地址)
想好了再点页内偏移 13 位,虚页号 19 位:TLB 组号 2 位,标记 17 位。原 Cache 的“组号 + 块内地址”是 12 位,不超过 13 位,仍可并行查。
TLB 改为全相联
想好了再点没有组号,标记就是整个虚页号(20 位),查找时与 16 项同时比较。
物理地址改为 32 位
想好了再点页框号 20 位,Cache 的 Tag 20 位,TLB 每项 18 + 1 + 20 = 39 位。
改用单级页表
想好了再点TLB 缺失时只要读 1 次页表项,但每个进程的页表有 220 项 × 4B = 4MB,而且要连续存放。
缺页处理程序返回前把新页表项装入 TLB
想好了再点重新执行时 TLB 命中,第 (4) 问变为共查 TLB 2 次、读页表项 2 次。
改用反置页表(每个物理页框一项,每项 8B)
想好了再点物理地址 30 位、页 4KB → 218 个页框,反置页表 = 218 × 8B = 2MB,全系统只要一张,与进程个数、虚拟地址空间大小无关;但查表要按(进程号,虚页号)搜索(常用哈希),(5) 那种“按进程算页表大小”的问法就不成立了(8.5 反置页表)。

丢分点清单

  • 组相联 TLB 拿整个虚页号当标记去比较(要去掉组号位)。
  • Cache 的 Tag 按虚拟地址位数算(应按物理地址)。
  • 有效位为 0 的 TLB 项 / Cache 行当成命中。
  • 缺页后忘了“重新执行指令”,TLB 次数少算一次。
  • 把缺页说成硬件处理,或把 Cache 缺失说成要操作系统处理。
  • 十六进制地址直接心算切段,切错位置。

本类其他真题

  • 2014-45:接 2014-44 的循环:R2 的终值、指令 Cache 命中率、哪条指令可能溢出/缺页、读磁盘和查 TLB 的次数。
  • 2016-45:全相联 TLB + 2 路组相联 Cache:A ~ G 各字段位数、块号 4099 的组号和 Tag、缺页 vs Cache 缺失开销、为什么页面回写。
  • 2018-44:看图说话:物理地址位数、TLB 映射方式和存储器类型、Cache 附加位和总容量、一个地址是否命中。
  • 2019-46:同一页吗、指令 Cache 的地址划分、call 指令只能在哪组命中。
  • 2021-44:2 路组相联 TLB:虚页号、TLB 标记和组号、LRU 替换谁、地址变长后表项多几位。

11.8计组 ⑦ I/O 性能、总线与存储带宽:CPU 到底被占了多少

★★★ 高频综合题

先读:1.8 性能指标 9.1 总线 9.2 总线事务 9.4 三种 I/O 方式 5.4 中断响应 10.1 磁盘

基本功:知识篇里对应的例题和总结表(先做这些,再做本类真题)

这类题长什么样

一类“算账”题:给出主频、CPI、设备速率、每次中断或 DMA 的开销,问 CPU 被这个设备占用了百分之几、能不能用某种方式、总线/主存带宽够不够、磁盘访问要多久。公式只有几个,难点全在“单位翻译”和“一次传多少”。

常见问法考过的地方
中断 / 查询 / DMA 方式下 CPU 时间占比2009-43,2018-43
最多隔多久查询一次、能不能用中断方式2018-43(1)(2)
完成一批传送的总时间和 CPU 时间、中断响应做什么2016-44(2)
MIPS、每秒 Cache 缺失次数、主存带宽、DMA 请求次数2012-43
总线带宽、突发传送时间、程序执行时间2013-43
多体交叉存储器带宽2012-43(4),2013-43(3)
串行通信一帧几位、每秒几个字符2016-44(1)
磁盘地址字段位数、扇区访问时间、DMA 与 CPU 谁优先2022-44,2012-43(3)

做这类题必须会的 6 件事

① 单位先翻译。频率和速率里的 K、M、G 是 103、106、109(500MHz = 5 × 108 次/秒,2MB/s = 2 × 106 B/s);存储容量、块、页里的 K、M 是 210、220(4KB = 4096B)。时钟周期 = 1 / 主频(500MHz → 2ns);每条指令时间 = CPI × 时钟周期;MIPS = 主频 ÷ (CPI × 106)。

② 三种 I/O 方式,CPU 花时间的地方不一样。

时间 →(红色 = CPU 在为这个设备干活;绿色 = CPU 在跑别的程序) 程序查询 CPU 全程陪着 查 查 查 查 查 查 查 查 查 查 反复读状态寄存器“好了没?”→ 好了就搬一个数据 中断 每个数据一次 ↑ 设备每准备好一个数据(如 4B)就发一次中断 红色小段 = 中断响应 + 中断服务程序(保护现场、搬数据、恢复现场) DMA 每个数据块一次 预处理 后处理 CPU 照常跑别的程序 DMA 控制器一个字一个字地搬(周期挪用:偶尔借用一个存储周期) 设置主存地址、字数、方向 块传完发一次中断
图 11-8 程序查询、中断、DMA 的时间线。程序查询:CPU 一直在“问”,数据没来也在等;中断:设备好了才打断 CPU,每个数据(如 4B)打断一次;DMA:CPU 只在一整块数据的开头(设置 DMA 控制器)和结尾(处理完成中断)出场,中间由 DMA 控制器搬。所以同样的数据量,CPU 负担:查询 ≫ 中断 ≫ DMA。

③ 一个万能公式。

每秒次数 = 数据率 ÷ 每次传送的数据量(中断:一个传送单位;DMA:一个数据块) 每次 CPU 开销(周期)= 指令条数 × CPI + 其他开销周期(中断响应、DMA 预处理 + 后处理) CPU 占比 = 每秒次数 × 每次开销 ÷ 主频 最长查询间隔 = 接口数据缓冲寄存器大小 ÷ 数据率 能用中断的条件:一次中断的处理时间 < 相邻两个数据到来的间隔

④ 中断和 DMA 的过程要会说。中断响应(硬件,隐指令):关中断 → 保存断点(PC)和程序状态 → 识别中断源、取得服务程序入口送 PC。中断服务程序:保护现场 → (开中断)→ 传送数据 → 恢复现场 → 开中断 → 中断返回。DMA:预处理(CPU 设置主存起始地址、传送字数、方向,启动设备)→ 数据传送(DMA 控制器接管总线,周期挪用:每满一个接口缓冲请求一次总线)→ 后处理(传完发中断,CPU 做校验等收尾)。DMA 和 CPU 抢总线时 DMA 优先:设备数据不及时取走会被覆盖而丢失,CPU 等一下只是慢一点。

⑤ 总线和主存的带宽。

总线带宽 = 每个总线周期传送的字节数 × 总线时钟频率(如 4B × 200MHz = 800MB/s) 一次突发传送 = 送地址和命令 + 存储器准备数据 + 数据个数 × 总线周期 m 体低位交叉的最大带宽 = m × 每体字长 ÷ 存储周期 每秒 Cache 缺失次数 = 每秒指令数 × 每条访存次数 × 缺失率;所需主存带宽 = 缺失次数 × 块大小

⑥ 串行通信与磁盘(计组视角)。异步串行一帧 = 1 位起始位 + 数据位 + 校验位 + 停止位(起始位题目不说也要算)。磁盘地址 = 柱面号 | 磁头号 | 扇区号,各取 ⌈log2个数⌉ 位;访问一个扇区 = 平均寻道 + 转半圈 + 转过一个扇区的时间(磁盘调度、文件分配等操作系统部分见 ⑫)。

解题模板

  1. 翻译单位。主频 → 每秒周期数、周期长度;速率 → 每秒字节数(106);块/页大小 → 字节数(210)。
  2. 找“一次传多少”。中断:接口一次交给 CPU 的数据量(32 位 = 4B);DMA:一个数据块;周期挪用:接口缓冲寄存器大小。
  3. 求每秒次数和每次开销。写出“每秒 × 次,每次 × 个周期”两句。
  4. 求占比或做比较。占比 = 次数 × 开销 ÷ 主频;可行性 = 比较“处理时间”和“数据间隔”。
  5. 过程题按步骤列:中断响应三件事、DMA 三个阶段。
完整示范2018 年第 43 题计组

假定计算机的主频为 500MHz,CPI 为 4。现有设备 A 和 B,其数据传输率分别为 2MBps 和 40MBps,对应 I/O 接口中各有一个 32 位数据缓冲寄存器。请回答下列问题,要求给出计算过程。

(1) 若设备 A 采用定时查询 I/O 方式,每次输入/输出都至少执行 10 条指令。设备 A 最多间隔多长时间查询一次才能不丢失数据?CPU 用于设备 A 输入/输出的时间占 CPU 总时间的百分比至少是多少?

(2) 在中断 I/O 方式下,若每次中断响应和中断处理的总时钟周期数至少为 400,则设备 B 能否采用中断 I/O 方式?为什么?

(3) 若设备 B 采用 DMA 方式,每次 DMA 传送的数据块大小 1000B,CPU 用于 DMA 预处理和后处理的总时钟周期数为 500,则 CPU 用于设备 B 输入/输出的时间占 CPU 总时间的百分比最大是多少?

先在纸上自己做一遍,再点这里展开满分解答

考点9.4 程序查询、中断、DMA训练营 ⑦

先把条件翻译成大白话
  • 主频 500MHz → 每秒 5 × 108 个周期,1 个周期 2ns;CPI = 4。
  • 设备 A 2MB/s、设备 B 40MB/s;接口里各有一个 32 位(4B)数据缓冲寄存器——设备每送满 4B,CPU 就得把它取走,否则下一个 4B 会把它覆盖。
  • 所以“多久必须处理一次” = 4B ÷ 数据率:A 是 4B ÷ 2MB/s = 2μs,B 是 4B ÷ 40MB/s = 0.1μs。
(1) A 用定时查询:最长多久查一次?CPU 占比至少多少?
答题卡这样写

接口缓冲寄存器 32 位,设备 A 每 4B ÷ 2MB/s = 2μs 装满一次,为不丢失数据,最多间隔 2μs 必须查询一次。

每秒至少查询 2MB/s ÷ 4B = 5 × 105 次,每次至少 10 × 4 = 40 个时钟周期,CPU 占比至少 = 5 × 105 × 40 ÷ (500 × 106) = 4%。

(2) 中断方式下每次中断至少 400 周期,B 能用中断方式吗?

比较两个时间:处理一次中断要多久?下一个数据多久就到?处理时间比间隔还长,数据必然被覆盖。

答题卡这样写

不能。每次中断响应和处理至少需要 400 × 2ns = 0.8μs;而设备 B 每 4B ÷ 40MB/s = 0.1μs 就会送来一个新的 32 位数据。中断还没处理完,缓冲寄存器中的数据已经被覆盖多次,会丢失数据。(或:每秒需中断 107 次,需要 107 × 400 = 4 × 109 个周期,超过 CPU 每秒 5 × 108 个周期。)

(3) B 用 DMA(块 1000B,预/后处理 500 周期),CPU 占比最大多少?
答题卡这样写

每秒 DMA 传送次数最多为 40MB/s ÷ 1000B = 4 × 104 次,每次 CPU 开销 500 个周期,CPU 占比最大 = 4 × 104 × 500 ÷ (500 × 106) = 4%。

  • 处理间隔上限 = 接口缓冲大小 ÷ 数据率。
  • 能否用中断:单次中断处理时间必须小于数据到来的间隔。
  • 三种方式的占比都用同一个公式:每秒次数 × 每次周期数 ÷ 主频。

模拟大题:同一台机器上的中断和 DMA(按 408 风格自编)

这类真题(2009-43、2012-43、2016-44、2018-43、2022-44)的核心永远是“每秒多少次 × 每次多少周期 ÷ 每秒总周期”。下面这道自编题把中断方式和 DMA 方式放在同一个磁盘上比较,加上“来不来得及”的判断和周期挪用,是本类问法的完整组合。

模拟题中断 vs DMA 的 CPU 开销计组 · 约 10 分

某计算机的 CPU 主频为 1GHz,系统总线宽 32 位,总线时钟周期为 10ns,每个总线周期传送 32 位。系统中有打印机和磁盘两台外设。

  • 打印机:采用中断方式,每秒打印 2000 个字符,每个字符请求一次中断。每次中断的开销(响应 + 服务程序)为 400 个时钟周期。
  • 磁盘:数据传输率为 5.12MB/s(1MB = 106B),扇区大小为 512B,接口中有一个 32 位数据缓冲寄存器。

(1) CPU 用于打印机 I/O 的时间占整个 CPU 时间的百分比是多少?(2 分)

(2) 若磁盘也用中断方式,每次中断传送 32 位,每次中断的开销为 400 个时钟周期。CPU 用于磁盘 I/O 的时间占比是多少?会不会丢失数据?若每次中断的开销为 1000 个时钟周期呢?(3 分)

(3) 磁盘改用 DMA 方式,每个扇区启动一次 DMA,CPU 为每次 DMA 做预处理和后处理共需 1000 个时钟周期。CPU 用于磁盘 I/O 的时间占比是多少?(2 分)

(4) DMA 采用周期挪用方式,每传送 32 位占用一个总线周期。DMA 占用了总线时间的百分之多少?DMA 与 CPU 同时请求总线时,应让谁优先?为什么?DMA 传送结束时,DMA 控制器还要向 CPU 发什么请求?做什么用?(3 分)

先在纸上自己做一遍,再点这里展开满分解答

考点9.4 DMA 与 CPU 共享主存9.4 四种 I/O 方式对比9.1 总线性能指标5.4 中断响应

先把条件翻译成大白话
  • 主频 1GHz:每秒 109 个时钟周期,1 个周期 1ns。
  • CPU 占比 = 每秒 I/O 次数 × 每次的周期数 ÷ 每秒总周期数。中断方式是每次传一个数据中断一次;DMA 方式是每传一块数据才让 CPU 管一次。
  • 速率里的 M 是 106,扇区大小 512B 是 29。5.12 × 106 ÷ 512 = 10 000 个扇区每秒,这个数题目是凑好的。
(1) 打印机的 CPU 占比
答题卡这样写

每秒中断 2000 次,每次 400 个时钟周期。CPU 占比 = 2000 × 400 ÷ 109 = 0.08%。

(2) 磁盘用中断方式:占比与是否丢数据

先算每秒要中断几次;再算两次数据到达的间隔,和每次中断的开销比一比:开销比间隔长,就来不及把数据取走。

答题卡这样写

每次中断传 4B,每秒中断次数 = 5.12 × 106B/s ÷ 4B = 1.28 × 106 次。

CPU 占比 = 1.28 × 106 × 400 ÷ 109 = 51.2%。

两次数据准备好的间隔 = 4B ÷ 5.12MB/s = 781.25ns,相当于 781.25 个时钟周期。每次中断只用 400 个周期,下一个 32 位数据到来之前处理得完,不会丢失数据,但 CPU 一半以上的时间都花在磁盘上了。

若每次中断开销为 1000 个周期:1000 > 781.25,上一次中断还没处理完,缓冲寄存器里的数据就被下一个数据覆盖,会丢失数据(占比算出来是 128% > 100%,也说明不可能)。

“会不会丢数据”要比较每次中断的开销和两次数据到达的间隔,不能只看占比小于 100%。

(3) 改用 DMA
答题卡这样写

每秒传送 5.12 × 106 ÷ 512 = 104 个扇区,即每秒 104 次 DMA。

CPU 占比 = 104 × 1000 ÷ 109 = 1%。

与中断方式的 51.2% 相比大幅下降:数据由 DMA 控制器直接在磁盘接口和主存之间传送,CPU 只在每块数据的开始和结束时参与。

(4) 周期挪用、优先级和传送结束中断
答题卡这样写

每秒传送 1.28 × 106 个 32 位数据,每个占用一个 10ns 的总线周期。DMA 每秒占用总线 1.28 × 106 × 10ns = 12.8ms,占总线时间的 1.28%。

同时请求总线时,DMA 优先。磁盘是高速设备,数据来了必须及时写入主存,否则会被后来的数据覆盖而丢失;CPU 晚一个存储周期访存,只是稍慢一点,不会出错。

一个扇区传送完,DMA 控制器向 CPU 发出中断请求,CPU 在中断服务程序中做后处理:检查传送是否正确,决定是继续传下一块还是结束,并唤醒等待这次 I/O 的进程。

DMA 方式中也有中断,但只在“一整块传完”时中断一次,而不是每个数据中断一次。DMA 请求在每个存储周期(总线周期)结束时响应;中断请求在指令执行结束时响应。

  • CPU 占比 = 次数/秒 × 周期/次 ÷ 主频;中断按“数据”数次数,DMA 按“块”数次数。
  • “来不来得及”要比较每次处理的时间和数据到达的间隔。
  • 周期挪用占的是总线周期,不是 CPU 的时钟周期。DMA 的优先级高于 CPU,因为高速设备的数据不能等。

举一反三:条件一变,答案怎么变

以上面的模拟题为基准,每行只改一个条件。先自己想出新答案和理由,再点开核对。

条件怎么变新答案和理由
主频改为 500MHz
想好了再点每秒的周期数减半,各项占比翻倍:打印机 0.16%;磁盘用中断方式为 102.4%,不可能完成;DMA 为 2%。
接口数据寄存器改为 16 位(中断方式,每次 400 周期)
想好了再点每秒中断 2.56 × 106 次,两次数据的间隔只有约 390.6 个周期,小于 400,来不及,会丢数据。
DMA 每次传送 4KB(8 个扇区)
想好了再点每秒 5.12 × 106 ÷ 4096 = 1250 次 DMA,CPU 占比 = 1250 × 1000 ÷ 109 = 0.125%。
总线周期改为 20ns
想好了再点DMA 占用的总线时间 = 1.28 × 106 × 20ns = 2.56%。
改用程序查询方式
想好了再点CPU 要不停地查询设备状态,查询速度还必须跟得上数据到达的速度,CPU 几乎全被占用。这种方式只适合低速设备。
问每传送一个扇区,CPU 要处理几次中断
想好了再点DMA 方式下只有 1 次:一块数据传完时 DMA 控制器发中断,CPU 做后处理。
DMA 改用“停止 CPU 访存”方式:每个扇区传送期间 DMA 一直占用主存
想好了再点磁盘每秒送来 5.12MB ÷ 512B = 104 个扇区,每个扇区要 100μs 才送完。磁盘连续工作时 104 × 100μs = 1s,CPU 始终不能访存;而周期挪用只占 1.28% 的总线时间(程序核对)。这就是停止 CPU 访存方式的缺点(9.4 三种方式)。

丢分点清单

  • 速率里的 M 当成 220(或反过来把 4KB 当 4000B)——题目两种都出现时要分清。
  • 中断次数按“字节”算了(应按每次传送的单位,如 4B)。
  • DMA 开销也按每个字算(应按每个数据块)。
  • 串行通信漏掉起始位。
  • 2016-44 类题目:把“总时间”和“CPU 时间”混为一谈。
  • 多体交叉带宽按单体算,或者突发传送把每个体的 40ns 都加上。
  • “DMA 优先”只写结论不写理由(数据会丢失)。

本类其他真题

  • 2009-43:中断方式和 DMA 方式的 CPU 占比(11.1 里的答题示范就是它的第 1 问)。
  • 2012-43:MIPS、缺失次数、主存带宽、缺页次数、DMA 请求次数、DMA 优先级、四体交叉带宽。
  • 2013-43:CPU/总线周期、总线带宽、突发传送时间、含 Cache 缺失的执行时间。
  • 2016-44:异步串行一帧、中断方式传 1000 个字符的总时间与 CPU 时间、中断响应。
  • 2022-44:磁盘地址字段、扇区平均访问时间、DMA 请求次数与优先级。

11.9操作系统 ⑧ PV 同步互斥:从读题到写出不死锁的代码

★★★ 高频综合题

先读:7.1 为什么并发会出错 7.2 实现互斥 7.3 信号量 7.4 PV 大题五步法 7.6 死锁

基本功:知识篇里对应的例题和总结表(先做这些,再做本类真题)

这类题长什么样

操作系统大题里考得最多的一类:18 年里有 13 道(几乎每年一道)。题目讲一个生活场景(信箱、银行、博物馆、植树、哲学家吃饭),要求“添加信号量和 P、V 操作,写出完整过程,说明信号量含义和初值”;另有少数题问“某段互斥代码对不对”。评分看三样:信号量定义和初值、每个进程的完整代码、关系是否正确(不死锁、并发度够、信号量不多余)。

题型考过的地方
生产者—消费者及变形(多类产品、多生产多消费、两个缓冲区)2009-45,2014-47,2015-45
容量 + 通道 / 服务台(“最多 n 个”“一次只能一个”“叫号”)2011-45,2013-45
前驱关系(操作 A 在 B 之后)2020-45,2022-46
最大并发的互斥(读者—写者思想)2017-46
哲学家进餐2019-43
流水作业 + 共享工具,信号量尽可能少2024-46,2025-45
分析互斥实现是否正确(关中断、swap)2021-45,2023-45

做这类题必须会的 5 件事

① 信号量的值是什么意思。value > 0:还有 value 个资源可用;value = 0:刚好用完,没人在等;value < 0:没有资源,并且有 |value| 个进程在排队等。P(wait)= “申请一个,不够就排队睡觉”;V(signal)= “还回一个,有人排队就叫醒一个”。

② 信号量只有三种用法,初值一眼能看出来。

① 互斥:抢同一把“锁” P1P2 P(mutex) 临界区 V(mutex) P(mutex) 临界区 V(mutex) 锁 mutex 初值 1 同一个进程里 P、V 成对, 紧紧夹住临界区(越小越好) ② 同步:做完了“通知”对方 进程 A进程 B 做 x V(S) P(S) 做 y S 初值 0 B 先到 P(S) 会被挡住, 直到 A 做完 x 执行 V(S)。口诀:前 V 后 P ③ 资源计数:还剩几个 生产者消费者 full = 2(有几个产品) empty = 3(有几个空位) 生产者:P(empty) … V(full) 消费者:P(full) … V(empty) 初值 = 资源的初始数量
图 11-9 信号量的三种用法。任何一道 PV 大题拆开来,都是这三种的组合。判断一个约束属于哪种:“不能同时” → 互斥;“先……才能……” → 同步;“最多 n 个 / 有 n 个” → 资源计数。

③ 经典模型地图——看到这些字眼,就知道套哪个。

题目里出现对应模型信号量怎么设真题
缓冲区、放入/取出、满了等、空了等生产者—消费者empty = n,full = 0,mutex = 12009-45,2014-47
缓冲区里有几类东西,不同的人取不同的多类产品每类一个同步量(odd、even),生产者放完按类 V2009-45
某人要“连续”做 k 次别人才能做加一层同类互斥mutex2 = 1 包住 k 次循环2014-47
两个缓冲区、互相往对方那里放两套生产者—消费者Full_A/Empty_A/mutex_A 和 B 各一套,初值按题目给2015-45
最多容纳 n 人,出入口一次一人容量 + 通道empty = n;mutex = 1 只包“过门”2013-45
座位、叫号、服务员没人时休息服务台(睡眠理发师)座位 empty = n;顾客→服务员 full = 0;服务员→顾客 service = 02011-45
A、B 完成后才能做 C前驱图每条(跨进程的)边一个,初值 0,前 V 后 P2020-45,2022-46
有人只读、有人要写;“最大程度并发”读者—写者读读不互斥;每对“读者—写者”一个 mutex,写者全拿2017-46
围成一圈,每人要左右两个资源哲学家进餐每个资源一个 mutex;再限制同时拿的人数 ≤ n − 12019-43
几道工序接力 + 几个人共用一件工具流水作业相邻工序之间一个“半成品数”;共享工具一个 mutex;上限一个计数量2025-45
“定义尽可能少的信号量”按实际约束设只有先后 → 一个同步量;只怕同时 → 一个互斥量;独占的资源不设2024-46,2025-45

④ 两个没真考过大题、但必须会写的经典模板。

读者—写者(读者优先)
int count = 0;        // 正在读的人数
semaphore rw = 1;     // 读写互斥
semaphore mutex = 1;  // 保护 count
writer() {
  P(rw); 写; V(rw);
}
reader() {
  P(mutex);
  if (count == 0) P(rw);  // 第一个读者上锁
  count++;
  V(mutex);
  读;
  P(mutex);
  count--;
  if (count == 0) V(rw);  // 最后一个读者解锁
  V(mutex);
}

缺点:读者源源不断时写者会饿死。加一个 semaphore w = 1,读者和写者进门前都先 P(w)(读者拿到 rw 后立刻 V(w)),就成了“读写公平”版。

吸烟者(拓展,经典教材题)
semaphore offer1 = 0, offer2 = 0, offer3 = 0;
semaphore finish = 0;   // 抽完一支
provider() {
  while (1) {
    随机放两种材料 i;
    V(offer_i);         // 通知缺这两种的人
    P(finish);          // 等他抽完
  }
}
smoker_i() {
  while (1) {
    P(offer_i);
    卷烟、抽烟;
    V(finish);
  }
}

本质是“一个生产者、三类产品、每类一个消费者,且一次只放一份”,和 2009-45 的“奇偶”是一回事。

⑤ 分析互斥实现对不对(2021-45、2023-45 这类)。检查三件事:互斥(会不会两人同时进)、能前进(会不会谁都进不去、或关着中断死循环卡死系统)、原子性(“测试 + 设置”是不是一步完成,普通函数不行)。另外记住:开/关中断是特权指令,用户程序不能用。

解题模板(五步法)

  1. 找进程、写骨架。有几类进程?每类是循环(while(1))还是只做一次(顾客、参观者)?把题目给的动作按顺序抄下来。
  2. 逐句找约束。对每一句话问:是“不能同时”(互斥)、“先……才能……”(同步)、还是“最多/有 n 个”(资源)?列成清单。
  3. 一个约束一个信号量。写清名字、含义、初值(初值按题目给的初始状态:信箱初始有 x 封 → Full = x)。题目要求尽可能少时,删掉只有一个进程用的资源。
  4. 放 P、V。互斥:P、V 紧夹临界区;同步:前 V 后 P;资源:用前 P、用完 V(谁消耗谁 P,谁产生谁 V)。
  5. 检查四遍。① 每个进程里先 P 资源/同步量,再 P(mutex);② 拿着一个信号量去等另一个时,会不会形成环(死锁);③ 临界区外的动作(生产、消费、参观)有没有被错误锁住(并发度);④ 逐句对照题意,每个约束都落实了。
完整示范2025 年第 45 题操作系统

三个人一起植树,甲挖坑,乙放树苗入坑并填土,丙负责为新种树苗浇水。步骤依次为:挖树坑,放树苗,填土和浇水。现在有铁锹和水桶各一个,铁锹用于挖树坑,填土。水桶用于浇水。当树坑数量小于 3 时,甲才可以挖树坑。设初始坑 = 0,铁锹水桶均可用,定义尽可能少的信号量,用 wait() 和 signal() 操作描述植树过程中三人的同步互斥关系,并说明所用信号量的作用及其初值。

先在纸上自己做一遍,再点这里展开满分解答

考点7.4 PV 五步法(流水作业 + 共享工具)7.6 死锁训练营 ⑧

先把题目翻译成“约束清单”(逐句读题)
  • 流程:甲挖坑 → 乙放树苗、填土 → 丙浇水。
  • “树坑数量小于 3 时甲才可以挖” → 挖好但还没填土的坑最多 3 个 → pit = 3(还能再挖几个坑)。
  • 乙要有挖好的坑才能放树苗 → hole = 0(已挖好、待种的坑数)。
  • 丙要有种好(填好土)的树苗才能浇水 → tree = 0(待浇水的树苗数)。
  • 铁锹只有一把,甲挖坑、乙填土都要用 → shovel = 1(互斥)。
  • 水桶只有丙一个人用,没人跟他抢 → 不需要信号量(题目要求尽可能少)。
用 wait、signal 描述三人的同步互斥关系,说明信号量作用及初值
答题卡这样写
semaphore pit    = 3;   // 还允许挖的树坑数(坑数小于 3 才能挖),初值 3
semaphore hole   = 0;   // 已挖好、待放树苗的坑数,初值 0
semaphore tree   = 0;   // 已种好(填土)、待浇水的树苗数,初值 0
semaphore shovel = 1;   // 铁锹的互斥使用,初值 1

甲() {
    while (1) {
        wait(pit);          // 坑数小于 3 才能挖
        wait(shovel);
        挖树坑;
        signal(shovel);
        signal(hole);       // 多了一个待种的坑
    }
}
乙() {
    while (1) {
        wait(hole);         // 有坑才能种
        放树苗;
        wait(shovel);
        填土;
        signal(shovel);
        signal(pit);        // 坑被填上,坑数减少,甲又可以挖
        signal(tree);       // 通知丙浇水
    }
}
丙() {
    while (1) {
        wait(tree);         // 有种好的树苗才浇水
        用水桶浇水;
    }
}

① 甲必须先 wait(pit) 再 wait(shovel):反过来时,坑已满 3 个的情况下甲拿着铁锹等坑位,乙要铁锹填土才能腾出坑位,二者互相等待 → 死锁。② 给水桶设信号量不算错,但不满足“尽可能少”。③ signal(pit) 放在填土之后:坑被填上才不再算“树坑”。

  • 流水作业:相邻两道工序之间各设一个“产品数”信号量(hole、tree)。
  • “数量小于 k 才能做” → 初值 k 的资源信号量,做完的一方 P,消耗掉的一方 V。
  • 只有一个进程使用的资源不需要信号量。

模拟大题:单车道桥(按 408 风格自编)

PV 大题最常见的“举一反三”,就是把读者—写者换个场景:同方向的车 = 同一类读者(可以一起上桥),相反方向 = 互斥。再加一个“最多 k 辆”的容量限制,就把计数器、方向锁、资源计数三种信号量用法放进了一道题。后面两问考的是 PV 题最容易丢分的两件事:饥饿和死锁。

模拟题单车道桥:方向互斥 + 容量限制操作系统 · 约 8 分

一座东西向的单车道桥:同一方向的车可以一辆接一辆地同时在桥上行驶,相反方向的车不能同时在桥上;由于承重限制,桥上最多同时有 k 辆车。每辆车是一个进程,只过一次桥。

(1) 用信号量和 P、V 操作描述东来车辆和西来车辆的同步过程,说明每个信号量的含义和初值。(5 分)

(2) 你的算法会不会让某一方向的车长时间过不了桥?若会,怎样修改,使车辆大体按到达的先后顺序过桥?(2 分)

(3) 有同学把 P(cap) 写在了“进桥”那段 P(mE) 和 V(mE) 之间,又把下桥时的 V(cap) 挪到了“cntE−−”那段 P(mE) 之后。这样会出现什么问题?(1 分)

先在纸上自己做一遍,再点这里展开满分解答

考点7.4 PV 大题五步法7.5 经典问题7.6 死锁训练营 ⑧ 读者—写者模板

先把条件翻译成大白话
  • 桥是一个要互斥使用的资源,但互斥的是两个方向,不是每辆车:这就是读者—写者里“读者之间不互斥”的做法,只不过两边都是“读者”。
  • 每个方向一个计数器:第一辆上桥的车“抢桥”,最后一辆下桥的车“还桥”;计数器本身要互斥修改。
  • 最多 k 辆:一个初值为 k 的资源信号量,上桥前 P,下桥后 V。
(1) 同步算法
答题卡这样写
int cntE = 0, cntW = 0;    // 已取得通行方向的东来、西来车辆数
semaphore mE = 1, mW = 1;  // 互斥访问 cntE、cntW
semaphore bridge = 1;      // 桥的使用权(哪个方向在用)
semaphore cap = k;         // 桥上的空位数

东来车辆() {
    P(mE);
    if (cntE == 0) P(bridge);   // 东向第一辆:抢桥
    cntE++;
    V(mE);
    P(cap);                     // 占一个空位
    过桥;
    V(cap);
    P(mE);
    cntE--;
    if (cntE == 0) V(bridge);   // 东向最后一辆:还桥
    V(mE);
}
西来车辆() {                     // 与东来对称,cntE、mE 换成 cntW、mW
    P(mW);
    if (cntW == 0) P(bridge);
    cntW++;
    V(mW);
    P(cap);
    过桥;
    V(cap);
    P(mW);
    cntW--;
    if (cntW == 0) V(bridge);
    V(mW);
}

cntE 只能在 mE 的保护下读和改:“判断 cntE == 0”和“cntE++”必须在同一个临界区里,否则两辆东来的车可能都以为自己是第一辆,都去 P(bridge),第二辆就会被自己方向的车挡住。

(2) 饥饿与“先来先过”
答题卡这样写

会。只要东来的车源源不断,cntE 就一直不为 0,东向始终不还桥,西来的车会一直等在 P(bridge) 上,发生饥饿。

修改:增加信号量 semaphore queue = 1;,让所有车在“取得方向”这一段之前先排队:

东来车辆() {
    P(queue);                   // 按到达顺序排队
    P(mE);
    if (cntE == 0) P(bridge);
    cntE++;
    V(mE);
    V(queue);
    P(cap);  过桥;  V(cap);
    P(mE);
    cntE--;
    if (cntE == 0) V(bridge);
    V(mE);
}                               // 西来车辆对称修改

西来的车到达后,拿着 queue 等在 P(bridge) 上。之后到达的东来车辆都被挡在 P(queue) 外,不能再上桥。桥上的东来车辆走完后还桥,西来的车就能通过。

这和本节“读者—写者”模板下面讲的“读写公平”改法完全一样:所有进程进门前先 P 同一个排队信号量,拿到通行权后立刻 V 它。

(3) 在临界区里等 cap 的后果
答题卡这样写

会死锁。桥上已有 k 辆东来的车时,新来的东来车辆拿着 mE 阻塞在 P(cap) 上。桥上的车下桥时要先 P(mE) 才能修改 cntE、之后才执行 V(cap),而 mE 被等待者占着,于是它们也阻塞,永远不会执行 V(cap)。双方互相等待,形成死锁。

如果只挪了 P(cap)、下桥时仍是“先 V(cap) 再 P(mE)”,则不会死锁:桥上的车不用拿 mE 就能还出空位(程序穷举所有交错执行核对过)。但拿着 mE 等空位期间,同方向后来的车连计数都做不了,下桥的车也要等着改 cntE,并发度明显下降。

PV 题的一条通用规矩:拿着互斥信号量时,不要去 P 一个可能阻塞的资源信号量(生产者—消费者里“先 P(empty) 再 P(mutex)”也是这个道理)。

  • “同类可以一起、异类互斥”都用计数器 + 第一个上锁、最后一个解锁的模板。
  • 有容量限制就再加一个初值为 k 的资源信号量。
  • 检查两件事:会不会有一方饿死(加排队信号量);有没有拿着 mutex 去等别的信号量(会死锁)。

举一反三:条件一变,答案怎么变

以上面的模拟题(单车道桥)为基准,每行只改一个条件。先自己想出新答案和理由,再点开核对。

条件怎么变新答案和理由
桥上的车数不限
想好了再点去掉 cap 的 P、V,其余不变。
桥上一次只能有 1 辆车(不管方向)
想好了再点只要一个 semaphore bridge = 1。每辆车 P(bridge)、过桥、V(bridge),计数器都不需要。
两个方向的车必须一辆一辆交替过桥(东边先)
想好了再点用两个同步信号量 turnE = 1、turnW = 0。东来车辆:P(turnE)、过桥、V(turnW);西来车辆:P(turnW)、过桥、V(turnE)。
只有从东往西一个方向,最多 k 辆
想好了再点只要一个资源信号量 cap = k。
阅览室:读者可以同时阅读,管理员整理时不能有读者
想好了再点读者按本题“东来车辆”的写法(计数器,第一个进来的上锁,最后一个出去的解锁);管理员 P(room)、整理、V(room)。

丢分点清单

  • 没写信号量的含义或初值(这是单独的得分点)。
  • 先 P(mutex) 再 P(资源),缓冲区满/空时死锁。
  • 把不访问共享资源的动作(生产、消费、参观、思考)也放进临界区,降低并发。
  • 初值写错:题目给了初始数量(x、y、M − x)却写成 0 或满。
  • 只执行一次的进程写成 while 循环,或循环进程漏了 while。
  • “最大并发”题只用一把锁,读—读也互斥了。
  • “尽可能少”题设了多余的信号量(水桶、同一线程内的先后)。
  • V 放得太晚(2022-46 把 V 放到 D 之后),无谓地降低并发。

本类其他真题

  • 2009-45:一个生产者、奇数/偶数两类消费者。
  • 2011-45:银行叫号:座位、取号机、双向同步。
  • 2013-45:博物馆:容量 500 + 出入口互斥。
  • 2014-47:多生产者多消费者 + 一个消费者连续取 10 件。
  • 2015-45:A、B 信箱辩论:两套生产者—消费者交叉,初值按 x、y、M、N。
  • 2017-46:三个线程读写复数 y、z,要求最大程度并发。
  • 2019-43:n 个哲学家、m 个碗。
  • 2020-45 / 2022-46:前驱关系;后者只为跨线程的边设信号量。
  • 2021-45:用开/关中断实现 wait、signal 的两种方法谁对。
  • 2023-45:用 swap 指令实现互斥:改错、能否用普通函数代替。
  • 2024-46:缓冲区 B 的三种操作,信号量尽可能少。

11.10操作系统 ⑨ CPU 调度:画甘特图、算周转、设计优先数

★★ 常考综合题

先读:6.2 进程状态 6.6 调度的时机与标准 6.7 调度算法

基本功:知识篇里对应的例题和总结表(先做这些,再做本类真题)

这类题长什么样

调度作为大题只出过两次(2016-46 设计优先数、2026-45 画甘特图数调度次数),但它是操作系统选择题里出题最多的单个考点。两类问法:给一组进程和一种算法,算周转/等待时间或调度次数;问某种算法的性质(会不会饥饿、是不是抢占、对谁有利)。

常见问法考过的地方
画甘特图,算平均周转 / 等待时间、调度次数、首次调度时刻2026-45,2012-29,2016-24,2017-23,2018-24,2019-27,2022-25,2023-29,2024-30
为什么会饥饿,设计动态优先数2016-46
时间片、时钟中断间隔改变的影响2026-45(2)
算法的特点(公平、饥饿、适合谁、要用哪些数据结构)2009-24,2010-26,2011-23,2013-31,2014-23,2017-27,2020-26,2021-25,2025-25
什么时候会发生调度、时钟中断做什么2012-30,2018-29,2021-27

做这类题必须会的 5 件事

① 四个时间指标。

周转时间 = 完成时刻 − 到达时刻 带权周转时间 = 周转时间 ÷ 实际运行时间(≥ 1,越接近 1 越好) 等待时间 = 周转时间 − 运行时间(有 I/O 时还要减去 I/O 时间) 响应比 = (已等待时间 + 要求运行时间) ÷ 要求运行时间

② 六种算法一张表。

算法怎么选下一个抢占?饥饿?一句话特点
FCFS 先来先服务最早到达的否否公平但对短作业不利
SJF 短作业优先运行时间最短的非抢占;抢占版叫 SRTN会(长作业)平均周转时间最短
优先级优先级最高的两种都有会(静态时)动态提高等待者的优先级(老化)可避免饥饿
HRRN 高响应比响应比最大的否否兼顾短作业和等待久的作业
RR 时间片轮转就绪队列队首,用完时间片排队尾是(时间片到)否分时系统用;时间片太大退化为 FCFS,太小切换开销大
多级反馈队列高优先级队列优先,每级时间片不同,用完降级是可能(长进程沉底)综合了前几种的优点
进程(到达, 运行):P1(0, 7) P2(2, 4) P3(4, 1) P4(5, 4) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 先来先服务 FCFS P1 P2 P3 P4 平均周转 8.75 短作业优先 SJF(非抢占) P1 P3 P2 P4 平均周转 8.00 最短剩余时间 SRTN(抢占) P1 P2 P3 P2 P4 P1 平均周转 7.00 高响应比优先 HRRN P1 P3 P2 P4 平均周转 8.00 时间片轮转 RR(q = 2) P1 P2 P1 P3 P2 P4 P1 P4 P1 平均周转 9.00
图 11-10 同一组进程,五种算法。SRTN(抢占式最短剩余时间)平均周转最短;RR 为了公平让每个进程都“排队轮流”,平均周转反而最长;这组数据里 HRRN 和 SJF 恰好相同(P3 又短、等得也不短)。自己动手时,每到一个“决策点”(有进程完成、有进程到达且允许抢占、时间片到)就停下来列出就绪队列再选。

③ 什么时候调度、什么时候不能调度。会发生调度:当前进程运行结束、阻塞(等 I/O)、时间片用完、(抢占式下)有更高优先级进程就绪、中断/系统调用返回用户态之前。不能调度:正在处理中断时、正在执行原语或操作内核临界区时、正在进行进程切换时。另外,没有就绪进程时 CPU 运行闲逛进程(idle),它的优先级最低,一有进程就绪就让出 CPU。

④ 题目规则永远优先。2026-45 规定“只有时钟中断时才抢占”,12ms 到的高优先级进程也要等到 20ms;“优先数”小优先还是“优先级”大优先;同级按到达先后还是按进入就绪队列先后;时间片用完的进程与新到达的进程同时进队谁在前(408 常见约定:新到达的在前)。

⑤ 饥饿与老化。饥饿 = 某个就绪进程长时间得不到 CPU(它不是死锁,别的进程在正常推进)。静态优先级、SJF 都可能让某些进程饥饿;解决办法是让等待时间参与优先级计算,等得越久优先级越高(2016-46)。

解题模板

  1. 抄规则。算法、是否抢占、何时可抢占、优先级方向、同级怎么排、时间片多长、时间片用完怎么处理。
  2. 列到达表。进程、到达时刻、运行时间、优先级。
  3. 找决策点,逐个推。每个决策点写出“就绪队列:进程(优先级,进队时刻,剩余时间)”,按规则选一个,写下它运行到下一个决策点。
  4. 画甘特图。标出每段的起止时刻,核对总时长 = 各进程运行时间之和(加上空闲时间)。
  5. 算指标。列表:完成时刻、周转、带权周转、等待;再求平均。
  6. 数次数。调度次数 = 甘特图上“换人/开始”的次数(同一进程接着运行不算);中断次数按题目规定的间隔数。
完整示范2026 年第 45 题操作系统

(本题满分 7 分)系统采用优先级(优先级越大表示优先级越高)与时间片轮转调度算法,仅当发生时钟中断时才触发抢占 CPU 操作,时钟中断间隔为 10ms。进程首次进入就绪队列时,其时间片为 50ms。若进程因时间片用完而返回就绪队列,其优先级值减 1;若进程被更高优先级进程抢占而返回就绪队列,其优先级值保持不变。当多个进程优先级相同时,先进入就绪队列的进程优先被调度。四个进程的到达时刻、初始优先级与 CPU 运行时间如下表所示:

进程到达就绪队列时间(ms)优先级CPU 运行时间(ms)
P110395
P210420
P312240
P414560

(1) 从 10ms 开始进程调度,直至所有进程调度结束,此时中断次数与 CPU 调度次数分别为多少?P1、P2、P3、P4 各自的首次调度发生在哪个时刻?(5 分)

(2) 若时间片由 50ms 改为 100ms,CPU 调度次数将增大、不变还是减少?若时钟中断间隔由 10ms 改为 1ms,系统开销将增大、不变还是减少?(2 分)

先在纸上自己做一遍,再点这里展开满分解答

考点6.7 优先级 + 时间片轮转、画甘特图5.3 时钟中断训练营 ⑨

说明:2026 年真题的官方答案尚未公开,下面按题目给出的规则逐步推演。

先把规则抄下来(这类题一半的分在“读对规则”)
  • 优先级数值越大越优先;同优先级按进入就绪队列的先后。
  • 只在时钟中断时刻(10、20、30……ms)才检查抢占;进程运行完则立即调度下一个。
  • 时间片 50ms:时间片用完 → 优先级 − 1,回到就绪队列尾;被更高优先级抢占 → 优先级不变,回队尾。
  • P1(到达 10,优先级 3,95ms)、P2(10,4,20ms)、P3(12,2,40ms)、P4(14,5,60ms)。
(1) 中断次数和调度次数?各进程首次被调度的时刻?

从 10ms 开始一步一步推,每一步写清“此刻就绪队列里有谁、各自优先级、进队时刻”。

时刻事件调度
10P1(3)、P2(4) 到达,CPU 空闲① 选 P2
12、14P3(2)、P4(5) 到达,不是时钟中断,不抢占—
20时钟中断:P4(5) > P2(4),抢占;P2 已运行 10、剩 10,优先级仍 4,进队(20)② 选 P4
30~60时钟中断,无更高优先级—
70P4 时间片用完(剩 10),优先级 5 → 4,进队(70);P2(4, 20) 与 P4(4, 70) 同级,P2 先进队③ 选 P2
80P2 运行完④ 选 P4(4)
90P4 运行完⑤ 选 P1(3)
140P1 时间片用完(剩 45),优先级 3 → 2,进队(140);P3(2, 12) 先进队⑥ 选 P3
180P3 运行完⑦ 选 P1
225P1 运行完,全部结束—
答题卡这样写

甘特图:10~20 P2,20~70 P4,70~80 P2,80~90 P4,90~140 P1,140~180 P3,180~225 P1。

CPU 调度次数 = 7(10、20、70、80、90、140、180 时刻)。

时钟中断每 10ms 一次,从 10ms 到所有进程结束(225ms)共发生于 10、20、…、220ms,中断次数 = 22。

首次调度时刻:P1 为 90ms,P2 为 10ms,P3 为 140ms,P4 为 20ms。

中断次数的统计口径(是否计入 10ms 那一次、是否算到最后一次调度为止)题面没有说死,考场上写清“统计的是 10ms 至 225ms 之间的时钟中断,共 22 次”,按步骤给分。

(2) 时间片改为 100ms,调度次数?时钟中断间隔改为 1ms,系统开销?
答题卡这样写

时间片 100ms 时:10 调度 P2;20 P4 抢占;P4 运行 60ms 在 80 完成,调度 P2;90 P2 完成,调度 P1;P1 的 95ms 不超过时间片,185 完成,调度 P3。共 5 次,调度次数减少(时间片变长,因时间片用完而引起的调度变少)。

时钟中断间隔改为 1ms:中断次数增加为原来的 10 倍,每次中断都要执行时钟中断处理程序,系统开销增大。

  • 抢占式调度也可能只在特定时刻(时钟中断)检查抢占——一定按题目规则来。
  • 画甘特图时,每个决策点都写出“就绪队列(优先级、进队时刻)”,同级比进队时刻。
  • 时间片越长,调度次数越少;时钟中断越频繁,系统开销越大。

模拟大题:同一组进程,三种算法(按 408 风格自编)

调度出过的两道大题,一道设计优先数(2016-46),一道画优先级 + 时间片的甘特图(2026-45)。选择题里最常考的是 HRRN、SRTN 和 RR 这几种算法的计算,所以这道题用同一组进程把三种算法各推一遍,最后比较它们的性质。所有结果都用程序模拟核对过。

模拟题HRRN、SRTN、RR 同题对比操作系统 · 约 10 分

单 CPU 系统中有 5 个进程,到达时刻和 CPU 运行时间如下表所示(单位 ms),进程在运行过程中不做 I/O,忽略进程切换的开销。

进程P1P2P3P4P5
到达时刻01236
运行时间84952

(1) 采用非抢占式高响应比优先(HRRN)调度。写出每次调度时各就绪进程的响应比,给出调度顺序,求平均周转时间和平均带权周转时间。(3 分)

(2) 采用抢占式最短剩余时间优先(SRTN)调度:新进程到达时,若它的运行时间小于当前进程的剩余时间,就抢占 CPU;剩余时间相同时,先到达的优先。画出甘特图,求平均周转时间和平均等待时间。(3 分)

(3) 采用时间片轮转(RR)调度,时间片为 3ms。进程到达后插入就绪队列尾;若新进程到达与某进程时间片用完发生在同一时刻,新到达的进程先进队。画出甘特图,求平均周转时间。CPU 调度共发生多少次(从 0 时刻第一次调度算起,同一进程连续运行不算新的调度)?P5 的首次调度发生在哪个时刻?(3 分)

(4) 本题中哪种算法的平均周转时间最短?SRTN 有什么缺点?HRRN 为什么不会出现这个缺点?RR 的时间片取多大时会退化为先来先服务?(1 分)

先在纸上自己做一遍,再点这里展开满分解答

考点6.7 每种算法的考点细节6.7 一组进程五种算法对比6.6 调度的标准

先把条件翻译成大白话
  • 周转时间 = 完成时刻 − 到达时刻;带权周转时间 = 周转时间 ÷ 运行时间;等待时间 = 周转时间 − 运行时间。
  • 响应比 R = 1 + 已等待时间 ÷ 运行时间。HRRN 是非抢占的,只在当前进程运行完时才算响应比。
  • SRTN 在每个新进程到达的时刻都要比一次;RR 按“时间片用完”和“进程结束”两种事件推进。RR 在同一时刻进队的先后顺序题目已经规定:新到达的在前。
(1) 非抢占 HRRN

0 时刻只有 P1,它先运行到 8。之后每当一个进程结束,就对所有已到达的进程算一次响应比,选最大的。

答题卡这样写
决策时刻各就绪进程的响应比 R = 1 + 等待 ÷ 运行选中
0只有 P1P1(0~8)
8P2:1 + 7/4 = 2.75;P3:1 + 6/9 ≈ 1.67;P4:1 + 5/5 = 2;P5:1 + 2/2 = 2P2(8~12)
12P3:1 + 10/9 ≈ 2.11;P4:1 + 9/5 = 2.8;P5:1 + 6/2 = 4P5(12~14)
14P3:1 + 12/9 ≈ 2.33;P4:1 + 11/5 = 3.2P4(14~19)
19只剩 P3P3(19~28)

调度顺序:P1 → P2 → P5 → P4 → P3。

进程P1P2P3P4P5
完成时刻812281914
周转时间81126168
带权周转时间12.75≈ 2.893.24

平均周转时间 = (8 + 11 + 26 + 16 + 8) ÷ 5 = 13.8ms;平均带权周转时间 = (1 + 2.75 + 2.89 + 3.2 + 4) ÷ 5 ≈ 2.77。

等待时间要用“决策时刻 − 到达时刻”,例如 8 时刻 P2 已经等了 8 − 1 = 7ms。HRRN 不抢占:P2 在 1 时刻到达时 P1 照样运行到 8。

(2) 抢占式 SRTN

每个到达时刻(1、2、3、6)和每个完成时刻都比较一次“剩余时间”,最小的运行。

答题卡这样写

1 时刻:P2(4)< P1 剩余 7,P2 抢占。2、3 时刻:P3(9)、P4(5)都不小于 P2 的剩余时间,P2 继续运行。5 时刻:P2 完成,在 P1(7)、P3(9)、P4(5)中选 P4。6 时刻:P5(2)< P4 剩余 4,P5 抢占。8 时刻:P5 完成,选 P4(剩余 4)。12 时刻:P4 完成,选 P1(7)。19 时刻:选 P3。

甘特图:0~1 P1,1~5 P2,5~6 P4,6~8 P5,8~12 P4,12~19 P1,19~28 P3。

进程P1P2P3P4P5
完成时刻19528128
周转时间1942692
等待时间1101740

平均周转时间 = 60 ÷ 5 = 12ms;平均等待时间 = 32 ÷ 5 = 6.4ms。

抢占时比较的是新进程的运行时间和当前进程的剩余时间,不是和它的总运行时间比。5 时刻选的是 P4(5),不是更早到达的 P1(剩余 7)。

(3) RR,时间片 3ms

每个决策点写出“就绪队列(从队头到队尾)”。3 时刻 P4 到达、P1 时间片用完,6 时刻 P5 到达、P2 时间片用完,这两处都按“新到达的先进队”排。

答题卡这样写
时刻事件就绪队列(队头 → 队尾)运行
0P1 到达P1P1(0~3)
3P2、P3 已在队中;P4 到达;P1 用完时间片(剩 5)P2、P3、P4、P1P2(3~6)
6P5 到达;P2 用完时间片(剩 1)P3、P4、P1、P5、P2P3(6~9)
9P3 剩 6P4、P1、P5、P2、P3P4(9~12)
12P4 剩 2P1、P5、P2、P3、P4P1(12~15)
15P1 剩 2P5、P2、P3、P4、P1P5(15~17)完成
17—P2、P3、P4、P1P2(17~18)完成
18—P3、P4、P1P3(18~21)
21P3 剩 3P4、P1、P3P4(21~23)完成
23—P1、P3P1(23~25)完成
25—P3P3(25~28)完成

完成时刻:P1 = 25,P2 = 18,P3 = 28,P4 = 23,P5 = 17。周转时间依次为 25、17、26、20、11,平均周转时间 = 99 ÷ 5 = 19.8ms。

CPU 调度发生在 0、3、6、9、12、15、17、18、21、23、25 时刻,共 11 次;P5 的首次调度在 15ms。

如果把规则看反,3 时刻让 P1 排在 P4 前面,后面整张表都会错。进程在时间片中途完成(例如 P5 在 17 结束)要立即调度下一个,不用等满 3ms。

(4) 三种算法比较
答题卡这样写

平均周转时间:SRTN 12ms < HRRN 13.8ms < RR 19.8ms,SRTN 最短。

SRTN 的缺点:总是先运行剩余时间短的进程。如果不断有短进程到达,长进程(如 P3)会一直得不到 CPU,产生饥饿。另外,它要预先知道运行时间,抢占也会带来更多切换。

HRRN 不会饥饿:响应比 R = 1 + 等待时间 ÷ 运行时间,一个进程等得越久,R 越大,最终一定会超过新到达的短进程而被选中。所以它既照顾短进程,又不会让长进程无限等待。

RR 的时间片 ≥ 最长的运行时间(本题 9ms)时,每个进程都能一次运行完,退化为 FCFS。时间片太小,则进程切换过于频繁,开销变大。

  • 非抢占算法只在“有进程结束”时决策;抢占算法在“有进程到达”时也要决策。
  • RR 的关键是写出每一步的队列,同一时刻进队的先后顺序一定按题目规定。
  • 作为参考,非抢占 SJF 做同一组进程,平均周转时间是 13.4ms,介于 SRTN 和 HRRN 之间。运行时间已知、忽略切换开销时,SRTN 能使平均周转时间和平均等待时间最短。

举一反三:条件一变,答案怎么变

以上面的模拟题(5 个进程)为基准,每行只改一个条件。先自己想出新答案和理由,再点开核对。

条件怎么变新答案和理由
RR(3ms),但时间片用完的进程先于同一时刻到达的新进程进队
想好了再点3 时刻队列变为 P2、P3、P1、P4……,平均周转时间 19.6ms,仍调度 11 次。
RR 时间片改为 2ms
想好了再点平均周转时间 18.2ms,调度 14 次,切换更频繁。
RR 时间片改为 4ms
想好了再点平均周转时间 18.6ms,调度 9 次。
RR 时间片 ≥ 9ms
想好了再点每个进程一次运行完,退化为 FCFS:P1、P2、P3、P4、P5 依次运行,平均周转时间 16.6ms,调度 5 次。可见 RR 的平均周转时间不一定比 FCFS 短,它的优势是响应快、公平。
改为非抢占 SJF
想好了再点0~8 运行 P1,之后依次是 P5、P2、P4、P3,平均周转时间 13.4ms。
问平均等待时间
想好了再点等待时间 = 周转时间 − 运行时间,所以平均等待时间 = 平均周转时间 − 平均运行时间(本题为 5.6ms)。例如 SRTN:12 − 5.6 = 6.4ms。
改为先来先服务(FCFS)
想好了再点顺序 P1(0~8)、P2(8~12)、P3(12~21)、P4(21~26)、P5(26~28),周转时间 8、11、19、23、22,平均 16.6ms(程序核对)。只要 2ms 的 P5 排在长进程后面等了 20ms,这就是护航效应(6.7)。

丢分点清单

  • 把“优先数”当“优先级”,方向弄反。
  • 非抢占算法中途换进程,或抢占算法没在新进程到达时检查。
  • RR 中新到达进程和被剥夺进程的入队顺序与题目约定不一致。
  • 周转时间用“完成时刻”直接当周转(忘了减到达时刻)。
  • 设计优先数公式时 waitTime 的符号写反。
  • 把饥饿说成死锁。

本类其他真题

11.11操作系统 ⑩ 分页、多级页表与页面置换

★★★ 高频综合题

先读:8.5 分页 8.7 虚拟内存与置换算法 8.8 一次访存的全过程

基本功:知识篇里对应的例题和总结表(先做这些,再做本类真题)

这类题长什么样

操作系统大题的第二主力(9 道)。给出地址结构和页表(或页目录起始地址),让你:切地址、算页表大小、算页表项的地址、做地址变换、算访问时间、按某种置换算法决定淘汰谁。和 ⑥ 的区别:⑥ 关心 TLB 和 Cache 的硬件结构,⑩ 关心页表本身和置换策略。

常见问法考过的地方
页大小、页表最大多大、页目录和页表共占几页2013-46(1),2015-46(1)(2)
页号 / 页目录号 / 页表索引是多少(或写表达式)2010-46(1),2013-46(2),2017-45(2),2020-46(1),2024-45
页表项(页目录项)的物理地址、页框号、物理地址2009-46(2),2013-46(3),2020-46(1),2024-45
有 TLB、缺页时每次访问要多长时间2009-46(1)
按 FIFO / LRU / CLOCK / 题目自定义策略置换后的页框2009-46(2),2010-46(2)(3),2012-45
拼出虚拟地址、访问几个二级页表、代码占几页2015-46(3),2017-45(1),2018-45(1)
PDBR 存什么、进程/线程切换时变不变、改进 CLOCK 要哪些位2018-45(2)(3)
连续性、局部性2020-46(2)(3),2012-45(4)

做这类题必须会的 6 件事

① 切地址:先看偏移几位。页大小 2k → 页内偏移 k 位,剩下的高位是页号。4KB 页(k = 12)时十六进制最后 3 位就是偏移,很方便;1KB、4MB 这种 k 不是 4 的倍数时,必须展开成二进制切(2010-46、2024-45)。物理地址 = 页框号 ∥ 页内偏移(拼接,不是相加)。

② 页表有多大、表项在哪。

页表项数 = 2^(页号位数)  页表大小 = 页表项数 × 页表项大小 页表项地址 = 页表起始地址 + 页号 × 页表项大小 多级页表:每一级的索引位数 = log₂(页大小 ÷ 页表项大小)(让每张表正好一页) 页目录号 = (LA >> 22) & 3FFH,页表索引 = (LA >> 12) & 3FFH(10 | 10 | 12 结构)

③ 二级页表怎么走——跟着一个真实地址走一遍。

例:2020-46,a[1][2] 的虚拟地址 1080 1008H 页目录号 10 位 = 042H 页号 10 位 = 001H 页内偏移 12 位 = 008H PDBR0020 1000H 进程切换时换它 页目录(1 页) 第 042H 项:00301H ⋮⋮ 二级页表(页框 00301H) 第 001H 项:页框号 F ⋮ 数据页(页框 F) 偏移 008H:a[1][2] 0020 1000H + 042H×4= 0020 1108H 0030 1000H + 001H×4= 0030 1004H 物理地址 = F ∥ 008H
图 11-11 二级页表:三次“基址 + 索引 × 表项大小”。PDBR 给页目录的起点,页目录号选中一个目录项,目录项里是二级页表所在的页框号(× 页大小就是二级页表的起点);页号选中一个页表项,页表项里是数据所在的页框号;最后拼上偏移。每一步都要访存一次——所以才需要 TLB。

④ 有效访问时间。TLB 命中:tTLB + t内存;TLB 未命中、页在内存:tTLB + t内存(查页表)+ t内存(取数);缺页:再加缺页处理时间,处理完重新执行这条指令(又一次 tTLB + t内存)。

⑤ 置换算法和分配策略。

算法淘汰谁要点
OPT 最佳以后最长时间不用的缺页最少,但要预知未来,只用来比较
FIFO最早装入的(看装入时刻)可能出现 Belady 异常(页框多了缺页反而多)
LRU最久没被访问的(看最近访问时刻)效果接近 OPT,需要硬件支持(计数器或栈)
CLOCK指针扫描:访问位 1 → 清 0 跳过;遇到 0 → 淘汰全是 1 时转一圈回到起点淘汰
改进 CLOCK先找 (访问位 0, 修改位 0);找不到再找 (0, 1),扫描时把访问位清 0;还不行重复页表项要有访问位和修改位(2018-45)

分配策略:固定分配局部置换(页框数不变,只在自己的页里换,2009-46、2010-46)、可变分配全局置换、可变分配局部置换。驻留集太小 → 频繁缺页,抖动;工作集 = 最近一段时间内访问过的页的集合,驻留集应不小于工作集。

⑥ 几个常考的“为什么”。页表基址寄存器(PDBR)里是物理地址;进程切换时换它,同一进程的线程切换不换。数组在虚拟地址空间连续,物理上不必连续。页表本身也可以放在虚拟地址空间里(2024-45),这时它也有自己的页号和页表项。

解题模板

  1. 写地址结构。页大小 → 偏移位数 → 页号(各级索引)位数;在草稿上画出竖线分段。
  2. 十六进制地址 → 二进制 → 切段。4KB 页可以直接去掉最后 3 位;其他情况一律二进制。
  3. 查表。表项地址 = 基址 + 索引 × 表项大小;看有效位;有效 → 页框号;无效 → 缺页 → 按规定算法选淘汰页(列出每个页的装入时刻/最近访问时刻/访问位)。
  4. 拼物理地址。页框号 ∥ 偏移,再换回十六进制。
  5. 算时间。按“TLB → 页表 → 缺页处理 → 重新执行”逐项相加,每一项写出来源。
完整示范2009 年第 46 题操作系统

请求分页管理系统中,假设某进程的页表内容如下表所示:

页号页框(Page Frame)号有效位(存在位)
0101H1
10
2254H1

页面大小为 4KB,一次内存的访问时间是 100ns,一次快表(TLB)的访问时间是 10ns,处理一次缺页的平均时间 10⁸ ns(已含更新 TLB 和页表的时间),进程的驻留集大小固定为 2,采用最近最少使用置换算法 (LRU) 和局部淘汰策略。假设

① TLB 初始为空;

② 地址转换时先访问 TLB,若 TLB 未命中,再访问页表(忽略访问页表之后的 TLB 更新时间);

③ 有效位为 0 表示页面不在内存,产生缺页中断,缺页中断处理后,返回到产生缺页中断的指令处重新执行。

设有虚地址访问序列 2362H、1565H、25A5H,请问:

(1) 依次访问上述三个虚地址,各需多少时间?给出计算过程。

(2) 基于上述访问序列,虚地址 1565H 的物理地址是多少?请说明理由。

先在纸上自己做一遍,再点这里展开满分解答

考点8.5 快表与有效访问时间8.7 缺页与 LRU训练营 ⑩

先把条件翻译成大白话
  • 页 4KB → 页内偏移 12 位 = 3 个十六进制位,所以 2362H 这类 16 位地址的最高一个十六进制位就是页号:2362H → 页 2,1565H → 页 1,25A5H → 页 2。
  • TLB 10ns,访存 100ns,缺页处理 108ns(已包含更新 TLB 和页表)。
  • 先查 TLB,未命中再查页表(页表在内存,要访存一次);缺页处理完重新执行该指令(再查一次 TLB,这次命中)。
  • 驻留集固定 2 个页框,局部 LRU 置换;当前内存里有页 0(101H)和页 2(254H)。
(1) 依次访问 2362H、1565H、25A5H 各需多少时间?
答题卡这样写

2362H:页号 2。TLB 初始为空,未命中 10ns;访问页表 100ns,页 2 有效(页框 254H);按物理地址访存 100ns。共 10 + 100 + 100 = 210ns(之后页 2 的表项装入 TLB)。

1565H:页号 1。TLB 未命中 10ns;访问页表 100ns,有效位为 0,缺页;缺页处理 108ns(含更新 TLB 和页表);返回后重新执行该指令,TLB 命中 10ns,访存 100ns。共 10 + 100 + 108 + 10 + 100 = 100 000 220ns。

25A5H:页号 2,TLB 命中 10ns,访存 100ns,共 110ns。

缺页那一次最容易漏掉“重新执行时的 TLB 10ns + 访存 100ns”。

(2) 1565H 的物理地址是多少?
答题卡这样写

访问 1565H 时页 1 缺页,驻留集已满(页 0、页 2),采用局部 LRU 置换:页 2 刚被访问过,页 0 最久未被访问,淘汰页 0,页 1 装入页 0 腾出的页框 101H。

物理地址 = 页框号 101H ∥ 页内偏移 565H = 10 1565H。

  • 有效访问时间:TLB 命中 = tTLB + tmem;未命中 = tTLB + 2tmem;缺页再加处理时间和重新执行的时间。
  • 物理地址 = 页框号拼上页内偏移(不是相加),十六进制直接写在一起。

模拟大题:1KB 页 + 四种置换算法(按 408 风格自编)

分页题的真题多用 4KB 页,十六进制最后 3 位就是偏移。下面这道题故意用 1KB 页,偏移 10 位,不是 4 的倍数,必须展开成二进制切。然后在同一张页表上让 FIFO、LRU、CLOCK、改进型 CLOCK 各选一个淘汰页,四个算法的依据一次对比清楚,最后再考 Belady 现象。结果都用程序模拟核对过。

模拟题地址变换与置换算法对比操作系统 · 约 9 分

某系统采用请求分页,逻辑地址 16 位,页大小 1KB,按字节编址。给进程 P 分配了 4 个页框,采用固定分配、局部置换。当前时刻为 260,P 的页表如下(页 1、5、6 等其余页都不在内存;访问位由操作系统定期清零):

页号存在位页框号装入时刻最近访问时刻访问位 A修改位 M
0107H6023001
2112H13015010
312CH2625011
4105H2024000

(1) 逻辑地址中页号和页内偏移各占几位?进程的页表最多有多少项?(1 分)

(2) 求逻辑地址 0E7FH 对应的物理地址。(2 分)

(3) 访问逻辑地址 1A3CH 时会发生什么?分别用 FIFO、LRU、CLOCK 和改进型 CLOCK 算法,应淘汰哪一页?CLOCK 类算法的替换指针当前指向页 3,扫描顺序为 3 → 0 → 2 → 4 → 3。按 LRU 算法,1A3CH 的物理地址是多少?(4 分)

(4) 另有一个进程的页面访问序列为 1, 2, 3, 4, 1, 2, 5, 1, 2, 3, 4, 5。分别给它 3 个和 4 个页框,用 FIFO 算法各缺页几次(开始时页框为空)?这说明什么?LRU 会不会出现这种现象?(2 分)

先在纸上自己做一遍,再点这里展开满分解答

考点8.5 分页地址变换8.7 页面置换算法训练营 ⑩ 切地址

先把条件翻译成大白话
  • 页 1KB = 210B,偏移 10 位,页号 16 − 10 = 6 位。十六进制的边界在 4 位上,10 位偏移会切在一个十六进制数字中间,必须写成二进制再切。
  • 四个算法各看表中的一列:FIFO 看“装入时刻”,LRU 看“最近访问时刻”,CLOCK 看访问位 A,改进型 CLOCK 看 (A, M)。
(1) 地址结构
答题卡这样写

页内偏移 10 位(第 9~0 位),页号 6 位(第 15~10 位)。页表最多 26 = 64 项。

(2) 0E7FH 的物理地址
答题卡这样写

0E7FH = 0000 11 | 10 0111 1111B,页号 = 000011B = 3,偏移 = 10 0111 1111B = 27FH。

页 3 在内存,页框号 2CH。物理地址 = 2CH × 1KB + 27FH = 1011 00 | 10 0111 1111B = B27FH。

不能直接把 0E7FH 的最后 3 个十六进制位当偏移:那是 4KB 页的切法。1KB 页的偏移是 10 位,E 的二进制 1110 中,高 2 位属于页号,低 2 位属于偏移。

(3) 1A3CH:缺页,四种算法各淘汰谁
答题卡这样写

1A3CH = 0001 10 | 10 0011 1100B,页号 = 000110B = 6,偏移 = 23CH。页 6 不在内存,发生缺页。P 已用满 4 个页框,采用局部置换,所以要在 P 自己的页中淘汰一页。

FIFO:淘汰最早装入的页,装入时刻最小的是页 4(20),淘汰页 4。

LRU:淘汰最久没有被访问的页,最近访问时刻最小的是页 2(150),淘汰页 2。

CLOCK:从页 3 开始扫描。页 3 的 A = 1,置 0 后跳过;页 0 的 A = 0,淘汰页 0。页 0 的 M = 1,要先把它写回磁盘。

改进型 CLOCK:第一轮找 (A, M) = (0, 0) 的页,且不改动访问位。页 3 为 (1, 1),页 0 为 (0, 1),页 2 为 (1, 0),都不符合;页 4 为 (0, 0),淘汰页 4,不用写回。

按 LRU,页 6 装入页 2 腾出的页框 12H,物理地址 = 12H × 1KB + 23CH = 4800H + 23CH = 4A3CH。

改进型 CLOCK 第一轮只找 (0, 0),不清访问位;第一轮找不到,第二轮才找 (0, 1),并把经过的页的 A 清 0。它比 CLOCK 多考虑修改位,优先淘汰没被改过的页,省去一次写回磁盘。

(4) Belady 现象
答题卡这样写

FIFO,3 个页框:缺页 9 次。FIFO,4 个页框:缺页 10 次。

页框变多了,缺页次数反而增加,这就是 Belady 异常。FIFO 只按装入顺序淘汰,与页面的使用情况无关,所以会出现这种现象。

LRU 不会出现。同一个时刻,n 个页框中的页一定包含在 n + 1 个页框中的页里(LRU 是“栈式算法”),所以页框越多,缺页次数只会减少或不变。本序列用 LRU:3 个页框缺页 10 次,4 个页框缺页 8 次。

  • 页大小不是 16 的幂(1KB、2KB、4MB)时,一律展开成二进制再切。
  • FIFO 看装入时刻,LRU 看最近访问时刻,CLOCK 看 A 位,改进型 CLOCK 看 (A, M),而且第一轮不改 A 位。
  • 只有 FIFO 会出现 Belady 异常;OPT 和 LRU 都不会。

举一反三:条件一变,答案怎么变

以上面的模拟题为基准,每行只改一个条件。先自己想出新答案和理由,再点开核对。

条件怎么变新答案和理由
页大小改为 2KB
想好了再点偏移 11 位,页号 5 位。0E7FH = 00001 | 110 0111 1111B,页号 1,偏移 67FH。
CLOCK 的指针改为指向页 2
想好了再点页 2 的 A = 1,置 0 后跳过;页 4 的 A = 0,淘汰页 4。
改为可变分配、全局置换
想好了再点可以淘汰其他进程的页,P 的页框数会随之变化;缺页时 P 的页框数可能增加。
Belady 那个序列,FIFO 给 5 个页框
想好了再点序列里只有 5 个不同的页面,只在每页第一次访问时缺页,共 5 次。
访问 0E7FH 是一次写操作
想好了再点页 3 的访问位 A 置 1,修改位 M 也置 1(本来已是 1)。以后淘汰页 3 时要先写回磁盘。
逻辑地址改为 32 位(页 1KB,页表项 4B)
想好了再点页号 22 位,单级页表有 222 项 × 4B = 16MB。一页只能放 256 个表项(8 位索引),需要 ⌈22 ÷ 8⌉ = 3 级页表。
系统采用页面缓冲算法(PBA),FIFO 淘汰的页 4 不久又被访问
想好了再点页 4 没有修改过(M = 0),淘汰时挂到空闲页链表尾,页 6 用的是链首的另一个页框。只要页 4 的页框还没被分配出去,内容就还在:这次访问仍算一次缺页,但不用读磁盘,从链表取回即可(2022-30;8.7 PBA)。
(4) 的序列改用 OPT 算法,3 个和 4 个页框
想好了再点OPT 分别缺页 7 次和 6 次(程序模拟核对):页框多了缺页减少,OPT 不会出现 Belady 异常。
(4) 的序列,第 8 次访问时窗口 Δ = 4 的工作集
想好了再点第 5~8 次访问是 1、2、5、1,工作集 = {1, 2, 5},大小为 3(8.7 工作集)。

丢分点清单

  • 页不是 4KB 时仍按十六进制位切。
  • 物理地址用“页框号 + 偏移”做加法,而不是拼接(或者页框号忘了乘页大小)。
  • 页表项地址忘了乘表项大小(× 4)。
  • 缺页那次访问忘了“重新执行”的时间。
  • FIFO 看成最近访问时刻、LRU 看成装入时刻(两者正好相反)。
  • CLOCK 扫描时没有把访问位清 0,或者指针起点不对。
  • 二级页表占用页数只算了页目录。

本类其他真题

  • 2010-46:页 1KB,逻辑地址 17CAH 的页号;FIFO 和 CLOCK 分别置换后的物理地址。
  • 2012-45:自定义的“周期扫描回收 + 空闲链”置换策略,按时间线推页框号。
  • 2013-46:一级页表大小、二级页表的索引表达式、代码段的两个页表项地址与页框号。
  • 2015-46:二级页表共占多少页、两个地址用了几张二级页表。
  • 2017-45:代码占几页、访问页目录和页表的第几项、scanf 时的进程状态。
  • 2018-45:拼虚拟地址、PDBR、改进型 CLOCK 需要的位。
  • 2020-46:二维数组元素的页目录项和页表项物理地址(上图)。
  • 2024-45:4MB 大页、页表放在虚拟地址空间中:页表项的虚、实地址和页框号。

11.12操作系统 ⑪ 文件系统:目录、inode、混合索引与读盘次数

★★★ 高频综合题

先读:10.4 文件、FCB 与 inode 10.5 目录 10.6 物理结构 10.7 空闲空间 10.8 全局结构 10.9 open + read

基本功:知识篇里对应的例题和总结表(先做这些,再做本类真题)

这类题长什么样

文件系统大题 7 道,套路非常集中:选哪种物理结构并说理由、索引结构能支持多大的文件、读某个字节要读几次盘、目录文件里写的是什么、硬链接、FAT、删除/创建要改哪些元数据。

常见问法考过的地方
连续 / 链接 / 索引哪种合适,FCB 里要哪些字段2011-46
块号占几字节、最大文件长度(直接、混合索引、链接、FAT)2012-46,2014-46(2),2016-47(2),2018-46(1)
读某个字节 / 最后一个块要读几次盘、用到几级间接2016-47(4),2018-46(3),2022-45(3)(4),2026-46(1)
目录文件的内容、硬链接2016-47(1),2022-45(1)(2)
FAT 表项存在哪、FAT 多大2016-47(2)(3)
插入一条记录要访问几次盘2014-46
最多能存多少文件(inode 数 vs 数据块数)2018-46(2),2026-46(1)
删除目录要改哪些元数据、inode 在哪个盘块2026-46
FCB 集中存放还是分散2011-46(2)

做这类题必须会的 6 件事

① 从文件名到数据的一条链。目录项(文件名 → inode 号)→ inode(属性 + 地址项)→(间接索引块)→ 数据块。目录本身也是一个文件,它的“数据”就是一条条目录项。硬链接 = 两个目录项指向同一个 inode(inode 里有链接计数);软链接 = 一个新文件,内容是另一个文件的路径名。

目录项 course1 → 10doc → 10 两个名字同一个 inode= 硬链接(2022-45) inode 10 属性:大小、链接数… 直接 0 直接 1 ⋮(直接地址项 d 个) 直接 d−1 一级间接 二级间接 三级间接 数据块 一级索引块N 个块号 数据块 二级索引块 一级索引块 数据块 inode 已在内存时,读一个数据块要读几次盘: 直接:1 次 一级:2 次 二级:3 次 N = 块大小 ÷ 地址项大小(4KB ÷ 4B = 1024)
图 11-12 混合索引一张图。直接地址项管前 d 块,一级间接再管 N 块,二级管 N² 块,三级管 N³ 块。读一个数据块:每多一层索引块就多读一次盘;如果 inode 还不在内存,再加读 inode 的那 1 次;如果连目录都要从盘上读,再加上读目录的次数。

② 三种物理结构一张表。

结构FCB / inode 里记什么随机访问文件增长缺点
连续分配起始块号、块数快(直接算)难外部碎片;插入/增长要搬移(2014-46)
链接分配(隐式)首块号、末块号慢(顺链读)容易每块要放指针;一块坏全断
链接分配(显式 FAT)首块号(其余在 FAT)较快(FAT 在内存中查)容易FAT 占内存;FAT[i] = 块 i 的下一块
索引分配索引块/地址项快容易索引块占空间;大文件要多级

③ 三个万能公式。

N = 块大小 ÷ 地址项(块号)大小 混合索引最大文件 = (d + N + N² + N³) × 块大小 逻辑块号 = ⌊字节偏移 ÷ 块大小⌋;0 ~ d−1 在直接项,d ~ d+N−1 在一级,再往后在二级…… 块号位数 ≥ log₂(磁盘块总数);FAT 大小 = 表项数 × 表项大小

④ 数读盘次数的方法。一格一格走那条链,问每一步“这个东西现在在内存里吗”:目录文件在不在?inode 在不在?要经过几层索引块?最后读数据块 1 次。题目常说“FAT 和某目录已在内存”“inode 已读入内存”,这些就不算。

⑤ 空闲空间管理。位示图:一块一位,大小 = 块数 ÷ 8 字节(2010-45:16384 块 → 2KB);块号 b ↔ 第 ⌊b ÷ 字长⌋ 个字、第 b mod 字长 位(注意题目从 0 还是 1 编号)。空闲链表、成组链接(UNIX)也要知道。

⑥ 创建、删除、打开要动哪些东西。创建:分配 inode(改 inode 位示图)→ 初始化 inode → 在父目录中加目录项 →(写数据时分配数据块,改磁盘位示图)。删除:反过来——释放数据块和索引块、释放 inode、删除父目录中的目录项(有硬链接时只减链接计数,减到 0 才真正释放)。打开:按路径逐级查目录,找到 inode 读入内存,建立打开文件表项,返回文件描述符。

解题模板

  1. 写出三个数:块大小、地址项大小、直接地址项个数 d;算 N。
  2. 画覆盖范围:直接 [0, d),一级 [d, d+N),二级 [d+N, d+N+N²)……(单位:块)。
  3. 最大文件 = 总块数 × 块大小;块号位数不够时,还要受磁盘总块数限制。
  4. 某个偏移:先求逻辑块号,查它落在哪一级,再按“内存里已有什么”数读盘次数。
  5. 目录/链接题:画出目录树,每个目录文件只写它的直接孩子;inode 号相同即硬链接。
  6. 元数据操作题:按“数据块 → inode → 目录项”的顺序逐项写,并说明在哪个位示图中改哪一位。
完整示范2022 年第 45 题操作系统

某文件系统的磁盘块大小为 4KB,目录项由文件名和索引节点号构成,每个索引节点占 256 字节,其中包含直接地址项 10 个,一级、二级和三级间接地址项各 1 个,每个地址项占 4 字节。该文件系统中子目录 stu 的结构如题 45(a) 图所示:stu 包含子目录 course 和文件 doc,course 子目录包含文件 course1 和 course2。各文件的文件名、索引节点号、占用磁盘块的块号如题 45(b) 图所示。

文件名索引节点号磁盘块号
stu1…
course2…
doc10x
course11030
course2100…
题 45(b) 图(按原卷整理)

请回答下列问题。

(1) 目录文件 stu 中每个目录项的内容是什么?

(2) 文件 doc 占用的磁盘块的块号 x 的值是多少?

(3) 若目录文件 course 的内容已在内存,则打开文件 course1 并将其读入内存,需要读几个磁盘块?说明理由。

(4) 若文件 course2 的大小增长到 6MB,为了存取 course2 需要使用该文件索引节点的哪几级间接地址项?说明理由。

先在纸上自己做一遍,再点这里展开满分解答

考点10.5 目录、硬链接10.4 inode10.6 混合索引训练营 ⑪

先把条件翻译成大白话
  • 目录项 = (文件名,索引节点号);文件的属性和数据块地址都在 inode 里。
  • doc 和 course1 的索引节点号都是 10——同一个 inode,说明它们是同一个文件的两个名字(硬链接)。
  • 块 4KB、地址项 4B → 一个间接块放 1024 个块号;inode 有 10 个直接地址项。
(1) 目录文件 stu 中每个目录项的内容
答题卡这样写

stu 包含子目录 course 和文件 doc,其目录项为:(course,2)和(doc,10)。

course1、course2 是 course 目录的内容,不写在 stu 里。

(2) doc 占用的磁盘块号 x
答题卡这样写

doc 与 course1 的索引节点号相同(都是 10),二者是同一文件的硬链接,共享同一个索引节点,数据块也相同,所以 x = 30。

(3) course 目录已在内存,打开并读入 course1 要读几个磁盘块?
答题卡这样写

course 目录在内存中,可直接得到 course1 的索引节点号 10;读入 10 号索引节点所在的磁盘块(1 次),从中得到数据块号 30;再读磁盘块 30(1 次)。共需读 2 个磁盘块。

(4) course2 增长到 6MB,要用到哪几级间接地址项?
答题卡这样写

6MB ÷ 4KB = 1536 个块。每个间接块可存 4KB ÷ 4B = 1024 个块号。10 个直接地址项 + 一级间接 1024 项共可表示 1034 个块 < 1536,剩下的块需要二级间接地址项。所以需要使用一级和二级间接地址项。

  • inode 号相同 → 硬链接 → 同一个文件(数据、属性都一样)。
  • 判断用到几级:逐级累加能表示的块数,直到覆盖文件的块数。

模拟大题:从路径名到第 5 000 000 个字节(按 408 风格自编)

文件系统的真题分散地考了最大文件、读盘次数、目录内容、FAT、硬链接。下面这道自编题把它们串成一条线:先按路径找到文件,再读文件中间的一个字节,然后换成 FAT 比较,最后问硬链接和软链接。数字都用程序算过。

模拟题inode 混合索引全流程操作系统 · 约 10 分

某文件系统的磁盘块大小为 4KB,块号占 4B。每个文件有一个 inode,其中有 10 个直接地址项、1 个一级间接地址项、1 个二级间接地址项和 1 个三级间接地址项。每个目录文件都只占 1 个磁盘块;目录项中存放文件名和 inode 号;inode 不在内存时,读一个 inode 需要读 1 次磁盘。根目录的 inode 已在内存中。

(1) 该文件系统支持的单个文件最大长度是多少?(写出算式,并估算大小)(2 分)

(2) 进程打开文件 /home/wang/data.bin(此前没有打开过这条路径上的任何文件)。从根目录开始,查找过程中需要读几次磁盘?依次读的是什么?(2 分)

(3) 打开后,进程读该文件偏移量为 5 000 000 处的 1 个字节。该字节在文件的第几个逻辑块(从 0 起)?用到 inode 中的哪个地址项?需要再读几次磁盘?设 inode 已在内存中,间接索引块不在内存中。(3 分)

(4) 若磁盘容量为 64GB,改用 FAT 管理(显式链接),FAT 表项至少占几字节?FAT 共占多少空间?FAT 全部在内存中时,读 (3) 中的字节要读几次磁盘?和混合索引相比,FAT 的随机访问慢在哪里?(2 分)

(5) 执行 ln /home/wang/data.bin /tmp/d(硬链接)后,再删除 /home/wang/data.bin,/tmp/d 还能读吗?若建的是软链接(符号链接),结果呢?(1 分)

先在纸上自己做一遍,再点这里展开满分解答

考点10.6 混合索引10.5 目录查找读盘次数10.6 三种分配方式对比10.5 硬链接与软链接

先把条件翻译成大白话
  • 每个索引块能放 N = 4KB ÷ 4B = 1024 个块号。
  • 各地址项管的逻辑块范围:直接 0~9;一级间接 10~1033(1024 块);二级间接 1034 ~ 1034 + 1024² − 1;三级间接再往后。
  • 路径查找的节奏是“读目录内容 → 找到下一级的 inode 号 → 读那个 inode → 再读它的目录内容……”,根的 inode 已在内存。
(1) 最大文件长度
答题卡这样写

N = 4KB ÷ 4B = 1024。最大文件 = (10 + 1024 + 10242 + 10243) × 4KB = 40KB + 4MB + 4GB + 4TB,约 4TB。

实际能存多大,还受磁盘容量和块号位数的限制。块号 4B 最多能表示 232 个块,即 16TB 的磁盘空间,比 4TB 大,所以这里的上限由索引结构决定。

(2) 打开 /home/wang/data.bin 读几次盘
答题卡这样写

① 读根目录的目录文件(1 块),找到 home 的 inode 号;② 读 home 的 inode;③ 读 home 的目录文件,找到 wang 的 inode 号;④ 读 wang 的 inode;⑤ 读 wang 的目录文件,找到 data.bin 的 inode 号;⑥ 读 data.bin 的 inode,装入内存。

共读磁盘 6 次。

打开文件只读到 inode 为止,不读数据块。每一级目录都要读两次:先读它的 inode,再读它的内容,根目录的 inode 已在内存时只读内容。

(3) 读偏移 5 000 000 处的字节
答题卡这样写

逻辑块号 = ⌊5 000 000 ÷ 4096⌋ = 1220,块内偏移 = 5 000 000 − 1220 × 4096 = 2880。

1220 ≥ 1034,落在二级间接地址项管的范围内。在二级间接范围内的序号 = 1220 − 1034 = 186:第一级索引块的第 186 ÷ 1024 = 0 项,第二级索引块的第 186 项。

需读磁盘 3 次:一级索引块(二级间接地址项指向的块)、二级索引块、数据块。

算逻辑块号时要减去前面各级管的块数(10 + 1024),不能直接拿 1220 去除以 1024。

(4) 换成 FAT
答题卡这样写

64GB ÷ 4KB = 224 个块,表项至少 24 位 = 3B。FAT 有 224 项,共占 224 × 3B = 48MB。

FAT 在内存中时,从首块号出发沿 FAT 链查找 1220 次,都在内存中完成,只需读数据块 1 次。

慢在:要找第 1220 块,必须从首块开始在 FAT 中顺着链走 1220 步,不能像索引那样直接算出位置。而且 FAT 要常驻内存,磁盘越大,FAT 越大。

(5) 硬链接与软链接
答题卡这样写

硬链接:/tmp/d 的目录项直接指向同一个 inode,inode 中的链接计数由 1 变为 2。删除 /home/wang/data.bin 只删去一个目录项,计数减为 1,inode 和数据块都还在,/tmp/d 仍能读。

软链接:/tmp/d 是一个单独的小文件,里面存的是路径名“/home/wang/data.bin”。原文件删除后路径失效,/tmp/d 不能读(悬空链接)。

  • 混合索引:先算逻辑块号,再减去前面各级管的块数,确定用哪一级;读盘次数 = 索引块层数 + 1。
  • 路径查找:每一级“读目录内容 + 读下一级 inode”。
  • FAT 用内存换磁盘访问:随机访问不读盘,但要在内存中顺链查找,FAT 本身也占内存。

举一反三:条件一变,答案怎么变

以上面的模拟题(块 4KB,块号 4B)为基准,每行只改一个条件。先自己想出新答案和理由,再点开核对。

条件怎么变新答案和理由
块大小改为 1KB(块号仍 4B)
想好了再点N = 256,最大文件 = (10 + 256 + 2562 + 2563) × 1KB ≈ 16GB。偏移 5 000 000 在逻辑块 4882,4882 − 266 = 4616,属于二级间接(一级索引第 18 项,二级索引第 8 项),仍读盘 3 次。
块号改为 8B(块 4KB)
想好了再点N = 512,最大文件约 512GB。逻辑块 1220 − 522 = 698,属于二级间接(第 1 项、第 186 项),读盘 3 次。
inode 不在内存中
想好了再点先读 inode,再加 1 次,共 4 次。
读偏移 30 000 处的字节
想好了再点逻辑块 7,在直接地址项中,只读数据块 1 次。
路径多一级:/home/wang/2026/data.bin
想好了再点每多一级目录,多读 2 次(目录内容 + 下一级的 inode),共 8 次。
目录文件占多个块,需要顺序查找
想好了再点读目录内容的次数增加。要找的目录项在各块的概率相同时,平均读 (块数 + 1) ÷ 2 块。
inode 改用链接方案:只有 1 个指向索引块的指针,每个索引块的最后一项指向下一个索引块
想好了再点每个索引块放 4096 ÷ 4 − 1 = 1023 个块号。偏移 5 000 000 在逻辑块 1220,1220 ÷ 1023 = 1 余 197,在第 2 个索引块中:沿链读 2 个索引块,再读数据块,共 3 次(程序核对;10.6 链接方案)。
另一个进程也打开了 data.bin
想好了再点第二次 open 仍按路径查目录,找到的是同一个 inode。系统打开文件表中 data.bin 仍只有一项,打开计数变为 2,inode 已在内存,不用再读;第二个进程在自己的打开文件表中有一项,读写指针从 0 开始,和第一个进程互不影响(10.4 打开文件表)。

丢分点清单

  • 把 N 算成“块大小 ÷ inode 大小”(应除以地址项大小)。
  • 最大文件忘了加直接地址项那一部分,或者多级的层数弄错。
  • 数读盘次数时把已经在内存里的 inode / 目录 / FAT 也算进去,或者漏了读 inode 那一次。
  • 目录文件内容写了孙子目录的项。
  • “最多存多少文件”只看数据块,不看 inode 个数。
  • 链接分配最大文件用整个块大小算(每块要扣掉指针)。
  • 删除文件只写“删除目录项”。

本类其他真题

  • 2011-46:一次写入、不再修改的文件选哪种分配方式;FCB 集中存放的好处。
  • 2012-46:4TB 文件系统的块号字节数、直接索引最大文件、连续区 + 索引的最大文件和字段分配。
  • 2014-46:连续分配和链接分配下插入第 30 条记录的访盘次数、链接分配的最大文件。
  • 2016-47:FAT 文件系统:目录文件内容、FAT 大小、簇号存放位置、读第 5000 字节访问哪些簇。
  • 2018-46:inode 混合索引:最大文件、最多存多少个 5600B 文件、F1/F2 取最后簇号的时间比较。
  • 2026-46:inode 所在盘块、读偏移 21460 最多读几块、最多文件数、删除目录要改的元数据。

11.13操作系统 ⑫ 磁盘:容量、地址换算、调度与访问时间

★★★ 高频综合题

先读:10.1 磁盘结构与访问时间 10.2 磁盘调度 10.3 初始化与引导 10.7 位示图

基本功:知识篇里对应的例题和总结表(先做这些,再做本类真题)

这类题长什么样

磁盘单独成题不多(2010-45、2019-44,另有计组的 2022-44 和引导题 2021-46),但每道都很“送分”:容量、簇号 ↔ 物理地址、调度顺序和移动距离、访问时间、位示图、SSD 用什么调度。公式记住、题目条件读对就能拿满。

常见问法考过的地方
磁盘容量、地址字段位数2019-44(1),2022-44(1)
按某种调度算法的访问顺序、移动磁道数2010-45(2),2019-44(2)
簇号 / 块号 → 柱面、磁头、扇区;由谁完成2019-44(3)
读若干扇区的总时间 / 平均访问时间2010-45(2),2022-44(2)
空闲块用位示图管理2010-45(1)
换成 SSD / Flash 用什么调度2010-45(3)
格式化、分区、引导的顺序2021-46

做这类题必须会的 5 件事

① 结构与容量。盘面数 = 磁头数;每个盘面上同一半径的磁道组成一个柱面。容量 = 柱面数 × 磁头数 × 每道扇区数 × 扇区大小。物理地址 = (柱面号, 磁头号, 扇区号),各字段位数 = ⌈log2个数⌉。

② 簇号(块号)↔ 物理地址。编号顺序是“先扇区、再磁头、再柱面”(同一柱面上的块编完才换柱面,减少寻道)。设每道 s 个簇、每柱面 h × s 个簇:柱面号 = ⌊b ÷ (h·s)⌋;余数 r → 磁头号 = ⌊r ÷ s⌋;余数 × 每簇扇区数 = 起始扇区号。这一转换由磁盘驱动程序完成。

③ 访问时间 = 寻道 + 旋转延迟 + 传输。平均旋转延迟 = 转半圈 = (60 ÷ 转速) ÷ 2;传输一个扇区 = 转一圈 ÷ 每道扇区数。多个随机扇区就每个都加一次旋转延迟和传输时间。

④ 调度算法——同一组请求,四种走法。

FCFS 先来先服务 移动 498 道 0 50 100 150 199 100 55 58 39 18 90 160 150 38 184 SSTF 最短寻道优先 移动 248 道 0 50 100 150 199 100 90 58 55 39 38 18 150 160 184 SCAN 电梯(向大方向) 移动 250 道 0 50 100 150 199 100 150 160 184 90 58 55 39 38 18 C-SCAN 循环扫描(向大方向) 移动 322 道 0 50 100 150 199 100 150 160 184 18 38 39 55 58 90
图 11-13 磁头在 100 号磁道,请求 55、58、39、18、90、160、150、38、184。SSTF 每次去最近的,总距离短,但远处的请求可能饥饿;SCAN(电梯)先沿一个方向走到头再回头,不会饥饿;C-SCAN 只在一个方向上服务,到头后直接“跳回”另一端,各处等待时间更均匀。408 答案里 SCAN/C-SCAN 通常只走到最远的请求就回头(即 LOOK / C-LOOK 的走法),返回的那一段是否计入移动距离要按题意(2010-45 计入了从 120 回到 30 的 90 道)。

⑤ SSD、格式化与引导。SSD / Flash 没有机械运动,随机访问任何位置时间相同,用 FCFS 即可;按页读写、按块擦除,需要磨损均衡(由 SSD 自己的控制器/FTL 完成)。准备一块启动盘:物理格式化(划扇区)→ 分区 → 逻辑格式化(建文件系统、根目录)→ 安装操作系统;开机:ROM 引导程序 → MBR 中的磁盘引导程序 → 分区引导程序 → 操作系统初始化(2021-46,见 ⑬)。

解题模板

  1. 翻译几何参数:柱面数、磁头(盘面)数、每道扇区数、扇区大小、簇 = 几个扇区 → 每道几簇、每柱面几簇。
  2. 换算地址:簇号 → 柱面 → 磁头 → 扇区,写出每一步的除法和余数。
  3. 排调度顺序:标出磁头位置和移动方向,按算法列出顺序,逐段写距离再求和。
  4. 算时间:寻道(距离 × 每道时间,或平均寻道时间)+ 每个请求的旋转延迟 + 每个请求的传输时间。
  5. 写理由:SSD 为什么 FCFS、为什么 SSTF 会饥饿、为什么由驱动程序转换地址。
完整示范2019 年第 44 题操作系统

某计算机系统中的磁盘有 300 个柱面,每个柱面有 10 个磁道,每个磁道有 200 个扇区,扇区大小为 512B。文件系统的每个簇包含 2 个扇区。请回答下列问题:

(1) 磁盘的容量是多少?

(2) 假设磁头在 85 号柱面上,此时有 4 个磁盘访问请求,簇号分别为 100260、60005、101660 和 110560。若采用最短寻道时间优先 (SSTF) 调度算法,则系统访问簇的先后次序是什么?

(3) 第 100530 簇在磁盘上的物理地址是什么?将簇号转换成磁盘物理地址的过程是由 I/O 系统的什么程序完成的?

先在纸上自己做一遍,再点这里展开满分解答

考点10.1 磁盘容量与地址10.2 SSTF9.5 驱动程序训练营 ⑫

先把条件翻译成大白话
  • 300 个柱面,每柱面 10 个磁道(= 10 个磁头),每道 200 个扇区,扇区 512B。
  • 1 簇 = 2 个扇区 → 每道 100 簇,每柱面 1000 簇。簇号按“柱面 → 磁道 → 扇区”的顺序连续编号。
  • 簇号 → 物理地址:簇号 ÷ 1000 = 柱面号(余数 r);r ÷ 100 = 磁道号(余数 s);扇区号 = s × 2。
(1) 磁盘容量
答题卡这样写

容量 = 300 × 10 × 200 × 512B = 307 200 000B = 300 000KB(约 293MB)。

(2) 磁头在 85 号柱面,SSTF 下访问簇的次序
答题卡这样写

每柱面 10 × 200 ÷ 2 = 1000 个簇,簇号 ÷ 1000 得柱面号:100260 → 100,60005 → 60,101660 → 101,110560 → 110。

从 85 号柱面出发,每次选离当前柱面最近的请求:100(距 15)→ 101(距 1)→ 110(距 9)→ 60(距 50)。

访问簇的次序为:100260、101660、110560、60005。

(3) 第 100530 簇的物理地址?簇号转物理地址由什么程序完成?
答题卡这样写

100530 ÷ 1000 = 100 余 530 → 柱面号 100;530 ÷ 100 = 5 余 30 → 磁道号(磁头号)5;该簇是本磁道的第 30 簇,起始扇区号 = 30 × 2 = 60。

物理地址:柱面号 100、磁头号 5、扇区号 60(该簇占第 60、61 号扇区)。

簇号到磁盘物理地址的转换由磁盘驱动程序完成。

  • 簇号 → (柱面, 磁头, 扇区):逐级除以“每柱面簇数”“每道簇数”,余数继续往下算。
  • 和具体设备的物理细节打交道的是驱动程序。

模拟大题:一块磁盘从容量问到访问时间(按 408 风格自编)

磁盘单独出的大题只有两道(2010-45、2019-44),但它们的问法都在下面这道自编题里,而且放在同一块磁盘上连着问:容量和地址位数、位示图定位、簇号换算物理地址、两种调度算法、总访问时间。所有数字都用程序算过。

模拟题磁盘综合操作系统 · 约 10 分

某磁盘的参数如下:

  • 柱面:200 个(0~199);磁头:4 个(0~3);每个磁道 64 个扇区(0~63);扇区大小 512B。
  • 转速 6000 转/分;磁头每移动一个柱面需要 0.2ms。

文件系统以簇为单位分配空间,1 簇 = 同一磁道上 4 个相邻扇区(2KB)。簇号从 0 开始编号,顺序是:同一磁道内按扇区号,一个磁道编完换同一柱面的下一个磁头,一个柱面编完再换下一个柱面。

系统用位示图记录所有簇的空闲状态。位示图按 32 位字存放,字号和位号都从 0 开始,簇号 = 32 × 字号 + 位号。

(1) 该磁盘的容量是多少 MB?物理地址(柱面号, 磁头号, 扇区号)至少需要多少位?(2 分)

(2) 位示图占多少字节?簇 3000 对应位示图中哪个字的哪一位?(2 分)

(3) 簇 3000 对应的物理地址是什么?写出柱面号、磁头号和扇区号的范围。簇 3007 和簇 3008 呢?为什么要按“先扇区、再磁头、最后柱面”的顺序给簇编号?(2 分)

(4) 磁头刚读完簇 3000,正向柱面号增大的方向移动。请求队列中的柱面号依次为 120、30、60、150、10、85、40,每个请求读 1 个簇。分别用 SSTF 和 LOOK 调度,写出访问顺序和磁头移动的总柱面数。LOOK 即电梯算法,到达该方向上最远的请求就掉头。(2 分)

(5) 按 SSTF 的顺序读完这 7 个簇,共需多少时间?每个簇在磁道上的位置随机,忽略控制器的开销。(2 分)

先在纸上自己做一遍,再点这里展开满分解答

考点10.1 磁盘结构与访问时间10.2 调度算法同题对比10.7 位示图10.1 三个“为什么”

先把条件翻译成大白话
  • 1 簇 = 4 个扇区,所以每个磁道有 64 ÷ 4 = 16 簇,每个柱面有 4 × 16 = 64 簇,一共 200 × 64 = 12 800 簇。
  • 转速 6000 转/分,转一圈 = 60 ÷ 6000 s = 10ms,平均旋转延迟是半圈,即 5ms。读 1 簇就是读 4 个扇区,用时 4/64 × 10ms = 0.625ms。
  • 簇号 → 物理地址:先用 ÷ 64 求柱面号,余数再 ÷ 16 求磁头号,最后的余数 × 4 得到起始扇区号。
(1) 容量和物理地址位数
答题卡这样写

容量 = 柱面数 × 磁头数 × 每道扇区数 × 扇区大小 = 200 × 4 × 64 × 512B = 26 214 400B = 25MB。

柱面号 ⌈log2200⌉ = 8 位,磁头号 log24 = 2 位,扇区号 log264 = 6 位,物理地址至少 8 + 2 + 6 = 16 位。

存储容量里的 M 是 220:26 214 400 ÷ 220 = 25。柱面有 200 个,要取 ⌈log2200⌉ = 8 位,不能写成 7 位(27 = 128 < 200)。

(2) 位示图的大小和簇 3000 的位置
答题卡这样写

簇总数 = 200 × 4 × 64 ÷ 4 = 12 800 个,每个簇占 1 位,位示图大小 = 12 800 ÷ 8 = 1600B(即 400 个 32 位字)。

3000 = 32 × 93 + 24,所以簇 3000 对应第 93 个字的第 24 位(字号、位号都从 0 开始)。

位示图管理的单位是“簇”,不是扇区。按扇区算会得到 6400B,是正确结果的 4 倍。

(3) 簇号 → 物理地址,以及为什么这样编号
答题卡这样写

簇 3000:3000 ÷ 64 = 46 余 56,所以柱面号是 46;56 ÷ 16 = 3 余 8,所以磁头号是 3;8 × 4 = 32,扇区号为 32~35。物理地址为 (46, 3, 32~35)。

簇 3007:3007 = 64 × 46 + 63,63 = 16 × 3 + 15,15 × 4 = 60,物理地址为 (46, 3, 60~63)。

簇 3008:3008 = 64 × 47 + 0,物理地址为 (47, 0, 0~3)。编号连续的两个簇,一个在 46 号柱面的最后一个位置,一个在 47 号柱面的开头。

原因:编号连续的簇先占满同一个磁道,再换同一柱面的其他磁头。换磁头只是电子切换,不用移动磁臂,一个柱面全部用完才移到下一个柱面。这样,连续存放的文件读写时寻道次数最少,速度最快。

(4) SSTF 与 LOOK 的访问顺序和移动距离

SSTF 每次去离当前位置最近的请求,不管方向;LOOK 先沿给定方向(增大)走到最远的请求,再掉头。

答题卡这样写

SSTF:46 → 40 → 30 → 10 → 60 → 85 → 120 → 150。移动柱面数 = 6 + 10 + 20 + 50 + 25 + 35 + 30 = 176。

LOOK:46 → 60 → 85 → 120 → 150 → 40 → 30 → 10。移动柱面数 = (150 − 46) + (150 − 10) = 104 + 140 = 244。

SSTF 在第一步就朝柱面号减小的方向走了,因为 40 离 46 最近。题目给出的移动方向只对 SCAN、LOOK、C-SCAN 起作用。本例 SSTF 的距离更短,但如果不断有请求落在磁头附近,远处的请求会饥饿。

(5) 按 SSTF 读完 7 个簇的总时间
答题卡这样写

寻道时间 = 176 × 0.2ms = 35.2ms。

每个请求的旋转延迟 = 转半圈 = (60 ÷ 6000) s ÷ 2 = 5ms;传输 1 簇 = 4 ÷ 64 × 10ms = 0.625ms。

总时间 = 35.2 + 7 × (5 + 0.625) = 35.2 + 39.375 = 74.575ms。

“位置随机”意味着 7 个请求每个都要加一次平均旋转延迟,不能只加一次。这里的传输时间是 4 个扇区的时间,不是 1 个扇区。

  • 一上来先把“每道几簇、每柱面几簇、转一圈几 ms”这三个量算出来,后面每一问都要用。
  • 簇号换算就是连除两次:先 ÷ 每柱面簇数,再 ÷ 每道簇数;反过来,簇号 = (柱面 × 每柱面簇数) + (磁头 × 每道簇数) + 道内簇序号。
  • 访问时间 = 寻道 + 旋转延迟 + 传输;请求有几个,后两项就加几次。

举一反三:条件一变,答案怎么变

以上面的模拟题为基准,每行只改一个条件。先自己想出新答案和理由,再点开核对。

条件怎么变新答案和理由
转速改为 7200 转/分
想好了再点转一圈 8.33ms,平均旋转延迟 4.17ms,读一簇 0.52ms。总时间 = 35.2 + 7 × (4.17 + 0.52) ≈ 68.0ms。
1 簇 = 8 个扇区
想好了再点每道 8 簇,每柱面 32 簇。簇 3000 → (93, 3, 0~7)。簇总数 6400,位示图 800B。
磁头改为向柱面号减小的方向移动,用 LOOK
想好了再点46 → 40 → 30 → 10 → 60 → 85 → 120 → 150,移动 (46 − 10) + (150 − 10) = 176 个柱面。
改用 SCAN(走到 199 号柱面才掉头)
想好了再点(199 − 46) + (199 − 10) = 342 个柱面。
改用 C-SCAN(到 199 后回到 0,返回距离计入)
想好了再点46 → 60 → 85 → 120 → 150 →(199 → 0)→ 10 → 30 → 40,移动 153 + 199 + 40 = 392 个柱面。
换成 SSD
想好了再点没有寻道时间和旋转延迟,访问时间与位置无关,用 FCFS 即可;要由 SSD 的控制器做磨损均衡。
簇 3000 中有一个扇区坏了,磁盘控制器用同一磁道上的备用扇区替换了它
想好了再点文件系统和驱动程序看到的簇号、物理地址都不变,控制器内部把这个扇区的读写转到备用扇区,对操作系统透明;只是备用扇区和另外 3 个扇区不相邻,读这个簇可能要多等一些旋转时间(10.3 坏块)。

预测大题:建在 SSD RAID 5 上的文件系统 拓展

RAID 和 SSD 在真题里只考过选择题。下面这道把它们和位示图、块号放在一起,作为预测。磨损次数用程序模拟了 12 万次随机小写核对。

模拟题RAID 5 + SSD + 文件系统计组 + 操作系统 · 约 11 分

某服务器用 4 块容量为 512GB 的 SSD(盘号 0~3)组成 RAID 5,条带单元为 4KB。第 k 个条带的校验块放在第 3 − (k mod 4) 号盘上(条带 0 在 3 号盘、条带 1 在 2 号盘、条带 2 在 1 号盘、条带 3 在 0 号盘,依此循环),其余 3 块盘按盘号从小到大依次存放该条带的 3 个数据块。在这个 RAID 5 上建立文件系统,盘块大小为 4KB(与条带单元相同),逻辑块号从 0 开始依次占用各条带的数据块,用位示图管理空闲块。

(1) 文件系统的可用容量是多少?共有多少个盘块?块号至少需要几位?位示图占多少空间?(3 分)

(2) 逻辑块 10 存放在哪块盘上?它所在条带的校验块在哪块盘上?(2 分)

(3) 文件系统改写逻辑块 10(4KB)时,RAID 5 要对 SSD 做哪几次读写?与直接写一块 SSD 相比,多了几次 I/O?(2 分)

(4) 若改用 RAID 4(校验块固定放在 3 号盘),在大量均匀随机的单块写入下,4 块盘被写入的次数之比约为多少?为什么说 RAID 5 对 SSD 的寿命更有利?(2 分)

(5) 2 号盘坏了,读逻辑块 10 要读哪些块?另外,删除文件时文件系统只在位示图中把块标为空闲,SSD 并不知道这些块已经没用了,这会带来什么问题?(2 分)

先在纸上自己做一遍,再点这里展开满分解答

考点10.1 RAID10.1 SSD10.7 位示图

先把条件翻译成大白话
  • RAID 5 每个条带有 4 个块:3 个数据块 + 1 个校验块,所以只有 3/4 的空间放数据。
  • 校验块 = 同一条带 3 个数据块的异或;任意一块盘坏了,用其余 3 块异或就能算回来。
  • SSD 每块的擦写次数有限,写得越多坏得越快。
(1) 容量、块数、位示图
答题卡这样写

可用容量 = 3 × 512GB = 1536GB(1.5TB)。盘块数 = 1536GB ÷ 4KB = 1536 × 218 = 384M(402 653 184)个。228 < 384M ≤ 229,块号至少 29 位(实际用 4B 存放)。位示图 384M 位 = 48MB。

(2) 逻辑块 10 在哪
答题卡这样写

每个条带 3 个数据块:10 ÷ 3 = 3 余 1,在条带 3 中的第 2 个数据块。条带 3 的校验块在 3 − 3 = 0 号盘,数据块依次在 1、2、3 号盘上(逻辑块 9、10、11),所以逻辑块 10 在 2 号盘(该盘的第 4 个条带单元)。

(3) 小写要几次 I/O
答题卡这样写

① 读 2 号盘上逻辑块 10 的旧数据;② 读 0 号盘上的旧校验块;③ 算新校验 = 旧校验 ⊕ 旧数据 ⊕ 新数据;④ 写 2 号盘的新数据;⑤ 写 0 号盘的新校验。共 2 次读、2 次写,4 次 I/O;直接写一块 SSD 只要 1 次写,多了 3 次。

不用读同一条带的另外两个数据块:异或的性质让“旧校验 ⊕ 旧数据 ⊕ 新数据”直接等于新校验。

(4) RAID 4 和 RAID 5 的磨损
答题卡这样写

每次单块写都要写 1 个数据块和 1 个校验块。RAID 4 的校验全在 3 号盘:设共写 N 次,3 号盘被写 N 次,数据写平均分到 0~2 号盘,各约 N/3 次,比例约为 1 : 1 : 1 : 3(模拟 12 万次:约 4 万、4 万、4 万、12 万)。

RAID 5 的校验块轮流放在各盘,每块盘约被写 N/2 次,4 块盘磨损均匀(模拟:每块约 6 万次)。SSD 的擦写次数有限,RAID 4 的校验盘会先磨坏;RAID 5 相当于在盘与盘之间做了磨损均衡。

(5) 坏盘读取与删除文件
答题卡这样写

读 1 号盘的逻辑块 9、3 号盘的逻辑块 11、0 号盘的校验块,三者异或得到逻辑块 10,共读 3 块。此时阵列处于降级状态,再坏一块就会丢数据,应尽快换盘重建。

删除问题:SSD 仍把这些块对应的页当作有效数据,垃圾回收时还会把它们搬来搬去,增加写放大,降低性能和寿命。解决办法是文件系统删除文件时向 SSD 发 TRIM 命令,告诉它哪些逻辑块已经不用了(拓展)。

  • RAID 5:可用容量 (n − 1)/n;小写 = 2 读 2 写;坏一块用其余 n − 1 块异或恢复。
  • 校验块轮流放,既避免了校验盘成为写热点,也让各块 SSD 磨损均匀。
  • 文件系统的“空闲”和 SSD 的“无效页”是两本账,要靠 TRIM 对上。

丢分点清单

  • 簇号换算时忘了“一簇有几个扇区”(2019-44 是 2 个,本节模拟题是 4 个),每道簇数算错。
  • 旋转延迟用了一整圈(应是半圈),或者多个扇区只加了一次。
  • C-SCAN 返回段的计算口径不写清楚。
  • SSTF 两边等距时没按题目规定处理。
  • 磁头移动方向题目给了却没用。

本类其他真题

  • 2010-45:位示图管理 16384 个块、C-SCAN 读 4 个扇区的总时间、Flash 用什么调度。
  • 2022-44(计组):磁盘地址字段位数、扇区平均访问时间、DMA 请求次数。
  • 2021-46:启动顺序、做启动盘的步骤(见 ⑬)。

11.14操作系统 ⑬ 过程分析题:谁在执行、CPU 在什么态、进程在什么状态

★★★ 高频综合题

先读:3.6 进程的地址空间 5.4 中断的完整接力 5.6 系统调用 6.2 进程状态 9.5 I/O 软件层次 9.6 一次 scanf 的全过程 10.3 引导

基本功:知识篇里对应的例题和总结表(先做这些,再做本类真题)

这类题长什么样

近几年的新题型:不算数,而是给你一个过程(读一个字符、开机、运行一段 C 程序),问步骤的先后、每一步由谁执行、CPU 处于用户态还是内核态、进程处于什么状态、某个东西放在地址空间的哪个区域。这类题是计组和操作系统融合得最紧的地方,靠的是把几条“流水线”记熟。

常见问法考过的地方
给出打乱的操作,排出正确顺序2023-46(1),2021-46(1)(2)
某一步之后 CPU 一定切换 / 进程才能被调度2023-46(2)
哪一步属于驱动程序 / 中断处理程序 / 系统调用2023-46(3),2025-46(2),2019-44(3)
此时进程是什么状态、CPU 在什么态2023-46(4),2025-46(1),2017-45(3)
某变量 / 代码 / PCB 在地址空间的哪个区域2025-46
中断响应时 CPU 做了什么2016-44(2),2025-44(2)

做这类题必须会的 4 条“流水线”

① 一次设备输入的全过程(最重要)。

进程 P 内核(内核态) 调度程序 其他进程 键盘 运行:scanf→ 系统调用 陷入 没有字符可读② P 进阻塞队列 P 处于阻塞态(等键盘) 选别的进程 其他进程运行(用户态) ⑥ 用户按键键盘发中断 中断 ④ 键盘中断处理程序 ③ 驱动:字符读入系统缓冲区 ① 唤醒 P插入就绪队列 选中 P ⑤ 从系统调用返回
图 11-14 读一个字符:② → ⑥ → ④ → ③ → ① → ⑤(2023-46 的编号)。蓝色格子都在内核态执行。P 在 ② 之后是阻塞态(所以 CPU 一定切给别人),在 ① 之后是就绪态(才可能被调度),直到 ⑤ 回到用户态继续运行。直接和键盘控制器打交道的 ③ 是驱动程序;④ 期间 P 仍在阻塞态。

② “谁在执行、在什么态”对照表。

执行者CPU 状态典型动作
用户程序、库函数(scanf、printf、malloc 的用户部分)用户态计算、调用库函数、执行陷入指令
系统调用处理 / 设备无关软件内核态检查参数、查缓冲区、分配设备、把进程挂到阻塞队列
设备驱动程序内核态读写设备控制器的寄存器、启动设备、把数据搬进系统缓冲区、簇号 → 物理地址
中断处理程序内核态响应设备完成,调用驱动取数据,唤醒等待的进程
调度程序内核态从就绪队列选进程,进程切换
硬件(中断隐指令)—关中断、保存断点和 PSW、识别中断源、转入处理程序、切到内核态

③ 进程地址空间里“住着谁”。从高地址到低地址:内核区(PCB、页表、内核栈等内核数据)→ 用户栈(局部变量、参数、返回地址)→ 共享库映射区(printf 等库代码)→ 堆(malloc)→ 读/写数据段(全局、静态变量)→ 只读代码段(函数的机器指令、常量字符串)。指针变量放在哪由它自己的类型决定,它指向的东西放在哪要另看(2025-46 的 ptr 在数据段,它指向的字符串在堆)。

④ 开机与准备启动盘。开机:ROM 中的引导程序 → 磁盘引导程序(MBR)→ 分区引导程序 → 操作系统初始化程序。准备启动盘:物理格式化 → 分区 → 逻辑格式化 → 安装操作系统;扇区在物理格式化时划分,根目录在逻辑格式化时建立。

解题模板

  1. 先按时间讲一遍故事。用“进程发出请求 → 发现要等 → 阻塞 → 设备完成 → 中断 → 取数据 → 唤醒 → 调度 → 返回”这条主线,把题目给的每个编号操作放到正确的位置上。
  2. 给每一步标三样东西:谁执行(用户程序 / 内核某部分 / 硬件)、CPU 在什么态、进程在什么状态。
  3. 回答“之后一定……”类问题:找状态发生变化的那一步(进阻塞队列 → 一定切换;进就绪队列 → 才能被调度)。
  4. 地址空间题:先判断变量类型(全局 / 局部 / malloc / 代码 / 内核数据),再对号入座。
完整示范2023 年第 46 题操作系统

进程 P 通过执行系统调用从键盘接收一个字符的输入,已知此过程中与进程 P 相关的操作包括:①将进程 P 插入就绪队列;②将进程 P 插入阻塞队列;③将字符从键盘控制器读入系统缓冲区;④启动键盘中断处理程序;⑤进程 P 从系统调用返回;⑥用户在键盘上输入字符。以上编号①~⑥仅用于标记操作,与操作的先后顺序无关。请回答下列问题。

(1) 按照正确的操作顺序,操作①的前一个和后一个操作分别是上述操作中的哪一个?操作⑥的后一个操作上述操作中的哪一个?

(2) 在上述哪个操作之后 CPU 一定从进程 P 切换到其他进程?在上述哪个操作之后 CPU 调度程序才能选择进程 P 执行?

(3) 完成上述哪个操作的代码属于键盘驱动程序?

(4) 键盘中断处理程序执行时,进程 P 处于什么状态?CPU 处于内核态还是用户态?

先在纸上自己做一遍,再点这里展开满分解答

考点9.6 一次输入的全过程6.2 进程状态9.5 I/O 软件层次训练营 ⑬

先把这件事按时间顺序讲一遍
  • P 执行系统调用要读一个字符,这时键盘缓冲区里还没有字符 → P 只能等:② 把 P 插入阻塞队列,CPU 切换去运行别的进程。
  • 过了一会儿,⑥ 用户在键盘上输入字符,键盘控制器向 CPU 发中断。
  • CPU 响应中断,④ 启动(执行)键盘中断处理程序,它调用驱动程序 ③ 把字符从键盘控制器读入系统缓冲区,然后唤醒 P:① 把 P 插入就绪队列。
  • P 被调度程序选中后继续执行,⑤ 从系统调用返回,拿到字符。
(1) ① 的前一个和后一个操作?⑥ 的后一个操作?
答题卡这样写

正确顺序为 ② → ⑥ → ④ → ③ → ① → ⑤。

操作 ① 的前一个操作是 ③,后一个操作是 ⑤;操作 ⑥ 的后一个操作是 ④。

(2) 哪个操作后 CPU 一定切换到其他进程?哪个操作后调度程序才能选 P?
答题卡这样写

操作 ②(P 插入阻塞队列)之后,P 进入阻塞态,不能继续运行,CPU 一定从 P 切换到其他进程。

操作 ①(P 插入就绪队列)之后,P 处于就绪态,调度程序才能选择 P 执行。

(3) 哪个操作的代码属于键盘驱动程序?
答题卡这样写

操作 ③(将字符从键盘控制器读入系统缓冲区):它直接读写键盘控制器中的寄存器,与具体硬件相关,属于键盘驱动程序。

(4) 键盘中断处理程序执行时,P 的状态?CPU 在什么态?
答题卡这样写

P 处于阻塞态(中断处理程序执行完 ③ 后才把它唤醒);CPU 处于内核态(中断处理程序是操作系统内核的代码)。

  • 读设备的标准时间线:系统调用 → 阻塞 → 设备完成 → 中断 → 中断处理/驱动取数据 → 唤醒(就绪)→ 被调度 → 系统调用返回。
  • 跟具体硬件寄存器打交道的是驱动程序;中断处理程序和驱动都在内核态运行。

模拟大题:一次缺页的全过程(按 408 风格自编)

真题考过“读一个字符”(2023-46)、开机引导(2021-46)和地址空间(2025-46)。还有一条很可能出题的流水线没考过:缺页。它一题就能把计组(异常的类型、断点、MMU)和操作系统(阻塞、唤醒、调度)都考到。下面按 2023-46 的问法自编一道。

模拟题缺页处理过程分析操作系统 · 约 9 分

单 CPU 系统采用请求分页存储管理。进程 P 在用户态执行指令 mov eax, [ebx],读取数组的一个元素,该元素所在的页不在内存中,内存中还有空闲页框,就绪队列中有进程 Q。这次访问涉及下列操作(顺序已打乱):

  1. ① 调度程序选中 P,恢复 P 的现场
  2. ② MMU 查页表,发现该页的存在位为 0,产生缺页异常
  3. ③ 磁盘控制器完成读操作,向 CPU 发出中断请求
  4. ④ 缺页异常处理程序分配一个空闲页框,调用磁盘驱动程序启动磁盘读入该页,并把 P 插入阻塞队列
  5. ⑤ 页面读入完成后,内核修改 P 的页表项(填入页框号、存在位置 1),唤醒 P,把它插入就绪队列
  6. ⑥ P 执行 mov 指令,这次访存成功,读出数据
  7. ⑦ CPU 保存断点和程序状态字,切换到内核态,转去执行缺页异常处理程序
  8. ⑧ 调度程序选中进程 Q 运行

(1) 写出这些操作的正确执行顺序。(2 分)

(2) 哪些操作是由硬件直接完成的,不执行任何程序代码?哪些操作在内核态执行?执行 ⑥ 时 CPU 处于什么态?(2 分)

(3) 在操作 ④ 与 ⑤ 之间,P 处于什么状态?在 ⑤ 与 ① 之间呢?操作 ③ 发生时,CPU 正在执行哪个进程?(2 分)

(4) 缺页异常属于故障、自陷还是终止?操作 ⑦ 保存的断点是哪条指令的地址,为什么?如果换成 P 执行系统调用 read(),返回后接着执行哪条指令?(2 分)

(5) 操作 ② 的缺页异常和操作 ③ 的磁盘中断,CPU 分别在什么时候发现并处理?(1 分)

先在纸上自己做一遍,再点这里展开满分解答

考点8.7 缺页处理全过程5.3 故障、自陷、终止5.4 中断的完整接力6.2 状态转换

先把条件翻译成大白话
  • 主线和“读一个字符”一样,只是起点换成了异常:出错 → 进内核 → 发起 I/O、阻塞 → 调度别人 → 设备中断 → 唤醒 → 调度回来 → 继续。
  • 有两点和系统调用不同:缺页是 MMU 在指令执行过程中发现的;处理完要让原来那条指令重新执行。
  • 有空闲页框,所以不用选淘汰页,也不用写回。
(1) 正确顺序
答题卡这样写

② → ⑦ → ④ → ⑧ → ③ → ⑤ → ① → ⑥。

MMU 发现缺页(②),CPU 响应异常进入内核(⑦)。缺页处理程序启动磁盘并让 P 阻塞(④),CPU 转去运行 Q(⑧)。磁盘读完发中断(③),内核修改页表并唤醒 P(⑤)。P 再次被调度(①),重新执行 mov 指令(⑥)。

④ 之后 P 已经阻塞,CPU 一定会切换给别的进程(⑧),不会空等磁盘。⑤ 只是把 P 放进就绪队列,要等 ① 被调度后 P 才真正运行。

(2) 硬件完成的、内核态执行的
答题卡这样写

由硬件直接完成的:②(MMU 地址转换时检测)、③(磁盘控制器发中断请求)、⑦(中断隐指令:保存断点和 PSW、切换到内核态、转到处理程序)。

在内核态执行的:④、⑤、⑧、①。缺页异常处理程序、磁盘驱动程序、中断处理程序、调度程序都是内核代码。

执行 ⑥ 时 CPU 处于用户态:从异常返回时恢复了 P 的 PSW,回到用户态继续执行 P 的指令。

(3) P 的状态、③ 发生时的当前进程
答题卡这样写

④ 与 ⑤ 之间:P 处于阻塞态,在等待磁盘把页面读入。

⑤ 与 ① 之间:P 处于就绪态,已被唤醒,等待调度。

③ 发生时,CPU 正在执行进程 Q。随后的中断处理在 Q 运行期间进行,它刚开始执行时 P 仍是阻塞态,由 ⑤ 唤醒后才变为就绪态。

不要以为磁盘一发中断,P 就立刻恢复运行。中断处理只负责唤醒 P,让它进入就绪态;P 要等调度程序选中(①)才能再运行。

(4) 缺页异常的类型与断点
答题卡这样写

缺页异常属于故障,而且是可以恢复的故障。

⑦ 保存的断点是引起缺页的这条 mov 指令本身的地址,不是下一条指令的地址。原因:这条指令在取操作数时被打断,并没有执行完;把页面调入后要重新执行它,所以 ⑥ 执行的仍是这条 mov 指令。

系统调用属于自陷(陷入)。执行陷入指令时,这条指令本身就已经完成了,所以断点是陷入指令的下一条指令的地址,返回后从下一条接着执行。

(5) 异常与中断的发现和处理时机
答题卡这样写

缺页异常是在 mov 指令执行过程中(访存时)由 MMU 发现的,CPU 立即处理,不等这条指令结束。

磁盘中断是在 Q 运行时到来的外部中断。CPU 在 Q 的当前指令执行结束后检查中断请求,而且要在开中断的状态下才响应。

  • 缺页题的主线:异常 → 阻塞 → 调度别人 → 中断 → 唤醒 → 调度回来 → 重新执行原指令。
  • 变式:没有空闲页框,并且选中的淘汰页被修改过(修改位为 1)。这时 ④ 要先按置换算法选出淘汰页,把它写回磁盘,再读入新页。P 要等两次磁盘操作,阻塞时间更长。
  • “断点是哪条指令”只看异常类型:故障返回原指令,自陷和中断返回下一条。

举一反三:条件一变,答案怎么变

以上面的模拟题(一次缺页)为基准,每行只改一个条件。先自己想出新答案和理由,再点开核对。

条件怎么变新答案和理由
没有空闲页框,选中的淘汰页修改位为 1
想好了再点④ 中要先按置换算法选出淘汰页,启动磁盘把它写回,再读入新页。P 在两次 I/O 期间都是阻塞态。
P 执行 read() 从磁盘读文件
想好了再点执行陷入指令进入内核,属于自陷,断点是下一条指令。I/O 完成后 P 被唤醒;被调度后,从 read 之后继续执行。
P 做除法时除数为 0
想好了再点属于故障,但处理程序无法修复,一般终止 P,不会返回重新执行。
P 访问的地址超出了自己的地址空间
想好了再点地址越界(存储保护错),通常终止进程(“段错误”)。
P 运行时时间片用完
想好了再点时钟中断(外部中断)。P 由运行态变为就绪态(不是阻塞态),断点是下一条指令。
TLB 缺失,但页在内存中
想好了再点由 MMU 硬件查页表得到页框号(x86 的做法),不进入操作系统,P 一直是运行态。只有页不在内存时才会产生缺页异常。
P 执行的是写指令 mov [ebx], eax;该页在内存中,但它是 fork 后与父进程写时复制共享的只读页
想好了再点MMU 查页表:页在内存,但要写只读页 → 保护异常(故障),陷入内核。内核发现这是写时复制页,就分配一个空闲页框、复制原页内容,把 P 的页表项改为指向新页框并设为可写,然后返回重新执行这条写指令。整个过程没有磁盘 I/O,P 不阻塞,也不用调度 Q(8.7 写时复制)。
P 改用异步 I/O(aio_read)读文件
想好了再点P 发出请求后立即返回,继续在用户态运行,不进入阻塞态;磁盘完成后由中断处理程序把数据放好,再由系统用信号或回调通知 P(9.5 三种 I/O)。

丢分点清单

  • 认为中断处理程序执行时进程 P 已经是就绪态或运行态(它要等唤醒之后才就绪)。
  • 把“启动中断处理程序”放在“读字符”之后(先进中断处理程序,再由它调用驱动读字符)。
  • scanf、printf 属于库函数,但它们的功能最终要经系统调用由驱动程序完成——两者都要答。
  • 把 PCB 放在用户区。
  • 引导顺序漏掉 ROM 中的引导程序。

本类其他真题

  • 2021-46:多阶段引导的顺序、做启动盘的四个操作顺序、扇区划分和根目录建立在哪一步。
  • 2025-46:一段 malloc + scanf 程序:PCB、main 代码、ptr、length、字符串分别在哪个区,哪些函数要用驱动程序。
  • 2017-45(3):scanf 过程中进程状态的变化、CPU 是否进入内核态。
  • 2016-44(2)、2025-44(2):中断/异常响应时 CPU 做什么(计组角度)。

11.15整卷模拟:两套限时大题卷

自编模拟卷

前面 13 类题型是一类一类练的。真实考试里,计组和操作系统的 4 道大题连在一起出现,而且一道题常常横跨好几类:一道 Cache 题会问到分页,一道 I/O 题会问到中断屏蔽。下面两套卷子按 408 的格式出题(第 43、44 题计组,第 45、46 题操作系统,分值与 2026 年相同:10 + 15 + 7 + 8 = 40 分),场景和数字都是新的,每题都附满分答案和评分细则。

怎么用这两套卷
  1. 限时 65 分钟,闭卷,一次做完 4 道题,写在纸上。408 全卷 180 分钟,这 4 道题一般要留出 60~70 分钟。
  2. 做完再点开答案,按评分细则逐个得分点给自己打分:写出了关键步骤就给分,只有结果、没有过程的要扣分。
  3. 记下每题的得分。某一题低于该题分值的一半,就回到它“涉及题型”标出的那几节重新练。

模拟卷一

卷一第 43 题计组 · 10 分 · 涉及题型 ①

某 32 位计算机,int 型用 32 位补码表示,float 型采用 IEEE 754 单精度格式。编译器会把乘以常数的运算优化为移位和加法。有如下 C 代码:

int   x = 0x0CCCCCCD;
int   a = x * 10;      // 被编译为 (x << 3) + (x << 1)
int   y = -7;
int   q = y / 4;
int   r = y >> 2;
float f = x;

(1) x << 3、x << 1 和 a 的机器数各是多少(用十六进制表示)?a 的值是多少?这次乘法是否溢出?说明理由。(3 分)

(2) 若改用乘法指令计算 x × 10,得到 64 位乘积。本例的 64 位乘积是多少?硬件怎样根据乘积判断 int 型乘法是否溢出?(2 分)

(3) q 和 r 的值各是多少?为什么编译器不能把 y / 4 直接编译成算术右移 2 位?编译器通常怎样处理?(2 分)

(4) f 的机器数是多少(用十六进制表示)?f 的值与 x 相差多少?为什么?(3 分)

做完再点开:满分答案与评分细则

考点2.3 移位2.4 溢出判断2.5 乘法2.6 舍入

(1) 移位相加与溢出
答题卡这样写

x << 3 = 6666 6668H,x << 1 = 1999 999AH,a = 6666 6668H + 1999 999AH = 8000 0002H,按补码读值为 −231 + 2 = −2147483646。

溢出。x = 214748365,真实乘积 2147483650 > 231 − 1;从运算上看,两个正数相加得到负数,最高位进位 Cn = 0、次高位进位 Cn−1 = 1,OF = 1。

(2) 用 64 位乘积判断溢出
答题卡这样写

64 位乘积 = 0000 0000 8000 0002H。

判断规则:带符号乘法取低 32 位作结果,只有当高 32 位的每一位都等于低 32 位的最高位时才不溢出。本例低 32 位的最高位是 1,而高 32 位全为 0,所以溢出。

(3) 除法与算术右移
答题卡这样写

q = −1(C 语言整数除法向 0 取整,−1.75 → −1);r = −2(算术右移相当于向下取整,−1.75 → −2)。

y 为负数时两者结果不同,所以不能直接用右移代替除法。编译器的做法是:y 为负时先加上偏置 22 − 1 = 3 再右移,即 (−7 + 3) >> 2 = −1,与除法结果一致。

(4) int 转 float 的舍入
答题卡这样写

x = 1100 1100 1100 1100 1100 1100 1101B,共 28 位有效位,= 1.100 1100 … 1101B × 227。阶码 E = 27 + 127 = 154 = 1001 1010B。

尾数只能放 23 位,要舍去最低 4 位 1101B。它大于一半(1000B),按就近舍入要进 1,尾数 = 100 1100 1100 1100 1100 1101B。

f = 0 10011010 10011001100110011001101B = 4D4C CCCDH,值 = x − 13 + 16 = 214748368,比 x 大 3。原因:float 只有 24 位有效位,x 需要 28 位,无法精确表示。

小问得分点分值
(1)x << 3、x << 1 的机器数正确1
a 的机器数和值正确1
判断为溢出,并给出理由(真值超范围或 OF = 1)1
(2)64 位乘积正确1
写出“高 32 位全等于低 32 位最高位才不溢出”1
(3)q = −1、r = −21
说明取整方向不同,并给出加偏置的处理1
(4)阶码 154 和规格化形式正确1
舍入过程正确,机器数 4D4C CCCDH1
相差 3,并说明有效位只有 24 位1
卷一第 44 题计组 · 15 分 · 涉及题型 ⑤⑥⑩

某计算机的虚拟地址和物理地址都是 32 位,按字节编址,页大小 4KB。TLB 共 16 项,全相联,LRU 替换。数据 Cache 容量 16KB,4 路组相联,块大小 64B,LRU 替换,用物理地址访问。操作系统采用请求分页、局部置换、LRU 置换算法。有如下程序:

int a[1024][1024];            // 起始虚拟地址 0100 0000H
for (j = 0; j < 1024; j++)    // 变量 i、j、sum 都在寄存器中
    for (i = 0; i < 1024; i++)
        sum += a[i][j];

程序开始时 TLB 和 Cache 都为空,a 的所有页都不在内存中。

(1) 数组 a 共占多少页?a[1][2] 的虚拟地址是多少?它的虚页号和页内偏移各是多少?(3 分)

(2) 物理地址中 Cache 的 Tag、组号、块内地址各占几位?数组元素落在 Cache 的哪一组,为什么与操作系统把页放在哪个页框无关?(3 分)

(3) 执行这段程序时,访问数组 a 的 Cache 命中率是多少?若把两重循环交换(i 在外层),命中率是多少?(4 分)

(4) 按原程序,访问 a 时 TLB 的命中率是多少?交换循环后呢?(3 分)

(5) 若系统只给 a 分配 512 个页框,原程序和交换后的程序各发生多少次缺页?(2 分)

做完再点开:满分答案与评分细则

考点8.3 Cache8.5 TLB8.7 置换算法训练营 ⑤ 模拟题

先把条件翻译成大白话
  • 一行 a[i] 有 1024 × 4B = 4KB,恰好一页,而且 a 的起始地址页对齐:第 i 行就是第 i 页。
  • Cache 行数 = 16KB ÷ 64B = 256,4 路,所以有 64 组:块内地址 6 位,组号 6 位,两者合起来 12 位,正好是页内偏移。
  • 原程序按列访问:相邻两次访问相差 4KB,即换了一页,而页内偏移不变。
(1) 页数与地址
答题卡这样写

a 占 1024 × 1024 × 4B = 4MB,共 4MB ÷ 4KB = 1024 页。

a[1][2] 的虚拟地址 = 0100 0000H + (1 × 1024 + 2) × 4 = 0100 0000H + 1008H = 0100 1008H,虚页号 = 01001H,页内偏移 = 008H。

(2) Cache 地址划分
答题卡这样写

块内地址 6 位(第 5~0 位),组号 6 位(第 11~6 位),Tag = 32 − 12 = 20 位(第 31~12 位)。

组号和块内地址都在低 12 位,也就是页内偏移里。地址转换只替换页号,页内偏移不变,所以一个元素落在哪一组只由它的页内偏移决定,与页框号无关。

(3) Cache 命中率
答题卡这样写

原程序:命中率为 0。a[i][j] 与 a[i+1][j] 相差 4KB,页内偏移相同,所以同一列的 1024 个元素都映射到同一组,而一组只有 4 行。内层循环每访问一个元素就调入一个新块,这一组中的块不断被替换。等到访问下一列 a[i][j+1] 时,它所在的块(与 a[i][j] 同一块)早已被换出,所以每次访问都缺失。

交换循环后:命中率为 15/16 = 93.75%。按行顺序访问,每块 64B 放 16 个 int,每块只有第一次访问缺失。

判断按列访问的命中率,关键是算清楚“同一组要放多少个块”:这里是 1024 个块,远超 4 路。

(4) TLB 命中率
答题卡这样写

原程序:命中率为 0。内层循环依次访问第 0、1、…、1023 页,每次都换一页。TLB 只有 16 项,按 LRU 替换,一页被访问过之后,要再过 1024 次访问才会用到它,而那时它早已被换出。

交换循环后:命中率为 1023/1024 ≈ 99.9%。每一页连续访问 1024 次,只有第一次 TLB 缺失。

(5) 缺页次数
答题卡这样写

原程序:1024 × 1024 = 1 048 576 次。每一列都按第 0 ~ 1023 页的顺序访问一遍。只有 512 个页框,LRU 下要访问的页恰好总是最久没用、已被换出的那一页,每次访问都缺页。

交换循环后:1024 次。每页只在第一次访问时缺页,用完这一页才访问下一页。

这就是“循环访问的页数多于页框数”时 LRU 的最坏情况,也是程序局部性差会导致抖动的原因。

小问得分点分值
(1)1024 页1
a[1][2] 的虚拟地址 0100 1008H1
虚页号 01001H、偏移 008H1
(2)Tag 20、组号 6、块内 62
说明组号在页内偏移中1
(3)原程序命中率 0,并说明同一列映射到同一组、放不下2
交换后 93.75%,并说明每块 16 个元素2
(4)原程序 TLB 命中率 0,并给出理由2
交换后 1023/10241
(5)原程序 1 048 576 次1
交换后 1024 次1
卷一第 45 题操作系统 · 7 分 · 涉及题型 ⑧

理发店里有 1 位理发师、1 把理发椅和 n 把供等候的椅子。没有顾客时,理发师在理发椅上睡觉。顾客到来时:若理发师在睡觉,就叫醒他;若理发师正忙而还有空的等候椅,顾客就坐下等候;若等候椅已坐满,顾客直接离开。

(1) 用信号量和 P、V 操作描述理发师和顾客的同步过程,说明信号量的含义和初值。(5 分)

(2) 有同学想用一个初值为 n 的信号量 chairs 表示空的等候椅,让顾客先执行 P(chairs) 再坐下。这样做为什么不符合题意?(2 分)

做完再点开:满分答案与评分细则

考点7.4 PV 五步法7.3 信号量训练营 ⑧

先把条件翻译成大白话
  • 理发师和顾客是一对同步关系:理发师等顾客(没顾客就睡),顾客等理发师(理发师忙就等)。两个方向各用一个同步信号量。
  • “坐满就离开”要求顾客先判断、再决定是否等待,信号量的 P 操作做不到,只能用一个受 mutex 保护的计数器。
(1) 同步算法
答题卡这样写
int waiting = 0;          // 正在等候的顾客数
semaphore mutex = 1;      // 互斥访问 waiting
semaphore customers = 0;  // 等候理发的顾客数,理发师在它上面睡觉
semaphore barber = 0;     // 理发师是否准备好,顾客在它上面等

理发师() {
    while (1) {
        P(customers);     // 没有顾客就睡觉
        P(mutex);
        waiting--;        // 一位顾客离开等候椅
        V(barber);        // 通知这位顾客:理发师准备好了
        V(mutex);
        理发;
    }
}
顾客() {
    P(mutex);
    if (waiting < n) {
        waiting++;
        V(customers);     // 若理发师在睡觉,唤醒他
        V(mutex);
        P(barber);        // 等理发师准备好
        坐上理发椅,理发;
    } else {
        V(mutex);         // 等候椅已满,离开
    }
}

顾客在 else 分支里也必须 V(mutex),否则后面的人都进不来。若题目还要求“理完发顾客才能离开”,就再加一个 semaphore finished = 0:理发师理完 V(finished),顾客理发后 P(finished)。

(2) 为什么不能用 P(chairs)
答题卡这样写

等候椅坐满时,chairs 的值为 0,顾客执行 P(chairs) 会阻塞等待,一直等到有空椅。但题目要求坐满时顾客直接离开。P 操作不能“试一下、不行就走”,所以要用计数器 waiting 配合 mutex,先判断再决定是否等待。

小问得分点分值
(1)信号量和计数器的定义、含义、初值正确1
理发师:P(customers) 等顾客,V(barber) 通知顾客2
顾客:在 mutex 保护下判断 waiting < n,满了就离开(两条路径都释放 mutex)1
顾客:V(customers) 唤醒理发师,P(barber) 等待1
(2)指出 P(chairs) 会阻塞而不是离开,需要“先判断再决定”2
卷一第 46 题操作系统 · 8 分 · 涉及题型 ⑪ 及 9.7 缓冲

某文件系统的磁盘块大小为 1KB,块号占 4B。文件 F 共占 10 个磁盘块,它的 FCB 已在内存中,FCB 中记有 F 的首块号和末块号。

(1) 若 F 采用隐式链接分配(每块最后 4B 存放下一块的块号),读 F 中偏移量为 5000 处的字节需要读几次磁盘?若改为显式链接分配(FAT 已在内存中)呢?(3 分)

(2) 若 F 的第 3 块(从 0 开始编号)损坏,两种分配方式下各会有什么后果?(1 分)

(3) 顺序读入 F 的全部 10 块并逐块处理:把一块读入系统缓冲区需 100μs,从缓冲区传到用户区需 20μs,CPU 处理一块需 50μs。采用单缓冲和双缓冲时,各需要多少时间?(3 分)

(4) 在隐式链接分配下,向 F 的末尾追加一块,至少要访问几次磁盘?(1 分)

做完再点开:满分答案与评分细则

考点10.6 三种分配方式对比9.7 单缓冲与双缓冲训练营 ⑪

(1) 读偏移 5000 处的字节
答题卡这样写

隐式链接:每块存数据 1024 − 4 = 1020B。5000 ÷ 1020 = 4 余 920,在第 4 块。只能从首块开始顺着指针读第 0、1、2、3、4 块,共 5 次。

显式链接:指针都在 FAT 中,每块存数据 1024B。5000 ÷ 1024 = 4 余 904,也在第 4 块。在内存的 FAT 中从首块号出发找到第 4 块的块号,只读这一块,共 1 次。

隐式链接每块要留 4B 放指针,有效数据只有 1020B,算块号时不能再除以 1024。

(2) 一块损坏的后果
答题卡这样写

隐式链接:第 4 块的块号存放在第 3 块中,第 3 块损坏后链就断了,第 3 块及其后的所有块都无法访问。显式链接:链保存在 FAT 中,只丢失第 3 块的数据,其余各块仍能访问。

(3) 单缓冲与双缓冲
答题卡这样写

设 T = 100μs(读入缓冲区)、M = 20μs(传到用户区)、C = 50μs(处理)。第一块要依次经过 T、M、C,共 170μs;之后每块增加一个“周期”。

单缓冲:每块的周期 = max(C, T) + M = 120μs,总时间 = 170 + 9 × 120 = 1250μs。

双缓冲:每块的周期 = max(T, C + M) = 100μs,总时间 = 170 + 9 × 100 = 1070μs。

单缓冲时,下一块要等缓冲区里的数据传走(M)才能读入,所以 M 不能和 T 重叠;双缓冲时两个缓冲区轮流使用,M + C 可以和 T 完全重叠。

(4) 追加一块
答题卡这样写

至少 3 次:把新数据写入一个空闲块(1 次);读出原来的末块(1 次),把新块的块号填进它的指针后写回(1 次)。FCB 中的末块号在内存中修改。

小问得分点分值
(1)隐式链接:块内有效数据 1020B,块号 4,读 5 次2
显式链接:读 1 次1
(2)隐式链接后续全部丢失,FAT 只丢这一块1
(3)写出单缓冲、双缓冲每块的周期1
单缓冲 1250μs1
双缓冲 1070μs1
(4)3 次,并说明写新块、读写原末块1

模拟卷二

卷二第 43 题计组 · 10 分 · 涉及题型 ⑦⑬ 及 5.5 多重中断

某计算机有 A、B、C 三个中断源,中断响应优先级为 A > B > C。通过设置中断屏蔽字,把中断处理优先级改为 C > A > B。屏蔽字按 A、B、C 的顺序各占 1 位,1 表示屏蔽。中断向量表从主存 0 号单元开始,每个中断向量占 4 字节,A、B、C 的中断类型号分别为 8、9、10。

A、B、C 的中断服务程序分别需要执行 10μs、20μs、15μs。t = 0 时 B、C 同时发出中断请求,t = 5μs 时 A 发出中断请求。CPU 原来在开中断状态下执行主程序。忽略中断响应、保存和恢复现场等开销,各中断服务程序在设置好新的屏蔽字后立即开中断。

(1) 写出 A、B、C 的中断服务程序中设置的屏蔽字。(3 分)

(2) 画出 CPU 执行程序的轨迹,写出 A、B、C 的中断服务程序各自完成的时刻。(4 分)

(3) B 的中断向量地址是多少?中断向量地址与中断向量有什么区别?(2 分)

(4) t = 5μs 时 A 的请求被屏蔽了,这个请求会丢失吗?它在什么时候被响应?(1 分)

做完再点开:满分答案与评分细则

考点5.5 多重中断与屏蔽字5.5 响应顺序 ≠ 处理顺序5.4 中断的完整接力

(1) 屏蔽字
答题卡这样写

每个中断服务程序屏蔽它自己,以及处理优先级比它低的中断。处理优先级 C > A > B:

A 的屏蔽字 = 110(屏蔽 A、B);B 的屏蔽字 = 010(只屏蔽自己);C 的屏蔽字 = 111(全部屏蔽)。

(2) 执行轨迹
答题卡这样写

t = 0:B、C 同时请求,按响应优先级先响应 B。B 的服务程序设置屏蔽字 010 后开中断,C 没有被屏蔽,立即被响应,C 执行 0 ~ 15μs。

t = 5:A 请求,但 C 的屏蔽字为 111,A 被屏蔽,等待。

t = 15:C 完成,返回 B,恢复屏蔽字 010。A 没有被屏蔽,立即被响应,A 执行 15 ~ 25μs。

t = 25:A 完成,返回 B,B 执行 25 ~ 45μs,然后返回主程序。

轨迹:主程序 → B(刚进入就被 C 打断)→ C → B(刚恢复就被 A 打断)→ A → B → 主程序。完成时刻:C 为 15μs,A 为 25μs,B 为 45μs。

先被响应的 B 最后才完成:响应顺序由硬件决定,处理顺序由屏蔽字决定,两者可以不同。

(3) 中断向量地址
答题卡这样写

B 的中断向量地址 = 9 × 4 = 36 = 0024H。

中断向量是中断服务程序的入口地址;中断向量地址是存放这个入口地址的存储单元的地址。CPU 响应中断时,由中断类型号算出向量地址,从中取出中断向量送入 PC。

(4) 被屏蔽的请求
答题卡这样写

不会丢失。A 的请求保存在中断请求寄存器(中断请求触发器)中。t = 15μs 时 C 返回,屏蔽被解除,A 在这时被响应。

小问得分点分值
(1)三个屏蔽字各 1 分3
(2)轨迹正确(B 先响应、C 立即嵌套、A 在 15 被响应)2
三个完成时刻正确2
(3)向量地址 0024H1
正确区分中断向量与中断向量地址1
(4)不丢失,t = 15μs 时被响应1
卷二第 44 题计组 · 15 分 · 涉及题型 ②③④

某 16 位计算机按字节编址,采用 16 位定长指令字,有 8 个通用寄存器 r0 ~ r7。指令格式如下:

类型格式(从高位到低位)指令
R 型op(4 位)| rs(3 位)| rt(3 位)| rd(3 位)| funct(3 位)add、sub、and、or、slt 等:rd ← rs op rt;所有 R 型指令的 op 都是 0000,用 funct 区分
I 型op(4 位)| rs(3 位)| rt(3 位)| imm(6 位,补码)addi:rt ← rs + imm;lw:rt ← M[rs + imm];sw:M[rs + imm] ← rt;beq:若 rs = rt,则 PC ← PC + 2 + imm × 2

该机先采用单周期数据通路实现。数据通路中有 3 个二选一多路选择器:RegDst 选写入寄存器的编号(0 选 rt,1 选 rd);ALUSrc 选 ALU 的第二个操作数(0 选 rt 的内容,1 选符号扩展后的 imm);MemtoReg 选写回寄存器的数据(0 选 ALU 结果,1 选数据存储器读出的数据)。另有控制信号 RegWrite、MemRead、MemWrite 和 Branch(Branch 与 ALU 的“结果为 0”信号相与,决定下一条指令的地址)。

(1) R 型指令最多有几条?除 R 型外,最多还能定义几种 I 型指令?imm 的取值范围是多少?lw 能访问的地址范围是多少(用 rs 表示)?(3 分)

(2) 某条 beq 指令位于 1020H,其 imm 字段为 3AH(6 位),转移成功时目标地址是多少?若要转移到 1040H,imm 字段应为多少?(3 分)

(3) 写出 add、lw、sw、beq 四条指令执行时 7 个控制信号的取值(无关的写 ×)。(4 分)

(4) 各部件的延迟为:取指令 200ps,读寄存器 100ps,ALU 150ps,数据存储器 200ps,写寄存器 50ps,其余忽略。单周期 CPU 的时钟周期至少多长?若改为 5 段流水线(段寄存器延迟 20ps),时钟周期多长?执行 106 条指令(不发生阻塞)时,流水线 CPU 比单周期 CPU 快多少倍?(3 分)

(5) 在有转发的 5 段流水线中,若 lw r1, 0(r2) 之后紧跟 add r3, r1, r4,会发生什么?编译器可以怎样避免?(2 分)

做完再点开:满分答案与评分细则

考点3.1 扩展操作码与字段3.2 相对寻址4.2 单周期数据通路4.4 冒险的对策

(1) 指令条数与字段范围
答题卡这样写

R 型共用 op = 0000,用 3 位 funct 区分,最多 8 条。op 共 16 种,除去 0000,最多还能定义 15 种 I 型指令。

imm 是 6 位补码,范围 −32 ~ +31。lw 访问的地址为 (rs) − 32 ~ (rs) + 31。

(2) beq 的转移目标
答题卡这样写

3AH 的 6 位二进制为 111010,是 −6 的补码。目标 = 1020H + 2 + (−6) × 2 = 1022H − 0CH = 1016H。

转移到 1040H:imm = (1040H − 1022H) ÷ 2 = 1EH ÷ 2 = 15 = 001111B,即 0FH。

两处容易错:偏移相对的是 PC + 2(下一条指令的地址);imm 以“指令字”(2 字节)为单位,要乘 2。

(3) 控制信号
答题卡这样写
指令RegDstALUSrcMemtoRegRegWriteMemReadMemWriteBranch
add1001000
lw0111100
sw×1×0010
beq×0×0001

理由:add 写 rd,两个操作数都来自寄存器;lw 写 rt,用 rs + imm 算地址,写回的是存储器读出的数据;sw 和 beq 不写寄存器,所以 RegDst、MemtoReg 无关;beq 用 ALU 比较 rs 与 rt(做减法,看结果是否为 0),第二个操作数来自 rt。

不写寄存器时,RegWrite 必须是 0,不能写 ×;不写存储器时 MemWrite 必须是 0。只有“选了也不会被用到”的多路选择器信号才能写 ×。

(4) 时钟周期与加速比
答题卡这样写

单周期的时钟周期由最慢的指令 lw 决定:200 + 100 + 150 + 200 + 50 = 700ps。

流水线的时钟周期 = 最长一段 + 段寄存器延迟 = 200 + 20 = 220ps。

单周期:106 × 700ps = 700μs;流水线:(5 + 106 − 1) × 220ps ≈ 220μs。流水线约快 700 ÷ 220 ≈ 3.18 倍。

(5) load-use
答题卡这样写

add 要用 lw 读出的 r1,而 r1 要到 lw 的 M 段结束才得到,转发也来不及,流水线要阻塞 1 个时钟周期(load-use 冒险)。

编译器可以调整指令顺序,在 lw 和 add 之间插入一条与它们无关的指令;找不到合适的指令时插入 nop。

小问得分点分值
(1)R 型 8 条、I 型最多 15 种2
imm 范围与 lw 地址范围1
(2)目标地址 1016H(含 −6 与 ×2 的过程)2
imm = 0FH1
(3)每条指令一行,每行 1 分4
(4)700ps、220ps2
加速比约 3.181
(5)阻塞 1 拍、调度指令或插 nop2
卷二第 45 题操作系统 · 7 分 · 涉及题型 ⑨⑬

单 CPU 系统中有一台磁盘,磁盘一次只为一个进程服务。进程 P1、P2 在 0 时刻都已进入就绪队列,P1 的优先级高于 P2。它们的运行过程如下(忽略调度和切换的开销):

P1:计算 20ms → 磁盘 I/O 30ms → 计算 10ms
P2:计算 40ms → 磁盘 I/O 20ms → 计算 20ms

(1) 采用抢占式优先级调度,画出 CPU 和磁盘的甘特图,求 P1、P2 的完成时刻和 CPU 利用率。(3 分)

(2) 按 (1) 的调度,写出 P2 从开始到结束经历的状态变化。(1 分)

(3) 改为非抢占式优先级调度,P1、P2 的完成时刻和 CPU 利用率各是多少?与 (1) 相比,两个进程的平均周转时间有什么变化?(3 分)

做完再点开:满分答案与评分细则

考点6.7 调度算法6.2 状态转换训练营 ⑨

(1) 抢占式
答题卡这样写

CPU:0 ~ 20 P1;20 ~ 50 P2;50 时 P1 的 I/O 完成,优先级高,抢占 P2:50 ~ 60 P1(P1 完成);60 ~ 70 P2(算完剩下的 10ms);70 ~ 90 空闲;90 ~ 110 P2。

磁盘:20 ~ 50 P1;70 ~ 90 P2。

完成时刻:P1 为 60ms,P2 为 110ms。CPU 忙 20 + 30 + 10 + 10 + 20 = 90ms,利用率 = 90 ÷ 110 ≈ 81.8%。

(2) P2 的状态变化
答题卡这样写

就绪(0)→ 运行(20)→ 就绪(50,被 P1 抢占)→ 运行(60)→ 阻塞(70,等磁盘 I/O)→ 就绪(90,I/O 完成)→ 运行(90)→ 结束(110)。

(3) 非抢占式
答题卡这样写

CPU:0 ~ 20 P1;20 ~ 60 P2(50 时 P1 就绪,但不能抢占);60 ~ 70 P1(完成);70 ~ 80 空闲;80 ~ 100 P2。磁盘:20 ~ 50 P1;60 ~ 80 P2。

完成时刻:P1 为 70ms,P2 为 100ms。CPU 利用率 = 90 ÷ 100 = 90%。

平均周转时间:抢占式为 (60 + 110) ÷ 2 = 85ms,非抢占式为 (70 + 100) ÷ 2 = 85ms,相同。抢占式让高优先级的 P1 早完成 10ms,但推迟了 P2 的 I/O,CPU 多空闲了 10ms,P2 晚完成 10ms。

这类“计算 + I/O”交替的题,要同时画 CPU 和设备两条时间线。进程做 I/O 时处于阻塞态,不占 CPU。

小问得分点分值
(1)甘特图正确(含 50 时的抢占)1
P1 60ms、P2 110ms1
CPU 利用率 81.8%1
(2)状态变化序列完整1
(3)P1 70ms、P2 100ms1
CPU 利用率 90%1
平均周转时间相同,并说明原因1
卷二第 46 题操作系统 · 8 分 · 涉及题型 ⑩ 及 8.6 段页式

某系统采用段页式存储管理,按字节编址。逻辑地址 32 位:段号(8 位)| 段内页号(12 位)| 页内偏移(12 位);页大小 4KB,物理地址 32 位,页表项占 4B。当前进程的段表长度为 3,段表如下:

段号页表长度(该段的页数)页表起始物理地址
050010 0000H
120010 1000H
280010 2000H

段 2 的页表中,第 3 项的内容为:存在位 1,页框号 02F5AH。

(1) 一个进程最多能有多少个段?每段最长多少?(1 分)

(2) 逻辑地址 0200 3A10H 的段号、段内页号和页内偏移各是多少?访问的页表项的物理地址是多少?最后得到的物理地址是多少?(3 分)

(3) 访问逻辑地址 0105 0100H 和 0500 0000H 时,各会发生什么?(2 分)

(4) 设访问一次主存需 100ns,访问 TLB 需 10ns,TLB 命中率为 90%(TLB 命中时直接得到页框号),不考虑缺页。有效访问时间是多少?没有 TLB 时呢?(2 分)

做完再点开:满分答案与评分细则

考点8.6 分段与段页式8.5 TLB 与有效访问时间训练营 ⑩

先把条件翻译成大白话
  • 段页式的地址变换走三步:查段表(得到该段页表的位置和长度)→ 查页表(得到页框号)→ 访问数据。没有 TLB 时一共访存 3 次。
  • 两处越界检查:段号 ≥ 段表长度;段内页号 ≥ 该段的页表长度。
(1) 段数与段长
答题卡这样写

最多 28 = 256 个段;每段最多 212 页,每段最长 212 × 4KB = 16MB。

(2) 地址变换
答题卡这样写

0200 3A10H:段号 = 02H,段内页号 = 003H,页内偏移 = A10H。

段号 2 < 段表长度 3,查段表得页表起始地址 0010 2000H、页表长度 8;页号 3 < 8,不越界。页表项地址 = 0010 2000H + 3 × 4 = 0010 200CH。

页表项存在位为 1,页框号 02F5AH,物理地址 = 02F5AH × 4KB + A10H = 02F5 AA10H。

(3) 两种越界
答题卡这样写

0105 0100H:段号 1,段内页号 050H(= 80),页内偏移 100H。段 1 的页表长度为 2,80 ≥ 2,段内页号越界,产生越界中断(异常)。

8 | 12 | 12 位正好对应十六进制的 2 | 3 | 3 位:0105 0100H 切成 01 | 050 | 100,段内页号是 050H,不要看成 005H。

0500 0000H:段号 5 ≥ 段表长度 3,段号越界,产生越界中断(异常)。

(4) 有效访问时间
答题卡这样写

TLB 命中:10 + 100 = 110ns。TLB 未命中:10 + 100(段表)+ 100(页表)+ 100(数据)= 310ns。

EAT = 0.9 × 110 + 0.1 × 310 = 130ns。没有 TLB 时,每次都要访存 3 次,为 300ns。

段页式比纯分页多查一次段表,没有 TLB 时访存次数从 2 次变成 3 次,所以更依赖 TLB。它的好处是既能按段共享和保护,又没有外部碎片。

小问得分点分值
(1)256 个段、每段 16MB1
(2)段号、页号、偏移1
页表项地址 0010 200CH1
物理地址 02F5 AA10H1
(3)第一个地址:段内页号越界1
第二个地址:段号越界1
(4)130ns(含命中、未命中两种情况的时间)1
300ns1
做完两套卷之后

把两套卷的 8 道题按得分排序。得分率最低的两道,回到它们标出的“涉及题型”,重做那一类的真题和模拟题,再看一遍“举一反三”变式表。每类大题的套路是固定的,丢分通常是因为漏了某一步,而不是不会。

第 12 篇 · 考前冲刺

考场作战手册:题型模板、公式、易错与真题索引

前十篇是“学”,第 11 篇是“练大题”,这一篇是考前“速查”,第 13 篇是“满分冲刺”(冷考点、专练、三套全真模拟卷、知识点细目表)。把计组和操作系统的综合题按题型归类,每类给出固定的解题步骤和对应的真题年份;再附公式速查、高频易错点、2009—2026 年每年大题的检索表,以及按年份刷全部 451 道计组/操作系统真题的题目网格(12.8)。建议:学完一篇就来这里找对应题型,把列出的真题按年份做一遍;考前一周只看这一篇。

用法:点链接跳回讲解;点题号直接在页内做题;真题原卷在 01_真题 文件夹综合题分值:计组 2 道约 23~25 分,操作系统 2 道约 15 分

12.1答题规范:怎么把会做的分拿满

完整版(含“容易丢分的写法 vs 步骤分拿满的写法”对比)见 11.1,这里是考前速查。

  1. 先写已知条件的“翻译”:比如“页 4KB → 页内偏移 12 位”“按字节编址”。阅卷按步骤给分,结果错了过程还有分(真题评分说明常写“只给结果、未给过程,每问给一半分”)。
  2. 公式先写符号式再代数:如“占比 = 每秒中断次数 × 每次开销 / 主频 = 125000 × 100 / 500M = 2.5%”。
  3. 地址题一律展开成二进制,在二进制上画竖线切字段,再换回十六进制。
  4. 单位写全:B 和 b、K = 210 还是 103、ns/μs/ms。
  5. PV 题写清三件事:每个信号量的含义和初值、每个进程的完整伪代码(循环写出来)、关键 P/V 旁边加注释。
  6. 题目条件优先于教材默认:流水线是否转发、Cache 是直写还是回写、“数值大优先级高”还是反过来、页号是十进制还是十六进制……题目说了算。

12.2计组综合题:七种题型

题型解题步骤真题讲解
① C 程序的数据表示与运算写出每个变量的位宽和有无符号 → 求机器数(十六进制)→ 赋值/运算时判断:扩展(看原类型)、截断、按新类型读 → 判断溢出看 OF(有符号)或 CF(无符号)→ 浮点看 24 位有效位2011-43,2017-43,2020-43,2021-43(3),2025-442.2 2.4 2.6 训练营
② 指令格式与机器代码从格式图数位数:操作码位数 → 指令条数;寄存器编号位数 → 寄存器个数;偏移位数 → 范围(补码)→ 编码时逐字段写二进制再拼 → 相对寻址:目标 = 本指令地址 + 指令长度 + 偏移(注意单位)→ 从机器码判断大小端 → 地址差推断编址单位、元素大小2010-43,2013-44,2014-44,2015-44,2017-44,2019-45,2021-43,2023-44,2024-43/44,2026-433.1 3.2 3.3 训练营
③ 数据通路与控制信号先写这条指令要做的几件事 → 每件事拆成“源 out + 目的 in” → 单总线每节拍只能传一个数 → 访存 = 地址进 MAR → 读 → MDR → ALU 一端要先进暂存器 → 取指阶段共用:PCout,MARin / MemR,MDRinE,PC+1 / MDRout,IRin2009-44,2015-43/44,2022-43,2024-43,2026-444.2 训练营
④ 流水线列每条指令“写哪个寄存器、读哪些寄存器” → 找 RAW → 按题目规则画时空图(无转发:后继的 ID 在前驱 WB 之后;load-use 即使转发也停 1 拍;分支停几拍按题目)→ 理想周期数 = k + n − 12012-44,2014-44(4),2016-19,2019-18,2023-194.4 训练营
⑤ Cache 地址、容量、命中率块内位 = log₂块大小;组数 = 行数/路数;Tag = 其余 → 每行位数 = 数据 + Tag + 有效 + 脏(回写) + 替换位 → 命中率:算元素大小、一块几个元素、起始地址是否对齐 → 访问顺序下每块首次缺失 → 检查是否被同组其他块挤掉2010-44,2015-15,2016-45,2018-44,2020-44,2021-16,2023-43,2025-438.3 训练营
⑥ 虚拟存储 + TLB + Cache 综合虚拟地址 → 页号/偏移 → TLB(全相联:比整个页号;组相联:页号低位是组号)→ 页表(有效位)→ 物理地址 = 页框号 ∥ 偏移 → 再按 Cache 格式切 → 组内比 Tag 且看有效位 → 缺页要访盘并重新执行指令2011-44,2014-45,2016-45,2018-44,2019-46,2021-44,2023-43,2025-438.5 8.8 训练营
⑦ I/O 性能每秒次数 = 速率 / 每次传的数据量 → 每次 CPU 开销(指令数 × CPI + 其他)→ 占比 = 次数 × 开销 / 主频 → 能否用中断:比较“数据到来间隔”和“中断处理时间” → DMA 看块大小;周期挪用每满一个缓冲区请求一次总线2009-43,2012-43,2013-43,2016-44,2018-43,2022-449.4

12.3操作系统综合题:六种题型

题型解题步骤真题讲解
① PV 同步互斥五步法:找进程和动作 → 找约束(互斥、先后、容量)→ 每个约束一个信号量(写含义和初值)→ 互斥夹紧、同步前 V 后 P、资源用前 P 用后 V → 检查:先 P 资源再 P 互斥;是否死锁;是否满足“最大并发”2009-45,2011-45,2013-45,2014-47,2015-45,2017-46,2019-43,2020-45,2021-45,2022-46,2023-45,2024-46,2025-457.4 训练营
② 调度计算标到达时刻 → 按算法(是否抢占、优先数大小方向)画甘特图 → 列表:完成、周转 = 完成 − 到达、带权 = 周转/运行、等待 = 周转 − 运行 → 求平均;有调度开销时每次上 CPU 前加上2012-29,2016-24,2016-46,2018-24,2019-27,2022-25,2023-29,2024-30,2026-456.7 训练营
③ 分页地址变换与页表页大小 → 偏移位数 → 页号(多级:每级位数 = log₂(页大小/表项大小))→ 页表项地址 = 基址 + 索引 × 表项大小 → 有效位判缺页 → 置换算法选页框 → 物理地址 = 页框号 ∥ 偏移 → 时间 = TLB + 页表访存 + 数据访存 (+ 缺页,且要重新执行)2009-46,2010-46,2012-45,2013-46,2015-46,2017-45,2018-45,2020-46,2024-458.5 8.7 训练营
④ 文件系统N = 块大小 / 地址项大小 → 最大文件 = (直接 + N + N² + N³) × 块大小 → 某偏移在第几个逻辑块 → 落在直接/一级/二级 → 读盘次数 → 目录项 = 文件名 + inode 号 → 硬链接共享 inode → FAT:表项存下一块号2011-46,2012-46,2014-46,2016-47,2018-46,2022-45,2026-4610.4 10.6 训练营
⑤ 磁盘容量 = 柱面 × 磁头 × 扇区 × 扇区大小 → 访问时间 = 寻道 + 半圈 + 传输 → 簇号 → 柱面/磁头/扇区(除法取整、取余)→ 调度算法算顺序和移动距离 → 位示图大小与定位2010-45,2019-44,2022-4410.1 10.2 10.7 训练营
⑥ 过程分析(I/O、引导、地址空间)按时间顺序写出每一步:谁执行(用户程序/内核/驱动/中断处理程序/硬件)、CPU 在什么态、进程在什么状态、数据在哪里2017-45(3),2021-46,2023-46,2025-469.6 10.3 3.6

12.4公式速查

主题公式
单位210=1K,220=1M,230=1G;容量用 2 的幂,频率/速率用 10 的幂;1B = 8b;时钟周期 = 1/主频
性能CPU 时间 = 指令数 × CPI / 主频;平均 CPI = Σ 比例 × CPI;MIPS = 主频 / (CPI × 106);Amdahl:加速比 = 1 / [(1 − f) + f/s],f 按时间算
补码n 位范围 −2n−1~2n−1−1;[−x]补 = 2n − x = 取反加 1;最高位权重 −2n−1;定点小数补码 −1 ~ 1 − 2−(n−1),原码/反码 ±(1 − 2−(n−1))
溢出判断同号相加结果变号;Cs ⊕ C1 = 1;双符号位 01 正溢出、10 负溢出;浮点数只看阶码是否上溢
IEEE 754float:1 + 8(偏置 127)+ 23;值 = (−1)S×1.M×2E−127;E=0 非规格化 0.M×2−126;E=255 ∞/NaN;最大 2128−2104;最小规格化 2−126;最小正非规格化 2−149(机器数 0000 0001H);默认就近舍入,正好一半时取偶;相邻两个 float 的间距 = 2指数 − 23(224 附近为 2,大数吃小数,2.6)
标志位无符号 A>B:CF=0 且 ZF=0;有符号 A<B:SF⊕OF=1;加法 OF = 两同号加出异号;减法 CF = 借位
流水线T = (k + n − 1)Δt;Δt = 最长段 + 段寄存器延迟;吞吐率 = n/T;加速比 = nkΔt/T;效率 = n/(k + n − 1);有阻塞时 CPI = 1 + Σ(事件频率 × 阻塞拍数)
微程序字段直接编码每字段 ⌈log2(微命令数 + 1)⌉ 位;断定法下地址 ⌈log2微指令总数⌉ 位;微指令总数 = Σ 各指令微程序 + 公共取指(间址、中断)微程序;n 条机器指令至少 n + 1 段微程序
扩展操作码下一级可用编码 = 上一级剩余编码 × 2让出的位数
主存芯片数 = 总容量/芯片容量;带宽 = 每次位数 / 存取周期;交叉:体号 = 地址 mod m,最大带宽 = m × 字 / 存取周期,连续读 n 字 = T + (n − 1)r(顺序方式 nT);DRAM 地址引脚减半;异步刷新间隔 = 刷新周期 / 行数;集中刷新死区 = 行数 × 存取周期,死时间率 = 死区 / 刷新周期
Cache组数 = 行数/路数;Tag = 地址位 − 组号位 − 块内位;每行 = 数据 + Tag + V [+ D] [+ 替换位];平均访问 = tc + (1−H)tm(同时访问时 H·tc + (1−H)tm);效率 e = tc / 平均访问时间;两级 Cache:tL1 + mL1 × (tL2 + mL2 × tm)(m 为各级缺失率)
分页偏移 = log₂页大小;页表大小 = 页数 × 表项;表项地址 = 基址 + 页号 × 表项大小;每级索引 = log₂(页大小/表项);EAT = tTLB + tm + (1−h)tm [+ p × t缺页];TLB 与页表同时查:h(tTLB + tm) + (1−h)·2tm;反置页表大小 = 物理页框数 × 表项大小;只看缺页:EAT = (1 − p)·tm + p·tf(p 为缺页率,tf 为缺页处理时间,8.9)
总线 / I/O带宽 = 宽度 × 每秒传送次数;CPU 占比 = 每秒次数 × 每次周期数 / 主频;周期挪用占用主存比例 = 每秒请求次数 × 存储周期;仲裁控制线:链式 3、计数器 2 + ⌈log2n⌉、独立请求 2n + 1;全互锁一次传送 ≥ 4 个信号传播时间;最大并发 I/O 速率 = Σ 各控制器同时能传的速率,再与总线带宽取小
磁盘访问 = 寻道 + 60/(2×转速) + 传输;容量 = 柱面 × 磁头 × 扇区 × 扇区大小;数据传输率 = 每秒转数 × 每道容量;平均寻道长度 = 总移动距离 / 请求数;RAID 可用容量:RAID 0 nC、RAID 1 nC/2、RAID 5 (n − 1)C,RAID 5 小写 = 2 读 2 写;SSD 写放大 = Flash 实际写入量 / 主机写入量
文件N = 块/地址项;最大文件 = (d + N + N² + N³) × 块;位示图位数 = 块总数;隐式链接读第 i 块(从 0 起)要 i + 1 次;目录顺序查找平均读 (目录块数 + 1)/2 块;链接方案每个索引块 N − 1 个块号,两层索引最多 N² 块;索引顺序文件分 √N 组平均约查 √N 次
缓冲单缓冲每块 max(C, T) + M;双缓冲每块 max(T, C + M)
调度周转 = 完成 − 到达;带权 = 周转/运行;响应比 = (等待 + 运行)/运行;实时:U = Σ Ci/Ti,EDF 可调度 ⇔ U ≤ 1,RMS 充分条件 U ≤ n(21/n − 1)
进程创建循环 n 次、每次都 fork:最后共 2n 个进程(含原进程),fork 执行 2n − 1 次;fork 在父进程返回子进程 PID,在子进程返回 0(6.3)
页面置换置换次数 = 缺页次数 − 填空页框的次数;工作集 W(t, Δ) = 最近 Δ 次访问中出现的不同页面;改进型 CLOCK 淘汰次序 (0,0) → (0,1) → (1,0) → (1,1),最多扫四轮
死锁不死锁最少资源 = Σ(需求i − 1) + 1;Need = Max − Allocation
信号量value < 0 时 |value| = 等待的进程数;m 个资源、n 个进程竞争(n > m):取值范围 m − n ~ m

12.5高频易错清单(91 条)

#易错说法 / 陷阱正确理解
1MAR 位数看实际装了多少内存看地址空间大小(2011-15)
2short → unsigned int 零扩展看原类型:原来有符号就符号扩展(2025-12)
3编码 float 时把隐藏的 1 也存进去1.M 的 1 不存;阶码 = 指数 + 127
4判断无符号溢出看 OF无符号看 CF,有符号看 OF
5相对寻址 EA = 本指令地址 + 偏移= 下一条指令地址 + 偏移(×单位)
6“CPU 速度提高 50%”,时间减半时间变为 1/1.5(2012-12)
7trap(陷入)指令是特权指令在用户态执行,是非特权指令
8中断隐指令保存通用寄存器硬件只保存 PC、PSW;通用寄存器由服务程序保存
9屏蔽字决定响应优先级屏蔽字决定处理优先级(2024-21)
10缺页处理后执行下一条指令重新执行发生缺页的那条(2019-14)
11缺页属于外部中断属于异常(故障)(2016-22)
12时间片用完 → 阻塞态→ 就绪态(2017-27)
13进程被唤醒就立即运行唤醒是阻塞 → 就绪
14线程共享同一进程的栈栈、寄存器私有;代码、数据、堆、文件共享
15生产者先 P(mutex) 再 P(empty)会死锁;先 P 资源信号量再 P 互斥信号量
16不安全状态 = 死锁不安全只是可能死锁;安全一定不死锁
17Cache 组号也要存在每一行组号不存,只存 Tag
18直写法需要脏位回写法才需要脏位(2020-44)
19Cache 命中只看 Tag 相等还要有效位 = 1(2018-44)
20虚拟内存大小由内存和磁盘决定由地址位数决定(2023-28)
21多级页表加快地址变换减少页表连续内存占用,变换更慢(2014-32)
22页表里的页号默认十六进制看题:2022-15 页号是十进制
23缺页次数 = 置换次数装入空页框的缺页不算置换(2019-29)
24FIFO、LRU 都有 Belady 异常只有 FIFO(2014-30)
25DMA 传送过程中 CPU 不能访存周期挪用只偷个别周期(2020-22)
26中断处理程序运行时,等输入的进程是运行态仍是阻塞态,被打断的是别的进程(2023-46)
27read 系统调用的参数是文件名是文件描述符 fd,文件名在 open 时用(2012-28)
28删除文件要删除所在目录只删目录项、释放 inode 和数据块(2013-23)
29访问权限存在目录项里存在 inode/FCB 里(2025-29)
30C-SCAN 的返回不算移动距离408 真题口径计入(2010-45、2024-32),以题目为准
31MIPS 高的机器一定跑得快MIPS 只在 ISA 相同时可比;比性能要比同一程序的执行时间(2017-12)
32Amdahl 定律里的比例 f 按指令条数算按时间占比算
33解释程序也生成目标程序编译、汇编生成目标程序,解释不生成
34微架构是 ISA 的物理实现是逻辑实现(2026-12)
35−128(8 位)、−1(定点小数)有原码只有补码能表示(整数的移码也能表示 −128);原码、反码的范围正负对称,没有它们
36由 [x]补 求 [−x]补:符号位不动,数值位取反加 1要连同符号位一起取反加 1(符号位不动的是“求补码/原码”)
37补码尾数 1.100…0(−1/2)是规格化数符号位与最高数值位相同,不是规格化;1.000…0(−1)是
38浮点加法尾数溢出就是结果溢出右规即可;结果是否溢出只看阶码(2009-13、2015-14)
39-1 < 0U 为真为假:有符号与无符号混用,−1 被当成 4294967295
40−7 >> 1 与 −7 / 2 结果相同算术右移向下取整得 −4,C 除法向 0 取整得 −3
41自增寻址 (R)+ 总是把 R 加 1加一个操作数的长度,看编址单位(2010-43 按字编址,加 1)
42lea eax, [ebx+8] 会访存只计算地址、不访存;mov eax, [ebx+8] 才访存
43单周期 CPU 可以用单总线数据通路不能:一个周期要同时传多个数据(2016-20)
44单总线一个节拍里可以有两个 Xout只能有一个 Xout;Xin 可以有多个
45有转发就不会有数据冒险阻塞load-use 仍要阻塞 1 拍(2024-19)
46中断、DMA、异常都在指令结束时响应中断:指令结束后;DMA:存储周期结束后;异常:指令执行中发现就处理
47中断向量 = 中断向量地址向量是服务程序入口地址;向量地址是存放入口地址的单元的地址
48“就绪 → 阻塞”“阻塞 → 运行”可能发生都不可能;被唤醒只能先进就绪
49SJF 的平均周转时间总是最短作业同时到达时 SJF 最短;不同时到达时抢占式 SRTN 更短
50资源分配图有环就一定死锁只在每类资源都只有 1 个时成立;有多个实例要化简判断
51管程中 x.signal() 没人等也会被记住没人等就什么也不发生;信号量的 V 才会记住(value + 1)
52分页的逻辑地址是二维的分页一维,分段、段页式二维
53设备分到了就能启动 I/O设备、控制器、通道三者都分配成功才行
54隐式链接分配也能随机访问只能顺序访问,读第 i 块要 i + 1 次;FAT 在内存时才能快速随机访问
55算术左移时移出的是 1 就溢出看符号位变没变:移位前最高两位不同才溢出(FFFFH 左移得 FFFEH,不溢出)
56相对转移的目标 = 本指令地址 + 偏移是下一条指令的地址 + 偏移(偏移为补码,机器码按小端存放)
57单总线下 R1 送 MAR 和送 ALU 要分两拍一个 out 可以同时有多个 in:R1out、MARin、Inc、Zin 可以在同一拍
582 路组相联就不会有冲突缺失同时要用的块数超过路数(如三个数组同组)时,LRU 下会反复互相替换,命中率可降到 0
59组相联 TLB 的标记 = 整个虚页号虚页号低位是组号,标记只是去掉组号后的高位
60“组号 + 块内地址”超过页内偏移,也能用虚拟地址选 Cache 组不能:组号的高位来自页框号,要等地址转换完成
61中断方式下 CPU 占比 < 100% 就不会丢数据要比较每次中断的开销与两次数据到达的间隔
62进程拿着 mutex 去 P 一个资源信号量没问题可能死锁:释放资源的进程也要先拿 mutex(生产者—消费者、单车道桥)
63RR 的平均周转时间一定比 FCFS 短不一定;RR 的优势是响应快、公平,时间片太小还会增加切换开销
64改进型 CLOCK 第一轮扫描就把访问位清 0第一轮只找 (0, 0)、不改访问位;第二轮找 (0, 1) 时才把经过的页的访问位清 0
651KB 页也能直接取十六进制最后 3 位当偏移偏移 10 位不是 4 的倍数,必须展开成二进制再切
66打开文件时就把数据块读进来open 只沿路径读目录和 inode;读数据块是 read 的事
67就近舍入遇到正好一半时直接进位一半时取偶:16777219 存成 16777220(尾数末位为 0 的那个)
68非规格化数(阶码全 0)的指数是 0 − 127 = −127按 1 − 127 = −126 算,且没有隐藏的 1:值 = 0.M × 2−126
69改进型 CLOCK 优先淘汰没修改过的页,(1, 0) 比 (0, 1) 先淘汰先看访问位再看修改位:(0, 1) 先于 (1, 0)(2016-26)
70页面缓冲算法 PBA 能降低缺页率只降低缺页的代价(被淘汰的页能直接取回、少读盘),缺页率不变(2022-30)
71LFU 是 LRU 的近似不是:LFU 只看次数,过去常用而现在不用的页会一直占着页框
72fork 时就把父进程的页全部复制给子进程写时复制:先共享并设为只读,谁写哪页才复制哪页
73内存映射文件把文件映射到物理地址空间映射到进程的虚拟地址空间,物理页框由缺页按需调入(2025-30)
74反置页表每个进程一张全系统一张,每个物理页框一项,大小只与物理内存有关
75独立请求方式的仲裁控制线是 2n 根王道口径 2n + 1(还有一根 BS);链式 3 根,计数器 2 + ⌈log2n⌉ 根
76全互锁一次握手只要一来一回两个信号请求↑、应答↑、请求↓、应答↓ 四步,每步都要等对方
77非阻塞 I/O 就是异步 I/O非阻塞立即返回后要进程自己反复查;异步由系统做完后通知进程
78循环缓冲中 in 追上 out 表示缓冲区全空表示全满,输入进程阻塞;out 追上 in 才是全空
79访问控制矩阵按行存就是访问控制表 ACL按列(每个文件一张)存才是 ACL;按行(每个用户一张)存是权限表
80系统打开文件表的表项里放着各进程的读写指针教材两级模型:读写指针在进程打开文件表,系统表一个文件只一项(2017-31、2020-23)
81RAID 5 坏两块盘也能恢复;条带化能提高可靠性RAID 5 只容忍 1 块;条带化只提速,可靠性靠镜像和校验(2013-20)
82SSD 的磨损均衡由文件系统负责由 SSD 控制器的 FTL 完成;文件系统只确定盘块大小(2025-32)
83扇区和簇(块)是一回事扇区是磁盘读写的最小单位;簇(块)是文件系统分配单位,由若干扇区组成
84两个线程访问不同的变量,就不会互相作废对方的 Cache 行若落在同一个 Cache 块里,会因“伪共享”反复作废(11.6 预测题)
85Cache 一致性协议保证了 x++ 不会丢失更新一致性只保证看到的值一致,读改写仍可能被打断,要靠互斥(第 7 篇)
86按字节编址,MDR 就是 8 位MDR 的位数 = 一次访存读写的位数,可以是 16、32、64 位;编址单位只决定 MAR 的位数(1.2 四种宽度)
87一段区域的末地址 = 起始地址 + 区域大小还要减 1:8KB 区域从 0800H 开始,末地址 = 0800H + 2000H − 1 = 27FFH(0.4)
88段页式地址(段号 8 位 | 段内页号 12 位 | 偏移 12 位)0105 0100H 的段内页号是 005H8 | 12 | 12 位正好是十六进制的 2 | 3 | 3 位:01 | 050 | 100,段内页号 050H = 80(11.15 卷二第 46 题)
89整数除法只有“除数为 0”一种异常还有商溢出:−231 ÷ (−1) 的商 231 超出 int 范围(2025-44)
90把 P(资源) 写进临界区,就一定死锁要看释放资源的一方是否也要先拿同一个 mutex:单车道桥下桥“先 V(cap) 再 P(mE)”时不死锁,只是并发度下降(11.9 程序穷举);稳妥的写法仍是先 P 资源、再 P 互斥
912013-44 数据通路里引出 OP、C、Z、N、OFFSET 的部件 ① 是 PC是指令寄存器 IR(指令的各字段都从它引出);② 是左移一位的移位器(得 2 × OFFSET),③ 是第二个加法器(图中第一个加法器只算 (PC) + 2)

12.62009—2026 年计组、操作系统综合题检索表

年份计组综合题操作系统综合题
200943 中断/DMA 的 CPU 占比 9.4;44 数据通路 ADD (R1),R0 4.245 奇偶数缓冲区 PV 7.4;46 TLB + 缺页访存时间 8.5
201043 指令格式与编码 3.3;44 Cache 与数组按行/列 8.345 位示图 + C-SCAN 10.2;46 FIFO/CLOCK 求物理地址 8.7
201143 8 位机有符号/无符号运算 2.2;44 页表 + Cache + TLB 8.845 银行取号 PV 7.4;46 一次写入文件的分配方式 10.6
201243 MIPS、Cache 缺失、DMA、交叉存储 9.4;44 流水线 4.445 驻留集扫描回收 8.7;46 索引表区与最大文件 10.6
201343 突发传送与执行时间 9.2;44 条件转移指令与数据通路 3.245 博物馆 PV 7.4;46 一级/二级页表 8.5
201444 RISC 循环代码与流水线 3.3 4.4;45 指令 Cache、缺页、TLB 次数 8.746 连续/链接分配访盘次数 10.6;47 连续取 10 件的生产者消费者 7.4
201543 单总线数据通路 4.2;44 控制信号与指令编码 4.245 信箱辩论 PV 7.4;46 二级页表 8.5
201644 中断方式读字符 9.4;45 TLB/Cache 字段位数 8.346 动态优先数防饥饿 6.7;47 FAT 10.6
201743 int/float 求和函数 2.6;44 机器代码、CF、shl 3.345 页目录索引 + scanf 状态 9.6;46 复数线程最大并发 7.4
201843 查询/中断/DMA 比较 9.4;44 TLB + Cache 硬件图 8.845 二级页表、PDBR、改进 CLOCK 8.5;46 inode 最大文件与文件数 10.6
201945 递归 n!、call、小端、溢出 3.3;46 分页与指令 Cache 3.343 哲学家与碗 7.4;44 磁盘容量、SSTF、簇号换 CHS 10.2
202043 乘法实现与溢出 2.5;44 8 路组相联 Cache 8.345 前驱关系 PV 7.4;46 二级页表求地址 8.5
202143 R/I/J 指令格式 3.1;44 TLB 组相联 LRU 8.345 开关中断实现信号量 7.2;46 引导与格式化顺序 10.3
202243 数据通路、SF/OF 表达式 4.2;44 磁盘地址、访问时间、DMA 10.145 目录、inode、硬链接 10.5;46 两线程前驱 PV 7.4
202343 数组的分页与 Cache 8.3;44 机器代码、寻址、缺页 3.245 swap 实现互斥 7.2;46 键盘输入全过程 9.6
202443 RISC-V 式数据通路与指令编码 4.2;44 sum+=a[i]、小端、分页 3.345 4MB 大页的页表项地址 8.5;46 缓冲区 B 的同步互斥 7.4
202543 数组的 Cache 与缺页 8.3;44 补码除法器与除法异常 5.645 植树 PV 7.4;46 进程地址空间与 scanf 3.6
202643 16 位机指令格式与编码 3.1;44 数据通路与控制信号 4.245 优先级 + 时间片调度 6.7;46 inode、位示图、删除目录 10.8

题号说明:2019 年操作系统在前(43、44)、计组在后(45、46);2014、2016 年计组是 44、45,操作系统是 46、47(这两年计网题提前了:2014 年计网是第 43 题,2016 年计网是第 41 题);其余年份一般是计组 43、44,操作系统 45、46。2026 年真题目前流传多个整理版本,题面细节以你手中的原卷为准。

12.7接下来怎么复习

  1. 第一遍(学):按第 0~10 篇顺序读,每篇里的真题卡先自己做再看解析,用小工具验证计算。
  2. 第二遍(练):每篇末尾的“本篇真题闯关”把这一篇涉及的所有真题按小节排好了,逐题做;按第 11 篇“大题满分训练营”逐类把综合题完整写一遍过程,写完对照每题的“答题卡这样写”。
  3. 第三遍(考):用 12.8 的年份网格,按年份把 2023—2026 年的计组+操作系统部分限时做完(选择题约 21 道、综合题 4 道),做错的题会标红,最后用“把所有错题连起来做”回炉。
  4. 第四遍(冲刺):第 13 篇先看 13.0 的热力图和 2027 预测,补 13.1 冷考点,做 13.2 ~ 13.4 的辨析、计算和融合综合题;再用 13.5 ~ 13.7 三套 80 分全真卷各限时 100 分钟做一遍,交卷看分科得分和错题。
  5. 考前一周:只看 12.4 公式、12.5 易错清单、各篇末尾的自测,以及 13.8 知识点细目表里还没勾上的条目。

12.8按年份刷真题:2009—2026 年计组与操作系统全部 451 题

下面每一行是一年,每个按钮是一道题(选择题标题号,红框粗体是综合题)。点开后在弹窗里作答:选择题点选项立即判对错并给出讲解,综合题先自己在纸上写,再展开分步解答并自评。弹窗里的“下一题”按这一年的题号顺序往下走,相当于做这一年卷子的计组+操作系统部分。

做题记录只存在你这台电脑的浏览器里(换浏览器或清除网站数据会丢失)。讲解是本讲义自己写的,答案以本机题库为准;2026 年真题的流传版本个别题面有出入,已在讲解中注明。

最后一句话

计组和操作系统其实在讲同一台机器:硬件提供机制(地址变换、中断、DMA、原子指令),操作系统利用机制制定策略(分页、缺页处理、调度、PV、文件系统)。做综合题时,先问自己“这一步是硬件做的还是操作系统做的”,很多题就迎刃而解。

第 13 篇 · 满分冲刺

满分冲刺:命题规律、新考点、辨析、计算、融合与全真模拟

前十篇解决“会不会”,第 11、12 篇解决“大题怎么写、考前查什么”,这一篇解决“稳不稳”。计组 + 操作系统从 66 分到 80 分,差的不是新知识,而是五件事:字眼和口径不出错、Ⅰ/Ⅱ/Ⅲ 组合题每一条都判对、考纲有而真题少的点也练过、计算题的单位和步骤一次算对、综合题把“硬件做什么、操作系统做什么”串成一条线。本篇先用 18 年真题的统计说清命题规律,再给新考点专项、概念辨析、计算题专练、融合综合题专练,最后是三套 80 分的全真模拟卷和一张可以逐条打勾的全书知识点细目表。

题目全部原创,答案和每个干扰项都由程序计算或模拟核对(00_三书精修/verify/08/p13/)练习题可以直接点选项判对错;模拟卷交卷后分科计分

13.0从 66 分到 80 分:满分差在哪里

按本书的分数目标,学完前十二篇大约能拿到计组 37、操作系统 29。剩下的 14 分,按我改卷和带学生的经验,几乎全丢在下面五个地方:

丢分的地方典型表现本书对策
① 字眼与口径按字编址还是按字节编址;K 是 210 还是 103(容量用前者、速率用后者);“至少 / 最多 / 一定 / 可能”看漏;“不可能的是”“错误的是”选反;教材口径不同(汤小丹的 SCAN = LOOK、蒋本珊的组相联约定、动态流水线的两种含义)各节“易错”框;12.5 易错清单;本篇 13.2、13.3
② 组合题漏判一条Ⅰ/Ⅱ/Ⅲ/Ⅳ 四条判对三条照样 0 分。操作系统的组合题在 2015—2020 年占了 28%,近六年仍有 20%(下图统计)本篇 13.2 概念辨析专练
③ 冷点、新点没准备2025 年考纲在进程通信里加了“信号”;多处理机调度、闲逛进程、总线仲裁、双端口 RAM、非规格化数从没出过题;硬布线控制器、多级 Cache、VFS、挂载、SPOOLing 都只考过一次本篇 13.1 专项
④ 计算题算错一步页表项数、Cache 标记位数、EAT、周转时间、磁盘时间……思路都对,错在单位换算、取整方向、漏算一次访存本篇 13.3 计算题满分专练
⑤ 综合题的细节分只写结果不写过程;PV 题信号量含义和初值没写、P 的顺序颠倒导致死锁;过程分析题说不清“这一步是硬件做的还是操作系统做的、CPU 在什么态”第 11 篇各类评分细则;本篇 13.4 融合专练、三套模拟卷的评分表

18 年的选择题落在哪里

091011121314151617181920212223242526合计概述2009 年 · 概述:1 道选择题12010 年 · 概述:2 道选择题22011 年 · 概述:2 道选择题22012 年 · 概述:1 道选择题12013 年 · 概述:1 道选择题12014 年 · 概述:1 道选择题12015 年 · 概述:1 道选择题12016 年 · 概述:2 道选择题22017 年 · 概述:1 道选择题12018 年 · 概述:0 道选择题2019 年 · 概述:2 道选择题22020 年 · 概述:1 道选择题12021 年 · 概述:2 道选择题22022 年 · 概述:2 道选择题22023 年 · 概述:1 道选择题12024 年 · 概述:1 道选择题12025 年 · 概述:2 道选择题22026 年 · 概述:2 道选择题225数据表示与运算2009 年 · 数据表示与运算:2 道选择题22010 年 · 数据表示与运算:2 道选择题22011 年 · 数据表示与运算:2 道选择题22012 年 · 数据表示与运算:3 道选择题32013 年 · 数据表示与运算:3 道选择题32014 年 · 数据表示与运算:2 道选择题22015 年 · 数据表示与运算:2 道选择题22016 年 · 数据表示与运算:2 道选择题22017 年 · 数据表示与运算:0 道选择题2018 年 · 数据表示与运算:6 道选择题62019 年 · 数据表示与运算:2 道选择题22020 年 · 数据表示与运算:2 道选择题22021 年 · 数据表示与运算:2 道选择题22022 年 · 数据表示与运算:2 道选择题22023 年 · 数据表示与运算:3 道选择题32024 年 · 数据表示与运算:3 道选择题32025 年 · 数据表示与运算:4 道选择题42026 年 · 数据表示与运算:2 道选择题244主存2009 年 · 主存:1 道选择题12010 年 · 主存:3 道选择题32011 年 · 主存:2 道选择题22012 年 · 主存:1 道选择题12013 年 · 主存:0 道选择题2014 年 · 主存:1 道选择题12015 年 · 主存:2 道选择题22016 年 · 主存:1 道选择题12017 年 · 主存:2 道选择题22018 年 · 主存:1 道选择题12019 年 · 主存:0 道选择题2020 年 · 主存:0 道选择题2021 年 · 主存:1 道选择题12022 年 · 主存:1 道选择题12023 年 · 主存:1 道选择题12024 年 · 主存:0 道选择题2025 年 · 主存:0 道选择题2026 年 · 主存:1 道选择题118Cache2009 年 · Cache:2 道选择题22010 年 · Cache:0 道选择题2011 年 · Cache:0 道选择题2012 年 · Cache:1 道选择题12013 年 · Cache:0 道选择题2014 年 · Cache:1 道选择题12015 年 · Cache:2 道选择题22016 年 · Cache:1 道选择题12017 年 · Cache:0 道选择题2018 年 · Cache:0 道选择题2019 年 · Cache:0 道选择题2020 年 · Cache:0 道选择题2021 年 · Cache:1 道选择题12022 年 · Cache:1 道选择题12023 年 · Cache:0 道选择题2024 年 · Cache:0 道选择题2025 年 · Cache:0 道选择题2026 年 · Cache:1 道选择题110虚拟存储 / TLB2009 年 · 虚拟存储 / TLB:0 道选择题2010 年 · 虚拟存储 / TLB:1 道选择题12011 年 · 虚拟存储 / TLB:0 道选择题2012 年 · 虚拟存储 / TLB:0 道选择题2013 年 · 虚拟存储 / TLB:1 道选择题12014 年 · 虚拟存储 / TLB:0 道选择题2015 年 · 虚拟存储 / TLB:0 道选择题2016 年 · 虚拟存储 / TLB:0 道选择题2017 年 · 虚拟存储 / TLB:0 道选择题2018 年 · 虚拟存储 / TLB:0 道选择题2019 年 · 虚拟存储 / TLB:0 道选择题2020 年 · 虚拟存储 / TLB:1 道选择题12021 年 · 虚拟存储 / TLB:0 道选择题2022 年 · 虚拟存储 / TLB:1 道选择题12023 年 · 虚拟存储 / TLB:0 道选择题2024 年 · 虚拟存储 / TLB:3 道选择题32025 年 · 虚拟存储 / TLB:0 道选择题2026 年 · 虚拟存储 / TLB:1 道选择题18指令系统2009 年 · 指令系统:2 道选择题22010 年 · 指令系统:0 道选择题2011 年 · 指令系统:2 道选择题22012 年 · 指令系统:0 道选择题2013 年 · 指令系统:1 道选择题12014 年 · 指令系统:1 道选择题12015 年 · 指令系统:0 道选择题2016 年 · 指令系统:1 道选择题12017 年 · 指令系统:2 道选择题22018 年 · 指令系统:1 道选择题12019 年 · 指令系统:0 道选择题2020 年 · 指令系统:1 道选择题12021 年 · 指令系统:0 道选择题2022 年 · 指令系统:2 道选择题22023 年 · 指令系统:1 道选择题12024 年 · 指令系统:0 道选择题2025 年 · 指令系统:1 道选择题12026 年 · 指令系统:1 道选择题116CPU(通路 / 控制 / 流水)2009 年 · CPU(通路 / 控制 / 流水):2 道选择题22010 年 · CPU(通路 / 控制 / 流水):1 道选择题12011 年 · CPU(通路 / 控制 / 流水):0 道选择题2012 年 · CPU(通路 / 控制 / 流水):1 道选择题12013 年 · CPU(通路 / 控制 / 流水):1 道选择题12014 年 · CPU(通路 / 控制 / 流水):1 道选择题12015 年 · CPU(通路 / 控制 / 流水):0 道选择题2016 年 · CPU(通路 / 控制 / 流水):2 道选择题22017 年 · CPU(通路 / 控制 / 流水):3 道选择题32018 年 · CPU(通路 / 控制 / 流水):1 道选择题12019 年 · CPU(通路 / 控制 / 流水):2 道选择题22020 年 · CPU(通路 / 控制 / 流水):1 道选择题12021 年 · CPU(通路 / 控制 / 流水):1 道选择题12022 年 · CPU(通路 / 控制 / 流水):1 道选择题12023 年 · CPU(通路 / 控制 / 流水):2 道选择题22024 年 · CPU(通路 / 控制 / 流水):1 道选择题12025 年 · CPU(通路 / 控制 / 流水):1 道选择题12026 年 · CPU(通路 / 控制 / 流水):1 道选择题122中断与异常2009 年 · 中断与异常:1 道选择题12010 年 · 中断与异常:1 道选择题12011 年 · 中断与异常:1 道选择题12012 年 · 中断与异常:1 道选择题12013 年 · 中断与异常:0 道选择题2014 年 · 中断与异常:0 道选择题2015 年 · 中断与异常:1 道选择题12016 年 · 中断与异常:1 道选择题12017 年 · 中断与异常:1 道选择题12018 年 · 中断与异常:0 道选择题2019 年 · 中断与异常:1 道选择题12020 年 · 中断与异常:3 道选择题32021 年 · 中断与异常:2 道选择题22022 年 · 中断与异常:0 道选择题2023 年 · 中断与异常:1 道选择题12024 年 · 中断与异常:1 道选择题12025 年 · 中断与异常:1 道选择题12026 年 · 中断与异常:2 道选择题218总线2009 年 · 总线:1 道选择题12010 年 · 总线:1 道选择题12011 年 · 总线:1 道选择题12012 年 · 总线:3 道选择题32013 年 · 总线:1 道选择题12014 年 · 总线:2 道选择题22015 年 · 总线:1 道选择题12016 年 · 总线:1 道选择题12017 年 · 总线:1 道选择题12018 年 · 总线:2 道选择题22019 年 · 总线:1 道选择题12020 年 · 总线:1 道选择题12021 年 · 总线:1 道选择题12022 年 · 总线:0 道选择题2023 年 · 总线:1 道选择题12024 年 · 总线:1 道选择题12025 年 · 总线:1 道选择题12026 年 · 总线:0 道选择题20I/O 方式2009 年 · I/O 方式:0 道选择题2010 年 · I/O 方式:0 道选择题2011 年 · I/O 方式:1 道选择题12012 年 · I/O 方式:0 道选择题2013 年 · I/O 方式:1 道选择题12014 年 · I/O 方式:2 道选择题22015 年 · I/O 方式:1 道选择题12016 年 · I/O 方式:0 道选择题2017 年 · I/O 方式:1 道选择题12018 年 · I/O 方式:0 道选择题2019 年 · I/O 方式:2 道选择题22020 年 · I/O 方式:1 道选择题12021 年 · I/O 方式:1 道选择题12022 年 · I/O 方式:1 道选择题12023 年 · I/O 方式:1 道选择题12024 年 · I/O 方式:1 道选择题12025 年 · I/O 方式:1 道选择题12026 年 · I/O 方式:0 道选择题14磁盘与 SSD2009 年 · 磁盘与 SSD:0 道选择题2010 年 · 磁盘与 SSD:0 道选择题2011 年 · 磁盘与 SSD:0 道选择题2012 年 · 磁盘与 SSD:0 道选择题2013 年 · 磁盘与 SSD:2 道选择题22014 年 · 磁盘与 SSD:0 道选择题2015 年 · 磁盘与 SSD:1 道选择题12016 年 · 磁盘与 SSD:0 道选择题2017 年 · 磁盘与 SSD:0 道选择题2018 年 · 磁盘与 SSD:0 道选择题2019 年 · 磁盘与 SSD:1 道选择题12020 年 · 磁盘与 SSD:0 道选择题2021 年 · 磁盘与 SSD:0 道选择题2022 年 · 磁盘与 SSD:0 道选择题2023 年 · 磁盘与 SSD:0 道选择题2024 年 · 磁盘与 SSD:0 道选择题2025 年 · 磁盘与 SSD:0 道选择题2026 年 · 磁盘与 SSD:0 道选择题41234道 / 年(空格 = 0 道)。按每题讲解的主归属小节归章,程序 verify/08/p13/stats.py 统计。
图 13-1 计组选择题:年 × 章(2009—2026,共 199 道)。每年 11 道(2009 年 12 道)。把鼠标放在格子上可看具体数字。
091011121314151617181920212223242526合计概述与运行环境2009 年 · 概述与运行环境:1 道选择题12010 年 · 概述与运行环境:1 道选择题12011 年 · 概述与运行环境:2 道选择题22012 年 · 概述与运行环境:3 道选择题32013 年 · 概述与运行环境:2 道选择题22014 年 · 概述与运行环境:1 道选择题12015 年 · 概述与运行环境:2 道选择题22016 年 · 概述与运行环境:1 道选择题12017 年 · 概述与运行环境:2 道选择题22018 年 · 概述与运行环境:2 道选择题22019 年 · 概述与运行环境:1 道选择题12020 年 · 概述与运行环境:1 道选择题12021 年 · 概述与运行环境:2 道选择题22022 年 · 概述与运行环境:4 道选择题42023 年 · 概述与运行环境:4 道选择题42024 年 · 概述与运行环境:2 道选择题22025 年 · 概述与运行环境:1 道选择题12026 年 · 概述与运行环境:1 道选择题133进程与线程2009 年 · 进程与线程:0 道选择题2010 年 · 进程与线程:1 道选择题12011 年 · 进程与线程:1 道选择题12012 年 · 进程与线程:1 道选择题12013 年 · 进程与线程:0 道选择题2014 年 · 进程与线程:2 道选择题22015 年 · 进程与线程:1 道选择题12016 年 · 进程与线程:0 道选择题2017 年 · 进程与线程:0 道选择题2018 年 · 进程与线程:1 道选择题12019 年 · 进程与线程:2 道选择题22020 年 · 进程与线程:1 道选择题12021 年 · 进程与线程:2 道选择题22022 年 · 进程与线程:1 道选择题12023 年 · 进程与线程:1 道选择题12024 年 · 进程与线程:3 道选择题32025 年 · 进程与线程:2 道选择题22026 年 · 进程与线程:1 道选择题120CPU 调度2009 年 · CPU 调度:1 道选择题12010 年 · CPU 调度:1 道选择题12011 年 · CPU 调度:1 道选择题12012 年 · CPU 调度:2 道选择题22013 年 · CPU 调度:1 道选择题12014 年 · CPU 调度:1 道选择题12015 年 · CPU 调度:0 道选择题2016 年 · CPU 调度:1 道选择题12017 年 · CPU 调度:2 道选择题22018 年 · CPU 调度:1 道选择题12019 年 · CPU 调度:1 道选择题12020 年 · CPU 调度:1 道选择题12021 年 · CPU 调度:1 道选择题12022 年 · CPU 调度:1 道选择题12023 年 · CPU 调度:1 道选择题12024 年 · CPU 调度:1 道选择题12025 年 · CPU 调度:0 道选择题2026 年 · CPU 调度:0 道选择题17同步与互斥2009 年 · 同步与互斥:1 道选择题12010 年 · 同步与互斥:2 道选择题22011 年 · 同步与互斥:1 道选择题12012 年 · 同步与互斥:0 道选择题2013 年 · 同步与互斥:0 道选择题2014 年 · 同步与互斥:0 道选择题2015 年 · 同步与互斥:0 道选择题2016 年 · 同步与互斥:3 道选择题32017 年 · 同步与互斥:0 道选择题2018 年 · 同步与互斥:3 道选择题32019 年 · 同步与互斥:0 道选择题2020 年 · 同步与互斥:1 道选择题12021 年 · 同步与互斥:0 道选择题2022 年 · 同步与互斥:0 道选择题2023 年 · 同步与互斥:0 道选择题2024 年 · 同步与互斥:0 道选择题2025 年 · 同步与互斥:0 道选择题2026 年 · 同步与互斥:2 道选择题213死锁2009 年 · 死锁:0 道选择题2010 年 · 死锁:0 道选择题2011 年 · 死锁:1 道选择题12012 年 · 死锁:1 道选择题12013 年 · 死锁:1 道选择题12014 年 · 死锁:1 道选择题12015 年 · 死锁:1 道选择题12016 年 · 死锁:1 道选择题12017 年 · 死锁:0 道选择题2018 年 · 死锁:1 道选择题12019 年 · 死锁:1 道选择题12020 年 · 死锁:1 道选择题12021 年 · 死锁:1 道选择题12022 年 · 死锁:1 道选择题12023 年 · 死锁:0 道选择题2024 年 · 死锁:0 道选择题2025 年 · 死锁:0 道选择题2026 年 · 死锁:0 道选择题11内存管理2009 年 · 内存管理:2 道选择题22010 年 · 内存管理:2 道选择题22011 年 · 内存管理:2 道选择题22012 年 · 内存管理:1 道选择题12013 年 · 内存管理:1 道选择题12014 年 · 内存管理:3 道选择题32015 年 · 内存管理:2 道选择题22016 年 · 内存管理:3 道选择题32017 年 · 内存管理:1 道选择题12018 年 · 内存管理:0 道选择题2019 年 · 内存管理:4 道选择题42020 年 · 内存管理:1 道选择题12021 年 · 内存管理:2 道选择题22022 年 · 内存管理:2 道选择题22023 年 · 内存管理:1 道选择题12024 年 · 内存管理:1 道选择题12025 年 · 内存管理:3 道选择题32026 年 · 内存管理:4 道选择题435文件管理2009 年 · 文件管理:3 道选择题32010 年 · 文件管理:2 道选择题22011 年 · 文件管理:0 道选择题2012 年 · 文件管理:0 道选择题2013 年 · 文件管理:3 道选择题32014 年 · 文件管理:2 道选择题22015 年 · 文件管理:2 道选择题22016 年 · 文件管理:0 道选择题2017 年 · 文件管理:3 道选择题32018 年 · 文件管理:0 道选择题2019 年 · 文件管理:1 道选择题12020 年 · 文件管理:3 道选择题32021 年 · 文件管理:1 道选择题12022 年 · 文件管理:0 道选择题2023 年 · 文件管理:2 道选择题22024 年 · 文件管理:1 道选择题12025 年 · 文件管理:3 道选择题32026 年 · 文件管理:0 道选择题26I/O 管理2009 年 · I/O 管理:1 道选择题12010 年 · I/O 管理:1 道选择题12011 年 · I/O 管理:2 道选择题22012 年 · I/O 管理:1 道选择题12013 年 · I/O 管理:1 道选择题12014 年 · I/O 管理:0 道选择题2015 年 · I/O 管理:1 道选择题12016 年 · I/O 管理:1 道选择题12017 年 · I/O 管理:1 道选择题12018 年 · I/O 管理:1 道选择题12019 年 · I/O 管理:0 道选择题2020 年 · I/O 管理:1 道选择题12021 年 · I/O 管理:0 道选择题2022 年 · I/O 管理:1 道选择题12023 年 · I/O 管理:1 道选择题12024 年 · I/O 管理:1 道选择题12025 年 · I/O 管理:0 道选择题2026 年 · I/O 管理:2 道选择题216磁盘管理2009 年 · 磁盘管理:1 道选择题12010 年 · 磁盘管理:0 道选择题2011 年 · 磁盘管理:0 道选择题2012 年 · 磁盘管理:1 道选择题12013 年 · 磁盘管理:1 道选择题12014 年 · 磁盘管理:0 道选择题2015 年 · 磁盘管理:1 道选择题12016 年 · 磁盘管理:0 道选择题2017 年 · 磁盘管理:1 道选择题12018 年 · 磁盘管理:1 道选择题12019 年 · 磁盘管理:0 道选择题2020 年 · 磁盘管理:0 道选择题2021 年 · 磁盘管理:1 道选择题12022 年 · 磁盘管理:0 道选择题2023 年 · 磁盘管理:0 道选择题2024 年 · 磁盘管理:1 道选择题12025 年 · 磁盘管理:1 道选择题12026 年 · 磁盘管理:0 道选择题91234道 / 年(空格 = 0 道)。按每题讲解的主归属小节归章,程序 verify/08/p13/stats.py 统计。
图 13-2 操作系统选择题:年 × 章(2009—2026,共 180 道)。每年 10 道。

从这两张图能读出五条规律,复习时间要据此分配:

  1. 计组的基本盘是“数据表示”:18 年 44 道,几乎年年 2 ~ 3 道(2018 年多达 6 道),补码、IEEE 754、C 语言类型转换、大小端是必拿分。其次是 CPU(22)、总线(20)、主存与中断(各 18)。
  2. 计组的 Cache、虚存、磁盘在选择题里“少”,在大题里“大”:Cache 选择题只有 10 道,但 43 ~ 46 题里 Cache 主考 4 次、兼考 5 次;虚存 + TLB + Cache 综合题主考 6 次。
  3. 操作系统的基本盘是“内存 + 概述与运行环境 + 文件”:合计 94 道,超过一半。内存管理几乎年年 2 道以上;概述与运行环境(用户态 / 内核态、中断、系统调用)2022、2023 两年各 4 道,是近年的明显升温点。
  4. 同步互斥、死锁的选择题少(13、11 道),但 PV 大题 18 年考了 13 次——这部分的分数主要在 45 / 46 题。
  5. 组合题:计组 2009—2014 / 2015—2020 / 2021—2026 三段分别占 10%、15%、11%;操作系统分别占 15%、28%、20%。操作系统的组合题明显更多,13.2 的辨析专练要重点做操作系统部分。

统计口径:每道选择题按本书讲解标注的主归属小节归章;组合题指题干中出现 Ⅰ/Ⅱ/Ⅲ 等多个说法、选项是说法组合的题。统计程序 verify/08/p13/stats.py,“考纲点在题面出现几次”由 cold.py 统计。

43 ~ 46 题的轮换规律

把 72 道综合题按第 11 篇的 13 类题型归类(每题按主考点归一类,“兼考”指该题某一问用到了这一类):

题型主考的年份兼考最近2027 的看法
② 指令格式与机器级代码2010、2013、2014、2017、2019、2021、2023、2024、202620152026出现最多,几乎隔年一次;汇编读代码、偏移量、栈帧、访存次数必须熟
③ 数据通路与控制信号2009、2015、2022、202620242026近五年三次(偶数年),2027 概率中等
⑤ Cache2010、2020、2023、20252011、2014、2016、2018、201920252023、2025 奇数年连考,且常和虚存一起出
⑥ 虚存 + TLB + Cache2011、2014、2016、2018、2019、20212023、20252021主考已 5 年没出现,一次访存全过程(13.4)是高概率题
⑦ I/O 方式与总线2009、2012、2013、2016、2018、2022—2022已 4 年没考:中断 / DMA 的 CPU 占用率、总线带宽要练熟
① 数据表示与运算2011、2017、2020、2025—2025刚考过,但选择题年年考,基础必须牢
④ 指令流水线20122014201214 年没有作为主考点:时空图、阻塞拍数、CPI 可能以一问的形式出现
⑧ PV 同步互斥2009、2011、2013、2014、2015、2017、2019—2025(每年)—20252019—2025 连续 7 年,2026 断档——2027 回归的概率最高
⑩ 分页、多级页表与置换2009、2010、2012、2013、2015、2017、2018、2020、2024—2024平均两年一次,2027 是常规窗口
⑪ 文件系统2011、2012、2014、2016、2018、2022、2026—2026刚考过(2026 考空闲空间 + 全局结构)
⑬ 过程分析题2021、2023、2025—2025奇数年隔年出现:缺页、系统调用、I/O 全过程里“谁在做、CPU 什么态、进程什么状态”
⑫ 磁盘2010、20192021、20222019常作为文件系统或 I/O 题的一问
⑨ CPU 调度2016、2026—2026刚考过

年份由题库程序统计(verify/08/p13/stats.py)。2014、2019 两年的计组 / 操作系统大题题号与常规不同(如 2019 年操作系统在 43、44),统计按内容归类,不按题号。

2027 年值得重点准备的点

考点真题情况为什么值得准备本书位置
PV 同步大题2019—2025 每年都有,2026 没有断档一年后回归的可能性最大;多类资源 + 容量、阶段同步(屏障)、读者写者变体是近年的方向7.4、11.9、13.4、三套卷
进程通信:信号从未考过2025 年考纲新写进“进程间通信”,新增条目常在一两年内出选择题6.3、13.1
虚存性能的影响因素2020-28、2022-30、2026-29 零散考过考纲独立条目,三年两次出题,热度在上升8.9、13.1
一次访存全过程(虚存 + TLB + Cache)主考 6 次,最近 2021计组最“融合”的综合题,5 年没作为主考点8.8、13.4
I/O 方式的 CPU 占用率大题最近 20224 年没考,计算模板固定,容易拿满分9.4、13.3、13.4
指令流水线大题只在 2012 主考选择题每年 1 道左右,大题一问的形式随时可能出现4.4、13.3
多处理器、硬件多线程、SMP、动态流水线只有 2017-17、2022-22 两题2022 年进入考纲,命题空间大4.5、4.4、13.1
多处理机调度、闲逛进程、线程调度从未单独出题2022 年起考纲写明“调度的实现”这几项6.6、13.1
SSD、RAID、FlashSSD 两题(2023、2025)外存的新内容,读写特性与磨损均衡是天然的辨析题10.1、13.1
文件系统的全局结构、VFS、挂载2021-46、2025-28、2026-462026 年大题刚用到全局结构,选择题会跟进10.8、13.1
这一篇怎么用
  • 一轮学完以后做 13.1、13.2:做错的题点链接回到对应小节,把那一节的“易错”框重读一遍。
  • 二轮复习做 13.3 计算专练(每类先看“固定步骤”,再限时做变式)和 13.4 融合专练(先画全过程图,再做题)。
  • 怎么作答:本篇和全书各节的练习题都可以直接点选项,点完立刻判对错并展开解析;三套模拟卷先只记下你的选择,点“交卷”后统一判分、列出错题,综合题按评分细则自评打分,计组、操作系统分别计分(记录保存在本机浏览器)。
  • 考前四周做三套模拟卷,每套限时 100 分钟(21 道选择题约 35 分钟、四道综合题约 65 分钟);也可以只做计组(55 分钟)或只做操作系统(45 分钟)。选择题错 1 道以内、综合题自评 90% 以上,才算达到满分水平。
  • 考前一周用 13.8 细目表逐条自查:每一条都能说出结论和理由,就勾上;勾不上的回原文。

13.1考纲有、真题少:新考点与冷考点专项

下面 11 组考点,要么是近几年新写进考纲、还没出过题,要么是十几年只考过一两次。每组先说“为什么可能考、会怎么考”,再给 2 ~ 3 道按 408 风格自编的选择题。题目全部是原创数据,答案和每个干扰项都由程序计算或模拟核对(verify/08/p13/new_topics.py),解析里写明每个错误选项“错在哪一步”。建议先盖住解析,每题限时 2 分钟;直接点选项就能判对错。

① 进程通信:信号

为什么可能考:2025 年起考纲把“信号”和共享内存、消息传递、管道并列写进进程间通信,至今没出过题。会怎么考:Ⅰ/Ⅱ/Ⅲ 判断信号的发送者、处理时机、处理方式;Ctrl+C 一类场景的事件排序;与信号量、中断辨析。复习:6.3 信号。

专项 1操作系统信号 · 原创
下列关于进程间通信中“信号”机制的叙述中,正确的是( )。
Ⅰ. 信号可以由内核发给进程,也可以由一个进程通过系统调用发给另一个进程
Ⅱ. 进程收到信号后,内核立即打断该进程正在执行的指令,转去执行信号处理函数
Ⅲ. 同一种信号在被处理之前多次到达,进程可能只处理一次
Ⅳ. 用户自定义的信号处理函数在内核态执行
  • A. 仅Ⅰ、Ⅲ
  • B. 仅Ⅰ、Ⅱ、Ⅲ
  • C. 仅Ⅲ、Ⅳ
  • D. 仅Ⅰ、Ⅲ、Ⅳ
展开解析

考点6.3 信号

A抓住三点:谁能发(内核、其他进程)、何时处理(返回用户态之前)、处理函数在哪执行(用户态)。

  • Ⅰ 对硬件异常、Ctrl+C 由内核发出;进程可以用 kill(pid, sig) 系统调用给别的进程发信号。
  • Ⅱ 错内核只是在 PCB 的待处理位图里置位,要等目标进程从内核态返回用户态之前才检查处理,不会在指令中途打断——那是硬件中断的行为。
  • Ⅲ 对每种信号只占 1 位,处理之前再来几次也只记一次(屏蔽期间发 3 次只处理 1 次,WSL 实测)。
  • Ⅳ 错处理函数是用户程序里的一段代码,内核只负责在返回用户态时安排跳过去,执行时 CPU 在用户态。

干扰项从哪来:B 是把信号当成硬件中断,以为“立即打断”;C 是只记得“同种信号只记一次”,却以为信号只能由内核发出,又误以为处理函数在内核态;D 是以为“内核安排调用”就等于“在内核态执行”

信号 = 便条:内核置位,返回用户态前检查,处理函数在用户态执行,同种不排队。

专项 2操作系统信号 · 原创
进程 P 用 signal() 为 SIGINT 注册了处理函数 h。P 在用户态执行一个死循环时,用户按下 Ctrl+C。下列事件发生的先后顺序是( )。
① 内核在 P 的 PCB 中将 SIGINT 对应的待处理位置 1
② CPU 响应键盘中断,转入内核
③ P 在用户态执行 h
④ 内核准备返回 P 的用户态时,检查到有待处理且未被屏蔽的信号
  • A. ①②④③
  • B. ②①④③
  • C. ②④①③
  • D. ②①③④
展开解析

考点6.3 跟我做一遍:Ctrl+C

BCtrl+C 首先是一次键盘中断;内核识别出它之后才“发”信号;处理一律推迟到返回用户态之前。

  • A 错信号不会凭空先出现:用户按键首先产生键盘中断,内核的终端驱动识别出 Ctrl+C 之后才置位,① 不可能在 ② 之前。
  • B 对② 键盘中断进内核 → ① 终端驱动识别 Ctrl+C,给前台进程 P 置位 → ④ 中断处理完、返回 P 的用户态前检查到 SIGINT → ③ 跳到用户态执行 h。
  • C 错把“检查”放到“置位”之前:内核检查时发现的正是 ① 刚置的那一位,先查后置就什么也查不到。
  • D 错以为处理函数在内核检查之前就已执行:是 ④ 的检查决定了要不要去执行 h,③ 不可能在 ④ 之前。

硬件中断 → 内核置位 → 返回用户态前检查 → 用户态处理函数。

专项 3操作系统信号与信号量 · 原创
下列关于信号与信号量的叙述中,错误的是( )。
  • A. 信号用于通知进程某个事件已经发生,信号量用于实现进程的同步与互斥
  • B. 对信号量执行 P 操作可能使调用进程阻塞;进程收到信号后不一定被终止
  • C. 用 kill 系统调用向进程发送 SIGTERM 信号后,目标进程一定立即终止
  • D. SIGKILL 信号不能被进程捕获、屏蔽或忽略
展开解析

考点6.3 信号 vs 信号量 vs 中断

Ckill 只是“发信号”:目标进程何时处理、怎么处理都不由发送方决定。

  • A 对一个是通知机制,一个是带等待队列的计数器(P、V 原语),用途不同。
  • B 对P 使 value 减到负数时进程阻塞;信号可以被捕获或忽略,默认动作也不全是终止(如 SIGCHLD 默认忽略)。
  • C 错本题选它。“一定立即终止”错两处:处理要等到目标进程返回用户态之前;SIGTERM 可以被捕获或忽略,进程可能根本不退出。
  • D 对SIGKILL、SIGSTOP 是两个“强制”信号,保证管理员总能终止或暂停进程。

能被捕获、屏蔽、忽略的信号,发送方都无法保证效果;只有 SIGKILL、SIGSTOP 例外。

② 多处理器、硬件多线程与向量处理器

为什么可能考:2022 年进入考纲,18 年里只有 2022-22 一道题,而它一题就同时考了 MIMD、SIMD、硬件多线程、SMP 四个点——说明命题人认为这部分适合出辨析题。会怎么考:细粒度 / 粗粒度 / SMT 的区别;SMP 共享地址空间;向量处理器省掉了哪些指令。复习:4.5 Flynn 分类、物理核与逻辑核。

专项 4计组硬件多线程 · 原创
下列关于硬件多线程与多处理器的叙述中,正确的是( )。
Ⅰ. 细粒度多线程可以在每个时钟周期切换线程
Ⅱ. 同时多线程(SMT)可以在同一个时钟周期内发射来自不同线程的指令
Ⅲ. 支持硬件多线程的处理器要为每个线程配置独立的 PC 和寄存器组
Ⅳ. 共享内存多处理器(SMP)中,每个处理器都有自己独立的物理地址空间
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅱ、Ⅲ、Ⅳ
  • C. Ⅰ、Ⅱ、Ⅲ、Ⅳ
  • D. 仅Ⅰ、Ⅱ、Ⅲ
展开解析

考点4.5 物理核与逻辑核4.5 UMA / NUMA

D硬件多线程“切换几乎不花时间”的前提是每个线程有自己的一套 PC 和寄存器;SMP 的关键词是“共享单一物理地址空间”。

  • Ⅰ 对细粒度 = 每拍轮换线程;粗粒度 = 遇到 Cache 缺失等长时间阻塞才换。
  • Ⅱ 对SMT(如 Intel 超线程)在同一拍里混着发射几个线程的指令,把空闲的功能部件填满。
  • Ⅲ 对正是因为每个线程的现场都在硬件里各有一份,切换时不用像进程切换那样保存、恢复寄存器。
  • Ⅳ 错SMP 中所有处理器共享单一的物理地址空间(2022-22 D),否则就不叫“共享内存”了;各有独立地址空间的是多计算机(消息传递)系统。

干扰项从哪来:A 是以为线程现场全靠操作系统保存,漏判 Ⅲ;B 是漏了 Ⅰ,又把 SMP 和多计算机系统混为一谈;C 是把四条都当成多处理器的“特点”全选

硬件多线程:多套 PC + 寄存器,共享功能部件;SMP:多个处理器共享一个物理地址空间。

专项 5计组向量处理器 · 原创
计算 C[i] = A[i] + B[i](i = 0 ~ 255)。标量 RISC 处理器每个元素需要执行 6 条指令(2 条取数、1 条加法、1 条存数、修改下标、条件转移各 1 条);向量处理器的向量寄存器长 64,每处理 64 个元素需执行 4 条向量指令(2 条向量取、1 条向量加、1 条向量存)外加 2 条循环控制指令。标量处理器执行的指令条数是向量处理器的( )。
  • A. 16 倍
  • B. 64 倍
  • C. 96 倍
  • D. 384 倍
展开解析

考点4.5 SIMD 与向量处理器

B两边都要按“总共处理 256 个元素”计数。

  1. 标量:256 × 6 = 1536 条。
  2. 向量:256 ÷ 64 = 4 轮,每轮 4 + 2 = 6 条,共 24 条。
  3. 1536 ÷ 24 = 64 倍。
  • A 错16 = 64 × 6 ÷ 24:标量只算了一个向量长度(64 个元素)的 384 条,却拿去和 256 个元素的 24 条比。
  • B 对1536 ÷ 24 = 64。
  • C 错96 = 1536 ÷ 16:向量版只数了 4 轮 × 4 条向量指令,漏了每轮 2 条循环控制。
  • D 错384 = 1536 ÷ 4:照搬“64 个元素只要 4 条向量指令”,忘了 256 个元素要分 4 轮做,也漏了循环控制。

SIMD / 向量:一条指令处理一组数据,省的是取指、译码和循环控制。

③ 超标量与动态流水线

为什么可能考:“超标量和动态流水线的基本概念”是考纲原文,只有 2017-17 涉及。会怎么考:多发射流水线的时间计算;动态调度“乱序执行、按序提交”的辨析;动态 / 静态调度的主体。复习:4.4 动态流水线。

专项 6计组动态流水线 · 原创
下列关于动态流水线(动态调度)的叙述中,正确的是( )。
Ⅰ. 指令可以不按程序顺序执行,但执行结果按程序顺序提交
Ⅱ. 动态调度由编译器在编译时完成
Ⅲ. 采用乱序执行后,存在写后读(RAW)相关的指令也不必等待
Ⅳ. 按序提交使得发生异常时仍能做到精确异常
  • A. 仅Ⅰ、Ⅳ
  • B. 仅Ⅰ、Ⅱ
  • C. 仅Ⅰ、Ⅲ、Ⅳ
  • D. 仅Ⅱ、Ⅲ、Ⅳ
展开解析

考点4.4 动态流水线

A“动态”指硬件在运行时调度;乱序只能让不相关的指令先走,真正的 RAW 照样要等。

  • Ⅰ 对乱序执行、按序提交,结果先放在重排序缓冲里。
  • Ⅱ 错编译器在编译时调整指令顺序叫静态调度;动态调度由硬件在运行时完成。
  • Ⅲ 错后一条要用的数据前一条还没算出来,谁也不能凭空得到,只能等;乱序执行让的是别的不相关指令先执行。
  • Ⅳ 对出异常的指令后面的指令即使算完也还没提交,可以整体作废,再从这条指令重新执行。

干扰项从哪来:B 是把“静态调度”和“动态调度”的主体(编译器 / 硬件)记反;C 是以为乱序执行能消除一切数据冒险;D 是同时犯了 B、C 的两个错误,还漏判 Ⅰ

乱序执行,按序提交;动态 = 硬件,静态 = 编译器。

专项 7计组超标量 · 原创
某 2 路超标量处理器(每个时钟周期最多同时发射、完成 2 条指令)采用 5 段流水线。执行 1000 条相互独立、无任何冒险的指令,理想情况下所需的时钟周期数为( )。
  • A. 1004
  • B. 500
  • C. 504
  • D. 2500
展开解析

考点4.4 流水线时间4.4 超标量

C先用 5 拍把第一批 2 条送完,以后每拍再完成 2 条。

  1. 第 1 批 2 条在第 5 拍结束时完成。
  2. 剩下 998 条 = 499 批,每拍完成一批,再要 499 拍。
  3. 共 5 + 499 = 504 拍(一般式:k + (n / m − 1),m 为发射宽度)。
  • A 错1004 = 5 + 999:按单发射流水线算,忘了每拍能完成 2 条。
  • B 错500 = 1000 ÷ 2:只算了“每拍 2 条”,漏了流水线装满之前的 4 拍。
  • C 对5 + (500 − 1) = 504。
  • D 错2500 = 1000 × 5 ÷ 2:把每条指令的 5 段当成串行执行,没有体现流水线的重叠。

m 路超标量 k 段流水线,n 条指令:k + (n/m − 1) 拍。

④ SSD、RAID 与 Flash

为什么可能考:SSD 2022 年进入考纲,只在 2023-22、2025-21、2025-32 的选项里出现过;RAID 只有 2013-20 一题;Flash 只有 2012-16。会怎么考:页写块擦、磨损均衡由谁做;RAID 的容量与容错;Flash 与 EEPROM 的区别。复习:10.1 SSD、RAID。

专项 8操作系统SSD · 原创
下列关于固态硬盘(SSD)的叙述中,正确的是( )。
Ⅰ. 以页为单位读写,以块为单位擦除
Ⅱ. 随机读的速度与顺序读接近
Ⅲ. 已写入数据的页可以直接覆盖写入新数据
Ⅳ. 静态磨损均衡会把长期不修改的数据迁移到擦除次数多的块上
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅰ、Ⅲ
  • C. Ⅰ、Ⅱ、Ⅲ、Ⅳ
  • D. 仅Ⅰ、Ⅱ、Ⅳ
展开解析

考点10.1 SSD

DSSD 的三个关键词:页写块擦、没有寻道、磨损均衡。

  • Ⅰ 对这是闪存的物理特性:写以页为单位,擦除以块(若干页)为单位。
  • Ⅱ 对没有机械的寻道和旋转延迟,随机读不比顺序读慢多少——所以对 SSD 做磁盘调度意义不大。
  • Ⅲ 错闪存的页写入前必须先擦除;SSD 的闪存翻译层把新数据写到另一个空页(异地更新),旧页标为无效,等垃圾回收。
  • Ⅳ 对冷数据占着“年轻”的块不动会造成磨损不均,静态磨损均衡把它们搬到老块上,让年轻块参与写入。

干扰项从哪来:A 是漏判了静态磨损均衡(只记得动态磨损均衡);B 是把 SSD 当成可以原地改写的磁盘,并漏判 Ⅱ;C 是以为闪存可以像内存一样直接覆盖写

页写块擦、异地更新、垃圾回收、磨损均衡(由 SSD 控制器做,不由文件系统做,2025-32)。

专项 9计组RAID · 原创
用 6 块容量均为 2TB 的磁盘组成 RAID 5。该阵列的可用容量,以及最多允许同时损坏而数据不丢失的磁盘数分别是( )。
  • A. 10TB、1 块
  • B. 12TB、0 块
  • C. 6TB、3 块
  • D. 8TB、2 块
展开解析

考点10.1 RAID

ARAID 5:分布式奇偶校验,相当于拿出 1 块盘的容量放校验,能容忍 1 块盘损坏。

  1. 可用容量 = (6 − 1) × 2TB = 10TB。
  2. 每个条带只有 1 个校验块,坏 1 块可用异或恢复,坏 2 块就无法恢复 → 1 块。
  • A 对(n − 1) × 单盘容量,容错 1 块。
  • B 错12TB、0 块是 RAID 0(只条带化、没有冗余)的数据,忘了 RAID 5 要留校验空间。
  • C 错6TB、3 块是 RAID 1 / RAID 10 镜像的思路(一半容量;每对镜像坏一块都能撑住),不是 RAID 5。
  • D 错8TB、2 块是以为 RAID 5 要两块盘放校验(那是 RAID 6 的做法,超出考纲)。

RAID 0:nC、不容错;RAID 1:nC/2;RAID 5:(n − 1)C、容错 1 块。

专项 10计组Flash · 原创
下列关于 Flash 存储器的叙述中,错误的是( )。
  • A. Flash 存储器是非易失性存储器,断电后信息不丢失
  • B. Flash 存储器由 EEPROM 发展而来,可以在线擦除和改写
  • C. Flash 存储器写入前要先擦除,写的速度比读慢
  • D. Flash 存储器可以以字节为单位进行擦除
展开解析

考点8.2 存储器分类10.1 SSD

DFlash(闪存)的“闪”指的是一次擦除一大块。

  • A 对靠浮栅存电荷,断电不丢,U 盘、SSD 都用它。
  • B 对EEPROM 能按字节电擦除但慢、贵;Flash 改成按块擦除,速度快、密度高。
  • C 对写前要擦,擦除又慢,所以写比读慢得多。
  • D 错本题选它。Flash 以块(扇区)为单位擦除,这正是它和 EEPROM 的区别,也是 SSD“页写块擦”的根源。

EEPROM:按字节擦写;Flash:按块擦除,非易失、读快写慢。

⑤ 总线仲裁与异步定时

为什么可能考:总线仲裁 18 年从未考过;“总线事务和定时”是考纲条目,全互锁的握手次数是天然的计算点。会怎么考:三种集中仲裁的控制线数;全互锁传输率。复习:9.2 仲裁、异步定时。

专项 11计组总线仲裁 · 原创
某总线上连接 16 个设备。若分别采用链式查询、计数器定时查询、独立请求三种集中式仲裁方式,所需的仲裁控制线(不含地址线、数据线)分别至少为( )。
  • A. 2、5、32
  • B. 3、6、32
  • C. 3、6、33
  • D. 3、18、33
展开解析

考点9.2 总线仲裁

C三种方式都有一根“总线忙”BS;链式多 BR、BG,计数器多 BR 和设备地址线,独立请求每个设备一对 BR/BG。

  1. 链式查询:BR、BG、BS 共 3 根。
  2. 计数器定时查询:BR、BS 2 根 + 设备地址线 ⌈log₂16⌉ = 4 根,共 6 根。
  3. 独立请求:每个设备一对 BRi、BGi,共 2 × 16 = 32 根,加 BS 共 33 根(王道口径 2n + 1)。
  • A 错2、5、32 是三种方式都漏了总线忙线 BS。
  • B 错3、6、32 是独立请求漏了 BS。
  • C 对3、6、33。
  • D 错18 = 16 + 2:计数器定时查询把“设备地址线”数成了设备个数 n,实际只要 ⌈log₂n⌉ 根就能编码 n 个设备。

控制线:链式 3、计数器 ⌈log₂n⌉ + 2、独立请求 2n + 1。

专项 12计组异步定时 · 原创
某异步总线采用全互锁方式。每个握手信号从发出到被对方确认需要 20ns,每次传送 32 位数据。该总线的最大数据传输率为( )。
  • A. 200MB/s
  • B. 50MB/s
  • C. 100MB/s
  • D. 12.5MB/s
展开解析

考点9.2 不互锁、半互锁、全互锁

B全互锁一次传送要 4 步握手:请求↑、回答↑、请求↓、回答↓,每步都要等对方确认。

  1. 一次传送时间 = 4 × 20ns = 80ns。
  2. 每次传 32 位 = 4B。
  3. 传输率 = 4B ÷ 80ns = 5 × 107 B/s = 50MB/s(速率的 M 取 106)。
  • A 错200MB/s = 4B ÷ 20ns:只算了一次握手,把全互锁当成“发一次请求就完成”。
  • B 对4B ÷ 80ns = 50MB/s。
  • C 错100MB/s = 4B ÷ 40ns:按半互锁或不互锁的两步握手算,没有数满全互锁的 4 步。
  • D 错12.5MB/s = 1B ÷ 80ns:把每次传送的 32 位当成了 1 字节。

全互锁:一次传送 4 个握手时间;速率单位的 M = 10⁶。

⑥ 非规格化数、移码与舍入

为什么可能考:IEEE 754 年年考,但阶码全 0 的非规格化数、单独考移码、舍入到偶数这几个角度几乎没出过。会怎么考:解码一个阶码全 0 的机器数;移码与补码互换;int 转 float 后值变了多少。复习:2.6 非规格化数、舍入、间距。

专项 13计组非规格化数 · 原创
IEEE 754 单精度浮点数的机器数为 0040 0000H,其真值为( )。
  • A. 2−127
  • B. 1.5 × 2−127
  • C. 2−128
  • D. 1.5 × 2−126
展开解析

考点2.6 非规格化数

A先看阶码:全 0 → 非规格化数,没有隐藏的 1,指数固定按 −126 算。

  1. 0040 0000H = 0 | 0000 0000 | 100 0000 … 0。
  2. S = 0;E = 0 → 非规格化,值 = 0.M × 2−126。
  3. M = 0.1B = 0.5 → 值 = 0.5 × 2−126 = 2−127(Python struct 解码核对)。
  • A 对0.1B × 2−126 = 2−127。
  • B 错1.5 × 2−127:当成规格化数补了隐藏的 1(1.1B = 1.5),指数又按 0 − 127 算。
  • C 错2−128:知道没有隐藏位(0.1B),但指数用了 0 − 127 = −127,忘了非规格化数的指数固定是 −126。
  • D 错1.5 × 2−126:指数 −126 对了,却又补了隐藏的 1。

E = 0:值 = 0.M × 2⁻¹²⁶(无隐藏位、指数 −126)。

专项 14计组移码 · 原创
某整数 x 用 8 位移码(偏置值 128)表示,机器数为 3FH,则 x 的 8 位补码表示为( )。
  • A. 3FH
  • B. C1H
  • C. 41H
  • D. BFH
展开解析

考点2.1 补码与移码

D同一个数的移码和补码只差符号位:符号位取反即可。

  1. 3FH = 0011 1111B = 63,x = 63 − 128 = −65。
  2. −65 的 8 位补码 = 256 − 65 = 191 = 1011 1111B = BFH。
  3. 直接法:3FH 的最高位 0 → 1,得 BFH。
  • A 错3FH:以为移码和补码相同,忘了偏置 128 使两者符号位相反。
  • B 错C1H = 1100 0001B 是 −65 的原码,题目要的是补码。
  • C 错41H = 65 的补码:把移码的最高位 0 当成了“正数”,符号判反了(移码最高位 0 表示负数)。
  • D 对BFH。

偏置 2ⁿ⁻¹ 的移码 = 补码的符号位取反;移码最高位 1 表示正数。

专项 15计组舍入 · 原创
int 型变量 i = 67108876(= 226 + 12),执行 float f = i; int j = f; 后(float 采用 IEEE 754 单精度、就近舍入),j 与 i 相比( )。
  • A. 相等
  • B. 小 4
  • C. 大 4
  • D. 小 12
展开解析

考点2.6 舍入2.6 间距与大数吃小数

C226 附近 float 的间距 = 226 − 23 = 8,只能表示 8 的倍数。

  1. 相邻可表示值:226 + 8 = 67108872 和 226 + 16 = 67108880。
  2. i 比 67108872 多 4,正好是半个间距 → 就近舍入取“偶数”:67108872 的尾数末位是 1(8 ÷ 8 = 1),67108880 的末位是 0 → 取 67108880。
  3. j = 67108880,比 i 大 4(gcc 实测)。
  • A 错以为 int 转 float 总能精确;float 只有 24 位有效位,超过 224 的整数就可能被舍入。
  • B 错小 4 是按截断(向 0 舍入)得到 67108872,忽略了“正好一半时取偶数”。
  • C 对舍入到 67108880,大 4。
  • D 错小 12 是把间距算成 32(误把尾数当 21 位),认为 12 不到半个间距而舍成 226。

间距 = 2^(指数 − 23);正好一半时取尾数末位为 0 的那个。

⑦ 调度的实现:闲逛进程、多处理机调度、线程调度

为什么可能考:2022 年起考纲把“调度器、闲逛进程、内核级线程与用户级线程调度、多处理机调度”写进“调度的实现”,至今没有单独出题。会怎么考:闲逛进程的性质;用户级线程下内核按什么调度;处理器亲和性、负载均衡。复习:6.6 闲逛进程与多处理机。

专项 16操作系统闲逛进程 / 多处理机 · 原创
下列关于 CPU 调度的叙述中,正确的是( )。
Ⅰ. 系统中没有其他就绪进程时,调度程序选择闲逛进程运行
Ⅱ. 闲逛进程的优先级最低,且它不会因为等待资源而阻塞
Ⅲ. 只支持用户级线程的系统中,内核调度的基本单位是线程
Ⅳ. 多处理机调度中的“处理器亲和性”是指尽量让进程在它上次运行的处理器上运行
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅰ、Ⅱ、Ⅳ
  • C. 仅Ⅱ、Ⅳ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

考点6.6 闲逛进程与多处理机6.6 处理器亲和性

B闲逛进程是“CPU 总得跑点什么”的兜底;用户级线程内核看不见;亲和性为了复用 Cache 里的数据。

  • Ⅰ 对就绪队列空时调度程序也有东西可选,“没有进程可运行”这种特殊情况就统一了。
  • Ⅱ 对它只在没别人可运行时才运行,一有就绪进程就让出;它不申请任何资源,所以永远不会阻塞。
  • Ⅲ 错用户级线程由用户态的线程库管理,内核看不见,内核调度的单位是进程。
  • Ⅳ 对进程换到别的处理器,原来处理器 Cache 里它的数据就白费了;亲和性正是为了避免这种开销。

干扰项从哪来:A 是漏判 Ⅳ(没接触过多处理机调度);C 是漏判 Ⅰ,误以为闲逛进程只在关机前运行;D 是以为“有线程就按线程调度”,忽略了用户级线程对内核不可见

只有用户级线程 → 内核按进程调度;有内核级线程 → 按内核级线程调度。

专项 17操作系统线程调度 · 原创
某单处理器系统采用时间片轮转调度,所有调度实体的时间片相同。进程 A 含 4 个用户级线程(多对一模型),进程 B 含 4 个内核级线程(一对一模型),系统中没有其他进程,且各线程都一直处于可运行状态。长期来看,进程 A 的 4 个线程合起来获得的 CPU 时间约占( )。
  • A. 20%
  • B. 50%
  • C. 80%
  • D. 12.5%
展开解析

考点6.4 用户级线程与内核级线程6.6 线程的调度

A内核只看见“调度实体”:A 整体算 1 个,B 的每个内核级线程各算 1 个。

  1. 内核看到的可运行实体:A(1 个)+ B 的 4 个内核级线程 = 5 个。
  2. 轮转时每个实体轮到的机会相同 → A 占 1/5 = 20%,再由 A 的线程库分给它的 4 个线程。
  • A 对1 ÷ 5 = 20%。
  • B 错50%:以为内核按进程平均分配(A、B 各一半),忽略了 B 的 4 个内核级线程各自参加轮转。
  • C 错80% 是 B 获得的份额,把 A、B 弄反了。
  • D 错12.5% = 1/8:以为 8 个线程平分 CPU,而且算的是 A 中一个线程的份额;就算按 8 个平分,A 的 4 个线程合起来也是 50%。

多对一模型下,一个进程无论有多少用户级线程,在内核眼里都只是一个调度实体。

⑧ 内存映射文件、写时复制与虚存性能

为什么可能考:内存映射文件只考过 2025-30;写时复制没有直接考过;“虚存性能的影响因素及改进方式”是考纲独立条目,近七年考了三次。会怎么考:映射时读不读文件;fork 后复制几页;EAT 的定量计算。复习:8.7 内存映射文件、写时复制、8.9 虚存性能。

专项 18操作系统内存映射文件 · 原创
下列关于内存映射文件的叙述中,错误的是( )。
  • A. 文件映射后,进程可以像访问内存数组一样通过指针读写文件内容
  • B. 多个进程映射同一个文件,可以实现进程间共享数据
  • C. 建立映射时,操作系统立即把整个文件读入内存
  • D. 被修改过的映射页,最终会被写回磁盘上的文件
展开解析

考点8.7 内存映射文件

C映射只是在页表里登记“这段虚拟地址对应文件的哪一部分”,真正读入靠缺页时按需调页。

  • A 对这正是内存映射文件的用处:用访存代替 read / write 系统调用。
  • B 对各进程的页表项指向同一批物理页框,一个进程写入,另一个立刻能看到。
  • C 错本题选它。建立映射时只建立对应关系,不读入数据;进程第一次访问某页时发生缺页,才从文件里把这一页读进来。
  • D 对脏页在被换出、解除映射或同步时写回文件,和请求分页里换出脏页一样。

mmap = 文件当“对换区”:按需调页、脏页写回、可共享。

专项 19操作系统写时复制 · 原创
某进程有 200 个可写的页,它调用 fork 创建子进程,系统采用写时复制。之后父进程修改了其中 2 个页,子进程修改了另外 3 个页(与父进程修改的页不同)。这期间因写时复制而复制的页数为( )。
  • A. 3
  • B. 200
  • C. 205
  • D. 5
展开解析

考点8.7 写时复制6.3 fork

Dfork 时一页都不复制,只把共享页标成只读;谁第一次写某个共享页,就为谁复制那一页。

  1. fork:父子共享全部 200 页,都标为只读(写时复制),复制 0 页。
  2. 父进程写 2 个页:各触发一次写保护异常,复制 2 页。
  3. 子进程写另外 3 个页:再复制 3 页。共 5 页。
  • A 错3:只算了子进程,忘了父进程写共享页同样要复制。
  • B 错200:以为 fork 时把整个地址空间都复制一遍——那正是写时复制要避免的。
  • C 错205:fork 时全复制,写的时候又复制一次,把两种做法叠加了。
  • D 对2 + 3 = 5 页。

写时复制:共享 + 只读,写哪页复制哪页。

专项 20操作系统虚存性能 · 原创
某请求分页系统采用一级页表,TLB 与主存串行查找。查 TLB 10ns,访问主存 100ns,TLB 命中率 90%(不考虑 Cache)。缺页率为 10−5,发生缺页时一次访存的总时间按 10ms 计(已含重新访问)。该系统的有效访存时间约为( )。
  • A. 120ns
  • B. 220ns
  • C. 210ns
  • D. 1120ns
展开解析

考点8.9 缺页率与 EAT8.8 组合与访存次数

B先算不缺页时的 EAT,再按缺页率加权。

  1. 不缺页时:10 + 100 + 0.1 × 100 = 120ns(每次查 TLB、访问数据;TLB 未命中的 10% 再查一次页表)。
  2. 10ms = 107 ns。
  3. EAT = (1 − 10−5) × 120 + 10−5 × 107 ≈ 120 + 100 = 220ns。
  • A 错120ns 是不缺页时的值,忽略了缺页(10−5 × 107 = 100ns,几乎让时间翻倍)。
  • B 对约 220ns。
  • C 错210ns:不缺页部分算成 10 + 100 = 110ns,漏了 TLB 未命中时多查一次页表的 0.1 × 100ns。
  • D 错1120ns:把 10ms 换成了 108 ns(多了一个 0),缺页部分变成 1000ns。

EAT = 不缺页时间 × (1 − p) + 缺页时间 × p;ms = 10⁶ ns。

⑨ 文件系统的全局结构、VFS、挂载与目录操作

为什么可能考:VFS(2025-28)、挂载(2021-46)、目录操作(2013-23)都只考过一次,2026-46 刚用到文件系统的全局结构。会怎么考:VFS 的作用;超级块里有什么;创建 / 删除文件要改哪些结构。复习:10.8 全局结构、VFS 与挂载、10.5 目录。

专项 21操作系统VFS / 挂载 · 原创
下列关于文件系统的叙述中,正确的是( )。
Ⅰ. 虚拟文件系统(VFS)为上层提供统一的文件操作接口,屏蔽各种具体文件系统的差异
Ⅱ. 挂载是把一个文件系统的根目录接到另一个文件系统的某个目录(挂载点)上
Ⅲ. 超级块记录整个文件系统的全局信息,如块大小、空闲块数、索引结点数
Ⅳ. 不同的文件系统挂载在同一棵目录树上后,必须使用相同的盘块大小
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅰ、Ⅲ
  • C. 仅Ⅱ、Ⅲ、Ⅳ
  • D. 仅Ⅰ、Ⅱ、Ⅲ
展开解析

考点10.8 全局结构、VFS 与挂载

DVFS 的意义就是“各管各的格式,对上一个接口”。

  • Ⅰ 对应用只调用 open / read / write,由 VFS 转给 ext4、FAT、NFS 等具体文件系统的实现(2025-28)。
  • Ⅱ 对挂载后访问挂载点,就进入了被挂载文件系统的根目录。
  • Ⅲ 对超级块是文件系统的“总说明书”,挂载时读入内存。
  • Ⅳ 错每个文件系统有自己的超级块、自己的块大小,正因为 VFS 屏蔽了差异,它们才能挂在同一棵目录树上。

干扰项从哪来:A 是以为超级块只是磁盘上的“第一块”、不知道它记录整个文件系统的全局信息,漏判 Ⅲ;B 是不清楚挂载就是“接到挂载点上”,漏判 Ⅱ;C 是以为挂在同一棵树上就要“统一格式”,误判 Ⅳ,又漏了 Ⅰ

VFS 统一接口;挂载 = 接到挂载点;超级块 = 一个文件系统的全局信息。

专项 22操作系统目录操作 · 原创
在采用索引结点(inode)的文件系统中创建一个新的空文件 /a/b/f,下列操作中不一定需要的是( )。
  • A. 为 f 分配数据块
  • B. 检查目录 b 中是否已有名为 f 的文件
  • C. 为 f 分配一个空闲的索引结点
  • D. 在目录 b 中添加 f 的目录项
展开解析

考点10.5 目录与目录操作10.4 FCB 与 inode

A创建文件 = 建“户口”(inode + 目录项);数据块等真正写入时再分配。

  • A 对空文件还没有任何数据,可以先不分配数据块,写入时再按需分配——“不一定需要”,本题选它。
  • B 错同一目录下不能重名,创建前一定要检查(这也是“创建文件前要先查一遍目录”的原因)。
  • C 错每个文件都必须有自己的索引结点记录属性和地址项,这一步一定要做。
  • D 错不在父目录里登记,就没法按路径名找到 f,这一步一定要做。

创建:查重名 → 分配 inode → 写目录项;数据块写入时才分配。

⑩ I/O 应用接口、SPOOLing 与坏块

为什么可能考:“输入输出应用程序接口(阻塞 / 非阻塞 I/O)”从未单独出题;SPOOLing 只考过 2016-31;坏块是 2022 年考纲新增的“磁盘管理”内容。会怎么考:三种 I/O 调用的区别;输入井、输出井在哪;扇区备用对谁透明。复习:9.5 三种 I/O、9.8 SPOOLing、10.3 坏块。

专项 23操作系统阻塞 / 非阻塞 / 异步 · 原创
下列关于 I/O 应用程序接口的叙述中,正确的是( )。
Ⅰ. 进程调用阻塞式 read 而数据尚未就绪时,进程由运行态转为阻塞态
Ⅱ. 非阻塞式 read 在数据未就绪时立即返回,进程不会阻塞
Ⅲ. 异步 I/O 发起后进程可以继续执行,I/O 完成时再通过信号或回调等方式得到通知
Ⅳ. 非阻塞 I/O 和异步 I/O 是同一个概念
  • A. 仅Ⅰ、Ⅱ
  • B. Ⅰ、Ⅱ、Ⅲ、Ⅳ
  • C. 仅Ⅰ、Ⅱ、Ⅲ
  • D. 仅Ⅱ、Ⅲ、Ⅳ
展开解析

考点9.5 阻塞、非阻塞与异步 I/O

C三种“等法”的区别在于:数据没好时,进程是睡着等、马上回来自己再问,还是交代完就走、等别人通知。

  • Ⅰ 对阻塞式调用在数据没准备好时把进程挂到设备等待队列,状态变为阻塞。
  • Ⅱ 对非阻塞调用立即返回“暂时没有数据”,进程继续运行,通常要过一会儿再来问(轮询)。
  • Ⅲ 对异步 I/O 把“读到哪个缓冲区”交代给内核就返回,内核完成后通知进程——数据搬运也由内核完成。
  • Ⅳ 错非阻塞 I/O 要进程自己反复来问、自己把数据取走;异步 I/O 由内核做完再通知,两者不同。

干扰项从哪来:A 是只熟悉阻塞和非阻塞两种调用,不知道异步 I/O 由内核完成后通知,漏判 Ⅲ;B 是把“非阻塞”和“异步”当成同一回事;D 是误判 Ⅳ,又以为阻塞式调用不改变进程状态

阻塞:睡着等;非阻塞:马上回来、自己再问;异步:交代完就走、内核做完通知。

专项 24操作系统SPOOLing · 原创
下列关于 SPOOLing 技术的叙述中,错误的是( )。
  • A. 输入井和输出井是在磁盘上开辟的存储区
  • B. SPOOLing 不需要外存的支持,只靠内存缓冲区就能实现
  • C. 用户进程的打印数据先送到输出井,打印机空闲时再由输出进程输出
  • D. SPOOLing 把一台独占的打印机变成可供多个用户“同时”使用的虚拟设备
展开解析

考点9.8 SPOOLing

BSPOOLing 的核心是用磁盘上的输入井、输出井模拟脱机输入输出。

  • A 对井在磁盘上,容量大;内存里的输入 / 输出缓冲区只是中转。
  • B 错本题选它。没有磁盘上的井,就没地方存放“排队等打印”的大量数据,也就无法把独占设备虚拟成共享设备。
  • C 对用户进程只把数据写进输出井就算“打印完了”,真正的打印由输出进程在打印机空闲时完成。
  • D 对每个用户都觉得自己独占了一台打印机——这是虚拟设备。

SPOOLing = 输入井 / 输出井(磁盘)+ 输入 / 输出缓冲区(内存)+ 输入 / 输出进程。

专项 25操作系统坏块 · 原创
下列关于磁盘坏块处理的叙述中,正确的是( )。
Ⅰ. 采用扇区备用方式时,由磁盘控制器用备用扇区替换坏扇区,对操作系统透明
Ⅱ. 由文件系统在 FAT 或坏块表中标记坏块时,坏块对操作系统不透明
Ⅲ. 采用扇区备用方式后,操作系统看到的逻辑扇区号会因坏扇区而出现跳号
Ⅳ. 备用扇区一般在低级格式化时预留
  • A. 仅Ⅰ、Ⅱ、Ⅳ
  • B. 仅Ⅰ、Ⅱ
  • C. 仅Ⅱ、Ⅲ、Ⅳ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

考点10.3 坏块

A两种处理方式的区别就是“谁来做”:控制器做 → 透明;操作系统做 → 不透明。

  • Ⅰ 对控制器维护坏块映射,把对坏扇区的访问重定向到备用扇区,操作系统完全不知道。
  • Ⅱ 对操作系统记下哪些块坏了,以后不再分配,坏块对它显然是可见的。
  • Ⅲ 错正因为“透明”,逻辑扇区号仍是连续的,坏扇区的编号被映射到备用扇区上,不会跳号。
  • Ⅳ 对低级格式化(物理格式化)时划分扇区、预留备用扇区并初始化坏块表。

干扰项从哪来:B 是漏判了 Ⅳ(不知道备用扇区何时准备);C 是把“透明”理解反了,以为操作系统能看到跳号;D 是四条全选,同样误判了 Ⅲ

扇区备用:控制器做、对 OS 透明、低级格式化时预留;标记坏块:OS 做、不透明。

⑪ 冷了很久的计组点:多级 Cache、双端口 RAM、控制器

为什么可能考:多级 Cache 只有 2023-19 一题;双端口 RAM 从未考过;硬布线控制器只在 2009-19 出现;专门考控制器原理的选择题 2017-18 以后就没有了(2021-17、2024-13、2025-16 只在选项里提到微指令、微程序)。这些都是“冷了很久、随时可能回来”的点。复习:8.3 多级 Cache、8.2 双端口 RAM、4.3 控制器。

专项 26计组多级 Cache · 原创
某计算机有两级 Cache。L1 命中时访问时间 2ns,L1 缺失率 10%;L2 的访问时间 10ns,L2 的局部缺失率(L1 缺失的访问中,L2 也缺失的比例)为 20%;主存访问时间 100ns。按“逐级查找、缺失才访问下一级”计算,平均访存时间为( )。
  • A. 4ns
  • B. 23ns
  • C. 14ns
  • D. 5ns
展开解析

考点8.3 多级 Cache

D平均时间 = L1 时间 + L1 缺失率 × (L2 时间 + L2 局部缺失率 × 主存时间)。

  1. L2 那一层的平均时间:10 + 0.2 × 100 = 30ns。
  2. 总平均:2 + 0.1 × 30 = 5ns(程序核对)。
  • A 错4ns = 2 + 0.1 × 0.2 × 100:漏了 L1 缺失时访问 L2 的那 10ns。
  • B 错23ns = 2 + 0.1 × 10 + 0.2 × 100:把 20% 的局部缺失率当成了全局缺失率,没乘 L1 的 10%。
  • C 错14ns = 2 + 10 + 0.1 × 0.2 × 100:以为每次访存都要访问 L2,L2 的 10ns 没乘缺失率 10%。
  • D 对2 + 0.1 × (10 + 0.2 × 100) = 5ns。

全局缺失率 = 各级局部缺失率相乘(这里 0.1 × 0.2 = 2%)。

专项 27计组双端口 RAM · 原创
双端口 RAM 的左、右两个端口同时访问存储器时,下列情况中会发生冲突、需要让其中一个端口等待的是( )。
  • A. 两个端口同时读同一个存储单元
  • B. 两个端口同时访问不同的存储单元
  • C. 一个端口写某存储单元,另一个端口同时读该单元
  • D. 两个端口同时写不同的存储单元
展开解析

考点8.2 双端口 RAM

C只有访问同一个单元、且至少一方在写时,才会出错。

  • A 错两边同时读同一单元,读出的都是同一个值,互不影响,不冲突。
  • B 错不同单元有各自的地址译码和数据通路,双端口 RAM 正是为此设计的。
  • C 对一边写、一边读同一单元,读出的可能是旧值也可能是新值(读出错误);控制逻辑发 BUSY 让一方等待。两边同时写同一单元则是写入错误,同样冲突。
  • D 错写的是不同单元,各写各的,不冲突。

双端口 RAM 冲突 = 同一单元 + 至少一方写;同读同一单元不冲突。

专项 28计组控制器 · 原创
下列关于硬布线控制器和微程序控制器的叙述中,正确的是( )。
Ⅰ. 硬布线控制器的控制信号由组合逻辑电路根据操作码、时序信号和状态条件直接产生
Ⅱ. 微程序控制器中,一条机器指令对应一段微程序
Ⅲ. 在同样的工艺条件下,硬布线控制器的速度比微程序控制器慢
Ⅳ. RISC 处理器多采用微程序控制器
  • A. 仅Ⅰ、Ⅱ、Ⅳ
  • B. 仅Ⅰ、Ⅱ
  • C. 仅Ⅱ、Ⅲ
  • D. 仅Ⅰ、Ⅲ、Ⅳ
展开解析

考点4.3 硬布线与微程序

B硬布线 = 组合逻辑,快、难改,RISC 爱用;微程序 = 存储逻辑,慢一点、易改,CISC 爱用。

  • Ⅰ 对这就是“硬布线”的定义:控制信号是电路直接“算”出来的。
  • Ⅱ 对每条机器指令的功能由控制存储器里的一段微程序(若干条微指令)解释执行。
  • Ⅲ 错硬布线不用逐条读控制存储器,速度更快(2009-19 考过“硬布线速度并不慢”)。
  • Ⅳ 错RISC 指令简单规整,适合硬布线;微程序多用于指令复杂的 CISC。

干扰项从哪来:A 是把 RISC 和 CISC 的控制器类型记反;C 是把硬布线和微程序的速度记反,并漏判 Ⅰ;D 是同时记反了速度和 RISC 的控制方式

硬布线:快、难改、RISC;微程序:慢、易改、CISC。

13.2概念辨析专练:Ⅰ / Ⅱ / Ⅲ / Ⅳ 组合题

组合题是选择题里最“不讲情面”的一类:四个说法判对三个,照样 0 分。13.0 的统计显示,操作系统的组合题在 2015—2020 年占了 28%。下面 28 道按考纲章节排列(计组 16 道、操作系统 12 道),说法都取自各节“易错”框里最容易混淆的结论。做法建议:

  1. 先判最有把握的一条,用它一次划掉两个选项;
  2. 再判剩下的选项之间有差别的那几条,所有选项都包含(或都不包含)的说法可以不判;
  3. 每条都要能说出理由——对完答案后逐条看解析,说不出理由的那条就是你的薄弱点,点链接回原文。

解析逐条给出对 / 错和理由,并写出每个干扰选项是“把哪一条当成了对的”或“漏选了哪一条”、为什么容易这样错。题目由 verify/08/p13/gen_judge.py 生成,生成时断言“正确选项 = 真说法的集合”,干扰项都只与正确答案差一两条。

计组(1)计算机系统概述

辨析 1计组计算机系统概述 · 原创
下列叙述中,正确的是( )。
Ⅰ. 冯·诺依曼机中,指令和数据以同等地位存放在存储器中,CPU 根据指令周期的不同阶段来区分取出的是指令还是数据
Ⅱ. 汇编语言程序员可以通过指令读取 MAR 和 MDR 的内容
Ⅲ. 同一台机器上运行两个程序,程序 A 的 MIPS 比程序 B 高,说明 A 的执行时间一定比 B 短
Ⅳ. 软件和硬件在逻辑功能上是等价的,同一功能既可以用硬件实现,也可以用软件实现
  • A. 仅Ⅰ、Ⅳ
  • B. 仅Ⅱ、Ⅳ
  • C. 仅Ⅰ、Ⅱ、Ⅳ
  • D. 仅Ⅰ、Ⅲ、Ⅳ
展开解析

考点1.3 指令的执行1.2 寄存器的可见性1.8 MIPS 的陷阱1.4 软硬件关系

A正确的是 Ⅰ、Ⅳ。

  • Ⅰ 对取指周期取出的一定是指令,执行周期按地址取出的是数据(2009-11)。
  • Ⅱ 错MAR、MDR 是 CPU 访存的内部接口,没有指令能直接访问它们,对汇编程序员透明。
  • Ⅲ 错执行时间 = 指令条数 ÷ (MIPS × 10⁶),两个程序的指令条数不同,MIPS 高的未必更快。
  • Ⅳ 对例如乘法既可以用乘法器做,也可以用加法和移位指令编程实现,只是速度和成本不同。

干扰项从哪来:B 是把 Ⅱ 当成对的(以为 CPU 里的寄存器都对程序员可见)、漏选 Ⅰ(以为指令和数据靠存放区域或标志位区分);C 是把 Ⅱ 当成对的(以为 CPU 里的寄存器都对程序员可见);D 是把 Ⅲ 当成对的(把“MIPS 越高越快”用到了不同程序之间)

辨析 2计组计算机系统概述 · 原创
下列叙述中,正确的是( )。
Ⅰ. CPU 执行时间 = 指令条数 × CPI × 时钟周期
Ⅱ. 主频提高一倍,任何程序的执行时间都会缩短为原来的一半
Ⅲ. 计算机是否提供乘法指令属于体系结构问题,乘法指令用专门的乘法器还是用加法器多次相加实现属于组成问题
Ⅳ. 系列机中新机型能运行旧机型上的程序,称为向前兼容
  • A. 仅Ⅰ
  • B. 仅Ⅲ
  • C. 仅Ⅰ、Ⅲ
  • D. 仅Ⅰ、Ⅱ、Ⅲ
展开解析

考点1.8 性能指标1.4 体系结构与组成1.4 系列机与兼容

C正确的是 Ⅰ、Ⅲ。

  • Ⅰ 对三个因素分别由程序(及编译器)、指令系统与实现、工艺共同决定。
  • Ⅱ 错访存、I/O 的时间不随主频成比例缩短,主频提高后 CPI 还可能变大(每次访存要等更多周期)。
  • Ⅲ 对体系结构是程序员看得见的属性(指令集),组成是这些属性的实现方式。
  • Ⅳ 错新机兼容旧机的程序叫向后兼容,这是系列机必须保证的;向前兼容不一定保证。

干扰项从哪来:A 是漏选 Ⅲ(把“有没有”与“怎么做”混为一谈);B 是漏选 Ⅰ(把 CPI 和 MIPS 的公式记混);D 是把 Ⅱ 当成对的(只看公式里的时钟周期这一项)

计组(2)数据的表示和运算

辨析 3计组数据的表示和运算 · 原创
下列叙述中,正确的是( )。
Ⅰ. n 位补码能表示的整数比 n 位原码多一个
Ⅱ. 两个补码数相加时,若最高位(符号位)产生进位,则结果一定溢出
Ⅲ. 采用双符号位(变形补码)时,运算结果的两个符号位为 10 表示负溢出
Ⅳ. CF 标志对带符号整数的运算没有意义
  • A. 仅Ⅰ、Ⅲ
  • B. 仅Ⅰ、Ⅲ、Ⅳ
  • C. 仅Ⅱ、Ⅲ、Ⅳ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

考点2.1 补码2.4 溢出判断2.4 双符号位2.4 四个标志位

B正确的是 Ⅰ、Ⅲ、Ⅳ。

  • Ⅰ 对补码 0 只有一种表示,多出的编码 100…0 表示 −2ⁿ⁻¹。
  • Ⅱ 错溢出看 OF = 最高位进位 ⊕ 次高位进位,例如 −1 + 1 最高位有进位但结果正确。
  • Ⅲ 对01 正溢出、10 负溢出、00 / 11 不溢出。
  • Ⅳ 对CF 是无符号运算的进位 / 借位;带符号运算看 OF。

干扰项从哪来:A 是漏选 Ⅳ(以为四个标志对所有运算都有意义);C 是把 Ⅱ 当成对的(把“有进位”当成“溢出”)、漏选 Ⅰ(以为原码、补码表示范围相同);D 是把 Ⅱ 当成对的(把“有进位”当成“溢出”)

辨析 4计组数据的表示和运算 · 原创
下列叙述中,正确的是( )。
Ⅰ. IEEE 754 单精度格式的阶码用偏置值为 127 的移码表示
Ⅱ. float 型能精确表示所有 int 型整数
Ⅲ. 两个规格化浮点数相加,尾数求和后可能需要右规,右规可能导致阶码上溢
Ⅳ. 浮点数的加法满足结合律
  • A. 仅Ⅰ
  • B. 仅Ⅲ
  • C. 仅Ⅰ、Ⅱ
  • D. 仅Ⅰ、Ⅲ
展开解析

考点2.6 IEEE 7542.6 精度与间距2.6 浮点加减2.6 结合律

D正确的是 Ⅰ、Ⅲ。

  • Ⅰ 对真值指数 = 阶码 − 127;注意不是教材一般移码的偏置 128。
  • Ⅱ 错float 只有 24 位有效位,大于 2²⁴ 的某些整数会被舍入(如 2²⁴ + 1)。
  • Ⅲ 对尾数出现 1x.xxx 要右移一位、阶码 +1,阶码原来已是最大值时就上溢。
  • Ⅳ 错每一步都要舍入:(2²⁴ + 1) + 1 = 2²⁴,而 2²⁴ + (1 + 1) = 2²⁴ + 2。

干扰项从哪来:A 是漏选 Ⅲ(以为尾数溢出就等于结果溢出,或反过来以为不会溢出);B 是漏选 Ⅰ(把偏置值记成 128);C 是把 Ⅱ 当成对的(以为 float 的范围比 int 大,就能装下所有 int)、漏选 Ⅲ(以为尾数溢出就等于结果溢出,或反过来以为不会溢出)

计组(3)存储器层次结构

辨析 5计组存储器层次结构 · 原创
下列叙述中,正确的是( )。
Ⅰ. DRAM 按行刷新,刷新地址由刷新地址计数器给出,不需要 CPU 参与
Ⅱ. SRAM 的读出是破坏性读出,读出后需要再生
Ⅲ. 采用低位交叉编址的多体存储器,相邻地址的单元分布在不同的存储体中
Ⅳ. ROM 和 RAM 一样,都采用随机存取方式
  • A. 仅Ⅰ、Ⅲ
  • B. 仅Ⅰ、Ⅲ、Ⅳ
  • C. 仅Ⅱ、Ⅲ、Ⅳ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

考点8.2 刷新8.2 SRAM 与 DRAM8.2 交叉编址8.2 存取方式

B正确的是 Ⅰ、Ⅲ、Ⅳ。

  • Ⅰ 对刷新是存储器内部的操作,CPU 不知道也不参与(只是刷新期间不能访问)。
  • Ⅱ 错破坏性读出是 DRAM(电容放电)的特点;SRAM 用触发器存储,读出不破坏。
  • Ⅲ 对低位作体号,连续访问时各体轮流启动,可以流水。
  • Ⅳ 对“随机存取”指访问任一单元的时间相同,与能不能写无关(2010-16)。

干扰项从哪来:A 是漏选 Ⅳ(把“随机存取”误解为“随机读写”);C 是把 Ⅱ 当成对的(把 SRAM、DRAM 的特点记反)、漏选 Ⅰ(以为刷新要 CPU 执行刷新程序);D 是把 Ⅱ 当成对的(把 SRAM、DRAM 的特点记反)

辨析 6计组存储器层次结构 · 原创
下列叙述中,正确的是( )。
Ⅰ. 全相联映射的 Cache,主存地址中没有组号(Cache 行号)字段
Ⅱ. 采用写回法的 Cache,每一行都需要设置脏位(修改位)
Ⅲ. Cache 行中标记的位数与主存容量无关
Ⅳ. 在 Cache 容量不变的前提下,增大块的大小一定能提高命中率
  • A. 仅Ⅰ
  • B. 仅Ⅰ、Ⅱ
  • C. 仅Ⅰ、Ⅱ、Ⅲ
  • D. 仅Ⅰ、Ⅱ、Ⅳ
展开解析

考点8.3 地址划分8.3 写策略8.3 块大小

B正确的是 Ⅰ、Ⅱ。

  • Ⅰ 对任何主存块可以放进任何一行,只能比较标记,地址 = 标记 + 块内地址。
  • Ⅱ 对被替换时要知道这一块改过没有,改过才写回主存;全写法不需要。
  • Ⅲ 错标记位数 = 主存地址位数 − 组号位数 − 块内地址位数,主存越大标记越长。
  • Ⅳ 错块大了能多利用空间局部性,但行数变少、冲突增多,块大到一定程度命中率反而下降。

干扰项从哪来:A 是漏选 Ⅱ(把脏位和有效位、写回法和全写法混淆);C 是把 Ⅲ 当成对的(以为标记只取决于 Cache 本身);D 是把 Ⅳ 当成对的(只想到空间局部性的好处)

辨析 7计组存储器层次结构 · 原创
下列叙述中,正确的是( )。
Ⅰ. TLB 命中时,所访问的页一定在主存中
Ⅱ. TLB 未命中时,一定会发生缺页
Ⅲ. 虚拟存储器中,主存与辅存之间只能采用写回法
Ⅳ. Cache 命中时,TLB 也一定命中
  • A. 仅Ⅰ
  • B. 仅Ⅲ
  • C. 仅Ⅰ、Ⅲ
  • D. 仅Ⅰ、Ⅱ、Ⅲ
展开解析

考点8.8 命中组合8.10 对照表

C正确的是 Ⅰ、Ⅲ。

  • Ⅰ 对TLB 只缓存在主存中的页的页表项,页被换出时对应项会作废。
  • Ⅱ 错TLB 只放一部分页表项,查页表后页可能就在主存里。
  • Ⅲ 对每次写都写磁盘代价太大,所以页面只在被换出时写回。
  • Ⅳ 错TLB 命中与 Cache 命中互不决定:页表项可能已被挤出 TLB,而数据块仍在 Cache 中。

干扰项从哪来:A 是漏选 Ⅲ(照搬 Cache 的两种写策略);B 是漏选 Ⅰ(以为 TLB 与缺页无关);D 是把 Ⅱ 当成对的(把 TLB 当成页表本身)

计组(4)指令系统

辨析 8计组指令系统 · 原创
下列叙述中,正确的是( )。
Ⅰ. 采用扩展操作码时,短操作码不能作为长操作码的前缀
Ⅱ. 相对寻址的有效地址 EA = (PC) + A 中,PC 是当前这条指令的地址
Ⅲ. 基址寻址中,基址寄存器的内容由操作系统或管理程序设定,程序执行过程中一般不变
Ⅳ. 立即寻址方式取操作数时需要访问一次主存
  • A. 仅Ⅰ
  • B. 仅Ⅲ
  • C. 仅Ⅰ、Ⅱ
  • D. 仅Ⅰ、Ⅲ
展开解析

考点3.1 扩展操作码3.2 相对寻址3.2 基址与变址3.2 寻址方式

D正确的是 Ⅰ、Ⅲ。

  • Ⅰ 对否则译码时无法区分一条指令到底是短操作码还是长操作码的开头。
  • Ⅱ 错执行阶段 PC 已经在取指后更新,指向下一条指令;偏移量相对的是下一条指令的地址。
  • Ⅲ 对基址寄存器面向系统(扩大寻址范围、重定位),变址寄存器面向用户(数组下标)。
  • Ⅳ 错操作数就在指令里,取指时一起取到,执行阶段不再访存。

干扰项从哪来:A 是漏选 Ⅲ(把基址寄存器和变址寄存器的用途记反);B 是漏选 Ⅰ(只记住了“扩展”,忘了前缀条件);C 是把 Ⅱ 当成对的(忘了 PC 在取指周期就已经加了指令长度)、漏选 Ⅲ(把基址寄存器和变址寄存器的用途记反)

辨析 9计组指令系统 · 原创
下列叙述中,正确的是( )。
Ⅰ. RISC 机器中只有 Load / Store 指令访问存储器
Ⅱ. x86 的 call 指令先把返回地址压栈,再跳转到被调函数的入口
Ⅲ. 带符号整数比较后的条件转移,依据的是 CF 标志
Ⅳ. 大端方式下,int 型变量的最低有效字节存放在最低地址
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅰ、Ⅲ
  • C. 仅Ⅰ、Ⅱ、Ⅲ
  • D. 仅Ⅰ、Ⅱ、Ⅳ
展开解析

考点3.4 CISC 与 RISC3.3 过程调用3.1 条件码2.7 大端与小端

A正确的是 Ⅰ、Ⅱ。

  • Ⅰ 对其他指令的操作数都在寄存器里,这让指令格式规整、利于流水线。
  • Ⅱ 对返回地址就是 call 的下一条指令的地址,ret 时弹出给 PC。
  • Ⅲ 错带符号比较看 SF、OF、ZF(如 jl 看 SF ⊕ OF);CF 用于无符号比较(jb、ja)。
  • Ⅳ 错大端是最高有效字节放在最低地址;题中说的是小端。

干扰项从哪来:B 是把 Ⅲ 当成对的(把有符号和无符号的转移条件记混)、漏选 Ⅱ(以为返回地址保存在固定寄存器里);C 是把 Ⅲ 当成对的(把有符号和无符号的转移条件记混);D 是把 Ⅳ 当成对的(把大端、小端记反)

计组(5)CPU(含中断与异常)

辨析 10计组CPU(含中断与异常 · 原创
下列叙述中,正确的是( )。
Ⅰ. 单周期 CPU 的时钟周期取决于执行时间最长的那条指令
Ⅱ. 微程序控制器中,每条机器指令对应一条微指令
Ⅲ. 字段直接编码方式中,相互排斥的微命令应安排在同一个字段里
Ⅳ. 取指周期完成的操作与正在执行的是哪一条指令无关
  • A. 仅Ⅰ、Ⅲ
  • B. 仅Ⅰ、Ⅲ、Ⅳ
  • C. 仅Ⅱ、Ⅲ、Ⅳ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

考点4.1 单周期 CPU4.3 微程序控制器4.3 编码方式4.1 指令周期

B正确的是 Ⅰ、Ⅲ、Ⅳ。

  • Ⅰ 对所有指令都在一个周期内完成,周期必须容得下最慢的指令(通常是 load)。
  • Ⅱ 错一条机器指令对应一段微程序,由若干条微指令组成。
  • Ⅲ 对同一字段一次只能译出一个微命令,互斥的放在一起正好;可同时出现的必须分开。
  • Ⅳ 对取指令、PC 加 1 对所有指令都一样,是公共操作。

干扰项从哪来:A 是漏选 Ⅳ(以为取指阶段已经根据操作码做了不同的事);C 是把 Ⅱ 当成对的(把“微程序”和“微指令”混为一谈)、漏选 Ⅰ(以为取各类指令的平均时间);D 是把 Ⅱ 当成对的(把“微程序”和“微指令”混为一谈)

辨析 11计组CPU(含中断与异常 · 原创
下列叙述中,正确的是( )。
Ⅰ. 采用转发(旁路)技术后,所有数据冒险都不会再引起流水线阻塞
Ⅱ. 指令 Cache 与数据 Cache 分离,可以减少结构冒险
Ⅲ. 流水线的段数越多,每条指令从取指到完成所用的时间越短
Ⅳ. 采用延迟转移时,延迟槽中的指令无论是否转移都要执行
  • A. 仅Ⅱ
  • B. 仅Ⅳ
  • C. 仅Ⅱ、Ⅳ
  • D. 仅Ⅰ、Ⅱ、Ⅳ
展开解析

考点4.4 冒险的对策4.4 流水线指标4.4 控制冒险

C正确的是 Ⅱ、Ⅳ。

  • Ⅰ 错load 指令的数据在 MEM 段末才有,紧跟着使用它的指令仍要阻塞 1 拍(load-use,2024-19)。
  • Ⅱ 对取指和访存数据可以同时进行,不再争用同一个存储器(2014-16)。
  • Ⅲ 错段数增加能提高吞吐率,但每条指令仍要依次经过所有段,加上段间寄存器延迟,单条指令的时间不会缩短。
  • Ⅳ 对编译器在延迟槽里放一条“转不转都要做”的指令,填补等待的周期。

干扰项从哪来:A 是漏选 Ⅳ(以为延迟槽里的指令只在不转移时执行);B 是漏选 Ⅱ(把结构冒险和数据冒险的对策混淆);D 是把 Ⅰ 当成对的(以为转发万能)

辨析 12计组CPU(含中断与异常 · 原创
下列叙述中,正确的是( )。
Ⅰ. 中断响应时,关中断、保存断点(PC)由硬件完成(中断隐指令)
Ⅱ. 缺页异常处理完成后,返回到引起缺页的那条指令重新执行
Ⅲ. CPU 在每条指令执行结束时检查外部中断请求,DMA 请求也只能在指令执行结束时响应
Ⅳ. 通过修改中断屏蔽字,可以改变中断的响应优先级
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅰ、Ⅲ
  • C. 仅Ⅰ、Ⅱ、Ⅲ
  • D. 仅Ⅰ、Ⅱ、Ⅳ
展开解析

考点5.4 硬件与软件的分工5.3 异常的分类9.4 DMA 与中断5.5 屏蔽字

A正确的是 Ⅰ、Ⅱ。

  • Ⅰ 对这几步必须一气呵成,不能靠可能被打断的软件来做。
  • Ⅱ 对缺页属于故障,指令没执行完,必须重新执行;自陷才返回下一条。
  • Ⅲ 错DMA 请求在每个机器周期(存取周期)结束时就可以响应,不必等整条指令结束。
  • Ⅳ 错屏蔽字改变的是处理优先级(谁能打断谁);响应优先级由硬件排队电路决定,不能改。

干扰项从哪来:B 是把 Ⅲ 当成对的(把 DMA 请求和中断请求的响应时机混为一谈)、漏选 Ⅱ(把故障与自陷的返回地址记反);C 是把 Ⅲ 当成对的(把 DMA 请求和中断请求的响应时机混为一谈);D 是把 Ⅳ 当成对的(把响应优先级和处理优先级混淆)

计组(6)总线

辨析 13计组总线 · 原创
下列叙述中,正确的是( )。
Ⅰ. 总线带宽是指总线单位时间内能传输的最大数据量
Ⅱ. 地址线和数据线分时复用,可以提高总线的数据传输率
Ⅲ. 异步总线用应答(握手)方式通信,不需要统一的时钟信号
Ⅳ. 突发传送方式下,一个总线事务只传送一次地址,就可以连续传送多个数据
  • A. 仅Ⅰ、Ⅲ
  • B. 仅Ⅰ、Ⅳ
  • C. 仅Ⅲ、Ⅳ
  • D. 仅Ⅰ、Ⅲ、Ⅳ
展开解析

考点9.1 总线带宽9.1 总线复用9.2 异步定时9.2 总线事务

D正确的是 Ⅰ、Ⅲ、Ⅳ。

  • Ⅰ 对常用 B/s 表示:总线宽度 × 每秒传送次数。
  • Ⅱ 错复用只是省引脚,地址和数据要分时传送,传输率不升反降(2018-21)。
  • Ⅲ 对一次传送的快慢由双方的应答决定,适合速度差别大的设备。
  • Ⅳ 对后续数据的地址由存储器自动递增,省掉了每次发地址的时间。

干扰项从哪来:A 是漏选 Ⅳ(以为每传一个数据都要先发地址);B 是漏选 Ⅲ(把同步、异步的特点记反);C 是漏选 Ⅰ(把带宽误当成总线的线数)

辨析 14计组总线 · 原创
下列叙述中,正确的是( )。
Ⅰ. 链式查询方式对电路故障敏感,离总线控制器越远的设备优先级越低
Ⅱ. 独立请求方式的响应速度最快,但所需的控制线最多
Ⅲ. 同步总线中所有设备使用同一个时钟,不适合连接速度差别很大的设备
Ⅳ. 总线宽度是指总线上地址线的条数
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅰ、Ⅱ、Ⅲ
  • C. 仅Ⅰ、Ⅱ、Ⅳ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

考点9.2 总线仲裁9.2 同步与异步9.1 总线的组成

B正确的是 Ⅰ、Ⅱ、Ⅲ。

  • Ⅰ 对授权信号沿设备串行传递,中间一个设备坏了,后面的都得不到总线。
  • Ⅱ 对每个设备一对请求 / 允许线,控制器直接排队选择,代价是 2n + 1 根线。
  • Ⅲ 对时钟周期要迁就最慢的设备,快设备的速度发挥不出来。
  • Ⅳ 错总线宽度通常指数据线的条数(一次能并行传送的位数);地址线条数决定寻址范围。

干扰项从哪来:A 是漏选 Ⅲ(以为同步总线一定比异步总线好);C 是把 Ⅳ 当成对的(把数据总线和地址总线的作用记混)、漏选 Ⅲ(以为同步总线一定比异步总线好);D 是把 Ⅳ 当成对的(把数据总线和地址总线的作用记混)

计组(7)输入 / 输出系统

辨析 15计组输入 / 输出系统 · 原创
下列叙述中,正确的是( )。
Ⅰ. 统一编址方式下,CPU 用访存指令访问 I/O 端口
Ⅱ. 独立编址方式下,I/O 端口地址可以与主存单元地址相同
Ⅲ. I/O 接口中的状态端口和控制端口不能共用同一个地址
Ⅳ. 程序查询方式下,CPU 和外设可以并行工作
  • A. 仅Ⅰ
  • B. 仅Ⅱ
  • C. 仅Ⅰ、Ⅱ
  • D. 仅Ⅰ、Ⅱ、Ⅲ
展开解析

考点9.3 端口编址9.3 I/O 接口9.4 三种 I/O 方式

C正确的是 Ⅰ、Ⅱ。

  • Ⅰ 对I/O 端口占用一部分主存地址空间,没有专门的 I/O 指令(2014-21)。
  • Ⅱ 对两个地址空间相互独立,靠 IN / OUT 这类专门指令区分访问的是哪一边。
  • Ⅲ 错状态端口只读、控制端口只写,读写方向不同,常共用一个端口地址。
  • Ⅳ 错CPU 要反复查询状态、等外设就绪,期间不能做别的事,两者是串行的。

干扰项从哪来:A 是漏选 Ⅱ(以为地址数值相同就会冲突);B 是漏选 Ⅰ(把统一编址和独立编址的特点记反);D 是把 Ⅲ 当成对的(以为每个寄存器都必须有自己的地址)

辨析 16计组输入 / 输出系统 · 原创
下列叙述中,正确的是( )。
Ⅰ. 程序中断方式下,每传送一个数据(字或字节)就要中断 CPU 一次
Ⅱ. DMA 方式下,数据直接在主存和外设之间传送,传送过程中不需要 CPU 执行程序
Ⅲ. DMA 传送结束时,DMA 控制器向 CPU 发出中断请求,由 CPU 做后处理
Ⅳ. 采用周期挪用方式时,DMA 与 CPU 同时请求访存,CPU 优先
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅰ、Ⅲ
  • C. 仅Ⅱ、Ⅲ
  • D. 仅Ⅰ、Ⅱ、Ⅲ
展开解析

考点9.4 中断方式9.4 DMA9.4 访存冲突

D正确的是 Ⅰ、Ⅱ、Ⅲ。

  • Ⅰ 对中断服务程序负责搬运这一个数据,所以高速设备用中断方式开销太大。
  • Ⅱ 对CPU 只在预处理和后处理时介入,传送由 DMA 控制器完成。
  • Ⅲ 对后处理包括校验、决定是否继续传送等,靠中断通知 CPU。
  • Ⅳ 错I/O 设备的数据不及时取走会丢失,所以 DMA 优先,CPU 让出(挪用)一个存取周期。

干扰项从哪来:A 是漏选 Ⅲ(以为 DMA 方式完全不用中断);B 是漏选 Ⅱ(以为 DMA 传送时 CPU 要执行搬运指令);C 是漏选 Ⅰ(把中断方式和 DMA 方式的中断频率混淆)

操作系统(1)概述与运行环境

辨析 17操作系统概述与运行环境 · 原创
下列叙述中,正确的是( )。
Ⅰ. 并发是指两个或多个事件在同一时间间隔内发生
Ⅱ. 访管(陷入)指令是特权指令,只能在内核态执行
Ⅲ. 用户程序通过系统调用请求操作系统服务,系统调用的处理在内核态进行
Ⅳ. 分时系统追求的首要目标是提高系统吞吐量
  • A. 仅Ⅰ、Ⅲ
  • B. 仅Ⅰ、Ⅳ
  • C. 仅Ⅰ、Ⅱ、Ⅲ
  • D. 仅Ⅰ、Ⅲ、Ⅳ
展开解析

考点1.5 四个特征5.1 特权指令5.6 系统调用1.6 操作系统的分类

A正确的是 Ⅰ、Ⅲ。

  • Ⅰ 对单 CPU 上宏观同时、微观交替;同一时刻发生的是并行。
  • Ⅱ 错访管指令正是在用户态执行、用来主动进入内核的,它是非特权指令。
  • Ⅲ 对用户态发出陷入指令,CPU 切到内核态执行系统调用处理程序。
  • Ⅳ 错分时系统首先追求及时响应(交互性);提高吞吐量是批处理系统的目标。

干扰项从哪来:B 是把 Ⅳ 当成对的(把分时系统和批处理系统的目标记混)、漏选 Ⅲ(以为系统调用在用户态完成);C 是把 Ⅱ 当成对的(以为“进内核的指令”就是特权指令);D 是把 Ⅳ 当成对的(把分时系统和批处理系统的目标记混)

辨析 18操作系统概述与运行环境 · 原创
下列叙述中,正确的是( )。
Ⅰ. 微内核结构中,文件系统等服务大多运行在用户态,频繁的模式切换会降低性能
Ⅱ. 开机引导时,BIOS 先从主引导记录(MBR)读入自检程序,再进行硬件自检
Ⅲ. 第一类虚拟机监控程序(VMM)直接运行在硬件上
Ⅳ. 时钟中断处理程序在内核态执行
  • A. 仅Ⅰ、Ⅲ
  • B. 仅Ⅰ、Ⅲ、Ⅳ
  • C. 仅Ⅱ、Ⅲ、Ⅳ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

考点1.7 内核结构1.7 BIOS 与 POST1.7 虚拟机5.2 中断和异常

B正确的是 Ⅰ、Ⅲ、Ⅳ。

  • Ⅰ 对服务之间靠消息经内核转发,每次都要在用户态和内核态之间切换(2023-23)。
  • Ⅱ 错自检(POST)程序固化在 ROM 的 BIOS 里,先自检,再读 MBR 中的引导程序。
  • Ⅲ 对也叫裸金属型;第二类运行在宿主操作系统之上。
  • Ⅳ 对中断处理程序都是内核的一部分,时钟中断还承担时间片计时等任务。

干扰项从哪来:A 是漏选 Ⅳ(以为只有系统调用才进内核);C 是把 Ⅱ 当成对的(把 POST 和读 MBR 的先后顺序颠倒)、漏选 Ⅰ(以为微内核又小又快);D 是把 Ⅱ 当成对的(把 POST 和读 MBR 的先后顺序颠倒)

操作系统(2)进程管理

辨析 19操作系统进程管理 · 原创
下列叙述中,正确的是( )。
Ⅰ. 引入线程后,进程是资源分配的基本单位,线程是处理机调度的基本单位
Ⅱ. 同一进程的各线程共享进程的地址空间,但各有自己的栈和寄存器上下文
Ⅲ. 进程可以由阻塞态直接转换为运行态
Ⅳ. 用户级线程之间的切换需要进入内核完成
  • A. 仅Ⅰ
  • B. 仅Ⅱ
  • C. 仅Ⅰ、Ⅱ
  • D. 仅Ⅰ、Ⅱ、Ⅲ
展开解析

考点6.4 线程6.2 状态转换6.4 用户级线程

C正确的是 Ⅰ、Ⅱ。

  • Ⅰ 对线程不拥有系统资源,共享所属进程的资源。
  • Ⅱ 对共享代码段、数据段、打开的文件;私有的是栈、PC 和寄存器(2011-25)。
  • Ⅲ 错阻塞的进程被唤醒后先进入就绪队列,等调度程序选中才运行。
  • Ⅳ 错用户级线程由用户态的线程库管理,切换不经过内核,这正是它快的原因。

干扰项从哪来:A 是漏选 Ⅱ(以为线程连栈也共享);B 是漏选 Ⅰ(以为线程也是资源分配单位);D 是把 Ⅲ 当成对的(以为被唤醒就立刻运行)

辨析 20操作系统进程管理 · 原创
下列叙述中,正确的是( )。
Ⅰ. 在处理中断的过程中,不能进行进程调度与切换
Ⅱ. 进程在普通临界区中时,不能被调度
Ⅲ. 时间片轮转调度中,时间片过大会使它退化为先来先服务
Ⅳ. 短作业优先算法的平均等待时间最短,而且不会使任何作业饥饿
  • A. 仅Ⅰ
  • B. 仅Ⅲ
  • C. 仅Ⅰ、Ⅱ
  • D. 仅Ⅰ、Ⅲ
展开解析

考点6.6 调度的时机6.7 时间片的大小6.7 各算法的考点

D正确的是 Ⅰ、Ⅲ。

  • Ⅰ 对中断处理过程复杂,与硬件密切相关,处理期间不能调度(要等处理完再调度)。
  • Ⅱ 错普通临界区(如访问打印机)里可以被调度下 CPU,其他进程想进同一临界区会被互斥机制挡住;不能调度的是内核程序的临界区。
  • Ⅲ 对每个进程都能在一个时间片内运行完,轮转就失去了意义。
  • Ⅳ 错平均等待时间最短是对的,但长作业可能一直被后来的短作业插队,产生饥饿。

干扰项从哪来:A 是漏选 Ⅲ(以为时间片越大响应越快);B 是漏选 Ⅰ(以为任何时候都可以调度);C 是把 Ⅱ 当成对的(把“普通临界区”和“内核临界区”混为一谈)、漏选 Ⅲ(以为时间片越大响应越快)

辨析 21操作系统进程管理 · 原创
下列叙述中,正确的是( )。
Ⅰ. Peterson 算法满足空闲让进、忙则等待和有限等待,但不满足让权等待
Ⅱ. 记录型信号量的值为 −2 时,表示有 2 个进程在该信号量的等待队列中
Ⅲ. 用 TSL(TestAndSet)指令实现互斥时,进不了临界区的进程不会忙等
Ⅳ. 同一时刻只允许一个进程在管程内执行
  • A. 仅Ⅰ、Ⅱ、Ⅳ
  • B. 仅Ⅰ、Ⅲ、Ⅳ
  • C. 仅Ⅱ、Ⅲ、Ⅳ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

考点7.2 Peterson 算法7.3 记录型信号量7.2 硬件方法7.5 管程

A正确的是 Ⅰ、Ⅱ、Ⅳ。

  • Ⅰ 对进不去时在 while 里空转(忙等),不释放处理机。
  • Ⅱ 对value < 0 时,其绝对值就是等待的进程数。
  • Ⅲ 错TSL 只保证“测试并设置”的原子性,进不去的进程仍在循环里反复测试,属于忙等,不满足让权等待。
  • Ⅳ 对管程的互斥由编译器 / 系统保证,程序员只需用条件变量处理同步。

干扰项从哪来:B 是把 Ⅲ 当成对的(以为硬件指令法就不会忙等)、漏选 Ⅱ(以为负值没有意义);C 是把 Ⅲ 当成对的(以为硬件指令法就不会忙等)、漏选 Ⅰ(以为 Peterson 满足全部四条准则);D 是把 Ⅲ 当成对的(以为硬件指令法就不会忙等)

辨析 22操作系统进程管理 · 原创
下列叙述中,正确的是( )。
Ⅰ. 系统处于不安全状态时,一定会发生死锁
Ⅱ. 规定所有进程按资源编号递增的顺序申请资源,可以破坏循环等待条件
Ⅲ. 资源分配图中出现环路,系统就一定发生了死锁
Ⅳ. 银行家算法中,系统处于安全状态时安全序列可能不唯一
  • A. 仅Ⅱ
  • B. 仅Ⅱ、Ⅳ
  • C. 仅Ⅰ、Ⅱ、Ⅳ
  • D. 仅Ⅱ、Ⅲ、Ⅳ
展开解析

考点7.7 银行家算法7.7 死锁预防7.6 资源分配图

B正确的是 Ⅱ、Ⅳ。

  • Ⅰ 错不安全状态只是“可能”死锁,进程不一定真会申请到最大需求。
  • Ⅱ 对按序申请时不可能形成环路(顺序资源分配法)。
  • Ⅲ 错只有每类资源都只有一个时,有环才等价于死锁;多实例资源有环不一定死锁。
  • Ⅳ 对只要存在一个安全序列就安全,往往能找到多个。

干扰项从哪来:A 是漏选 Ⅳ(以为安全序列是唯一的);C 是把 Ⅰ 当成对的(把“不安全”等同于“死锁”);D 是把 Ⅲ 当成对的(把单实例的结论推广到多实例)

操作系统(3)内存管理

辨析 23操作系统内存管理 · 原创
下列叙述中,正确的是( )。
Ⅰ. 动态分区分配会产生外部碎片,分页存储管理只可能产生内部碎片
Ⅱ. 分段存储管理的逻辑地址空间是二维的
Ⅲ. 多级页表可以减少页表占用的连续内存空间,也能减少地址变换时的访存次数
Ⅳ. 实现紧凑(拼接)需要动态重定位的支持
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅰ、Ⅳ
  • C. 仅Ⅰ、Ⅱ、Ⅳ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

考点8.4 碎片8.6 分页与分段8.5 多级页表8.4 紧凑

C正确的是 Ⅰ、Ⅱ、Ⅳ。

  • Ⅰ 对分页时每个进程只有最后一页可能没用满(内部碎片),页框之间不会留下无法使用的小块。
  • Ⅱ 对逻辑地址要同时给出段号和段内地址。
  • Ⅲ 错多级页表省空间,但每多一级就多一次访存(没有 TLB 时)。
  • Ⅳ 对程序被移动后,只要改重定位寄存器的值就能继续运行。

干扰项从哪来:A 是漏选 Ⅳ(以为静态重定位也能支持移动);B 是漏选 Ⅱ(把分页和分段的地址维数记反);D 是把 Ⅲ 当成对的(以为多级页表“两样都好”)

辨析 24操作系统内存管理 · 原创
下列叙述中,正确的是( )。
Ⅰ. FIFO 页面置换算法可能出现 Belady 异常,LRU 算法不会
Ⅱ. 系统发生抖动时,增大磁盘对换区的容量可以有效缓解
Ⅲ. 采用固定分配策略时,不能采用全局置换
Ⅳ. 一条指令执行期间可能发生多次缺页
  • A. 仅Ⅰ、Ⅲ
  • B. 仅Ⅰ、Ⅳ
  • C. 仅Ⅲ、Ⅳ
  • D. 仅Ⅰ、Ⅲ、Ⅳ
展开解析

考点8.7 置换算法8.9 抖动8.7 页框分配8.7 缺页中断

D正确的是 Ⅰ、Ⅲ、Ⅳ。

  • Ⅰ 对LRU、OPT 是栈算法,页框多时内存里的页集合包含页框少时的集合(2014-30)。
  • Ⅱ 错抖动是因为每个进程分到的页框少于工作集,对换区大小不改变缺页次数(2011-29)。
  • Ⅲ 对固定分配下进程的页框数不变,缺页时只能换自己的页;全局置换会改变页框数。
  • Ⅳ 对指令本身、源操作数、目的操作数都可能跨页且不在内存。

干扰项从哪来:A 是漏选 Ⅳ(以为一条指令最多缺一次页);B 是漏选 Ⅲ(把分配策略和置换范围看成可以任意搭配);C 是漏选 Ⅰ(以为所有置换算法都可能出现 Belady 异常)

操作系统(4)文件管理

辨析 25操作系统文件管理 · 原创
下列叙述中,正确的是( )。
Ⅰ. 打开文件时,操作系统把文件的 FCB(索引结点)读入内存,之后读写该文件不必再按路径名查目录
Ⅱ. 文件描述符是进程打开文件表中表项的索引
Ⅲ. 隐式链接分配支持对文件的随机访问
Ⅳ. 连续分配的文件,访问第 i 块时不需要先读前面的块
  • A. 仅Ⅰ、Ⅱ、Ⅳ
  • B. 仅Ⅰ、Ⅲ、Ⅳ
  • C. 仅Ⅱ、Ⅲ、Ⅳ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

考点10.4 打开文件10.4 打开文件表10.6 链接分配10.6 连续分配

A正确的是 Ⅰ、Ⅱ、Ⅳ。

  • Ⅰ 对open 返回文件描述符,以后的 read / write 用它直接找到内存中的打开文件表项。
  • Ⅱ 对每个进程有自己的打开文件表,fd 就是它的下标(2020-23 的教材两级表口径)。
  • Ⅲ 错要访问第 i 块,必须从第一块开始顺着指针读,只能顺序访问。
  • Ⅳ 对起始块号 + i 就是物理块号,可以直接定位。

干扰项从哪来:B 是把 Ⅲ 当成对的(把隐式链接和显式链接(FAT)的特点混淆)、漏选 Ⅱ(以为文件描述符是 inode 号);C 是把 Ⅲ 当成对的(把隐式链接和显式链接(FAT)的特点混淆)、漏选 Ⅰ(以为每次读写都要重新按路径查找);D 是把 Ⅲ 当成对的(把隐式链接和显式链接(FAT)的特点混淆)

辨析 26操作系统文件管理 · 原创
下列叙述中,正确的是( )。
Ⅰ. 硬链接与原文件共享同一个索引结点,删除原文件名后仍能通过硬链接访问文件
Ⅱ. 软链接(符号链接)文件中存放的是被链接文件的路径名
Ⅲ. 硬链接可以跨越不同的文件系统
Ⅳ. 位示图的大小与磁盘上已经被占用的块数有关
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅰ、Ⅲ
  • C. 仅Ⅰ、Ⅱ、Ⅲ
  • D. 仅Ⅰ、Ⅱ、Ⅳ
展开解析

考点10.5 硬链接与软链接10.7 位示图

A正确的是 Ⅰ、Ⅱ。

  • Ⅰ 对删除只是让链接计数减 1,计数不为 0 时文件不会被真正删除。
  • Ⅱ 对访问软链接时要按这个路径再查一次;原文件删除后软链接就失效了。
  • Ⅲ 错硬链接靠 inode 号连接,inode 号只在本文件系统内有意义;跨文件系统只能用软链接。
  • Ⅳ 错位示图为每一个盘块设 1 位,大小只取决于盘块总数。

干扰项从哪来:B 是把 Ⅲ 当成对的(把硬链接和软链接的限制记反)、漏选 Ⅱ(以为软链接也指向同一个 inode);C 是把 Ⅲ 当成对的(把硬链接和软链接的限制记反);D 是把 Ⅳ 当成对的(以为只为已用块或空闲块记位)

操作系统(5)输入 / 输出管理

辨析 27操作系统输入 / 输出管理 · 原创
下列叙述中,正确的是( )。
Ⅰ. 设备独立性是指用户程序使用逻辑设备名,由系统完成逻辑设备到物理设备的映射
Ⅱ. 设备驱动程序与具体设备的硬件特性密切相关
Ⅲ. 逻辑设备名到物理设备名的映射由设备驱动程序完成
Ⅳ. 采用双缓冲时,CPU 对一个缓冲区的处理与向另一个缓冲区输入数据可以并行
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅰ、Ⅳ
  • C. 仅Ⅰ、Ⅱ、Ⅳ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

考点9.5 设备独立性9.5 I/O 软件层次9.8 逻辑设备表9.7 双缓冲

C正确的是 Ⅰ、Ⅱ、Ⅳ。

  • Ⅰ 对程序不依赖具体设备,换设备不用改程序。
  • Ⅱ 对驱动程序要直接操作设备控制器的寄存器,一般由设备厂商提供。
  • Ⅲ 错映射由设备独立性软件完成(查逻辑设备表);驱动程序只管具体设备的操作。
  • Ⅳ 对两个缓冲区交替使用,这是双缓冲比单缓冲快的原因。

干扰项从哪来:A 是漏选 Ⅳ(以为双缓冲只是容量加倍);B 是漏选 Ⅱ(以为驱动程序与设备无关);D 是把 Ⅲ 当成对的(把设备独立性软件和驱动程序的职责记混)

辨析 28操作系统输入 / 输出管理 · 原创
下列叙述中,正确的是( )。
Ⅰ. SSTF 磁盘调度算法可能使某些请求长期得不到服务
Ⅱ. C-SCAN 算法在磁头返回时也为沿途的请求服务
Ⅲ. 磁盘的平均旋转延迟是旋转一周所需时间的一半
Ⅳ. 对固态硬盘进行磁盘调度,可以显著减少寻道时间
  • A. 仅Ⅰ
  • B. 仅Ⅲ
  • C. 仅Ⅰ、Ⅱ
  • D. 仅Ⅰ、Ⅲ
展开解析

考点10.2 磁盘调度10.2 SCAN 与 C-SCAN10.1 磁盘访问时间10.1 SSD

D正确的是 Ⅰ、Ⅲ。

  • Ⅰ 对磁头总在附近来回服务新到的请求,远处的请求可能“饥饿”。
  • Ⅱ 错C-SCAN 返回时直接回到起点、不服务;返回时也服务的是 SCAN。
  • Ⅲ 对扇区转到磁头下的等待时间在 0 ~ 一圈之间,平均半圈。
  • Ⅳ 错SSD 没有机械寻道,调度意义不大,一般 FCFS 即可(2010-45 第 3 问)。

干扰项从哪来:A 是漏选 Ⅲ(把平均旋转延迟算成转一整圈);B 是漏选 Ⅰ(以为只有 FCFS 才公平);C 是把 Ⅱ 当成对的(把 SCAN 和 C-SCAN 的回程记反)、漏选 Ⅲ(把平均旋转延迟算成转一整圈)

13.3计算题满分专练:固定步骤、单位口径、变式与常见错法

计组和操作系统的计算题,“思路对、结果错”是最常见的丢分方式。这一节把 19 类计算题各压成一个固定步骤,写明单位口径(K、M、G 是 210 还是 103,B 还是 bit,时钟周期还是秒),列出最常见的错法,再给两道变式。每道变式的四个选项里,三个错误答案都是按某种常见错法真实算出来的,解析会写清楚是哪一种——做错了就对照“错法”找到自己的问题。所有数字由 verify/08/p13/calc_cards.py 计算并断言(调度、银行家、置换、磁盘调度用模拟器,与正文 6.7、10.2 的例题核对一致)。

量口径
存储容量K = 210、M = 220、G = 230(主存、Cache、页、盘块、文件大小)
速率、频率K = 103、M = 106、G = 109(主频、带宽、数据传输率;题目另有说明时按题目)
B 与 bB = 字节、b = 位;总线宽度 32 位 = 4B;位示图按“位”计
时间1s = 103ms = 106μs = 109ns = 1012ps;主频 f 的时钟周期 = 1/f

① 整数的机器数与 C 语言类型转换

固定步骤
  1. 写出原值的 n 位补码(负数 = 2ⁿ − |x|)。
  2. 判断转换类型:同宽有↔无符号位串不变、只换解释;短→长按原类型扩展(有符号补符号位、无符号补 0);长→短截断保留低位。
  3. 按目标类型重新解释位串,写出十进制值。

单位口径:位数看类型:char 8、short 16、int 32、long long 64;十六进制一位 = 4 位二进制。

常见错法:短→长时按目标类型扩展(应按源类型:unsigned short → int 补 0);以为有符号转无符号会“取绝对值”;十六进制写错位数(FF9C 和 FFFFFF9C)。

计算 1计组类型转换 · 原创
C 语言程序段 short x = -300; unsigned short y = x; int z = y;(short 为 16 位,int 为 32 位)执行后,z 的值是( )。
  • A. −300
  • B. 65236
  • C. 4294966996
  • D. 300
展开解析

考点2.2 类型转换2.2 跟我判

B两步:−300 → unsigned short 位串不变;unsigned short → int 是“无符号短→长”,补 0。

  1. −300 的 16 位补码:65536 − 300 = 65236 = FED4H。
  2. y 是 unsigned short,位串不变,值为 65236。
  3. z = y:无符号数扩展为 32 位,高位补 0 → 0000 FED4H = 65236(gcc 实测)。
  • A 错−300:按 z 的类型(有符号)做了符号扩展,得到 FFFF FED4H;但扩展方式由源类型 unsigned short 决定,应补 0。
  • B 对65236。
  • C 错4294966996 = 2³² − 300:把 −300 直接当成 32 位无符号数,忽略了中间先截成 16 位的 unsigned short。
  • D 错300:以为有符号转无符号是“去掉负号”,实际是位串不变、按无符号重新解释。
计算 2计组符号扩展 · 原创
C 语言中 char c = 0x9C; int i = c;(char 为 8 位有符号类型,int 为 32 位)。执行后 i 的机器数(十六进制)是( )。
  • A. 0000 009CH
  • B. 8000 009CH
  • C. FFFF FF9CH
  • D. 0000 FF9CH
展开解析

考点2.2 符号扩展

C有符号短→长:用符号位填满高位。

  1. 9CH = 1001 1100B,最高位 1 → c 是负数(−100)。
  2. 扩展为 32 位,高 24 位全填 1 → FFFF FF9CH(仍是 −100,gcc 实测)。
  • A 错0000 009CH 是补 0 的零扩展(unsigned char 才这样),值变成了 156。
  • B 错8000 009CH:把符号位挪到最高位、其余补 0,这是原码的扩展思路,补码不能这样做。
  • C 对FFFF FF9CH = −100。
  • D 错0000 FF9CH:只扩展到了 16 位就停了,int 是 32 位。

② IEEE 754 编码、解码

固定步骤
  1. 编码:十进制 → 二进制 → 规格化成 1.M × 2e。
  2. S(正 0 负 1)、E = e + 127(双精度 + 1023)写成 8 位、M 写小数点后的部分(不含隐藏的 1),补够 23 位。
  3. 按 S | E | M 拼接,四位一组写十六进制。解码反过来:先看 E 是否全 0 / 全 1。

单位口径:单精度 1 + 8 + 23、偏置 127;双精度 1 + 11 + 52、偏置 1023;阶码全 0 时指数按 −126、无隐藏位。

常见错法:偏置写成 128(那是教材一般移码);把隐藏的 1 写进尾数字段;符号位、阶码的十六进制拼接错位。

计算 3计组编码 · 原创
float 型变量 x = −13.625,其 IEEE 754 单精度机器数为( )。
  • A. C1DA 0000H
  • B. C16D 0000H
  • C. 415A 0000H
  • D. C15A 0000H
展开解析

考点2.6 编码四步

D1101.101B = 1.101101B × 2³。

  1. 13.625 = 1101.101B = 1.101101B × 2³。
  2. S = 1;E = 3 + 127 = 130 = 1000 0010B;M = 1011 0100 0…0(23 位)。
  3. 1 1000 0010 1011 0100 0…0 → 1100 0001 0101 1010 0…0 = C15A 0000H(Python struct 核对)。
  • A 错C1DA 0000H:阶码用了 3 + 128 = 131,偏置记成了 128。
  • B 错C16D 0000H:把隐藏的 1 也写进了尾数字段(M 写成 1101101…)。
  • C 错415A 0000H:符号位写成 0,这是 +13.625。
  • D 对C15A 0000H。
计算 4计组解码 · 原创
float 型变量 x 的机器数为 C2B4 0000H,执行 int i = (int)x; 后 i 的值为( )。
  • A. −90
  • B. −45
  • C. −26
  • D. 90
展开解析

考点2.6 解码

A拆 S、E、M,指数 = E − 127,尾数前补隐藏的 1。

  1. C2B4 0000H = 1 | 1000 0101 | 011 0100 0…0。
  2. S = 1;E = 133,指数 = 133 − 127 = 6;1.M = 1.0110100B = 1.40625。
  3. x = −1.40625 × 2⁶ = −90.0,转 int 得 −90。
  • A 对−90。
  • B 错−45:指数按 133 − 128 = 5 算,偏置用错。
  • C 错−26 = −0.40625 × 64:忘了补隐藏的 1。
  • D 错90:机器数最高位 C 的二进制是 1100,符号位 1 表示负数,看漏了。

③ 加减运算与四个标志位

固定步骤
  1. 减法先变加法:x − y = x + ȳ + 1(Sub = 1)。
  2. 在 n 位加法器上做二进制加法,记下最高位进位 Cn 和次高位进位 Cn−1。
  3. OF = Cn ⊕ Cn−1;SF = 结果最高位;ZF = 结果是否全 0;CF = Cn ⊕ Sub。

单位口径:标志只看 n 位结果和进位,不看“数学上应有的结果”;CF 管无符号,OF 管有符号。

常见错法:减法时直接把加法器的进位当 CF(应异或 Sub);用真值的符号当 SF;把 OF 当成某一位的进位。

计算 5计组加法 · 原创
8 位加法器计算 x + y,x = 9AH,y = B5H。运算后标志 OF、SF、ZF、CF 的值依次为( )。
  • A. 1, 0, 0, 1
  • B. 0, 0, 0, 1
  • C. 1, 1, 0, 1
  • D. 1, 0, 0, 0
展开解析

考点2.4 四个标志位

A两个负数(按有符号看)相加得到正数 → 溢出。

  1. 9AH + B5H = 14FH,8 位结果 4FH,C₈ = 1。
  2. 低 7 位:1AH + 35H = 4FH < 80H → C₇ = 0。
  3. OF = 1 ⊕ 0 = 1;SF = 0(4FH 最高位);ZF = 0;CF = 1 ⊕ 0 = 1(程序模拟核对)。
  • A 对1, 0, 0, 1。
  • B 错OF = 0:把 OF 当成了次高位进位 C₇;OF 要 C₈ ⊕ C₇。
  • C 错SF = 1:按“两个负数相加应为负”来写 SF;SF 只看结果 4FH 的最高位,是 0。
  • D 错CF = 0:把 CF 取成了次高位进位;无符号 154 + 181 = 335 > 255,确有进位。
计算 6计组减法 · 原创
8 位加法器计算 x − y,x = 50H,y = C0H。运算后标志 OF、SF、ZF、CF 的值依次为( )。
  • A. 1, 1, 0, 0
  • B. 0, 1, 0, 1
  • C. 1, 0, 0, 1
  • D. 1, 1, 0, 1
展开解析

考点2.4 减法与 CF

Dx − y = x + ȳ + 1:50H + 3FH + 1。

  1. ȳ = 3FH,50H + 3FH + 1 = 90H,C₈ = 0。
  2. 低 7 位 50H + 3FH + 1 = 90H ≥ 80H → C₇ = 1。
  3. OF = 0 ⊕ 1 = 1(有符号 80 − (−64) = 144 > 127);SF = 1;ZF = 0;CF = C₈ ⊕ Sub = 0 ⊕ 1 = 1(无符号 80 < 192,有借位)。
  • A 错CF = 0:直接拿加法器的进位输出 C₈ = 0 当 CF,忘了减法时 CF = C₈ ⊕ 1。
  • B 错OF = 0:以为“正数减负数不会溢出”;减法要看成同号相加,80 + 64 超出 127。
  • C 错SF = 0:按真值 +144 的符号写 SF;结果 90H 的最高位是 1。
  • D 对1, 1, 0, 1。

④ CPI、MIPS 与执行时间

固定步骤
  1. 平均 CPI = Σ(各类指令占比 × 该类 CPI)——一定要加权。
  2. CPU 时间 = 指令条数 × CPI × 时钟周期 = 指令条数 × CPI ÷ 主频。
  3. MIPS = 主频 ÷ (CPI × 10⁶);比较两台机器用执行时间之比。

单位口径:主频的 G、M 是 10⁹、10⁶;时钟周期 = 1 ÷ 主频(2GHz → 0.5ns)。

常见错法:CPI 求简单平均;把主频当成时钟周期;只比主频或只比 CPI。

计算 7计组执行时间 · 原创
某 CPU 主频 2GHz。某程序共执行 4.2 × 10⁸ 条指令,其中运算类占 50%(CPI = 1)、访存类占 30%(CPI = 4)、转移类占 20%(CPI = 2)。该程序的 CPU 执行时间为( )。
  • A. 0.441s
  • B. 0.49s
  • C. 0.21s
  • D. 1.764s
展开解析

考点1.8 性能指标

A先加权求平均 CPI,再乘指令数、除主频。

  1. CPI = 0.5 × 1 + 0.3 × 4 + 0.2 × 2 = 2.1。
  2. 时间 = 4.2 × 10⁸ × 2.1 ÷ (2 × 10⁹) = 0.441s。
  • A 对0.441s。
  • B 错0.49s:CPI 取 (1 + 4 + 2) ÷ 3 ≈ 2.33 的简单平均,没按占比加权。
  • C 错0.21s:把 CPI 当成 1,只算了指令数 ÷ 主频。
  • D 错1.764s:把 2GHz 当成时钟周期 2ns 来乘;时钟周期应为 0.5ns。
计算 8计组机器比较 · 原创
同一程序在 M1、M2 上执行的指令条数相同。M1 主频 1.5GHz、CPI = 1.5;M2 主频 2GHz、CPI = 2.5。下列结论正确的是( )。
  • A. M2 比 M1 快,速度是 M1 的 1.33 倍
  • B. M1 比 M2 快,速度是 M2 的 1.25 倍
  • C. M1 比 M2 快,速度是 M2 的 1.67 倍
  • D. M2 比 M1 快,速度是 M1 的 1.25 倍
展开解析

考点1.8 性能指标

B比每条指令的平均时间 = CPI ÷ 主频。

  1. M1:1.5 ÷ 1.5GHz = 1ns / 条;M2:2.5 ÷ 2GHz = 1.25ns / 条。
  2. M1 更快,速度之比 = 1.25 ÷ 1 = 1.25。
  • A 错1.33 = 2 ÷ 1.5:只比了主频,忽略了 M2 的 CPI 更大。
  • B 对M1 是 M2 的 1.25 倍。
  • C 错1.67 = 2.5 ÷ 1.5:只比了 CPI,忽略了主频不同。
  • D 错数值 1.25 对了,但结论反了:时间短的是 M1。

⑤ 指令格式与扩展操作码

固定步骤
  1. 从最长地址码的指令开始:本级可用编码数 = 2本级操作码位数(第一级)或“上一级剩下的编码数 × 2扩展位数”。
  2. 本级用掉若干条,剩下的编码留作扩展前缀。
  3. 逐级做到最后一级;“最多”就是不再预留。

单位口径:字段位数:寄存器 2ⁿ 个 → n 位;立即数 n 位补码范围 −2ⁿ⁻¹ ~ 2ⁿ⁻¹ − 1。

常见错法:用剩余编码时又减了上一级的条数;只算到编码数没乘 2^扩展位数;立即数范围按无符号或按原码算。

计算 9计组扩展操作码 · 原创
某指令字长 16 位,每个地址码 4 位,采用扩展操作码。已有三地址指令 13 条、二地址指令 30 条,单地址指令最多可以有( )。
  • A. 18 条
  • B. 288 条
  • C. 80 条
  • D. 768 条
展开解析

考点3.1 扩展操作码

B三地址:4 位操作码;二地址:8 位;单地址:12 位。

  1. 三地址:2⁴ = 16 个编码,用 13,剩 3 个作前缀。
  2. 二地址:3 × 2⁴ = 48 个,用 30,剩 18 个。
  3. 单地址:18 × 2⁴ = 288 条(程序核对)。
  • A 错18 只是二地址剩下的编码数,还要每个扩展 4 位、乘 16。
  • B 对288。
  • C 错80 = (48 − 13 − 30) × 16:又把三地址的 13 条从二地址的编码里减了一次;那 13 条已在上一级扣过。
  • D 错768 = 3 × 16 × 16:忘了减去二地址用掉的 30 条。
计算 10计组字段范围 · 原创
某 RISC 机器指令字长 32 位,I 型指令的操作码 7 位,两个寄存器字段各 5 位,其余位是补码表示的立即数。立即数的取值范围是( )。
  • A. −32768 ~ 32767
  • B. 0 ~ 32767
  • C. −16384 ~ 16383
  • D. −16383 ~ 16383
展开解析

考点3.1 指令格式2.1 补码范围

C立即数位数 = 32 − 7 − 5 − 5 = 15。

  1. 15 位补码:−2¹⁴ ~ 2¹⁴ − 1 = −16384 ~ 16383。
  • A 错−32768 ~ 32767 是 16 位补码的范围,立即数位数数多了 1 位。
  • B 错0 ~ 32767 是按 15 位无符号数算的,题目说的是补码。
  • C 对−16384 ~ 16383。
  • D 错−16383 ~ 16383 是 15 位原码(或反码)的范围,补码负数多一个。

⑥ 寻址方式求有效地址

固定步骤
  1. 先认方式:立即 / 直接 / 间接 / 寄存器(间接)/ 基址 / 变址 / 相对 / 堆栈。
  2. 写公式:相对 EA = (PC) + A,PC 已指向下一条指令;变址 EA = (IX) + A;“先变址后间址” EA = ((IX) + A);“先间址后变址” EA = (A) + (IX)。
  3. 按编址单位和指令长度算 PC 的更新量;偏移量按补码读(注意符号)。

单位口径:按字节编址时 PC 每次加“指令的字节数”;偏移量是补码,E8H = −24。

常见错法:用本条指令的地址当 PC;偏移量当成无符号数;把有效地址当成操作数,或漏一次间址。

计算 11计组相对寻址 · 原创
某计算机按字节编址,转移指令长 2 字节,采用相对寻址,第 2 字节是 8 位补码偏移量。该指令位于 2000H,偏移量为 E8H,则转移目标地址为( )。
  • A. 1FE8H
  • B. 20EAH
  • C. 1FE9H
  • D. 1FEAH
展开解析

考点3.2 相对寻址

D目标 = 下一条指令的地址 + 偏移量。

  1. 取指后 PC = 2000H + 2 = 2002H。
  2. E8H 是补码,值为 −18H(−24)。
  3. 目标 = 2002H − 18H = 1FEAH。
  • A 错1FE8H = 2000H − 18H:用本条指令的地址当基准,忘了 PC 已经加 2。
  • B 错20EAH = 2002H + E8H:把偏移量当成无符号数 232。
  • C 错1FE9H = 2001H − 18H:PC 只加了 1,忘了指令长 2 字节、按字节编址。
  • D 对1FEAH。
计算 12计组复合寻址 · 原创
某指令采用“先变址、后间址”的寻址方式,形式地址 A = 0034H,变址寄存器 (IX) = 1200H。主存部分单元的内容为:(0034H) = 1500H,(1234H) = 2000H,(1500H) = 0FFFH,(2000H) = 3A5CH,(2700H) = 0042H。该指令的操作数为( )。
  • A. 0042H
  • B. 2000H
  • C. 3A5CH
  • D. 0FFFH
展开解析

考点3.2 寻址方式全表

C先变址:(IX) + A 得到一个地址;再间址:这个地址单元里存的才是有效地址。

  1. (IX) + A = 1200H + 0034H = 1234H。
  2. 间址:EA = (1234H) = 2000H。
  3. 操作数 = (2000H) = 3A5CH。
  • A 错0042H 是“先间址后变址”:EA = (0034H) + 1200H = 2700H,顺序做反了。
  • B 错2000H 是有效地址,不是操作数;少读了最后一次主存。
  • C 对3A5CH。
  • D 错0FFFH = ((0034H)):只做了间址,忘了先加变址寄存器。

⑦ 流水线时间、吞吐率与 CPI

固定步骤
  1. 时钟周期 = 最慢段的时间 + 段间寄存器延迟。
  2. 无阻塞时 n 条指令用 (k + n − 1) 个周期;有阻塞就逐条画时空图或用“1 + 平均阻塞周期”。
  3. 吞吐率 = n ÷ 总时间;加速比 = 不流水的时间 ÷ 流水的时间。

单位口径:ps、ns 换算:1ns = 1000ps;吞吐率常写成“条 / 秒”。

常见错法:周期取各段平均或漏了寄存器延迟;漏了装满流水线的 k − 1 拍;阻塞次数按全部指令而非实际发生的那部分算。

计算 13计组流水线时间 · 原创
某 5 段指令流水线各段的处理时间依次为 90ps、80ps、70ps、100ps、60ps,流水段寄存器的延迟为 20ps。连续执行 200 条指令(无阻塞)所需的时间为( )。
  • A. 20.4ns
  • B. 24.48ns
  • C. 80ns
  • D. 24.0ns
展开解析

考点4.4 流水线的指标

B周期由最慢段决定,再加寄存器延迟。

  1. 时钟周期 = 100 + 20 = 120ps。
  2. 周期数 = 5 + 200 − 1 = 204。
  3. 时间 = 204 × 120ps = 24480ps = 24.48ns。
  • A 错20.4ns = 204 × 100ps:漏了 20ps 的段间寄存器延迟。
  • B 对24.48ns。
  • C 错80ns = 200 × 400ps:按不流水执行(每条依次走完 5 段)算。
  • D 错24.0ns = 200 × 120ps:漏了装满流水线的前 4 个周期。
计算 14计组阻塞与 CPI · 原创
某 5 段流水线采用转发。程序中 load 指令占 30%,其中一半紧跟着使用其结果的指令(每次阻塞 1 个周期);转移指令占 20%,其中 70% 实际发生转移,发生转移时要冲刷 3 个周期,不转移时无阻塞。其余指令无阻塞。平均 CPI 为( )。
  • A. 1.75
  • B. 1.72
  • C. 1.57
  • D. 1.42
展开解析

考点4.4 实际 CPI4.4 load-use

CCPI = 1 + Σ(事件频率 × 每次阻塞周期)。

  1. load-use:0.3 × 0.5 × 1 = 0.15。
  2. 转移:0.2 × 0.7 × 3 = 0.42。
  3. CPI = 1 + 0.15 + 0.42 = 1.57。
  • A 错1.75:所有转移指令都按阻塞 3 拍算(0.2 × 3 = 0.6),忽略了 30% 不转移的没有阻塞。
  • B 错1.72:所有 load 都按阻塞 1 拍算(0.3),忽略了只有一半紧跟使用。
  • C 对1.57。
  • D 错1.42:漏了 load-use 阻塞,以为“有转发就不阻塞”。

⑧ Cache:地址划分、总容量、平均访问时间

固定步骤
  1. 块内地址 = log₂(块大小);组数 = 行数 ÷ 路数,组号 = log₂(组数)(全相联没有);标记 = 地址位数 − 组号 − 块内。
  2. 每行总位数 = 数据 + 标记 + 有效位 [+ 脏位(写回)] [+ 替换位(LRU:log₂ 路数)];总容量 = 行数 × 每行位数。
  3. 命中率按“每块第一次访问缺失”数;平均时间注意题目给的是“先查 Cache 再访存”还是“同时访问”。

单位口径:Cache 容量的 K = 2¹⁰;按字节编址时块内地址按字节算。

常见错法:组号按行数算(当成直接映射);漏了脏位或 LRU 位;平均时间的两种口径混用。

计算 15计组总容量 · 原创
某计算机主存地址 32 位,按字节编址。数据 Cache 的数据区容量 64KB,采用 4 路组相联映射,块大小 64B,写回法,LRU 替换。Cache 的总容量(含数据、标记、有效位、脏位、LRU 位)为( )。
  • A. 531Kb
  • B. 532Kb
  • C. 512Kb
  • D. 534Kb
展开解析

考点8.3 地址划分8.3 写回与脏位

D先切地址求标记位数,再数每行的附加位。

  1. 块内 6 位;行数 64KB ÷ 64B = 1024,组数 1024 ÷ 4 = 256 → 组号 8 位;标记 = 32 − 8 − 6 = 18 位。
  2. 每行:512 位数据 + 18 位标记 + 1 位有效 + 1 位脏 + 2 位 LRU(4 路 → log₂4)= 534 位。
  3. 总容量 = 1024 × 534 = 546816 位 = 534Kb(程序核对)。
  • A 错531Kb = 1024 × (512 + 18 + 1):漏了写回法需要的脏位和 LRU 位。
  • B 错532Kb:标记算成 16 位——把 1024 行都当成“组”,用直接映射的 10 位行号划分地址。
  • C 错512Kb 只算了数据区(64KB = 512Kb),没算任何附加位。
  • D 对534Kb。
计算 16计组命中率与时间 · 原创
数组 int a[256][256] 按行优先存放,int 为 4B,Cache 块大小 32B,Cache 初始为空且容量足以容纳整个数组。程序按行依次读取每个元素一次。Cache 命中时访问时间 2ns;缺失时先访问主存把块调入(50ns),再从 Cache 读(2ns)。平均访问时间为( )。
  • A. 8.25ns
  • B. 8.0ns
  • C. 14.5ns
  • D. 6.25ns
展开解析

考点8.3 命中率与平均时间

A每块 8 个 int:第一次缺失、后 7 次命中。

  1. 命中率 = 7 / 8 = 87.5%。
  2. 每次都要访问 Cache 2ns;缺失的 12.5% 再加主存 50ns。
  3. 平均 = 2 + 0.125 × 50 = 8.25ns。
  • A 对8.25ns。
  • B 错8.0ns = 0.875 × 2 + 0.125 × 50:按“Cache 与主存同时访问”的口径算,和题目说的“先调块再读 Cache”不符。
  • C 错14.5ns:把每块算成 4 个元素(命中率 75%),块大小或 int 大小用错。
  • D 错6.25ns = 0.125 × 50:只算了缺失的代价,漏了每次都要访问 Cache 的 2ns。

⑨ 主存扩展与片选

固定步骤
  1. 芯片数 = (目标容量 × 目标字长) ÷ (芯片容量 × 芯片字长);先位扩展凑够字长,再字扩展凑够单元数。
  2. 片内地址 = log₂(芯片单元数),接低位地址线;剩下的高位接译码器产生片选。
  3. 某芯片的地址范围:片选位固定、片内地址从全 0 到全 1。

单位口径:4K = 2¹² = 1000H;8K = 2000H;16K = 4000H。

常见错法:只做字扩展不做位扩展;片内地址位数算错导致范围翻倍;译码器输入接的地址线位置看错。

计算 17计组字位扩展 · 原创
用 4K × 4 位的 SRAM 芯片构成 16K × 8 位的存储器(按字编址,地址从 0000H 开始连续编址)。所需芯片数,以及地址 3A5CH 所在芯片组的地址范围分别是( )。
  • A. 4 片,3000H ~ 3FFFH
  • B. 8 片,2000H ~ 3FFFH
  • C. 8 片,3000H ~ 3FFFH
  • D. 8 片,3000H ~ 33FFH
展开解析

考点8.2 字位扩展

C2 片并成 8 位一组(位扩展),4 组凑 16K(字扩展)。

  1. 芯片数 = (16K × 8) ÷ (4K × 4) = 8 片,每 2 片一组、共 4 组。
  2. 每组 4K = 1000H 个单元:0000H ~ 0FFFH、1000H ~ 1FFFH、2000H ~ 2FFFH、3000H ~ 3FFFH。
  3. 3A5CH 落在 3000H ~ 3FFFH。
  • A 错4 片:只做了字扩展(16K ÷ 4K),忘了 4 位芯片要 2 片并成 8 位。
  • B 错2000H ~ 3FFFH 是 8K 的范围:片内地址按 13 位算了,每组其实只有 4K(12 位)。
  • C 对8 片,3000H ~ 3FFFH。
  • D 错3000H ~ 33FFH 只有 400H = 1K 个单元:把 4K 换算成了 400H。
计算 18计组译码片选 · 原创
CPU 有 16 根地址线 A15 ~ A0,按字节编址。用 8K × 8 位的芯片构成存储器,A15 ~ A13 接 3-8 译码器的输入(A15 为最高位),译码器输出 Y5 选中的芯片地址范围是( )。
  • A. A000H ~ BFFFH
  • B. 5000H ~ 5FFFH
  • C. A000H ~ AFFFH
  • D. 0A00H ~ 0BFFH
展开解析

考点8.2 片选与地址范围

AY5 → A15 A14 A13 = 101,片内 13 位从全 0 到全 1。

  1. 8K = 2¹³,片内地址 A12 ~ A0。
  2. Y5:A15 A14 A13 = 101。
  3. 最小 1010 0000 0000 0000B = A000H,最大 1011 1111 1111 1111B = BFFFH。
  • A 对A000H ~ BFFFH。
  • B 错5000H ~ 5FFFH:把 101 放到了 A14 ~ A12、片内只按 12 位算,相当于译码器接错了位置。
  • C 错A000H ~ AFFFH 只有 4K:片内地址按 12 位算,漏了 A12。
  • D 错0A00H ~ 0BFFH:把 101 放到了 A11 ~ A9,译码位与片内地址位数都不对。

⑩ 中断方式、DMA 方式的 CPU 占用率

固定步骤
  1. 每秒发生几次:中断方式 = 设备速率 ÷ 每次中断传的数据量;DMA = 设备速率 ÷ 每块大小。
  2. 每次花多少时钟周期:中断 = 响应 + 处理;DMA = 预处理 + 后处理。
  3. 占用率 = 每秒次数 × 每次周期数 ÷ 主频。

单位口径:设备速率的 KB/s、MB/s 一般按 10³、10⁶(题目说明除外);数据宽度 32 位 = 4B。

常见错法:每次中断传的字节数看错;MB 按 2²⁰ 算;DMA 只算预处理或只算后处理。

计算 19计组中断方式 · 原创
某计算机主频 500MHz。某外设的数据传输率为 0.5MB/s,采用中断方式与主机交换数据,每次中断传送 32 位数据,每次中断的响应和处理共需 500 个时钟周期。CPU 用于该外设 I/O 的时间占整个 CPU 时间的百分比为( )。
  • A. 50%
  • B. 13.1%
  • C. 25%
  • D. 12.5%
展开解析

考点9.4 CPU 占用率的万能公式

D每秒中断次数 × 每次周期数 ÷ 主频。

  1. 每秒中断次数 = 0.5 × 10⁶B/s ÷ 4B = 1.25 × 10⁵ 次。
  2. 每秒用于中断的周期 = 1.25 × 10⁵ × 500 = 6.25 × 10⁷。
  3. 占比 = 6.25 × 10⁷ ÷ (5 × 10⁸) = 12.5%。
  • A 错50%:以为每次中断只传 1 字节,中断次数多算了 4 倍。
  • B 错13.1%:把 0.5MB/s 按 0.5 × 2²⁰ 算;速率的 M 取 10⁶。
  • C 错25%:把 32 位当成 2 字节。
  • D 对12.5%。
计算 20计组DMA 方式 · 原创
某计算机主频 500MHz。某外设的数据传输率为 2MB/s,采用 DMA 方式,每次 DMA 传送 1000B 的数据块;每次 DMA 的预处理需 600 个时钟周期,后处理(含中断响应)需 400 个时钟周期。CPU 用于该外设 I/O 的时间占比为( )。
  • A. 0.4%
  • B. 0.16%
  • C. 0.24%
  • D. 4%
展开解析

考点9.4 DMA

ADMA 每块只麻烦 CPU 两次:传送前的预处理、传送完的后处理。

  1. 每秒 DMA 次数 = 2 × 10⁶ ÷ 1000 = 2000 次。
  2. 每次占 600 + 400 = 1000 个周期,每秒 2 × 10⁶ 个周期。
  3. 占比 = 2 × 10⁶ ÷ (5 × 10⁸) = 0.4%。
  • A 对0.4%。
  • B 错0.16%:只算了后处理(结束时的中断),漏了预处理。
  • C 错0.24%:只算了预处理,漏了后处理。
  • D 错4%:把 500MHz 换成了 5 × 10⁷Hz,少了一个 0。

⑪ 总线带宽与突发传输

固定步骤
  1. 带宽 = 每次传送的数据量 × 每秒传送次数;每秒传送次数 = 时钟频率 ÷ 每次传送占用的时钟周期数(DDR 等一个周期传两次要乘 2)。
  2. 突发传输的“平均”传输率 = 一次事务传送的总字节数 ÷ 整个事务的时间(地址、等待周期都要算)。

单位口径:速率的 M = 10⁶;总线宽度 32 位 = 4B。

常见错法:把峰值带宽当成突发的平均传输率;漏了“每次传送占几个时钟周期”;MB/s 按 2²⁰。

计算 21计组带宽 · 原创
某同步总线的数据线宽 32 位,总线时钟频率 200MHz,每个总线周期占 2 个时钟周期、传送一次数据。该总线的带宽为( )。
  • A. 800MB/s
  • B. 400MB/s
  • C. 100MB/s
  • D. 381.5MB/s
展开解析

考点9.1 总线带宽

B每秒传送次数 = 200M ÷ 2。

  1. 每秒传送 200 × 10⁶ ÷ 2 = 10⁸ 次。
  2. 每次 4B → 带宽 = 400MB/s。
  • A 错800MB/s:按每个时钟周期传一次算,漏了“每个总线周期占 2 个时钟周期”。
  • B 对400MB/s。
  • C 错100MB/s:把 32 位当成 1 字节。
  • D 错381.5MB/s:用 2²⁰ 去换算 MB;速率的 M 是 10⁶。
计算 22计组突发传输 · 原创
某同步总线宽 32 位,时钟频率 200MHz,支持突发传输。一次突发读事务:第 1 个时钟周期送地址,第 2 ~ 4 个时钟周期等待,第 5 ~ 20 个时钟周期每周期传送一个数据(共 16 个)。一次突发读事务的平均数据传输率约为( )。
  • A. 800MB/s
  • B. 753MB/s
  • C. 640MB/s
  • D. 160MB/s
展开解析

考点9.2 突发传输

C整个事务的时间要从送地址算起。

  1. 事务共 20 个周期 × 5ns = 100ns。
  2. 传送 16 × 4B = 64B。
  3. 平均传输率 = 64B ÷ 100ns = 640MB/s。
  • A 错800MB/s 是峰值带宽(每周期 4B),没算送地址和等待的 4 个周期。
  • B 错753MB/s ≈ 64B ÷ 85ns:漏了 3 个等待周期,只算了地址 + 数据 17 个周期。
  • C 对640MB/s。
  • D 错160MB/s = 16B ÷ 100ns:以为每次只传 1 字节。

⑫ 磁盘访问时间与地址换算

固定步骤
  1. 访问时间 = 平均寻道时间 + 平均旋转延迟(转半圈)+ 传输时间(转过要读的扇区)。
  2. 转一圈的时间 = 60 ÷ 转速(rpm)秒;读 1 个扇区 = 一圈时间 ÷ 每道扇区数。
  3. 地址换算:编号顺序是“同一磁道 → 同一柱面换磁头 → 换柱面”:柱面 = 号 ÷ 每柱面单元数,余数 ÷ 每磁道单元数 = 磁头,再余 = 道内序号。

单位口径:rpm 是每分钟转数;平均旋转延迟是半圈;1 簇 = 几个扇区要先换算。

常见错法:旋转延迟按一整圈;把簇号当扇区号;编号顺序记反(先换柱面)。

计算 23操作系统访问时间 · 原创
某磁盘转速 7500rpm,平均寻道时间 6ms,每个磁道 400 个扇区。读取一个扇区的平均访问时间为( )。
  • A. 14.02ms
  • B. 6.02ms
  • C. 18ms
  • D. 10.02ms
展开解析

考点10.1 访问时间

D一圈 = 60 ÷ 7500 s = 8ms。

  1. 平均旋转延迟 = 8ms ÷ 2 = 4ms。
  2. 传输 1 个扇区 = 8ms ÷ 400 = 0.02ms。
  3. 总计 = 6 + 4 + 0.02 = 10.02ms。
  • A 错14.02ms:旋转延迟按转一整圈(8ms)算;平均只要半圈。
  • B 错6.02ms:漏了平均旋转延迟(半圈 4ms),以为寻道到位就能立刻读。
  • C 错18ms = 6 + 4 + 8:传输时间按转一整圈算,实际只转过 1/400 圈。
  • D 对10.02ms。
计算 24操作系统地址换算 · 原创
某磁盘有 200 个柱面、4 个磁头(盘面),每个磁道 50 个扇区(编号均从 0 开始)。文件系统以簇为分配单位,1 簇 = 2 个扇区;簇按“同一磁道 → 同一柱面的下一个磁头 → 下一个柱面”的顺序从 0 连续编号。簇号 3333 对应的(柱面号, 磁头号, 起始扇区号)是( )。
  • A. (33, 1, 16)
  • B. (33, 1, 8)
  • C. (16, 2, 33)
  • D. (133, 0, 16)
展开解析

考点10.1 三个“为什么”11.13 磁盘

A先换算成“每磁道 25 簇、每柱面 100 簇”。

  1. 每磁道 50 ÷ 2 = 25 簇,每柱面 25 × 4 = 100 簇。
  2. 3333 ÷ 100 = 33 余 33 → 柱面 33。
  3. 33 ÷ 25 = 1 余 8 → 磁头 1,道内第 8 簇 → 起始扇区 8 × 2 = 16(占 16、17 号扇区)。
  • A 对(33, 1, 16)。
  • B 错(33, 1, 8):把道内的簇序号 8 当成了扇区号,忘了乘 2。
  • C 错(16, 2, 33):把簇号当成扇区号,按每柱面 200 扇区、每道 50 扇区去除。
  • D 错(133, 0, 16):编号顺序弄反——以为先编完一个盘面的所有柱面再换磁头。

⑬ 调度:画甘特图算周转、带权周转、等待

固定步骤
  1. 按算法规则画甘特图:每到一个“事件点”(有进程到达、有进程完成、时间片用完)就重新判断谁运行。
  2. 周转时间 = 完成时间 − 到达时间;带权周转 = 周转 ÷ 服务时间;等待时间 = 周转 − 服务时间。
  3. 求平均时除以进程个数;检查甘特图总长 = 最后完成时刻。

单位口径:RR 中“新到达”和“刚用完时间片”同时入队时,本书约定新到达的排前面(题目另有说明按题目)。

常见错法:抢占式算法只在“进程完成时”才重新选择;周转时间忘了减到达时间;RR 入队顺序约定不一致。

计算 25操作系统SRTN · 原创
四个进程的到达时间和运行时间如下:P1(0, 8)、P2(1, 4)、P3(3, 2)、P4(6, 3)(括号内依次为到达时刻、运行时间)。采用最短剩余时间优先(SRTN,抢占式)调度,平均周转时间为( )。
  • A. 9.5
  • B. 7.25
  • C. 9.75
  • D. 10.25
展开解析

考点6.7 调度算法6.7 五种算法对比

B每当有新进程到达,就比较“剩余时间”。

  1. 0:只有 P1,运行;1:P2 到(4 < P1 剩 7)→ 抢占,P2 运行到 5。
  2. 5:P3(2)、P1(7)→ P3 运行到 7;7:P4(3)、P1(7)→ P4 运行到 10;P1 运行到 17。
  3. 周转:P1 17、P2 4、P3 4、P4 4 → 平均 29 ÷ 4 = 7.25(模拟器核对)。
  • A 错9.5 是非抢占 SJF 的结果(P1 先跑完 8 个单位):没有在 P2 到达时抢占。
  • B 对7.25。
  • C 错9.75 = (17 + 5 + 7 + 10) ÷ 4:用完成时间当周转时间,忘了减到达时间。
  • D 错10.25 是先来先服务的结果。
计算 26操作系统RR · 原创
同上题四个进程,改用时间片轮转(RR)调度,时间片为 2,新到达的进程与刚用完时间片的进程同时进入就绪队列时,新到达的排在前面。平均周转时间为( )。
  • A. 11.0
  • B. 10.25
  • C. 10.0
  • D. 11.25
展开解析

考点6.7 RR 的入队约定

C逐个时间片画甘特图,注意入队顺序。

  1. 甘特图:P1(0~2)、P2(2~4)、P1(4~6)、P3(6~8)、P2(8~10)、P4(10~12)、P1(12~14)、P4(14~15)、P1(15~17)。
  2. 完成时刻:P3 8、P2 10、P4 15、P1 17。
  3. 周转:17、9、5、9 → 平均 40 ÷ 4 = 10.0(模拟器核对)。
  • A 错11.0 是时间片取 3 时的结果,时间片看错。
  • B 错10.25 是先来先服务的结果,没有按时间片轮转。
  • C 对10.0。
  • D 错11.25 是另一种入队约定(被抢占的先回队尾、新到达的排后面)的结果,与题目给的约定相反。

⑭ 银行家算法

固定步骤
  1. Need = Max − Allocation,先把 Need 矩阵写出来。
  2. 安全性检查:Work = Available;反复找一个 Need ≤ Work 的进程,假设它运行完,Work += 它的 Allocation;所有进程都能完成 → 安全。
  3. 处理请求:① Request ≤ Need?② Request ≤ Available?③ 试分配后做安全性检查;三步都过才分配。

单位口径:向量比较要求每一类资源都满足 ≤。

常见错法:用 Max 而不是 Need 去和 Work 比;试分配后忘了做安全性检查;只比较了一类资源。

计算 27操作系统安全序列 · 原创
某系统有 A、B、C 三类资源,当前 Available = (2, 1, 2),各进程的资源情况如下:
进程Allocation(A B C)Max(A B C)
P01 1 24 3 3
P12 0 13 2 2
P22 2 05 3 1
P30 1 11 3 2
P41 0 23 1 4
下列进程序列中,是安全序列的是( )。
  • A. P2, P4, P1, P3, P0
  • B. P4, P1, P2, P3, P0
  • C. P3, P4, P2, P1, P0
  • D. P4, P2, P1, P3, P0
展开解析

考点7.7 银行家算法

D先求 Need:P0 (3,2,1)、P1 (1,2,1)、P2 (3,1,1)、P3 (1,2,1)、P4 (2,1,2),再逐个模拟。

  1. Work = (2,1,2):只有 P4 的 Need (2,1,2) ≤ Work → P4 完成,Work = (3,1,4)。
  2. 此时只有 P2 的 Need (3,1,1) ≤ Work(P0、P1、P3 都缺 B)→ Work = (5,3,4)。
  3. 之后 P1、P3、P0 依次都能满足 → P4, P2, P1, P3, P0 安全(程序枚举:所有安全序列都以 P4、P2 开头,共 6 个)。
  • A 错第一步 P2 的 Need (3,1,1) 中 A 需要 3,而 Work 的 A 只有 2——用 Max 或只看了 B、C 两类资源才会觉得能满足。
  • B 错P4 完成后 Work = (3,1,4),P1 的 Need (1,2,1) 中 B 需要 2,只有 1,卡在第二步。
  • C 错第一步 P3 的 Need (1,2,1) 中 B 需要 2,Work 的 B 只有 1。
  • D 对P4, P2, P1, P3, P0。
计算 28操作系统处理请求 · 原创
(接上题的状态)此时 P0 发出请求 Request = (1, 0, 0)。系统应当( )。
  • A. 立即分配,因为请求不超过 Need,也不超过 Available
  • B. 不分配,P0 等待,因为试分配后系统进入不安全状态
  • C. 拒绝,因为请求超过了 P0 的 Need
  • D. 拒绝,因为请求超过了 Available
展开解析

考点7.7 银行家算法

B三步检查缺一不可。

  1. (1,0,0) ≤ Need₀ (3,2,1) ✓;(1,0,0) ≤ Available (2,1,2) ✓。
  2. 试分配:Available = (1,1,2),P0 的 Allocation = (2,1,2)、Need = (2,2,1)。
  3. 安全性检查:五个进程的 Need 中,P4 缺 A,其余都缺 B,没有一个能满足 → 不安全,不分配(程序核对)。
  • A 错只做了前两步检查,漏了试分配后的安全性检查——这正是银行家算法与“有就给”的区别。
  • B 对试分配后不安全,P0 等待。
  • C 错Need₀ = Max₀ − Allocation₀ = (3,2,1),请求 (1,0,0) 没有超过。
  • D 错Available = (2,1,2),请求 (1,0,0) 没有超过。

⑮ 页表大小与多级页表

固定步骤
  1. 页内偏移 = log₂(页大小);页号位数 = 虚拟地址位数 − 偏移位数;页表项数 = 2页号位数。
  2. 每级页表占一页时:每级索引位数 = log₂(页大小 ÷ 页表项大小)。
  3. 级数 = ⌈页号位数 ÷ 每级位数⌉;最高一级用剩下的位数,表的大小 = 2剩余位数 × 表项大小,不一定满一页。

单位口径:KB / MB 的 K、M = 2¹⁰、2²⁰;页表项大小以字节计。

常见错法:忘了减去页内偏移;以为顶级页表也一定占满一页;页表项大小用错(4B / 8B)。

计算 29操作系统二级页表 · 原创
某系统虚拟地址 32 位,页面大小 8KB,页表项 4B,采用二级页表,每张二级页表恰好占一个页面。页目录表(一级页表)的大小为( )。
  • A. 8KB
  • B. 2MB
  • C. 1KB
  • D. 4KB
展开解析

考点8.5 多级页表

C先算二级页表索引位数,剩下的就是页目录索引。

  1. 偏移 13 位,页号 19 位。
  2. 二级页表一页能放 8KB ÷ 4B = 2048 项 → 二级索引 11 位。
  3. 页目录索引 19 − 11 = 8 位 → 2⁸ × 4B = 1KB。
  • A 错8KB:以为页目录也必须占满一个页面;它只需要 256 项。
  • B 错2MB = 2¹⁹ × 4B 是单级页表的大小。
  • C 对1KB。
  • D 错4KB = 2¹⁰ × 4B:套用了“4KB 页、10 + 10 划分”的常见结构,没按本题 8KB 页面重新算。
计算 30操作系统级数 · 原创
某 64 位处理器实际使用 42 位虚拟地址,页面大小 4KB,页表项 8B,每级页表最多占一个页面。至少需要几级页表?最高一级页表占多大?( )
  • A. 3 级,4KB
  • B. 4 级,4KB
  • C. 5 级,512B
  • D. 4 级,64B
展开解析

考点8.5 多级页表

D每级 9 位,页号 30 位。

  1. 偏移 12 位,页号 42 − 12 = 30 位。
  2. 每级索引 log₂(4KB ÷ 8B) = 9 位 → 级数 ⌈30 ÷ 9⌉ = 4。
  3. 低三级用 27 位,最高级剩 3 位 → 2³ × 8B = 64B。
  • A 错3 级、4KB:页表项按 4B 算,每级 10 位(30 = 10 + 10 + 10);本题表项是 8B。
  • B 错级数对了,但以为最高一级页表也占满一页;它只有 8 项。
  • C 错5 级、512B:忘了减去 12 位页内偏移,拿 42 位直接按 9 位一级去分。
  • D 对4 级,64B。

⑯ 有效访存时间(TLB、多级页表、缺页)

固定步骤
  1. 画路径:查 TLB → 命中直接拿页框号;未命中按页表级数访问主存。
  2. 再访问数据本身一次(再考虑 Cache 时换成 Cache 的平均时间)。
  3. 加权:EAT = tTLB + (1 − h) × 级数 × tm + tm;有缺页再按缺页率加上缺页处理时间。

单位口径:题目说“TLB 与主存同时查”时,TLB 命中不必再加查 TLB 的时间以外的项,要按题目口径改公式。

常见错法:多级页表只算一次访存;把命中率当缺失率;忘了访问数据本身的那一次。

计算 31操作系统两级页表 + TLB · 原创
某系统采用二级页表和 TLB,先查 TLB,未命中再查页表(串行)。查 TLB 需 1ns,访问一次主存需 100ns,TLB 命中率 98%,不考虑缺页和 Cache。有效访存时间为( )。
  • A. 103ns
  • B. 297ns
  • C. 300ns
  • D. 105ns
展开解析

考点8.5 快表8.8 命中组合

DTLB 未命中时要访问两级页表,再访问数据。

  1. 每次:查 TLB 1ns + 访问数据 100ns。
  2. TLB 未命中(2%)再加两级页表 2 × 100ns。
  3. EAT = 1 + 100 + 0.02 × 200 = 105ns。
  • A 错103ns:TLB 未命中时只算了一次查页表,二级页表要访问两次主存。
  • B 错297ns:把 98% 当成了缺失率。
  • C 错300ns:没有利用 TLB,每次都查两级页表再取数据。
  • D 对105ns。
计算 32操作系统缺页的代价 · 原创
某请求分页系统(不考虑 TLB 和 Cache)访问主存需 100ns,缺页时总处理时间为 20ms(含重新访问)。要使有效访存时间不超过 150ns,缺页率最大约为( )。
  • A. 2.5 × 10−6
  • B. 7.5 × 10−6
  • C. 2.5 × 10−5
  • D. 5 × 10−6
展开解析

考点8.9 缺页率与 EAT

A(1 − p) × 100 + p × 2 × 10⁷ ≤ 150。

  1. 20ms = 2 × 10⁷ns。
  2. 100 + p × (2 × 10⁷ − 100) ≤ 150 → p ≤ 50 ÷ 19999900 ≈ 2.5 × 10⁻⁶。
  • A 对约 2.5 × 10⁻⁶。
  • B 错7.5 × 10⁻⁶ = 150 ÷ (2 × 10⁷):直接拿允许的总时间 150ns 去除,没减去不缺页时本来要的 100ns。
  • C 错2.5 × 10⁻⁵ = 50 ÷ (2 × 10⁶):把 20ms 换算成了 2 × 10⁶ns,少了一个 0。
  • D 错5 × 10⁻⁶ = 50 ÷ 10⁷:把缺页时间当成 10ms,看错了题目数据。

⑰ 页面置换:数缺页次数

固定步骤
  1. 画表:每列一次访问,行是页框内容,缺页的列打 ×。
  2. OPT 淘汰“以后最晚再用”的;LRU 淘汰“最久没用”的(往前看);FIFO 淘汰最早装入的;CLOCK 指针扫到访问位为 0 的就换,经过的 1 清 0。
  3. 数 × 的个数;看清“初始为空、开始的装入算不算缺页”(一般算)。

单位口径:缺页率 = 缺页次数 ÷ 访问次数。

常见错法:LRU 和 FIFO 混淆(命中不更新顺序就是 FIFO);CLOCK 忘了“第二次机会”;漏算开始时装入的缺页。

计算 33操作系统LRU · 原创
某进程分得 3 个页框(初始为空),页面访问序列为 1 2 4 3 2 0 3 2 5 0 3 0。采用 LRU 置换算法时,缺页次数为( )。
  • A. 6
  • B. 8
  • C. 9
  • D. 5
展开解析

考点8.7 置换算法

B淘汰“最近最久没被访问”的页,命中要更新它的最近访问时间。

  1. 1、2、4 装入缺 3 次;3 缺页,淘汰 1;2 命中;0 缺页,淘汰 4;3 命中;2 命中。
  2. 5 缺页,淘汰 0;0 缺页,淘汰 3;3 缺页,淘汰 2;0 命中。
  3. 共 8 次(模拟器核对;同一序列 OPT 6 次、CLOCK 7 次、FIFO 9 次)。
  • A 错6 是 OPT 的结果(淘汰以后最晚使用的页),不是 LRU。
  • B 对8 次,其中 3 次是开始时的装入。
  • C 错9 是 FIFO 的结果:命中时没有更新页的“最近使用”顺序。
  • D 错5 = 8 − 3:漏算了开始时装入 1、2、4 的 3 次缺页。
计算 34操作系统CLOCK · 原创
同上题的进程与访问序列(3 个页框,初始为空),改用简单 CLOCK 算法(页面装入或被访问时访问位置 1;缺页时指针循环扫描,遇到访问位为 1 的清 0 并跳过,遇到 0 的就替换,替换后指针指向下一个页框)。缺页次数为( )。
  • A. 9
  • B. 8
  • C. 7
  • D. 4
展开解析

考点8.7 CLOCK8.7 改进型 CLOCK

C与 FIFO 的区别:访问位为 1 的页有“第二次机会”。

  1. 1、2、4 装入(缺 3 次),访问位都是 1,指针回到第 1 个页框。
  2. 访问 3:扫一圈把三个访问位都清 0,回到页 1 换掉;访问 2 命中(置 1);访问 0:页 2 访问位为 1 → 清 0 跳过,换掉 4。
  3. 访问 3、2 命中;访问 5:换掉 3;访问 0 命中;访问 3:换掉 2;访问 0 命中。共缺页 7 次(模拟器逐步核对)。
  • A 错9 是 FIFO 的结果:没有给访问位为 1 的页第二次机会。
  • B 错8 是 LRU 的结果;CLOCK 只是 LRU 的近似,结果可能不同。
  • C 对7 次,比 FIFO 少两次:第 6 次访问(页 0)缺页时,页 2 刚被访问过、访问位为 1 被跳过,换出的是 4,所以第 8 次访问页 2 命中;第 11 次访问(页 3)换出的是 2 而不是 0,所以最后的页 0 命中。
  • D 错4 = 7 − 3:漏算了开始时的 3 次装入。

⑱ 磁盘调度:移动距离与平均寻道长度

固定步骤
  1. 写出服务顺序:SSTF 每次选最近的;SCAN 沿当前方向走到端点再回头;LOOK 走到最远请求就回头;C-SCAN 走到端点后直接返回另一端再同向服务;C-LOOK 只到最远请求、返回到最小请求。
  2. 逐段求 |差| 再相加;C-SCAN 的返回距离本书计入(2010-45 口径)。
  3. 平均寻道长度 = 总移动距离 ÷ 请求个数。

单位口径:磁道编号范围(如 0 ~ 199)决定 SCAN、C-SCAN 走到哪;方向看题目给的“当前移动方向”。

常见错法:SCAN 与 LOOK 混淆(走不走到端点);C-SCAN 的返回距离计不计入;请求个数数错。

计算 35操作系统C-SCAN · 原创
磁道号 0 ~ 199,磁头当前在 80 号磁道,正向磁道号增大的方向移动。请求序列为 42、133、7、95、168、61、120、15、150。采用 C-SCAN 算法(到达端点后立即返回 0 号磁道再继续向增大方向服务,返回距离计入),磁头移动的总磁道数为( )。
  • A. 180
  • B. 303
  • C. 311
  • D. 379
展开解析

考点10.2 磁盘调度

D80 → 199(端点)→ 0 → 61。

  1. 向上服务 95、120、133、150、168,到端点 199:119。
  2. 返回 0:199。
  3. 再向上服务 7、15、42、61:61。合计 119 + 199 + 61 = 379(模拟器核对)。
  • A 错180 = 379 − 199:返回的 199 个磁道没有计入,与题目要求不符。
  • B 错303 是 C-LOOK:只走到最远请求 168,再返回到最小请求 7。
  • C 错311 是 SCAN:到端点后掉头向下服务,而不是返回 0 再向上。
  • D 对379。
计算 36操作系统SSTF · 原创
同上题的磁头位置与请求序列,采用最短寻道时间优先(SSTF)算法,平均寻道长度约为( )。
  • A. 27.7
  • B. 34.6
  • C. 91.3
  • D. 31.1
展开解析

考点10.2 磁盘调度

A每次选离当前磁头最近的请求。

  1. 80 → 95 → 120 → 133 → 150 → 168 → 61 → 42 → 15 → 7。
  2. 总距离 = 88 + 161 = 249(向上 88,再折回到 7 走 161)。
  3. 平均 = 249 ÷ 9 ≈ 27.7。
  • A 对27.7。
  • B 错34.6 = 311 ÷ 9 是 SCAN 的结果(走到端点 199 再回头)。
  • C 错91.3 = 822 ÷ 9 是先来先服务的结果。
  • D 错31.1 = 249 ÷ 8:总距离对了,但请求个数数成了 8 个。

⑲ 索引结构、FAT 与位示图的大小

固定步骤
  1. 每个索引块能放的地址数 N = 块大小 ÷ 地址项大小。
  2. 逻辑块号 = ⌊偏移 ÷ 块大小⌋;依次减去直接地址数、N、N²……判断落在哪一级;读盘次数 = 该级的索引块数 + 1 次数据块(看清 inode 是否已在内存)。
  3. 位示图大小 = 盘块总数 位;FAT 表项 ≥ log₂(盘块总数) 位,再按题目要求取整到字节;FAT 大小 = 盘块数 × 表项大小。

单位口径:容量的 K、M、G 是 2¹⁰、2²⁰、2³⁰;位示图以“位”计,换成字节要 ÷ 8。

常见错法:忘了前面的直接地址块;把已在内存的 inode 也算一次读盘;位示图按字节算、FAT 表项不按需要的位数取。

计算 37操作系统混合索引 · 原创
某文件系统的索引结点含 10 个直接地址、1 个一次间接、1 个二次间接、1 个三次间接地址项。盘块 2KB,地址项 4B。某文件的索引结点已在内存中,要读取该文件偏移量为 1050000B 处的一个字节,该字节所在的数据块由哪一级地址项索引?需要读几次磁盘?( )
  • A. 二次间接,3 次
  • B. 一次间接,3 次
  • C. 一次间接,2 次
  • D. 一次间接,1 次
展开解析

考点10.6 混合索引

C逻辑块号 = 1050000 ÷ 2048 取整。

  1. N = 2KB ÷ 4B = 512。逻辑块号 = ⌊1050000 ÷ 2048⌋ = 512。
  2. 直接地址管 0 ~ 9;一次间接管 10 ~ 521 → 第 512 块在一次间接里。
  3. 读一次间接索引块 + 读数据块 = 2 次(inode 已在内存,不算)。
  • A 错二次间接、3 次:以为一次间接只能管到第 511 块,漏了前面 10 个直接地址(一次间接管到 521)。
  • B 错3 次:把已在内存的索引结点也算了一次读盘。
  • C 对一次间接,2 次。
  • D 错1 次:只读了数据块,漏了要先读出一次间接索引块才能知道数据块在哪。
计算 38操作系统位示图与 FAT · 原创
某磁盘容量 32GB,盘块大小 4KB。若用位示图管理空闲块,位示图的大小是多少?若改用 FAT,每个表项按字节对齐取最少的字节数,FAT 的大小是多少?( )
  • A. 8MB,24MB
  • B. 1MB,24MB
  • C. 1MB,32MB
  • D. 1MB,16MB
展开解析

考点10.7 位示图10.6 FAT

B盘块数 = 32GB ÷ 4KB = 2²³ = 8M。

  1. 位示图:每块 1 位 → 2²³ 位 = 2²⁰B = 1MB。
  2. FAT 表项至少 23 位 → 按字节取 3B。
  3. FAT = 2²³ × 3B = 24MB。
  • A 错8MB:位示图按每块 1 字节算了,应是每块 1 位。
  • B 对1MB,24MB。
  • C 错32MB:表项取了 4B;题目要求按需要的位数取最少字节数,23 位用 3B 就够。
  • D 错16MB:表项取了 2B,但 16 位只能表示 64K 个盘块,表示不了 8M 个。

13.4融合综合题专练:一次访存、一次 I/O、一条指令的一生

本书书名里的“融合”,在综合题里体现得最明显:一道题从硬件讲到操作系统,再回到硬件。下面三条主线是 408 综合题最常见的“全过程”。每条先用一张图把全过程画出来——硬件做什么、操作系统做什么、CPU 在什么态、进程在什么状态——再给两道按 43 ~ 46 题格式自编的综合题(一道偏计组、一道偏操作系统),附满分答案与评分细则。题目的数据和情境都与正文例题、第 11 篇不同;所有数字由 verify/08/p13/fuse.py 计算并断言(时空图由流水线模拟得到)。

主线一:一次访存的全过程

从 CPU 发出一个虚拟地址开始,到拿到数据为止。命中时全是硬件的事;只有缺页时操作系统才出场,而且要绕一大圈:异常 → 内核 → 磁盘 → 中断 → 唤醒 → 重新执行。

硬件MMU · TLBCache · 主存 操作系统(内核态) 磁盘 ① 发出虚拟地址(用户态) ② 查 TLB命中 → 跳到 ④ ③ 查页表访存 1 次 / 每级 ④ 拼物理地址 → 查 Cache缺失再访存调块 → 取到数据 存在位 = 0 缺页异常:硬件保存 PC、PSW,切到内核态 ⑤ 缺页处理:找空闲页框没有就选淘汰页,脏页先写回 ⑥ 启动磁盘读页P 阻塞,调度别的进程 ⑧ 中断处理唤醒 P(就绪) ⑨ P 再运行改页表、TLB ⑦ DMA 把所缺的页读入页框 读完发中断 ⑩ 中断返回,回到用户态,重新执行引起缺页的那条指令(这次 ② 未命中、③ 命中) CPU:用户态(①~③)→ 内核态(⑤⑥)→ 运行别的进程 → 内核态(⑧)…… 内核态(⑨)→ 用户态(⑩) 进程 P:运行 →(⑥)阻塞 →(⑧)就绪 →(⑨)运行;页命中时 ①→②→④ 全由硬件完成,操作系统根本不出场
图 13-3 一次访存的全过程。上面一行是硬件(命中时 ①→②→④,几纳秒),中间是操作系统(缺页时才出场,毫秒级),下面是磁盘。红色虚线:缺页处理完后回到 ① 重新执行这条指令。对照 8.8 一步步点的动画 和 8 种命中组合。
融合 1一次访存 · 计组12 分 · 涉及题型 ⑤ ⑥

某计算机按字节编址,虚拟地址 32 位,物理地址 28 位,页面大小 16KB,采用一级页表,页表项 4B。TLB 共 8 项,采用全相联映射,其中部分表项如下表左。数据 Cache 的数据区容量 32KB,采用 4 路组相联映射,块大小 32B,用物理地址访问;Cache 第 30 组的 4 行如下表右(其余信息略)。

有效位虚页号页框号
10002BH3F1H
10004AH2C7H
00012AH1A0H
10FFE0H0D5H
行有效位标记
014C2H
1058FH
2158FH
3118FH

(1) 虚拟地址中虚页号、页内偏移各占几位?用物理地址访问 Cache 时,标记、组号、块内地址各占几位?(3 分)

(2) 虚拟地址 0012 A3C4H 对应的物理地址是多少?访问时 TLB 是否命中?Cache 是否命中?要求写出组号和标记。(4 分)

(3) 设查 TLB 需 1ns,访问 Cache 需 2ns,访问主存需 80ns;Cache 缺失时先用 2ns 查 Cache,再用 80ns 从主存调块,调块的同时把数据送给 CPU;页表项不会出现在 Cache 中。分别求“TLB 命中、Cache 命中”和“TLB 缺失、页在主存、Cache 缺失”两种情况下这次访存所需的时间。(2 分)

(4) 访问虚拟地址 0040 2010H 时发现所在页不在主存。从发现缺页到这条访存指令执行完毕,按时间顺序写出主要步骤,并指出每一步由硬件还是操作系统完成。(3 分)

做完再点开:满分答案与评分细则

考点8.8 一次访存的全过程8.8 命中组合8.3 地址划分8.7 缺页处理

(1) 地址划分
答题卡这样写

页面 16KB = 214B → 页内偏移 14 位,虚页号 32 − 14 = 18 位。

块 32B → 块内地址 5 位;行数 32KB ÷ 32B = 1024,组数 1024 ÷ 4 = 256 → 组号 8 位;标记 = 28 − 8 − 5 = 15 位。

(2) 地址变换与命中判断
答题卡这样写

0012 A3C4H 的低 14 位是页内偏移 = 23C4H,其余高位是虚页号 = 0012A3C4H >> 14 = 0004AH。

TLB 中有有效的 0004AH 项,TLB 命中,页框号 2C7H。物理地址 = 2C7H × 214 + 23C4H = 0B1 E3C4H(28 位)。

0B1E3C4H = 0000 1011 0001 1110 0011 1100 0100B:块内地址 = 低 5 位 00100 = 4;组号 = 接下来 8 位 0001 1110 = 30;标记 = 高 15 位 = 58FH。第 30 组中行 2 有效且标记为 58FH → Cache 命中(行 1 标记相同但有效位为 0,不算)。

常见错误:照搬“4KB 页、12 位偏移”,把虚页号切成 0012AH——正好撞上 TLB 里那个无效的 0012AH 项,于是错判为 TLB 缺失。页面多大,偏移就几位,每题都要重算。

(3) 两种情况的时间
答题卡这样写

TLB 命中、Cache 命中:1 + 2 = 3ns。

TLB 缺失、页在主存、Cache 缺失:查 TLB 1ns + 访存查页表 80ns + 查 Cache 2ns + 主存调块 80ns = 163ns。

(4) 缺页的全过程
答题卡这样写

① 硬件(MMU)查页表发现存在位为 0,产生缺页异常;② 硬件保存断点(PC 指向这条访存指令)和 PSW,切换到内核态,转缺页异常处理程序;③ 操作系统找空闲页框,没有则按置换算法选淘汰页,淘汰页被修改过就先写回磁盘;④ 操作系统启动磁盘把所缺的页读入页框,进程阻塞、调度别的进程(读盘由磁盘控制器 / DMA 完成,完成后发中断);⑤ 操作系统的中断处理程序唤醒进程,进程再次运行时修改页表项(存在位置 1、填页框号)和 TLB;⑥ 中断返回(硬件恢复 PC、PSW,回到用户态),重新执行这条访存指令,这次能在页表中找到页框号,完成访存。

缺页处理完要回到引起缺页的那条指令重新执行,不是执行下一条——缺页是“故障”,不是“自陷”。

小问得分点分值
(1)虚页号 18 位、页内偏移 14 位1
标记 15 位、组号 8 位、块内 5 位(每错一个扣 1 分,扣完为止)2
(2)虚页号 0004AH、偏移 23C4H1
物理地址 0B1E3C4H1
TLB 命中1
组号 30、标记 58FH,Cache 命中(指出行 1 无效)1
(3)3ns1
163ns1
(4)步骤完整、顺序正确(含“重新执行该指令”)2
正确指出硬件、操作系统各自完成的步骤1
合计12

一次访存的四关:TLB → 页表 → Cache → 主存,前三关的地址划分每题都要按本题参数重新算;缺页是唯一要操作系统出场的情况。

融合 2一次访存 · 操作系统8 分 · 涉及题型 ⑩ ⑬

某系统采用请求分页存储管理,局部置换,进程 P 分得的页框已全部占满。P 在用户态执行一条访存指令时,所需的页 X 不在内存,按上面“一次访存的全过程”图的流程处理。回答:

(1) 缺页异常与一般的外部中断有哪些不同?(至少答两点)(2 分)

(2) 若采用页面缓冲算法,页 X 刚被淘汰不久、仍在空闲页链表中,图中哪些步骤可以省去?进程 P 还会阻塞吗?(2 分)

(3) 已知 P 的缺页率为 2 × 10−6,主存访问时间 100ns;缺页时若被淘汰的页未被修改,处理时间平均 8ms,若被修改过(须先写回)平均 16ms;被淘汰的页中有 40% 被修改过。不考虑 TLB 和 Cache,求 P 的有效访存时间。(2 分)

(4) 若操作系统换出页 Y 后,忘了作废 TLB 中 Y 的表项,会出现什么错误?(2 分)

做完再点开:满分答案与评分细则

考点8.7 缺页处理8.7 页面缓冲8.9 缺页率与 EAT

(1) 缺页异常 vs 外部中断
答题卡这样写

① 来源:缺页由正在执行的指令自己引起(内部异常),外部中断来自 CPU 以外的设备;② 检测时机:缺页在指令执行过程中(取指或访存时)检测并立即处理,外部中断在一条指令执行结束后才响应;③ 返回点:缺页处理完返回本条指令重新执行,外部中断返回下一条指令;④ 一条指令执行期间可能多次缺页。

(2) 页面缓冲命中
答题卡这样写

可以省去从磁盘读页的整段:⑥ 启动磁盘读页、⑦ DMA 传送、⑧ 磁盘中断唤醒都不需要了;⑤ 中也不必选淘汰页、写回脏页,只需把 X 从空闲页链表摘下来。

P 不会阻塞:缺页处理程序直接修改页表(存在位置 1、填页框号)后返回,重新执行该指令。缺页次数不变,但每次缺页的代价大大降低。

(3) 有效访存时间
答题卡这样写

平均缺页处理时间 tf = 0.6 × 8 + 0.4 × 16 = 11.2ms = 1.12 × 107ns。

EAT = (1 − 2 × 10−6) × 100 + 2 × 10−6 × 1.12 × 107 ≈ 100 + 22.4 = 122.4ns。

脏页要先写回,等于缺页一次做两次磁盘 I/O,所以平均处理时间要按比例加权;直接用 8ms 会得到 116ns。

(4) 忘了作废 TLB
答题卡这样写

Y 的旧表项还留在 TLB 里,以后访问 Y 的虚页时 TLB 命中,得到的仍是旧页框号——而这个页框现在装的是 X。于是读到的是 X 的数据(读错),写操作会破坏 X 的内容,而且不会产生缺页,Y 也永远调不回来。所以换出一页时必须同时作废 TLB 中对应的项(多核时还要通知其他核)。

小问得分点分值
(1)答出任意两点不同(来源、检测时机、返回点、可多次)2
(2)指出读盘相关步骤(启动磁盘、DMA 传送、中断唤醒)可省1
P 不阻塞,只改页表;缺页次数不变1
(3)平均处理时间 11.2ms1
EAT ≈ 122.4ns1
(4)TLB 命中得到旧页框号,访问到 X 的数据1
读错或破坏 X、且不产生缺页;应作废 TLB 项1
合计8

页面缓冲、写回脏页、TLB 作废,都是“缺页处理”这一段里的细节;能把它们放回全过程图的哪一步,就能答对。

主线二:一次 I/O 的全过程

以“读磁盘”为例。注意三件事:① 进程在启动设备之后就阻塞了,CPU 去运行别的进程;② 设备完成后是中断处理程序唤醒它,它只进入就绪态;③ 数据先到内核缓冲区,进程再次运行时才复制到用户区。

用户进程 P 内核软件(内核态) 硬件磁盘 · DMA ① read(fd, buf, n)陷入指令 → 内核态 ② 系统调用 +设备独立性软件查缓冲区、算逻辑块号 ③ 磁盘驱动程序写 DMA 寄存器、启动P 阻塞 → 调度 Q ④ DMA 控制器传送整块期间 CPU 运行 Q(周期挪用) ⑤ 发中断请求指令结束时响应 ⑥ 中断处理程序检查是否出错唤醒 P(就绪) ⑦ P 被调度数据复制到 buf系统调用返回 ⑧ read 返回回到用户态继续 (P 在 ③ ~ ⑥ 期间阻塞,CPU 运行别的进程) 层次:用户层(①⑧)→ 设备独立性软件(②⑦)→ 驱动(③)→ 硬件(④⑤)→ 中断处理(⑥);DMA 按块传送,只有 ⑤ 一次中断
图 13-4 一次读磁盘(DMA 方式)的全过程。四层软件各司其职:用户层发起请求、设备独立性软件查缓冲区和映射、驱动程序操作控制器、中断处理程序收尾。对照 9.6 一次 scanf 的全过程(键盘、中断驱动)。
融合 3一次 I/O · 操作系统8 分 · 涉及题型 ⑬

进程 P 调用 write(fd, s, 80) 向打印机输出 80 个字符。打印机采用中断驱动方式:驱动程序每次向打印机控制器的数据寄存器送 1 个字符,打印完这个字符后控制器发一次中断。下面是这次输出中发生的事件(顺序已打乱):

a. 驱动程序把第 1 个字符写入控制器的数据寄存器并启动打印,P 进入阻塞态,调度程序选中进程 Q 运行
b. P 执行 write 对应的陷入指令,CPU 由用户态进入内核态
c. 控制器打印完一个字符,向 CPU 发出中断请求
d. 中断处理程序把下一个字符送入数据寄存器
e. 最后一个字符打印完毕,中断处理程序唤醒 P
f. 设备独立性软件把 80 个字符从 P 的用户区复制到内核缓冲区,再调用打印机驱动程序
g. P 被调度运行,write 返回,CPU 回到用户态

(1) 写出这些事件的正确顺序(c、d 可以重复出现,用“(c d)×k”表示)。(2 分)

(2) 这次输出共引起多少次打印机中断?若改用 DMA 方式(一次传送整行),共引起几次?(2 分)

(3) 写出 P 在整个过程中的状态变化;事件 a ~ g 中哪些在内核态执行?(2 分)

(4) 为什么要先把字符复制到内核缓冲区,而不让驱动程序直接从 P 的用户区取字符?(2 分)

做完再点开:满分答案与评分细则

考点9.5 I/O 软件层次9.6 一次 scanf 的全过程9.4 中断方式与 DMA

(1) 顺序
答题卡这样写

b → f → a → (c d)×79 → c → e → g:陷入内核 → 复制到内核缓冲区并调用驱动 → 驱动送第 1 个字符、P 阻塞 → 每打完一个字符中断一次、送下一个(共 79 轮)→ 第 80 个字符打完的那次中断里唤醒 P → P 被调度,write 返回。

(2) 中断次数
答题卡这样写

中断驱动方式每打印完 1 个字符中断一次:80 次(最后一次用于唤醒 P)。改用 DMA,整行传送完才中断一次:1 次。

(3) 状态与模式
答题卡这样写

P:运行 →(a)阻塞 →(e)就绪 →(g)运行。

b 的陷入指令本身在用户态执行,执行后 CPU 进入内核态;f、a、d、e 以及 g 中 write 返回之前的部分都在内核态执行;c 是硬件动作(打印机控制器发请求),不属于“CPU 态”。

(4) 为什么用内核缓冲区
答题卡这样写

① P 在等待期间被阻塞,它的页可能被换出,中断处理程序运行时 CPU 正在执行别的进程,地址空间也不是 P 的,无法直接访问 P 的用户区;② 复制后 P 的用户区可以立即被重用(write 返回后 P 可以改 s);③ 缓和 CPU 与慢速打印机之间的速度差,并能把多次小的输出合并。

小问得分点分值
(1)顺序正确(b f a 在前,c d 循环,e g 在后)2
(2)80 次1
DMA 1 次1
(3)状态序列正确1
正确指出内核态执行的事件1
(4)答出“中断时不在 P 的地址空间 / P 的页可能被换出”1
答出用户区可重用或速度匹配1
合计8

I/O 全过程的骨架都一样:陷入 → 设备独立层 → 驱动启动设备、进程阻塞 → 设备完成发中断 → 中断处理唤醒 → 进程复制数据返回;中断驱动与 DMA 的区别只在“中断几次”。

融合 4一次 I/O · 计组10 分 · 涉及题型 ⑦

某计算机 CPU 主频 1GHz,总线宽 32 位、总线时钟 200MHz,每个总线周期占 1 个总线时钟、传送一个 32 位字。某磁盘的数据传输率为 40MB/s(M = 106),与主机交换数据时以 32 位字为单位。

(1) 若采用中断方式,每传送一个字中断一次,每次中断的响应和处理共需 300 个 CPU 时钟周期。CPU 用于该磁盘 I/O 的时间占比是多少?这种方式可行吗?(3 分)

(2) 改用 DMA 方式,每次 DMA 传送 4096B,预处理需 1000 个时钟周期,后处理(含中断)需 1500 个时钟周期。CPU 用于该磁盘 I/O 的时间占比是多少?(3 分)

(3) DMA 采用周期挪用方式,每传送一个字挪用一个总线周期。该总线的带宽是多少?磁盘传送占用了总线时间的百分之几?(2 分)

(4) 若同一总线上还有一个以 600MB/s 进行 DMA 传送的 SSD,两个设备能否同时以最大速率工作?此时留给 CPU 访存的总线带宽最多是多少?(2 分)

做完再点开:满分答案与评分细则

考点9.4 三种 I/O 方式9.4 DMA 与周期挪用9.1 总线带宽9.8 并发速率

(1) 中断方式
答题卡这样写

每秒中断次数 = 40 × 106B/s ÷ 4B = 107 次;每秒需要 107 × 300 = 3 × 109 个时钟周期。

占比 = 3 × 109 ÷ 109 = 300% > 100%,CPU 全部用来处理中断也来不及,会丢数据,不可行。

(2) DMA 方式
答题卡这样写

每秒 DMA 次数 = 40 × 106 ÷ 4096 ≈ 9765.6 次;每次 1000 + 1500 = 2500 个周期。

占比 = 9765.6 × 2500 ÷ 109 ≈ 2.44%。

数据块 4096B 是容量单位(212),速率 40MB/s 是 106,两者单位不同,不能约成“40MB ÷ 4KB = 104”。

(3) 总线带宽与占用
答题卡这样写

带宽 = 4B × 200 × 106/s = 800MB/s。磁盘每秒挪用 107 个总线周期,占 107 ÷ (2 × 108) = 5%(也可以按 40 ÷ 800 计算)。

(4) 两个设备并发
答题卡这样写

40 + 600 = 640MB/s ≤ 800MB/s,可以同时以最大速率工作;留给 CPU 访存的带宽最多 800 − 640 = 160MB/s(实际还要扣除仲裁等开销)。

小问得分点分值
(1)每秒中断次数 10⁷1
占比 300%1
结论:不可行(来不及、丢数据)1
(2)每秒 DMA 次数约 9765.61
每次 2500 周期1
占比约 2.44%1
(3)带宽 800MB/s1
占用 5%1
(4)可以(640 ≤ 800)1
剩余 160MB/s1
合计10

占用率 = 每秒次数 × 每次周期 ÷ 主频;中断方式算出来超过 100% 就是“不可行”的标准理由。

主线三:一条指令的一生

一条指令在流水线里走过五段,每一段都可能“出事”:阻塞、冲刷、缺页、溢出、越权;走完以后还要看一眼有没有外部中断。把这些可能性和处理方式放在一张图上,过程分析题、流水线题、异常题就是同一件事。

IF 取指PC → TLB → I-Cache ID 译码读寄存器、生成控制 EX 执行ALU / 算转移目标 MEM 访存地址 → TLB → D-Cache WB 写回写寄存器 指令结束查中断请求 正常流程:每段 1 个时钟周期(流水线中 5 条指令同时在不同段) 可能发生TLB 缺失、I-Cache 缺失取指缺页(故障) 可能发生数据相关 → 阻塞非法操作码(故障) 可能发生除 0、溢出自陷分支猜错 → 冲刷 可能发生数据缺页、越权访问D-Cache 缺失 → 等待 外部中断有请求且未屏蔽→ 中断响应 发生异常时(精确异常) ① 出事的指令和它后面已进入流水线的指令都作废,前面的指令照常完成;② 硬件保存断点:故障保存本条指令地址、自陷保存下一条指令地址; ③ 硬件保存 PSW、切到内核态,转异常处理程序;④ 处理完:缺页 → 返回本条重新执行;除 0、越权 → 不返回,内核发信号(如 SIGFPE),默认终止进程。 外部中断:只在一条指令执行完之后响应(中断周期),断点是下一条指令的地址;DMA 请求则在每个总线周期结束时就能响应,不必等整条指令。
图 13-5 一条指令的一生。上排是正常的五段;中间列出每一段可能发生的事件;下面是异常与外部中断的统一处理规则。对照 4.4 流水线、5.3 异常的分类、6.3 信号。
融合 5一条指令的一生 · 计组12 分 · 涉及题型 ④

某 32 位 RISC 处理器采用 IF、ID、EX、MEM、WB 五段流水线,按序发射。数据冒险用转发解决,但 load 指令后紧跟使用其结果的指令需阻塞 1 个周期;寄存器“前半周期写、后半周期读”;分支指令在 EX 段末确定是否转移,采用“预测不转移”,预测错误时冲刷已取入的指令。按字节编址,每条指令 4B,程序如下:

1000H  I1: lw   r1, 0(r2)
1004H  I2: add  r3, r1, r4
1008H  I3: sw   r3, 4(r2)
100CH  I4: beq  r3, r0, L     ; L = 1020H
1010H  I5: sub  r6, r6, r1

(1) 找出所有的写后读(RAW)数据相关,写出相关的指令对和寄存器。(2 分)

(2) 若 I4 不转移,从 I1 取指开始到 I5 写回结束共需多少个时钟周期?画出时空图。(3 分)

(3) 若 I4 发生转移,哪些指令被冲刷?转移目标指令在第几个时钟周期取指?(2 分)

(4) 若 I1 在 MEM 段(第 4 个时钟周期)访问数据时发生缺页:这属于哪一类异常?此时流水线中的其他指令怎样处理?缺页处理完后从哪个地址重新执行?该地址由谁保存?(3 分)

(5) 若 I3 执行期间到来一个外部中断请求(中断允许),CPU 在什么时候响应?中断返回后从哪个地址继续执行?(2 分)

做完再点开:满分答案与评分细则

考点4.4 冒险的对策4.4 数阻塞4.4 精确异常5.3 异常的分类5.4 硬件与软件的分工

(1) RAW 相关
答题卡这样写

I1 → I2(r1)、I2 → I3(r3)、I2 → I4(r3)、I1 → I5(r1)。

sw 也要“读” r3(要把它存进内存),beq 要读 r3 做比较——它们都是读者;I1 → I5 的距离远,不会引起阻塞,但仍是相关。

(2) 时空图
答题卡这样写
12345678910
I1IFIDEXMEMWB
I2IFID阻EXMEMWB
I3IF阻IDEXMEMWB
I4IFIDEXMEMWB
I5IFIDEXMEMWB

I1 的数据在第 4 周期 MEM 末才取到,I2 的 EX 推迟到第 5 周期(阻塞 1 拍),I3 跟着在 IF 多停 1 拍;I2 → I3、I2 → I4 的 r3 都能转发,不再阻塞。共 10 个周期(= 5 + 4 + 1,程序模拟核对)。

(3) 转移时
答题卡这样写

I4 在第 7 周期 EX 末确定转移。此前按“预测不转移”已取入的 I5(第 6 周期取指)和 1014H 处的指令(第 7 周期取指)被冲刷;目标指令(1020H)在第 8 个周期取指。

(4) MEM 段缺页
答题卡这样写

属于故障(可恢复的内部异常)。为保证精确异常,I1 本身和它后面已进入流水线的 I2(在 ID)、I3(在 IF)全部作废,不改变任何寄存器和内存。缺页处理完后从 I1 的地址 1000H 重新执行;该断点(以及 PSW)由硬件在异常响应时保存。

常见错误:认为返回到 I2(1004H)继续——那是自陷(如 syscall)的返回方式。

(5) 外部中断
答题卡这样写

外部中断在一条指令执行结束后才响应(中断周期检查):I3 执行完毕时响应。断点是下一条指令 I4 的地址 100CH,中断返回后从 I4 继续执行。

小问得分点分值
(1)四对相关全对(每漏一对扣 0.5 分)2
(2)时空图正确(I2 阻塞 1 拍、I3 随之停顿)2
共 10 个周期1
(3)冲刷 I5 和 1014H 处指令1
目标在第 8 周期取指1
(4)故障1
I2、I3 作废1
从 1000H 重新执行,断点由硬件保存1
(5)I3 执行结束时响应1
返回到 100CH(I4)1
合计12

一条指令可能在任一段“出事”:数据相关 → 阻塞;分支猜错 → 冲刷;缺页 → 作废并重来;外部中断 → 等它做完再说。

融合 6异常、中断与信号 · 操作系统8 分 · 涉及题型 ⑬

进程 P 在用户态运行,先后遇到下列四个事件:

① 执行 mov eax, [ebx] 时,所访问的页不在内存
② 执行整数除法指令 idiv ecx 时,ecx = 0
③ 执行 syscall 指令请求 write 服务
④ P 的时间片用完时到来一次时钟中断

(1) 四个事件各属于哪一类(故障、自陷、终止、外部中断)?分别在什么时候被 CPU 检测或响应?(4 分)

(2) 四个事件处理完后,CPU 分别返回到哪里(本条指令 / 下一条指令 / 不返回)?处理之后 P 处于什么状态?(4 分)

做完再点开:满分答案与评分细则

考点5.3 中断和异常的分类5.4 一次中断的完整接力6.3 信号5.6 系统调用

(1) 类型与检测时机
答题卡这样写
事件类型何时检测 / 响应
① 缺页故障指令执行过程中(访存时)由 MMU 检测,立即处理
② 除 0故障(不可恢复)执行除法指令时由运算部件检测,立即处理
③ syscall自陷(陷阱)执行这条指令本身就是“有意”进入内核
④ 时钟中断外部中断当前指令执行结束后,在中断周期响应

除 0 按王道口径也归为“故障”,但它无法通过处理程序修复,所以不返回(见第 (2) 问);有的教材把这类归入“终止”,答题时写出“不可恢复、终止进程”就不会扣分。

(2) 返回点与进程状态
答题卡这样写
事件返回到处理后 P 的状态
① 缺页本条指令(重新执行 mov)读盘期间阻塞,读完就绪,再被调度时运行
② 除 0不返回内核向 P 发 SIGFPE 信号;P 没有捕获时默认动作是终止 → 终止态
③ syscall下一条指令write 完成前若需等待设备则阻塞;完成后返回,继续运行
④ 时钟中断下一条指令(断点)时间片用完 → 就绪态,排到就绪队列尾;以后被调度时从断点继续

④ 的返回点虽然是下一条指令,但不一定“马上”返回:调度程序会先让别的进程运行。

小问得分点分值
(1)四个事件的类型各 0.5 分2
四个事件的检测 / 响应时机各 0.5 分2
(2)四个事件的返回点各 0.5 分2
四个事件处理后 P 的状态各 0.5 分2
合计8

同一个“进内核”,返回点由类型决定:故障回本条、自陷回下一条、外部中断回断点(下一条)、不可恢复的故障不回来;信号是内核把“结果”告诉进程的方式。

13.5全真模拟卷 A(计组 + 操作系统 80 分 · 限时 100 分钟)

本卷按 408 的题号和分值出题:计组单选第 12 ~ 22 题(22 分)、操作系统单选第 23 ~ 32 题(20 分)、计组综合第 43、44 题(11 + 12 分)、操作系统综合第 45、46 题(8 + 7 分),合计 80 分,对应 408 全卷中计组 45 分、操作系统 35 分。建议用时 100 分钟:选择题 35 分钟,综合题 65 分钟。也可以在上方的工具条里选择“只做计组”(55 分钟)或“只做操作系统”(45 分钟)。所有数字和程序结论都已核对(verify/08/p13/mock_a.py、mock/a_c.c、mock/a45_pv.py)。

一、单项选择题 · 计组(第 12 ~ 22 题,每题 2 分,共 22 分)

卷 A · 12计组性能
程序 P 在主频 1.6GHz 的机器上运行需要 5s,平均 CPI 为 2。改进后主频提高到 2GHz,但由于访存等待的时钟周期数增多,平均 CPI 变为 2.4(指令条数不变)。改进后 P 的运行时间为( )。
  • A. 4s
  • B. 4.8s
  • C. 6s
  • D. 3.33s
展开解析

考点1.8 性能指标

B先由原来的数据求出指令条数,再用新的 CPI 和主频算时间。

  1. 指令条数 = 5 × 1.6 × 10⁹ ÷ 2 = 4 × 10⁹。
  2. 新时间 = 4 × 10⁹ × 2.4 ÷ (2 × 10⁹) = 4.8s。
  • A 错4s = 5 × 1.6 ÷ 2:只按主频的比例缩短,忽略了 CPI 变大。
  • B 对4.8s。
  • C 错6s = 5 × 2.4 ÷ 2:只按 CPI 变大的比例延长,忽略了主频提高。
  • D 错3.33s = 5 × (1.6/2) × (2/2.4):CPI 的比值倒过来用了,CPI 变大应使时间变长。
卷 A · 13计组C 类型转换
C 语言中 int x = -3; unsigned u = 3;(int 为 32 位补码),下列表达式中值为“真”的是( )。
Ⅰ. x < u Ⅱ. x + u == 0 Ⅲ. (int)(x * u) == -9 Ⅳ. x / 2 == (x >> 1)
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅰ、Ⅳ
  • C. 仅Ⅱ、Ⅲ
  • D. 仅Ⅱ、Ⅲ、Ⅳ
展开解析

考点2.2 类型转换2.3 移位

C有符号与无符号一起运算时,有符号数先转成无符号数(位串不变)。

  • Ⅰ 错x 转成 unsigned 是 2³² − 3 = 4294967293,大于 3,所以 x < u 为假(gcc 实测)。
  • Ⅱ 对(2³² − 3) + 3 = 2³²,按 32 位取模得 0,等于 0。
  • Ⅲ 对x × u 的低 32 位与 −9 的补码相同,强制转回 int 就是 −9。
  • Ⅳ 错x / 2 向 0 取整得 −1;x >> 1 是算术右移、向下取整得 −2,两者不等。

干扰项从哪来:A 是按数学上 −3 < 3 判 Ⅰ 为真,忘了 x 被转成了无符号数;B 是误判 Ⅰ,又以为负数的除法和算术右移结果一样;D 是以为 x / 2 和 x >> 1 总相等(只对非负数或能整除时成立)

卷 A · 14计组IEEE 754
下列 IEEE 754 单精度浮点数的机器数中,表示规格化数的是( )。
  • A. 0080 0000H
  • B. 0000 0001H
  • C. 7F80 0000H
  • D. 7FC0 0000H
展开解析

考点2.6 特殊值2.6 非规格化数

A先看阶码:全 0 → 非规格化或 0;全 1 → ∞ 或 NaN;其余才是规格化数。

  • A 对0080 0000H 的阶码为 0000 0001,是规格化数,值 = 1.0 × 2⁻¹²⁶,即最小的正规格化数。
  • B 错0000 0001H 的阶码全 0、尾数非 0,是非规格化数(最小的正非规格化数 2⁻¹⁴⁹)。
  • C 错7F80 0000H 的阶码全 1、尾数全 0,是 +∞。
  • D 错7FC0 0000H 的阶码全 1、尾数非 0,是 NaN(非数)。
卷 A · 15计组算术移位
某 8 位机器数 x = B5H 为补码表示。将 x 算术右移 2 位(移出的位丢弃),所得结果的真值是( )。
  • A. 45
  • B. −18
  • C. −13
  • D. −19
展开解析

考点2.3 算术移位

D补码算术右移:高位补符号位,相当于除以 4 后向下取整。

  1. B5H = 1011 0101B,真值 −75。
  2. 算术右移 2 位:1110 1101B = EDH,真值 −19(−75 ÷ 4 = −18.75,向下取整)。
  • A 错45 = 0010 1101B:做成了逻辑右移,高位补 0。
  • B 错−18:按 C 语言除法“向 0 取整”算的;算术右移是向下取整。
  • C 错−13:把 B5H 当成原码(−53),再按原码的数值部分右移、向 0 取整。
  • D 对−19。
卷 A · 16计组多体交叉
某存储器由 4 个存储体按低位交叉方式编址,每个存储体的存取周期为 240ns,总线传输周期为 60ns。从某地址开始连续读取 10 个字,所需时间为( )。
  • A. 2400ns
  • B. 780ns
  • C. 600ns
  • D. 840ns
展开解析

考点8.2 多体交叉

B低位交叉:第一个字要一个完整存取周期,以后每隔一个总线周期出一个字。

  1. 满足流水条件:240 = 4 × 60。
  2. 时间 = 240 + (10 − 1) × 60 = 780ns。
  • A 错2400ns = 10 × 240:按单体(或高位交叉)顺序读,没有利用多体流水。
  • B 对780ns。
  • C 错600ns = 10 × 60:以为每个字都只要一个总线周期,忘了第一个字要等满 240ns。
  • D 错840ns = 240 + 10 × 60:间隔数多算了一个,10 个字之间只有 9 个间隔。
卷 A · 17计组Cache 映射
某 Cache 共 64 行,块大小 16B,采用 2 路组相联映射,按字节编址。主存地址 1A2BH 所在的块应映射到 Cache 的第( )组。
  • A. 34
  • B. 11
  • C. 17
  • D. 2
展开解析

考点8.3 映射方式

D组号 = 主存块号 mod 组数。

  1. 块号 = 1A2BH ÷ 16 = 1A2H = 418。
  2. 组数 = 64 ÷ 2 = 32,418 mod 32 = 2。
  • A 错34 = 418 mod 64:按 64 行的直接映射算了,2 路组相联只有 32 组。
  • B 错11 = 6699 mod 32:直接拿字节地址取模,没先去掉块内地址。
  • C 错17 = (1A2BH ÷ 32) mod 32:块内地址按 5 位算了,块大小 16B 只要 4 位。
  • D 对第 2 组。
卷 A · 18计组二级页表
某系统虚拟地址 32 位,页面 4KB,采用二级页表,页目录号、页表索引各 10 位。虚拟地址 00C0 3123H 的(页目录号, 页表索引, 页内偏移)为( )。
  • A. (003H, 003H, 123H)
  • B. (00CH, 031H, 23H)
  • C. (000H, C03H, 123H)
  • D. (00CH, 03H, 123H)
展开解析

考点8.5 多级页表

A写成二进制后按 10 | 10 | 12 切,而不是按十六进制数字切。

  1. 00C0 3123H = 0000 0000 11 | 00 0000 0011 | 0001 0010 0011B。
  2. 页目录号 = 00 0000 0011B = 003H;页表索引 = 00 0000 0011B = 003H;偏移 = 123H。
  • A 对(003H, 003H, 123H)。
  • B 错按十六进制数字 3 | 3 | 2 位切开,没换成二进制按 10 | 10 | 12 位切。
  • C 错把页目录号当成 8 位、页表索引当成 12 位来切。
  • D 错把页目录号当成 12 位、页表索引当成 8 位来切。
卷 A · 19计组栈与过程调用
某机器按字节编址,栈从高地址向低地址增长,SP 指向栈顶元素。执行 call 指令前 SP = 7FF0H;call 把 4 字节的返回地址压栈后转移。call 执行后 SP 的值为( )。
  • A. 7FF4H
  • B. 7FEFH
  • C. 7FECH
  • D. 7FF0H
展开解析

考点3.3 过程调用

C压栈 = 先 SP 减 4、再写入。

  • A 错7FF4H:当成栈向高地址增长,SP 加了 4。
  • B 错7FEFH:只减了 1,忘了返回地址占 4 个字节。
  • C 对SP = 7FF0H − 4 = 7FECH,返回地址存放在 7FECH ~ 7FEFH。
  • D 错7FF0H:以为 call 不改变 SP(把返回地址存进寄存器的机器才这样,与题设不符)。
卷 A · 20计组微指令格式
某微程序控制器采用水平型微指令,共有 37 个微命令,分成 5 个互斥组,各组分别含 7、3、15、4、8 个微命令,采用字段直接编码;判断测试字段 3 位,下地址字段 9 位。微指令字长至少为( )。
  • A. 26 位
  • B. 28 位
  • C. 49 位
  • D. 25 位
展开解析

考点4.3 微指令格式4.3 编码方式

B每个字段要留一个编码表示“不发命令”:位数 = ⌈log₂(n + 1)⌉。

  1. 各字段:⌈log₂8⌉ = 3、⌈log₂4⌉ = 2、⌈log₂16⌉ = 4、⌈log₂5⌉ = 3、⌈log₂9⌉ = 4,共 16 位。
  2. 字长 = 16 + 3 + 9 = 28 位。
  • A 错26 位:每段按 ⌈log₂n⌉ 计(14 位),没留“不发命令”的编码;4 个、8 个微命令的两段因此各少了 1 位。
  • B 对28 位。
  • C 错49 位 = 37 + 12:按直接编码(每个微命令一位)算了。
  • D 错25 位 = 16 + 9:漏了 3 位判断测试字段。
卷 A · 21计组中断屏蔽字
某计算机有 L0 ~ L3 四级中断,响应优先级从高到低为 L0、L1、L2、L3。现要求处理优先级从高到低为 L1、L3、L0、L2。屏蔽字中“1”表示屏蔽,各位从左到右对应 L0、L1、L2、L3。L0 的中断服务程序中应设置的屏蔽字为( )。
  • A. 1010
  • B. 1111
  • C. 0101
  • D. 1000
展开解析

考点5.5 屏蔽字

A某级的屏蔽字:屏蔽自己和处理优先级比自己低的,不屏蔽比自己高的。

  1. 处理优先级比 L0 高的是 L1、L3 → 对应位为 0;L0 自己和比它低的 L2 → 1。
  2. 屏蔽字(L0 L1 L2 L3)= 1 0 1 0。
  • A 对1010。
  • B 错1111:按响应优先级设(L0 响应最高,就屏蔽所有),题目要的是新的处理优先级。
  • C 错0101:把 1 和 0 的含义弄反了。
  • D 错1000:只屏蔽了自己,没屏蔽处理优先级更低的 L2。
卷 A · 22计组总线带宽
某同步总线的时钟频率为 100MHz,数据线 64 位,支持突发传送。一次总线读事务依次为:1 个时钟周期送地址、4 个时钟周期连续传送数据(每周期 64 位)、1 个时钟周期释放总线。连续进行这种读事务时,总线的有效数据传输率约为( )。
  • A. 800MB/s
  • B. 640MB/s
  • C. 66.7MB/s
  • D. 533MB/s
展开解析

考点9.1 总线带宽9.2 突发传输

D一次事务 6 个周期传 32B。

  1. 每个事务:4 × 8B = 32B,用时 6 × 10ns = 60ns。
  2. 传输率 = 32B ÷ 60ns ≈ 533MB/s。
  • A 错800MB/s = 8B × 100M:峰值带宽,没扣除送地址和释放总线的 2 个周期。
  • B 错640MB/s = 32B ÷ 50ns:漏了释放总线的 1 个周期。
  • C 错66.7MB/s = 4B ÷ 60ns:把每个周期传的 64 位当成了 8 位(1 字节)。
  • D 对约 533MB/s。

一、单项选择题 · 操作系统(第 23 ~ 32 题,每题 2 分,共 20 分)

卷 A · 23操作系统内核态
下列操作中,必须在内核态下执行的是( )。
Ⅰ. 修改页表基址寄存器的值 Ⅱ. 执行陷入(trap)指令 Ⅲ. 关中断 Ⅳ. 读取通用寄存器的值
  • A. 仅Ⅰ、Ⅲ
  • B. 仅Ⅰ、Ⅱ
  • C. 仅Ⅱ、Ⅳ
  • D. 仅Ⅰ、Ⅱ、Ⅲ
展开解析

考点5.1 特权指令

A会影响整个系统安全的操作才是特权操作。

  • Ⅰ 对页表基址决定当前进程能访问哪些内存,用户若能改它就能访问别人的内存,必须在内核态执行。
  • Ⅱ 错陷入指令正是在用户态执行、用来主动进入内核的,它不是特权指令。
  • Ⅲ 对关中断会让操作系统失去对 CPU 的控制(时钟中断都进不来),是特权指令。
  • Ⅳ 错读通用寄存器是最普通的操作,用户程序随时都在做。

干扰项从哪来:B 是把“用来进入内核的陷入指令”当成了特权指令,又漏了 Ⅲ;C 是把特权与非特权完全判反:选了陷入指令和读寄存器这两个普通操作,漏了真正的特权操作 Ⅰ、Ⅲ;D 是把陷入指令也当成特权指令

卷 A · 24操作系统线程
单处理器系统中,进程 P 采用一对一模型,含 3 个内核级线程 T1、T2、T3。T1 正在运行时调用阻塞式 read 读磁盘,下列叙述中正确的是( )。
  • A. T1、T2、T3 都进入阻塞态
  • B. 只有 T1 进入阻塞态,T2 或 T3 可以被调度运行
  • C. 进程 P 的 PCB 被移入磁盘的阻塞队列
  • D. T2、T3 可以与 T1 同时在 CPU 上运行
展开解析

考点6.4 线程

B一对一模型下,内核调度的单位是内核级线程。

  • A 错这是多对一模型(用户级线程)的情况:内核只看见进程,一个线程阻塞整个进程都阻塞。
  • B 对内核看得见每个线程,T1 阻塞后可以调度同一进程的 T2 或 T3。
  • C 错阻塞的是 T1 这个调度实体,挂进阻塞队列的是 T1 的 TCB,而不是整个进程。
  • D 错单处理器同一时刻只能运行一个线程;T1 已经阻塞,也不在 CPU 上。
卷 A · 25操作系统高响应比
某批处理系统采用高响应比优先调度(非抢占)。时刻 10 时上一个作业刚好结束,后备队列中有 4 个作业:J1(到达时刻 2,运行时间 6)、J2(到达时刻 5,运行时间 2)、J3(到达时刻 8,运行时间 1)、J4(到达时刻 9,运行时间 4)。此时被选中的作业是( )。
  • A. J1
  • B. J3
  • C. J4
  • D. J2
展开解析

考点6.7 调度算法

D响应比 = (等待时间 + 要求服务时间) ÷ 要求服务时间。

  1. J1:(8 + 6) / 6 ≈ 2.33;J2:(5 + 2) / 2 = 3.5;J3:(2 + 1) / 1 = 3;J4:(1 + 4) / 4 = 1.25。
  2. 最大的是 J2。
  • A 错J1 最早到达:按先来先服务选的。
  • B 错J3 运行时间最短:按短作业优先选的。
  • C 错J4:把响应比算成了“要求服务时间 ÷ 等待时间”(4 ÷ 1 最大),公式倒过来了。
  • D 对J2,响应比 3.5。
卷 A · 26操作系统信号量
用信号量 S 管理某类资源,S 的初值为 3。某时刻 S 的值为 −2,此时该类资源的可用个数和等待该资源的进程个数分别是( )。
  • A. 0 个、2 个
  • B. 3 个、2 个
  • C. 1 个、2 个
  • D. 2 个、0 个
展开解析

考点7.3 记录型信号量

Avalue ≥ 0 时表示可用资源数;value < 0 时没有可用资源,其绝对值是等待进程数。

  • A 对值为负说明资源已全部分出去,可用 0 个;|−2| = 2 个进程在等。
  • B 错把初值 3 当成现在可用的资源数;初值只是开始时的数量。
  • C 错3 − 2 = 1:把 −2 理解成“又用掉了 2 个”,其实值本身就表示当前状态。
  • D 错把 −2 的绝对值当成可用资源数,正好弄反。
卷 A · 27操作系统死锁
系统有某类资源 m 个,被 5 个进程共享,每个进程最多需要 3 个。为保证系统不会因该类资源发生死锁,m 至少为( )。
  • A. 10
  • B. 15
  • C. 11
  • D. 6
展开解析

考点7.6 不会死锁的最少资源数

C最坏情况:每个进程都只差 1 个,此时再有 1 个就能让一个进程完成。

  1. 每个进程先拿到 3 − 1 = 2 个:共 10 个,全部卡住。
  2. 再多 1 个,某个进程就能完成并释放资源 → m ≥ 5 × 2 + 1 = 11。
  • A 错10 = 5 × 2:恰好让每个进程都差 1 个而全部等待,正是会死锁的最坏情况。
  • B 错15 = 5 × 3:满足所有进程同时的最大需求,太多了,不是“至少”。
  • C 对11:10 个时可能全部卡住,再多 1 个就一定有进程能拿够 3 个完成。
  • D 错6 = 5 + 1:以为每个进程只要先拿到 1 个即可,没有按最大需求考虑最坏情况。
卷 A · 28操作系统分区分配
某系统采用动态分区分配,空闲分区按地址从低到高依次为:起始 100K、大小 50K;起始 200K、大小 30K;起始 300K、大小 120K;起始 500K、大小 80K。依次申请 60K、25K、70K 三块内存。采用下列哪种算法时,第三个请求无法满足?( )
  • A. 首次适应
  • B. 最佳适应
  • C. 最坏适应
  • D. 三种算法都能满足
展开解析

考点8.4 动态分区分配

C逐个算法模拟。

  1. 首次适应:60K → 300K 处(剩 60K);25K → 100K 处(剩 25K);70K → 500K 处 → 都能满足。
  2. 最佳适应:60K → 500K 处(剩 20K);25K → 200K 处(剩 5K);70K → 300K 处 → 都能满足。
  3. 最坏适应:60K → 300K 处(剩 60K);25K → 500K 处(剩 55K);70K 时最大空闲区只有 60K → 无法满足(程序模拟核对)。
  • A 错首次适应第三次从低地址找到 500K 处的 80K 分区,能满足。
  • B 错最佳适应把 120K 的大分区留到了最后,70K 能放进去。
  • C 对最坏适应总挑最大的分区,把大分区切碎了,留不下 70K。
  • D 错最坏适应不能满足,见左边模拟。
卷 A · 29操作系统工作集
某进程的页面访问序列为 3, 1, 2, 1, 4, 3, 4, 2, 5, 2, 5, 1。若工作集窗口大小 Δ = 4,则访问完最后一个页面时,该进程的工作集为( )。
  • A. {1, 2, 3}
  • B. {1, 2, 5}
  • C. {1, 2, 4, 5}
  • D. {1, 2, 3, 4, 5}
展开解析

考点8.7 工作集

B工作集 = 最近 Δ 次访问中出现过的页面。

  1. 最近 4 次访问是 5, 2, 5, 1 → 工作集 = {1, 2, 5}。
  • A 错{1, 2, 3} 是从序列开头数的 4 次(3, 1, 2, 1),方向弄反了。
  • B 对{1, 2, 5}。
  • C 错{1, 2, 4, 5}:把窗口理解成“最近 4 个不同的页面”(往前数到 4 才凑够 4 个),窗口数的是访问次数。
  • D 错把整个序列出现过的页都算进去了,这是“全部访问过的页”,不是工作集。
卷 A · 30操作系统打开文件表
采用教材的两级打开文件表结构(系统打开文件表 + 每个进程的打开文件表)。进程 P1 两次调用 open 打开文件 F,进程 P2 调用一次 open 打开 F,且都未关闭。此时系统打开文件表中与 F 有关的表项数及其打开计数为( )。
  • A. 3 个表项,计数各为 1
  • B. 1 个表项,计数为 2
  • C. 2 个表项,计数各为 1
  • D. 1 个表项,计数为 3
展开解析

考点10.4 打开文件表

D系统打开文件表中一个文件只有一项;打开计数记录被打开的次数。

  • A 错以为每次 open 都新建一个系统表项;系统表里一个文件只占一项(2020-23)。
  • B 错按“打开它的进程个数”计数;P1 打开了两次,计数应把每次 open 都算上。
  • C 错按进程建系统表项;每个进程各有一张自己的打开文件表,那才是按进程的。
  • D 对1 项,打开计数 3;P1 的打开文件表中有 2 项、P2 中有 1 项,各自记读写指针。
卷 A · 31操作系统FAT
某文件系统采用 FAT(显式链接)。文件 F 的目录项中起始块号为 5,FAT 中 FAT[5] = 9,FAT[9] = 3,FAT[3] = −1(结束)。FAT 已在内存中,块号从 0 开始编。读取 F 的第 2 个逻辑块(逻辑块号从 0 开始)所在的物理块号,以及为此需要读磁盘的次数分别是( )。
  • A. 9,1 次
  • B. 3,3 次
  • C. 3,1 次
  • D. 2,1 次
展开解析

考点10.6 FAT

C沿 FAT 链在内存中查找:逻辑块 0 → 5,1 → 9,2 → 3。

  • A 错9 是逻辑块 1:把逻辑块号从 1 开始数了。
  • B 错3 次:把 FAT 当成隐式链接,以为要逐块读盘才能找到下一块;FAT 在内存里,只读目标块。
  • C 对物理块 3,读 1 次。
  • D 错以为逻辑块号就是物理块号,那是连续分配从 0 号块开始时才有的情况。
卷 A · 32操作系统单缓冲
某系统采用单缓冲区读入数据。从磁盘把一块读入缓冲区用时 T = 80μs,从缓冲区传送到用户区用时 M = 20μs,CPU 处理一块用时 C = 50μs。连续读入并处理 10 块数据,所需总时间为( )。
  • A. 1000μs
  • B. 1500μs
  • C. 870μs
  • D. 1050μs
展开解析

考点9.7 单缓冲与双缓冲

D单缓冲时每块的“周期” = max(C, T) + M,再加上第一块和最后一块的首尾时间。

  1. 第 1 块:T + M = 100μs 后进入用户区,此后每 max(80, 50) + 20 = 100μs 出一块。
  2. 总时间 = T + M + C + (10 − 1) × 100 = 80 + 20 + 50 + 900 = 1050μs(事件模拟核对)。
  • A 错1000μs = 10 × 100:每块按周期 100μs 算,漏了最后一块的 CPU 处理 50μs。
  • B 错1500μs = 10 × (80 + 20 + 50):完全串行,没有利用“CPU 处理与磁盘读入并行”。
  • C 错870μs 是双缓冲的结果(周期变为 max(T, M + C) = 80μs)。
  • D 对1050μs。

二、综合应用题(第 43 ~ 46 题,共 38 分)

做完四道题后再展开答案,按评分细则给自己打分,把分数填进每题答案末尾的“自评得分”框;点上方的“交卷”时,选择题自动判分,综合题按你填的自评分计入,计组、操作系统分别统计。

卷 A第 43 题计组 · 11 分 · 涉及题型 ①

某 64 位计算机按字节编址,采用小端方式,C 语言中 char、short、int、double 分别占 1、2、4、8 个字节,int 用补码表示,float、double 分别采用 IEEE 754 单精度、双精度格式。编译器按“每个成员按自身大小对齐、结构体总长按最大成员的大小对齐”的规则安排结构体。有如下程序段:

struct rec { char c; int i; short s; double d; } r;   // r 的起始地址为 0000 1000H
r.i = -10;
r.s = (short) r.i;
r.c = (char) 300;
r.d = r.i;
float f = 16777217;

(1) r 的各成员相对于 r 的起始地址的偏移量分别是多少?sizeof(r) 是多少?若调整成员的先后顺序,sizeof 最小可以是多少?(3 分)

(2) 执行 r.i = -10; 后,地址 0000 1004H ~ 0000 1007H 中依次存放的是什么(用十六进制表示)?(2 分)

(3) 程序段执行后,r.s 和 r.c 的值各是多少?说明理由。(2 分)

(4) r.d 的机器数是多少(用十六进制表示)?f 的机器数是多少?f 的值与 16777217 是否相等?为什么?(4 分)

做完再点开:满分答案与评分细则

考点2.7 大小端与对齐2.2 类型转换2.6 IEEE 754

(1) 对齐
答题卡这样写

c 偏移 0;i 要按 4 对齐 → 偏移 4(中间填 3 字节);s 偏移 8;d 要按 8 对齐 → 偏移 16(10 ~ 15 填充);共 24 字节,已是 8 的倍数 → sizeof(r) = 24。

按 double、int、short、char 的顺序排:0、8、12、14,共 15 字节,补齐到 8 的倍数 → 最小 16(gcc 实测)。

(2) 小端存放
答题卡这样写

−10 的 32 位补码为 FFFF FFF6H。小端方式低字节放低地址:1004H ~ 1007H 依次为 F6H、FFH、FFH、FFH。

小端只颠倒字节的顺序,字节内部的位顺序不变,不能写成 6FH。

(3) 截断
答题卡这样写

r.s:int → short 截断保留低 16 位 FFF6H,按补码读是 −10(数值没变,因为 −10 在 short 范围内)。

r.c:300 = 0000 012CH,截断保留低 8 位 2CH = 44。

(4) 浮点编码
答题卡这样写

−10 = −1.01B × 2³:S = 1,阶码 = 3 + 1023 = 1026 = 100 0000 0010B,尾数 0100…0 → r.d = C024 0000 0000 0000H。

16777217 = 2²⁴ + 1 = 1.000…0001B × 2²⁴,需要 24 位小数,float 只有 23 位:舍去的部分正好是“一半”,按就近舍入到偶数,尾数末位保持 0 → f = 1.0 × 2²⁴,阶码 24 + 127 = 151 = 1001 0111B → f = 4B80 0000H,值为 16777216,与 16777217 不相等(gcc 实测)。原因:float 只有 24 位有效位。

双精度的偏置是 1023,不是 127;阶码字段 11 位。

小问得分点分值
(1)四个偏移量正确1
sizeof = 241
最小 161
(2)F6H FFH FFH FFH(顺序正确)2
(3)r.s = −101
r.c = 44,说明截断1
(4)r.d 机器数2
f 机器数 4B800000H1
不相等,说明有效位不足 / 舍入1
合计11

类型转换题的三件套:宽度变化(截断 / 扩展)、表示方法变化(整数 ↔ 浮点、舍入)、存放方式(大小端、对齐)。

卷 A第 44 题计组 · 12 分 · 涉及题型 ⑤

某计算机按字节编址,主存地址 32 位。数据 Cache 的数据区容量 2KB,采用 2 路组相联映射,块大小 32B,LRU 替换,写回法,写不命中时采用写分配,初始为空。int 型占 4 字节。二维数组 A、B 均为 int[32][32],按行优先存放,A 的起始地址为 0001 0000H,B 紧接在 A 之后。有如下程序(i、j 都在寄存器中,只有对 A、B 的访问经过数据 Cache):

for (i = 0; i < 32; i++)
    for (j = 0; j < 32; j++)
        B[j][i] = A[i][j];          // 矩阵转置:读 A 一次、写 B 一次

(1) 主存地址中 Cache 的标记、组号、块内地址各占几位?(2 分)

(2) A[i][j] 与 B[i][j] 是否映射到同一个 Cache 组?为什么?(2 分)

(3) 执行该程序时,读 A 的命中率和写 B 的命中率各是多少?说明理由。(4 分)

(4) 改用 8 × 8 分块转置:外两层循环按 8 × 8 的子块遍历,内两层在子块内按行读 A、按列写 B。写 B 的缺失次数和命中率各变为多少?为什么分块能提高命中率?(4 分)

做完再点开:满分答案与评分细则

考点8.3 地址划分8.3 组相联11.6 按行与按列8.9 程序的局部性

(1) 地址划分
答题卡这样写

块 32B → 块内地址 5 位;行数 2KB ÷ 32B = 64,组数 64 ÷ 2 = 32 → 组号 5 位;标记 32 − 5 − 5 = 22 位。

(2) 是否同组
答题卡这样写

B 的起始地址比 A 大 32 × 32 × 4B = 4KB。组号由地址的第 9 ~ 5 位决定,地址每增加 32 组 × 32B = 1KB,组号就循环一圈;4KB 是 1KB 的整数倍,所以 A[i][j] 和 B[i][j] 地址相差 4KB、组号相同,映射到同一组。

(3) 命中率
答题卡这样写

读 A:按行访问,一块装 8 个 int,每块第一次访问缺失、后 7 次命中(这一块每次循环都被访问,在 LRU 下不会被换出)→ 命中率 7/8 = 87.5%。

写 B:按列访问,B[j][i] 与 B[j+1][i] 相差一行 128B,每次都落在不同的块。固定 i 时一列要用到 32 个块;块号相差 4(128B ÷ 32B),组号依次相差 4,只用到 32 ÷ 4 = 8 个组,每组要放 4 个块,而每组只有 2 行(还要与 A 的块共用)。等到下一列 i + 1 要用同一批块时,它们早已被替换 → 每次写都缺失,命中率 0(模拟核对:读 A 896 / 1024,写 B 0 / 1024)。

常见错误:只看“块里有 8 个元素”就写 B 的命中率也是 7/8;按列访问要先算“同一组要放几个块、放不放得下”。

(4) 分块
答题卡这样写

在一个 8 × 8 子块里,写 B 的 8 列只涉及 B 的 8 行,每行的 8 个元素正好在同一块中:8 个块分在 8 个不同的组,每组只放 1 块 B(加上 A 的块也不超过 2 路),处理完这个子块之前不会被换出。于是 B 的每个块只在第一次写时缺失,缺失次数 = B 的块数 = 32 × 32 ÷ 8 = 128 次,命中率 = 1 − 128/1024 = 87.5%(模拟核对)。

原因:分块把“访问同一批块”的时间拉近了,让被访问的块在被替换之前就把它用完(提高了时间局部性)。

小问得分点分值
(1)标记 22、组号 5、块内 52
(2)同组,理由(相差 4KB 是 1KB 的整数倍)2
(3)读 A 87.5% 及理由2
写 B 0 及理由(每组要放的块数超过 2 路)2
(4)缺失 128 次、命中率 87.5%2
说明分块提高了局部性2
合计12

按列访问的 Cache 题,关键一步是“同一组要放几个块”:块号的间隔决定用到几个组,组数 × 路数决定放不放得下。

卷 A第 45 题操作系统 · 8 分 · 涉及题型 ⑧

某数据库服务器上有若干查询进程和更新进程共享数据库 DB。要求:① 多个查询进程可以同时查询 DB,但同时进行查询的进程不能超过 3 个;② 更新进程必须独占 DB,更新时不能有查询或其他更新;③ 写者优先:只要有更新进程在等待,新到的查询进程就不能开始查询。

(1) 请用信号量机制实现查询进程和更新进程的同步与互斥,说明所用信号量的含义和初值。(6 分)

(2) 若把更新进程中的两个 P 操作次序颠倒为 P(rw); P(w);,可能出现什么问题?(2 分)

做完再点开:满分答案与评分细则

考点7.4 PV 五步法11.9 PV 训练营7.3 信号量

(1) 信号量与代码
答题卡这样写
semaphore cap = 3;     // 同时进行的查询不超过 3 个
semaphore w = 1;       // 写优先:更新进程一来就先占住它,后来的查询进不了
semaphore rw = 1;      // 数据库互斥(查询作为一个整体与更新互斥)
semaphore mutex = 1;   // 保护 count
int count = 0;         // 正在查询的进程数

查询进程() {                       更新进程() {
    P(cap);                            P(w);
    P(w);                              P(rw);
    P(mutex);                          更新数据库;
    if (count == 0) P(rw);             V(rw);
    count++;                           V(w);
    V(mutex);                      }
    V(w);
    查询数据库;
    P(mutex);
    count--;
    if (count == 0) V(rw);
    V(mutex);
    V(cap);
}

思路:cap 限制同时查询的人数(计数信号量);count + mutex + rw 是读者—写者的经典结构;再加一个 w,读、写进程都先 P(w),更新进程拿到 w 之后一直到更新完才释放,期间新来的查询全被挡在 P(w) 上,从而写者优先。

程序验证:对 4 个查询进程、2 个更新进程各执行 2 轮做状态空间穷举(约 189 万个状态),没有出现超过 3 个同时查询、更新与查询同时进行、两个更新同时进行,也没有死锁(verify/08/p13/mock/a45_pv.py)。

查询进程必须在开始查询之前就 V(w),否则所有查询只能一个一个进行,违反“可以同时查询”。

(2) P 的次序颠倒
答题卡这样写

可能死锁。设查询进程 R 已 P(cap)、P(w),正在 P(mutex) 之前;更新进程 U 先执行 P(rw) 成功,再执行 P(w) 被阻塞(w 在 R 手里);R 接着做 P(mutex)、发现 count == 0,执行 P(rw) 被阻塞(rw 在 U 手里)。R 等 U 的 rw,U 等 R 的 w,形成循环等待。穷举程序也找到了这样的死锁状态。

小问得分点分值
(1)信号量 cap、w、rw、mutex 及 count 的含义与初值2
查询进程:cap 的 P / V、计数与 rw 的处理正确2
更新进程正确,且实现写者优先(先 P(w) 再 P(rw))2
(2)会死锁,并给出循环等待的过程2
合计8

复杂的读者—写者题 = 经典结构(count + mutex + rw)+ 额外约束各加一个信号量(人数上限 → 计数信号量,写者优先 → 排队用的 w)。

卷 A第 46 题操作系统 · 7 分 · 涉及题型 ⑩

某系统按字节编址,虚拟地址 32 位,页面大小 4KB,采用二级页表:页目录号 10 位、页表索引 10 位、页内偏移 12 位,页表项 4B,页目录和用到的二级页表都常驻内存。进程 P 依次访问下列虚拟地址(十六进制):

0040 2A10、0040 4C08、0080 1C30、0040 4C0C、0040 2A14、0041 2000、0080 1C34、0040 2A18、0040 3B20、0080 1C38

(1) 虚拟地址 0080 1C30H 的页目录号、页表索引、页内偏移分别是多少?(2 分)

(2) 访问上述地址,P 至少需要几张二级页表?页目录和这些二级页表共占多少内存?(2 分)

(3) 系统为 P 的数据页分配 3 个页框(初始为空,页表不占这 3 个页框),采用 LRU 置换算法。写出每次访问是否缺页(缺页时写出被淘汰的页),共缺页几次?(2 分)

(4) 若改用 FIFO 置换算法,共缺页几次?(1 分)

做完再点开:满分答案与评分细则

考点8.5 多级页表8.7 置换算法

(1) 切地址
答题卡这样写

0080 1C30H = 0000 0000 10 | 00 0000 0001 | 1100 0011 0000B:页目录号 2,页表索引 1,页内偏移 C30H。

(2) 二级页表
答题卡这样写

10 个地址的虚页号(高 20 位)为 00402、00404、00801、00404、00402、00412、00801、00402、00403、00801(十六进制),页目录号(虚页号的高 10 位)只有 1 和 2 两种 → 需要 2 张二级页表。每张页表、页目录各占 210 × 4B = 4KB,共 3 × 4KB = 12KB。

(3) LRU
答题卡这样写

页框内容(按最近使用排序)与缺页:00402 缺;00404 缺;00801 缺;00404 命中;00402 命中;00412 缺,淘汰 00801;00801 缺,淘汰 00404;00402 命中;00403 缺,淘汰 00412;00801 命中。共缺页 6 次(程序模拟核对)。

(4) FIFO
答题卡这样写

00412 进来时淘汰最早装入的 00402,于是第 8 次访问 00402 时又缺页……共缺页 7 次(程序模拟核对)。

小问得分点分值
(1)页目录号 2、页表索引 1、偏移 C30H2
(2)2 张二级页表1
共 12KB1
(3)过程正确1
6 次1
(4)7 次1
合计7

多级页表题先把虚拟地址写成二进制按位切;“需要几张二级页表”看出现了几种页目录号。

答案速查(卷 A)

计组:12 B 13 C 14 A 15 D 16 B 17 D 18 A 19 C 20 B 21 A 22 D

操作系统:23 A 24 B 25 D 26 A 27 C 28 C 29 B 30 D 31 C 32 D

综合题分值:第 43 题 11 分,第 44 题 12 分,第 45 题 8 分,第 46 题 7 分

13.6全真模拟卷 B(计组 + 操作系统 80 分 · 限时 100 分钟)

本卷题号与分值同卷 A:计组单选第 12 ~ 22 题、操作系统单选第 23 ~ 32 题,计组综合第 43、44 题(11 + 12 分),操作系统综合第 45、46 题(8 + 7 分),合计 80 分,限时 100 分钟。第 43 题的反汇编来自真实编译(WSL gcc -m32 -O0),第 44、45 题由流水线模拟和 PV 状态空间穷举核对(verify/08/p13/mock_b.py、mock/b43.c、mock/pipesim.py、mock/b45_pv.py)。

一、单项选择题 · 计组(第 12 ~ 22 题,每题 2 分,共 22 分)

卷 B · 12计组MFLOPS
某处理器主频 2GHz,支持 4 路 SIMD 单精度浮点运算(每个时钟周期最多完成 4 次浮点运算)。某程序共执行 6 × 109 次浮点运算,用时 1.5s。该程序达到的浮点性能占处理器峰值浮点性能的( )。
  • A. 50%
  • B. 200%
  • C. 25%
  • D. 75%
展开解析

考点1.8 MFLOPS

A峰值 = 主频 × 每周期浮点运算次数;实测 = 运算次数 ÷ 时间。

  1. 峰值 = 2 × 10⁹ × 4 = 8 GFLOPS。
  2. 实测 = 6 × 10⁹ ÷ 1.5s = 4 GFLOPS。
  3. 4 ÷ 8 = 50%。
  • A 对50%。
  • B 错200%:峰值没乘 SIMD 宽度 4,按 2 GFLOPS 算,超过 100% 本身就说明算错了。
  • C 错25%:峰值按“乘加算两次”取了 16 GFLOPS,题目说的是每周期最多 4 次。
  • D 错75% = 6 ÷ 8:实测值忘了除以用时 1.5s。
卷 B · 13计组乘法溢出
int 型变量 x = 65536,按 32 位补码计算 y = x * x(结果只保留低 32 位)。y 的值以及该乘法是否溢出,正确的是( )。
  • A. 0,不溢出
  • B. 0,溢出
  • C. 4294967296,不溢出
  • D. 2147483647,溢出
展开解析

考点2.5 乘法溢出

B真实乘积 2³² 需要 33 位,低 32 位全是 0。

  • A 错结果 0 对,但“结果为 0 就没溢出”是错的:判断乘法溢出要看完整乘积能否用 32 位表示,2³² 超出了 int 的范围。
  • B 对低 32 位为 0(WSL gcc -fwrapv 实测),真实乘积 4294967296 超出 int 范围,溢出。
  • C 错4294967296 = 2³² 根本放不进 32 位的 int。
  • D 错以为溢出时结果会“饱和”到最大值,补码运算只保留低位,不会饱和。
卷 B · 14计组浮点舍入
C 语言中 float f = 0.1f;(IEEE 754 单精度,就近舍入),f 在机器中实际存放的值与十进制数 0.1 相比( )。
  • A. 小于 0.1
  • B. 等于 0.1
  • C. 大于 0.1
  • D. 无法确定,与机器有关
展开解析

考点2.6 舍入2.6 精度

C0.1 = 0.000110011001100…B 无限循环,要舍入到 24 位有效位。

  1. 0.1 = 1.1001 1001 1001 1001 1001 100 | 1100…B × 2⁻⁴(竖线后是要舍去的部分)。
  2. 舍去部分 1100… 大于一半 → 进 1,尾数末段 …100 变成 …101 → 机器数 3DCC CCCDH。
  3. 实际值 0.100000001490…,大于 0.1(gcc 实测)。
  • A 错以为舍入就是截断(只舍不入);就近舍入时舍去部分超过一半要进位。
  • B 错0.1 化成二进制是无限循环小数,float 不能精确表示。
  • C 对舍入时进了 1,存的值比 0.1 略大。
  • D 错IEEE 754 规定了格式和默认舍入方式,结果在所有遵循标准的机器上都一样。
卷 B · 15计组先行进位
4 位先行进位加法器计算 A + B,A = 1011,B = 0110,C₀ = 0。设 Gᵢ = AᵢBᵢ,Pᵢ = Aᵢ ⊕ Bᵢ,C₄ = G₃ + P₃G₂ + P₃P₂G₁ + P₃P₂P₁G₀ + P₃P₂P₁P₀C₀。下列关于 C₄ 的说法正确的是( )。
  • A. C₄ = 0
  • B. C₄ = 1,由 G₃ 项产生
  • C. C₄ = 1,由 P₃P₂P₁P₀C₀ 项产生
  • D. C₄ = 1,由 P₃P₂G₁ 项产生
展开解析

考点2.4 加法器与先行进位

D先求各位的 G、P,再看哪一项为 1。

  1. G = (G₃G₂G₁G₀) = 0010,P = 1101。
  2. G₃ = 0,P₃G₂ = 0,P₃P₂G₁ = 1 · 1 · 1 = 1,C₄ = 1(核对:1011 + 0110 = 10001,确有进位)。
  • A 错只看了最高位 A₃B₃ = 1 · 0 = 0 有没有进位,忽略了低位产生、一路传上来的进位。
  • B 错G₃ = A₃B₃ = 0,第 3 位自己不产生进位。
  • C 错C₀ = 0,这一项为 0;而且 P₁ = 0。
  • D 对第 1 位产生进位(G₁ = 1),经第 2、3 位传递(P₂ = P₃ = 1)到 C₄。
卷 B · 16计组DRAM 刷新
某 DRAM 芯片的存储阵列有 1024 行,要求每行在 64ms 内至少刷新一次。若采用异步刷新,相邻两次刷新操作的时间间隔为( )。
  • A. 64ms
  • B. 62.5μs
  • C. 31.25μs
  • D. 62.5ns
展开解析

考点8.2 刷新

B异步刷新:把 64ms 平均分给 1024 行。

  1. 间隔 = 64ms ÷ 1024 = 0.0625ms = 62.5μs。
  • A 错64ms 是刷新周期(所有行都刷一遍的期限),集中刷新时才在这段时间里集中刷一次。
  • B 对62.5μs。
  • C 错31.25μs = 64ms ÷ 2048:多除了一个 2,以为每行要刷新两次。
  • D 错62.5ns:把 ms 换算成 ns 时少乘了 10³。
卷 B · 17计组写策略
某 Cache 采用全写法(直写)和非写分配策略。一段程序执行了 1000 次读和 600 次写,读命中率 90%,写命中率 80%。设读缺失时从主存调入一块记为访问主存一次,该程序执行期间访问主存的次数为( )。
  • A. 220
  • B. 820
  • C. 700
  • D. 600
展开解析

考点8.3 写策略

C全写法每次写都写主存;非写分配写缺失时不调块。

  1. 读缺失 1000 × 10% = 100 次,各调入一块:100 次。
  2. 写:全写法 600 次都要写主存(命中与否都写),非写分配不调块:600 次。
  3. 共 700 次。
  • A 错220 = 100 + 120:把写也当成只在缺失时才访存(写回法的思路)。
  • B 错820 = 100 + 600 + 120:写缺失时又调入一块,那是写分配。
  • C 对700。
  • D 错600 只算了写,漏了 100 次读缺失调块。
卷 B · 18计组段页式
某段页式虚拟存储系统的逻辑地址为:段号 8 位、段内页号 10 位、页内偏移 12 位,按字节编址。一个段的最大长度和一个进程最多拥有的页表个数分别是( )。
  • A. 4MB,256
  • B. 4KB,1024
  • C. 1MB,256
  • D. 4MB,1
展开解析

考点8.6 段页式

A段内地址 = 页号 + 偏移;每个段一张页表。

  • A 对段内地址 22 位 → 段长最大 2²² = 4MB;段号 8 位 → 最多 256 个段,每段一张页表 → 256 张。
  • B 错把段长算成一页(4KB)、页表数按段内页号位数算成 1024,把“段”和“页”混了。
  • C 错1MB = 2²⁰:段内页号和页内偏移各当成 10 位,偏移应是 12 位。
  • D 错以为整个进程只有一张页表,那是纯分页;段页式是每个段一张。
卷 B · 19计组小端与机器码
IA-32 中指令 mov eax, 0x12345678 的操作码为 B8H,其后紧跟 32 位立即数。该机器采用小端方式,这条指令按地址从低到高的机器码为( )。
  • A. B8 12 34 56 78
  • B. B8 78 56 34 12
  • C. B8 87 65 43 21
  • D. 78 56 34 12 B8
展开解析

考点2.7 小端3.3 机器级代码

B小端:立即数的最低字节放在最低地址;操作码在最前面不变。

  • A 错按大端写立即数:把最高字节 12H 放在了最低地址。
  • B 对B8 78 56 34 12(WSL as 汇编、objdump 实测)。
  • C 错把字节内部的十六进制数字也颠倒了;小端只颠倒字节的顺序。
  • D 错把操作码也当成数据一起颠倒了;指令的各字段顺序不变,只有多字节的数据按小端存放。
卷 B · 20计组单总线
某 CPU 采用单总线结构:ALU 的一个输入来自暂存器 Y,另一个来自总线,结果送暂存器 Z;MAR、MDR 与内部总线相连,MDR 还与存储器数据线相连,读一次主存占 1 个节拍,期间内部总线空闲。执行 ADD R1, (R2)(R1 ← (R1) + ((R2)))时,执行阶段至少需要的节拍数为( )。
  • A. 3
  • B. 5
  • C. 6
  • D. 4
展开解析

考点4.2 数据通路11.4 数据通路题

D一个节拍内总线只能传一个数据;读主存那一拍总线空着,可以顺便把 R1 送到 Y。

  1. ① R2out,MARin。
  2. ② 读主存:M(MAR) → MDR;同时 R1out,Yin(内部总线空闲,可并行)。
  3. ③ MDRout,ALU 加,Zin。
  4. ④ Zout,R1in。共 4 拍。
  • A 错3:以为 MDR 能直接加到 R1 上;ALU 的两个输入一个来自 Y、一个来自总线,结果必须经 Z 再送回 R1。
  • B 错5:把 R1 → Y 单独安排了一拍,没利用读主存时内部总线空闲的那一拍。
  • C 错6:又把 MDR 先送到 Y、再把 R1 经总线送 ALU,多了一次总线传送。
  • D 对4 拍。
卷 B · 21计组中断向量
某计算机的中断向量表从主存地址 0 开始存放,每个中断向量(服务程序入口地址)占 4 个字节,按字节编址。中断类型号为 2CH 的中断,其中断向量存放的地址(向量地址)是( )。
  • A. 002CH
  • B. 0058H
  • C. 00B0H
  • D. 02C0H
展开解析

考点5.4 类型号与向量地址

C向量地址 = 表的起始地址 + 类型号 × 每项字节数。

  1. 2CH × 4 = B0H → 向量地址 00B0H,该单元里存的才是服务程序的入口地址。
  • A 错002CH:直接拿类型号当地址,忘了乘每项的字节数。
  • B 错0058H = 2CH × 2:每项按 2 字节算了。
  • C 对00B0H。
  • D 错02C0H = 2CH × 16:把类型号左移了 4 位(当成十六进制多补一个 0)。
卷 B · 22计组程序查询
某计算机主频 200MHz,某外设的数据传输率为 20KB/s(K = 10³),接口的数据缓冲寄存器为 32 位。采用定时查询方式,每次查询并读取一个数据共需 400 个时钟周期。为不丢失数据,CPU 用于该外设查询的时间至少占 CPU 时间的( )。
  • A. 1%
  • B. 4%
  • C. 1.024%
  • D. 2%
展开解析

考点9.4 程序查询

A每秒至少查询(读取)的次数 = 数据率 ÷ 每次读的字节数。

  1. 每秒至少 20 × 10³ ÷ 4 = 5000 次。
  2. 5000 × 400 = 2 × 10⁶ 个周期,占 2 × 10⁶ ÷ (2 × 10⁸) = 1%。
  • A 对1%:每秒 5000 次、每次 400 周期。
  • B 错4%:以为每次只读 1 字节,查询次数多算了 4 倍。
  • C 错1.024%:把 20KB/s 按 20 × 2¹⁰ 算;题目已说明 K = 10³。
  • D 错2%:把“查询”和“读数据”各算了 400 个周期,题目说的是两者合计 400 个周期。

一、单项选择题 · 操作系统(第 23 ~ 32 题,每题 2 分,共 20 分)

卷 B · 23操作系统系统调用
下列关于系统调用的叙述中,错误的是( )。
  • A. 系统调用通过执行陷入(trap)指令进入内核
  • B. 系统调用的参数可以通过通用寄存器传递给内核
  • C. 用户程序每调用一次库函数 printf,都会立即引起一次 write 系统调用
  • D. 系统调用完成后,返回到陷入指令的下一条指令继续执行
展开解析

考点5.6 系统调用

C库函数在用户态运行,它自己决定什么时候真正发起系统调用。

  • A 对陷入指令在用户态执行,CPU 随即切到内核态。
  • B 对寄存器传参最常用,参数多时也可以放在内存里、只传地址。
  • C 错本题选它。printf 先把字符串格式化到用户态的缓冲区里,缓冲区满、遇到换行(终端)或调用 fflush 时才发起 write 系统调用。
  • D 对陷入属于自陷,断点是下一条指令。
卷 B · 24操作系统fork(拓展)
下列 C 程序段在 UNIX 系统中执行(fork 均成功,printf 立即输出),共输出几行“x”?( )
for (int i = 0; i < 2; i++)
    fork();
printf("x\n");
  • A. 2
  • B. 3
  • C. 6
  • D. 4
展开解析

考点6.3 fork

D每次 fork 后,父子进程都继续执行后面的代码。

  1. i = 0:1 个进程 fork → 2 个;i = 1:2 个各 fork → 4 个。
  2. 循环结束后 4 个进程各执行一次 printf → 4 行(WSL 实测)。
  • A 错2:只数了 fork 调用的次数。
  • B 错3:fork 次数加上原来的进程,没想到第二轮两个进程都会 fork。
  • C 错6 = 2 + 4 是 printf 写在循环里面时的结果,本题 printf 在循环之后。
  • D 对4 行。
卷 B · 25操作系统多级反馈
某系统采用三级反馈队列调度:第 1、2、3 级队列的时间片分别为 2、4、8,新进程进入第 1 级,用完时间片还没运行完就降到下一级(第 3 级用完时间片则留在第 3 级)。系统中只有一个进程 P,它需要运行 11 个时间单位。P 在第几级队列中运行结束?共被调度几次?( )
  • A. 第 2 级,2 次
  • B. 第 3 级,3 次
  • C. 第 3 级,2 次
  • D. 第 1 级,6 次
展开解析

考点6.7 多级反馈队列

B逐级扣除时间片。

  1. 第 1 级运行 2,剩 9,降到第 2 级。
  2. 第 2 级运行 4,剩 5,降到第 3 级。
  3. 第 3 级时间片 8 ≥ 5,运行完成 → 第 3 级、共 3 次。
  • A 错第 2 级结束时只运行了 2 + 4 = 6,还差 5。
  • B 对第 3 级,3 次。
  • C 错级数对了,但第 1、2、3 级各被调度一次,共 3 次。
  • D 错以为只有一个进程时就一直留在第 1 级按时间片 2 轮转;多级反馈用完时间片就降级,与有没有别的进程无关。
卷 B · 26操作系统前驱关系
四个语句的前驱关系为:S1 → S2,S1 → S3,S2 → S4,S3 → S4(箭头前面的必须先执行完)。按“每一条前驱关系设一个初值为 0 的信号量”的方法实现,共需要几个信号量?S4 之前要执行几次 P 操作?( )
  • A. 4 个,2 次
  • B. 3 个,1 次
  • C. 2 个,2 次
  • D. 4 个,1 次
展开解析

考点7.4 前驱关系

A每条边一个信号量:前驱执行完 V,后继开始前 P。

  • A 对4 条边 → 4 个信号量;S4 有两个前驱 S2、S3,要 P 两次(两个都完成才执行)。
  • B 错按结点个数减 1 设信号量,又以为 S4 只等一个前驱。
  • C 错按“有后继的结点”设信号量(S1、S2/S3 合用),这样 S2、S3 共享信号量后无法区分是谁完成。
  • D 错信号量个数对了,但 S4 只 P 一次就会在 S2、S3 只完成一个时提前执行。
卷 B · 27操作系统死锁检测
某系统有 R1、R2、R3 三类资源,数量分别为 2、2、1。某时刻四个进程的资源占有与尚需请求情况如下(括号内依次为 R1、R2、R3):P1 占有 (1, 0, 0)、请求 (0, 2, 0);P2 占有 (0, 1, 0)、请求 (2, 0, 0);P3 占有 (1, 0, 1)、请求 (0, 0, 0);P4 占有 (0, 1, 0)、请求 (0, 0, 1)。用死锁检测算法判断,发生死锁的进程是( )。
  • A. 没有死锁
  • B. P1、P2
  • C. P1、P2、P3、P4
  • D. P3、P4
展开解析

考点7.7 死锁检测

B先算 Available,再反复找“请求 ≤ Work”的进程,假设它完成并释放资源。

  1. Available = (2,2,1) − (2,2,1) = (0,0,0)。
  2. P3 请求为 0 → 完成,Work = (1,0,1);P4 请求 (0,0,1) ≤ Work → 完成,Work = (1,1,1)。
  3. P1 要 2 个 R2、P2 要 2 个 R1,都不能满足 → P1、P2 死锁(程序核对)。
  • A 错以为资源图中没有环就没死锁,或误把“请求”当成可以先满足一部分;P1、P2 各要 2 个、只能拿到 1 个,永远等下去。
  • B 对P1、P2。
  • C 错看到 Available = (0,0,0) 就判全部死锁;P3 不再请求资源,能运行完并释放。
  • D 错把能完成的进程和死锁进程弄反了:P3、P4 恰恰可以完成。
卷 B · 28操作系统分段越界
某进程的段表如下(段号:基址,段长):0:2000,500;1:3100,120;2:900,1000;3:5000,80。下列逻辑地址(段号,段内地址)中,访问时会产生越界中断的是( )。
  • A. (0, 499)
  • B. (3, 79)
  • C. (2, 1000)
  • D. (1, 119)
展开解析

考点8.6 分段的地址变换

C段内地址的合法范围是 0 ~ 段长 − 1。

  • A 错499 < 500,合法,物理地址 2499。
  • B 错79 < 80,合法,物理地址 5079。
  • C 对段 2 的段长 1000,段内地址只能是 0 ~ 999,1000 越界。
  • D 错119 < 120,合法,物理地址 3219。
卷 B · 29操作系统改进型 CLOCK
某进程有 4 个页框,采用改进型 CLOCK 置换算法(第一轮找访问位 A = 0、修改位 M = 0 的页,不修改访问位;找不到则第二轮找 A = 0、M = 1 的页,并把扫描过的页的访问位清 0;仍找不到则重复)。当前各页框依次为 P8 (A = 1, M = 0)、P3 (1, 1)、P5 (0, 1)、P6 (0, 0),指针指向 P8。此时发生缺页,被淘汰的页是( )。
  • A. P8
  • B. P3
  • C. P5
  • D. P6
展开解析

考点8.7 改进型 CLOCK

D第一轮只找 (0, 0),不改任何位。

  1. 从 P8 开始第一轮:P8 (1,0)、P3 (1,1)、P5 (0,1) 都不是,P6 (0,0) 是 → 淘汰 P6(程序模拟核对)。
  • A 错P8:按指针位置先换(FIFO 的想法),或以为第一轮就要把访问位清 0 再回到 P8。
  • B 错P3 (1,1) 刚被访问又被修改过,是最不该淘汰的。
  • C 错P5 是简单 CLOCK 的结果(只看访问位,清掉 P8、P3 后第一个 A = 0 的是 P5);改进型优先找没被修改过的 (0, 0)。
  • D 对P6:第一轮扫描就找到了 (0, 0) 的页,不必清任何访问位。
卷 B · 30操作系统目录查找
某文件系统采用树形目录,FCB 与索引结点分离,每个目录文件只占 1 个盘块;根目录的索引结点已在内存中,其他索引结点和所有目录文件都不在内存。打开 /a/b/c 并读取 c 的第 0 个数据块,共需要读磁盘几次?( )
  • A. 7
  • B. 4
  • C. 6
  • D. 8
展开解析

考点10.5 目录查找11.12 文件系统题

A每进一级目录:读该目录的索引结点 + 读目录文件;最后读 c 的索引结点和数据块。

  1. 读根目录文件(1)→ 找到 a 的索引结点号,读 a 的索引结点(2)→ 读 a 的目录文件(3)→ 读 b 的索引结点(4)→ 读 b 的目录文件(5)→ 读 c 的索引结点(6)→ 读 c 的第 0 块(7)。
  • A 对7 次。
  • B 错4:只数了三个目录文件和一个数据块,忘了 FCB 与索引结点分离时每一级都要先读索引结点。
  • C 错6:漏了最后读 c 的数据块(或漏了读 c 的索引结点)。
  • D 错8:把已在内存的根目录索引结点也算了一次读盘。
卷 B · 31操作系统位示图
某磁盘有 2 个盘片,两面都用于存储,每面 1000 个磁道,每个磁道 400 个扇区,扇区 512B。文件系统以簇为分配单位,簇大小 4KB。用位示图管理空闲簇,位示图至少占多少字节?( )
  • A. 12500B
  • B. 25000B
  • C. 200000B
  • D. 50000B
展开解析

考点10.7 位示图10.1 磁盘结构

B先算簇数:一位对应一簇。

  1. 盘面 2 × 2 = 4 个;扇区总数 4 × 1000 × 400 = 1.6 × 10⁶。
  2. 一簇 4KB = 8 个扇区 → 2 × 10⁵ 个簇 → 2 × 10⁵ 位 = 25000B。
  • A 错12500B:把 2 个盘片当成 2 个盘面,漏了“两面都用”。
  • B 对25000B。
  • C 错200000B:按扇区记位(1.6 × 10⁶ 位)或每簇用 1 字节,两种错法都得这个数。
  • D 错50000B:簇按 4 个扇区算,4KB 应是 8 个 512B 扇区。
卷 B · 32操作系统设备独立性
用户程序用逻辑设备名“printer”请求打印,系统把它映射为物理打印机 lp1。这一映射是由 I/O 软件的哪一层完成的?( )
  • A. 用户层 I/O 软件
  • B. 设备驱动程序
  • C. 设备独立性软件
  • D. 中断处理程序
展开解析

考点9.8 逻辑设备表9.5 I/O 软件层次

C逻辑设备名 → 物理设备名,靠逻辑设备表 LUT。

  • A 错用户层只负责发出请求(如调用库函数),它看到的只有逻辑设备名。
  • B 错驱动程序面对的已经是某一台具体的物理设备,只管怎么操作它的控制器。
  • C 对设备独立性软件查逻辑设备表,把逻辑名映射为物理设备,这正是“设备独立性”的实现。
  • D 错中断处理程序只在设备完成或出错时被唤起,不参与设备的选择。

二、综合应用题(第 43 ~ 46 题,共 38 分)

做完四道题后再展开答案,按评分细则给自己打分,把分数填进每题答案末尾的“自评得分”框;点上方的“交卷”时,选择题自动判分,综合题按你填的自评分计入,计组、操作系统分别统计。

卷 B第 43 题计组 · 11 分 · 涉及题型 ②

下列 C 函数统计数组 a 中负数的个数。在 IA-32 机器上用 gcc(-m32 -O0)编译后,count_neg 的反汇编如下(Intel 格式,第二列为机器码,十六进制)。

int count_neg(int a[], int n) {
    int i, c = 0;
    for (i = 0; i < n; i++)
        if (a[i] < 0) c++;
    return c;
}
08049186 <count_neg>:
 8049186:  55                     push   ebp
 8049187:  89 e5                  mov    ebp,esp
 8049189:  83 ec 10               sub    esp,0x10
 804918c:  c7 45 fc 00 00 00 00   mov    DWORD PTR [ebp-0x4],0x0
 8049193:  c7 45 f8 00 00 00 00   mov    DWORD PTR [ebp-0x8],0x0
 804919a:  eb 1d                  jmp    80491b9
 804919c:  8b 45 f8               mov    eax,DWORD PTR [ebp-0x8]
 804919f:  8d 14 85 00 00 00 00   lea    edx,[eax*4+0x0]
 80491a6:  8b 45 08               mov    eax,DWORD PTR [ebp+0x8]
 80491a9:  01 d0                  add    eax,edx
 80491ab:  8b 00                  mov    eax,DWORD PTR [eax]
 80491ad:  85 c0                  test   eax,eax
 80491af:  79 04                  jns    80491b5
 80491b1:  83 45 fc 01            add    DWORD PTR [ebp-0x4],0x1
 80491b5:  83 45 f8 01            add    DWORD PTR [ebp-0x8],0x1
 80491b9:  8b 45 f8               mov    eax,DWORD PTR [ebp-0x8]
 80491bc:  3b 45 0c               cmp    eax,DWORD PTR [ebp+0xc]
 80491bf:  7c db                  jl     804919c
 80491c1:  8b 45 fc               mov    eax,DWORD PTR [ebp-0x4]
 80491c4:  c9                     leave
 80491c5:  c3                     ret

(1) 局部变量 i、c 分别存放在什么位置(用相对 ebp 的地址表示)?参数 a、n 呢?(2 分)

(2) jl 指令的机器码为 7C DB,其中 DB 是什么?说明目标地址 804919CH 是怎样算出来的。jns 的转移目标又是怎样得到的?(3 分)

(3) 若调用 count_neg(b, 6),数组 b 中恰有 3 个负数,则地址为 80491BFH 的 jl 指令共执行几次?其中几次发生转移?地址为 80491B1H 的 add 指令执行几次?(3 分)

(4) 指令 mov eax, DWORD PTR [eax] 和 add DWORD PTR [ebp-0x4], 0x1 执行时各访问主存(数据)几次?(2 分)

(5) 从这段代码看,该处理器是 CISC 还是 RISC?给出一条依据。(1 分)

做完再点开:满分答案与评分细则

考点3.3 机器级代码3.2 相对寻址11.3 指令与机器级代码

(1) 变量与参数的位置
答题卡这样写

先执行的 mov [ebp-0x4], 0 对应 c = 0;循环变量 i 在 [ebp−0x8](它在 80491B9H 处与 n 比较、在 80491B5H 处加 1)。所以 i 在 [ebp−8]、c 在 [ebp−4]。参数 a 在 [ebp+8]、n 在 [ebp+0CH]([ebp+4] 是返回地址)。

(2) 相对转移的偏移
答题卡这样写

DB 是 8 位补码表示的相对偏移量,值为 −25H(−37)。jl 自身占 2 字节,执行时 PC 已指向下一条 80491C1H,目标 = 80491C1H − 25H = 804919CH。jns 的偏移 04:下一条地址 80491B1H + 4 = 80491B5H(跳过 c++)。

偏移量相对的是下一条指令的地址;按 jl 自身的地址 80491BFH 去减会差 2。

(3) 执行次数
答题卡这样写

开始先 jmp 到 80491B9H 判断 i < n。i = 0 ~ 5 时条件成立 6 次、i = 6 时不成立 1 次 → jl 执行 7 次,转移 6 次。80491B1H 的 c++ 只在 a[i] < 0 时执行 → 3 次。

(4) 访存次数
答题卡这样写

mov eax, [eax]:读一次 → 1 次。add [ebp−4], 1:先读出 c、加 1 后写回 → 2 次(一读一写)。

(5) CISC
答题卡这样写

CISC。依据:① 指令长度不固定(1 ~ 7 字节);② 运算指令可以直接访问存储器(如 add [ebp−4], 1、cmp eax, [ebp+0CH]),不是只有 Load / Store 才访存。

小问得分点分值
(1)i、c 的位置1
a、n 的位置1
(2)DB 是补码偏移 −37(−25H)1
jl 目标的计算(以下一条指令地址为基准)1
jns 目标 80491B5H1
(3)jl 执行 7 次1
转移 6 次1
add 执行 3 次1
(4)1 次1
2 次1
(5)CISC 及依据1
合计11

读反汇编先找循环的“骨架”:初始化 → 跳到条件判断 → 循环体 → 回到条件判断;再按“下一条指令地址 + 偏移”核对每个转移。

卷 B第 44 题计组 · 12 分 · 涉及题型 ④

某 RISC 处理器采用 IF、ID、EX、MEM、WB 五段流水线,按序发射,每段 1 个时钟周期。寄存器堆“前半周期写、后半周期读”;分支指令在 EX 段末确定是否转移,在此之前暂停取下一条指令(阻塞到确定为止)。下面的循环对数组求和(x10 指向数组,x11 为元素个数,x6 为累加和):

loop: lw   x5, 0(x10)       # I1
      add  x6, x6, x5       # I2
      addi x10, x10, 4      # I3
      addi x11, x11, -1     # I4
      bne  x11, x0, loop    # I5

(1) 写出循环中所有的写后读(RAW)相关,包括相邻两轮之间的相关。(2 分)

(2) 若不采用转发,画出第一轮循环的时空图。从一轮的 I1 取指到下一轮的 I1 取指相隔几个时钟周期?(4 分)

(3) 若采用转发(ALU 结果和 load 数据都能转发到 EX 段输入,但 load 后紧跟使用其结果的指令仍须阻塞 1 个周期),每轮相隔几个时钟周期?(3 分)

(4) 在 (3) 的条件下循环执行 100 次,共需多少个时钟周期?平均 CPI 是多少?若把 I3 调到 I1 与 I2 之间,每轮相隔几个周期?为什么调整后结果仍然正确?(3 分)

做完再点开:满分答案与评分细则

考点4.4 冒险的对策4.4 三种约定数阻塞4.4 实际 CPI11.5 流水线题

(1) RAW 相关
答题卡这样写

本轮内:I1 → I2(x5),I4 → I5(x11)。相邻两轮之间:I3 → 下一轮 I1(x10),I2 → 下一轮 I2(x6),I4 → 下一轮 I4(x11)。

只有距离很近的相关才会造成阻塞,但题目问“所有”相关时,跨轮的也要写。

(2) 不转发
答题卡这样写
12345678910111213
lwIFIDEXMEMWB
addIF阻阻IDEXMEMWB
addi x10IF阻阻IDEXMEMWB
addi x11IFIDEXMEMWB
bneIF阻阻IDEXMEMWB
下一轮 lwIF

I2 要读 I1 写的 x5:I1 在第 5 周期 WB,I2 最早在第 5 周期(后半周期)读,ID 阻塞 2 拍;I3、I4 随之后移;I5 要读 I4 写的 x11,I4 在第 10 周期 WB,I5 的 ID 在第 10 周期;I5 在第 11 周期 EX 段末确定转移,下一轮 I1 在第 12 周期取指。每轮相隔 11 个周期(流水线模拟核对)。

(3) 有转发
答题卡这样写

I2 用 I1 的 load 结果:阻塞 1 拍;I5 用 I4 的 x11:EX 段转发,不阻塞;但 I5 的 EX 在第 8 周期,下一轮 I1 第 9 周期才能取指。每轮相隔 8 个周期。

(4) 总时间与调序
答题卡这样写

100 轮共 802 个周期(最后一轮的 I5 在第 802 周期写回,程序模拟核对),500 条指令,CPI = 802 ÷ 500 ≈ 1.6。

调为 I1、I3、I2、I4、I5 后,I2 与 I1 之间隔了一条指令,load 结果可以及时转发,不再阻塞,每轮相隔 7 个周期(100 轮 702 周期)。结果仍正确:I3 改的是 x10,而 I1 在调整前后都在 I3 之前读 x10;I2 与 I3 之间没有相关。

小问得分点分值
(1)本轮两对、跨轮相关(至少写出 I3 → 下一轮 I1)2
(2)时空图正确2
11 个周期2
(3)8 个周期,说明 load-use 阻塞 1 拍、分支等 EX3
(4)802 周期、CPI 约 1.61
调序后 7 个周期1
说明调序不改变结果1
合计12

循环题按“每轮相隔几拍”算总时间:数据相关决定阻塞,分支决定下一轮何时取指;调整指令顺序是编译器的“软件插空”。

卷 B第 45 题操作系统 · 8 分 · 涉及题型 ⑧

某生产线有三类进程:生产进程 P1 每次生产 1 个零件放入缓冲区 B1(最多放 3 个零件);组装进程 P2 每次从 B1 取出 2 个零件组装成 1 个部件,放入缓冲区 B2(最多放 2 个部件);包装进程 P3 每次从 B2 取出 1 个部件包装。B1、B2 都只允许一个进程同时访问。

(1) 系统中 P1、P2、P3 各有一个,请用信号量机制实现它们的同步与互斥,说明信号量的含义和初值。(6 分)

(2) 若系统中有 3 个组装进程 P2,按 (1) 的写法可能出现什么问题?怎样修改?(2 分)

做完再点开:满分答案与评分细则

考点7.4 PV 五步法11.9 PV 训练营

(1) 信号量与代码
答题卡这样写
semaphore empty1 = 3, full1 = 0, mutex1 = 1;   // B1:空位、零件数、互斥
semaphore empty2 = 2, full2 = 0, mutex2 = 1;   // B2:空位、部件数、互斥

P1() {                    P2() {                       P3() {
  while (1) {               while (1) {                  while (1) {
    生产一个零件;              P(full1); P(full1);         P(full2);
    P(empty1);                P(mutex1);                   P(mutex2);
    P(mutex1);                从 B1 取 2 个零件;            从 B2 取 1 个部件;
    放入 B1;                  V(mutex1);                   V(mutex2);
    V(mutex1);                V(empty1); V(empty1);        V(empty2);
    V(full1);                 组装成 1 个部件;             包装;
  }                           P(empty2);                 }
}                             P(mutex2);               }
                              放入 B2;
                              V(mutex2);
                              V(full2);
                            }
                          }

要点:P2 要连续两次 P(full1),确认有 2 个零件后才进入 B1 取件;取完释放 2 个空位。同步信号量的 P 都放在互斥信号量的 P 之前。程序验证:对一个 P1、一个 P2、一个 P3 做状态空间穷举,B1、B2 都不越界、无死锁(verify/08/p13/mock/b45_pv.py)。

常见错误:P2 只做一次 P(full1) 就去取 2 个零件,B1 里只有 1 个时会取空。

(2) 多个组装进程
答题卡这样写

可能死锁:B1 中有 3 个零件时,3 个 P2 各自完成了第一次 P(full1)(full1 变为 0),都在第二次 P(full1) 上阻塞;而 B1 已满(empty1 = 0),P1 也无法再放零件。3 个 P2 都在等永远不会来的第二个零件。穷举程序找到了这种状态。

修改:增加信号量 take = 1,把两次 P(full1) 放进 P(take) … V(take) 之间,保证一个组装进程“一次拿两个”时别的组装进程不插进来(相当于 AND 型信号量 Swait(full1, full1))。穷举验证修改后无死锁。

小问得分点分值
(1)信号量含义与初值2
P1、P3 正确2
P2 两次 P(full1)、两次 V(empty1),P 的顺序正确2
(2)指出死锁及原因1
给出可行的修改1
合计8

“一次要多个资源”的进程,多次 P 之间可能被别人插进来——一个进程时没问题,多个进程就要用互斥把这几次 P 合成一次。

卷 B第 46 题操作系统 · 7 分 · 涉及题型 ⑪ ⑫

某文件系统的索引结点中有 10 个直接地址项、1 个一次间接地址项、1 个二次间接地址项,盘块大小 1KB,盘块号占 4B。磁盘每个柱面 100 个盘块,盘块号 b 所在柱面号为 ⌊b ÷ 100⌋。文件 F 的索引结点已在内存中,有关地址项为:直接地址第 0 项 = 4500;一次间接地址 = 7020,该索引块第 2 项 = 1530;二次间接地址 = 8830,该块第 0 项 = 9910,盘块 9910 的第 34 项 = 2240。

(1) 该文件系统支持的单个文件最大长度是多少 KB?(2 分)

(2) 进程依次读 F 的第 0、12、300 个逻辑块(逻辑块号从 0 开始),各由哪一级地址项索引?依次要读哪些盘块?(3 分)

(3) 磁头当前在 50 号柱面。按 (2) 的读盘顺序,磁头共移动多少个柱面?有人建议用 SSTF 把这些读盘请求重新排序以减少移动距离,可行吗?为什么?(2 分)

做完再点开:满分答案与评分细则

考点10.6 混合索引10.2 磁盘调度11.12 文件系统题

(1) 最大文件长度
答题卡这样写

每块可放 1KB ÷ 4B = 256 个盘块号。最大 = (10 + 256 + 256²) × 1KB = 65802KB。

(2) 逻辑块 → 盘块
答题卡这样写

第 0 块:直接地址第 0 项 → 读 4500。

第 12 块:12 − 10 = 2,在一次间接内 → 读索引块 7020,取第 2 项 → 读 1530。

第 300 块:300 − 10 − 256 = 34,在二次间接内,34 ÷ 256 = 0 余 34 → 读 8830 取第 0 项 9910 → 读 9910 取第 34 项 → 读 2240。

依次读:4500、7020、1530、8830、9910、2240,共 6 次。

(3) 移动距离与能否重排
答题卡这样写

柱面依次为 45、70、15、88、99、22:|50−45| + |45−70| + |70−15| + |15−88| + |88−99| + |99−22| = 5 + 25 + 55 + 73 + 11 + 77 = 246。

不能简单用 SSTF 重排:索引块必须先读出来,才知道下一个盘块号(读 7020 才知道 1530,读 8830、9910 才知道 2240),这些请求之间有依赖,不是同时摆在队列里的独立请求。(若把它们当成独立请求用 SSTF,距离只有 119,但这在本题中做不到。)改进办法是把索引块和数据块尽量放在相邻柱面上,或把常用的索引块缓存在内存里。

小问得分点分值
(1)65802KB2
(2)三个逻辑块的索引级别正确1
6 个盘块号及顺序正确2
(3)2461
不能重排及理由(有依赖关系)1
合计7

磁盘调度只能重排“已经知道地址的独立请求”;同一个文件的多级索引,必须一级一级读。

答案速查(卷 B)

计组:12 A 13 B 14 C 15 D 16 B 17 C 18 A 19 B 20 D 21 C 22 A

操作系统:23 C 24 D 25 B 26 A 27 B 28 C 29 D 30 A 31 B 32 C

综合题分值:第 43 题 11 分,第 44 题 12 分,第 45 题 8 分,第 46 题 7 分

13.7全真模拟卷 C(计组 + 操作系统 80 分 · 限时 100 分钟)

本卷题号与分值同卷 A:计组单选第 12 ~ 22 题、操作系统单选第 23 ~ 32 题,计组综合第 43、44 题(11 + 12 分),操作系统综合第 45、46 题(7 + 8 分),合计 80 分,限时 100 分钟。第 44 题的 TLB 与 Cache 并行查找、第 45 题的过程分析、第 46 题的“凑满再出发”同步,都是 13.0 预测表里的重点方向。数字和结论由 verify/08/p13/mock_c.py、mock/c46_pv.py 核对。

一、单项选择题 · 计组(第 12 ~ 22 题,每题 2 分,共 22 分)

卷 C · 12计组层次结构
下列关于计算机系统层次结构的叙述中,正确的是( )。
Ⅰ. 采用微程序控制的计算机中,微程序机器级位于传统机器语言级之下
Ⅱ. 汇编语言程序要经过汇编程序翻译成机器语言程序才能执行
Ⅲ. 操作系统级可以看作在传统机器语言级之上扩充出来的虚拟机
Ⅳ. 高级语言程序必须先编译成汇编语言程序才能执行
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅱ、Ⅲ
  • C. 仅Ⅰ、Ⅱ、Ⅲ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

考点1.4 计算机系统的层次

C层次从下到上:微程序级 → 机器语言级 → 操作系统级 → 汇编语言级 → 高级语言级。

  • Ⅰ 对每条机器指令由一段微程序解释执行,微程序级在机器语言级下面。
  • Ⅱ 对机器只认识机器语言,汇编程序(汇编器)完成翻译。
  • Ⅲ 对操作系统提供系统调用等“广义指令”,把裸机扩充成功能更强的虚拟机。
  • Ⅳ 错高级语言程序可以直接编译成机器语言目标程序,也可以由解释程序逐条解释执行,不一定经过汇编语言。

干扰项从哪来:A 是对操作系统级的定位不清楚,漏选 Ⅲ;B 是把微程序级和机器语言级的上下位置记反,漏选 Ⅰ;D 是以为编译一定要先生成汇编代码(gcc 的默认流程是这样,但不是“必须”)

卷 C · 13计组海明码
对 8 位数据采用海明码进行编码,要求能发现并纠正 1 位错误,至少需要的校验位位数是( )。
  • A. 4
  • B. 3
  • C. 5
  • D. 8
展开解析

考点2.8 校验码

Ak 位校验位要能区分“哪一位错了或没错”:2ᵏ ≥ n + k + 1。

  1. k = 3:2³ = 8 < 8 + 3 + 1 = 12,不够。
  2. k = 4:2⁴ = 16 ≥ 8 + 4 + 1 = 13 → 至少 4 位。
  • A 对4 位。
  • B 错3:只要求 2ᵏ ≥ n(8 个数据位),忘了校验位自己也可能出错、还要表示“无错”。
  • C 错5:这是能纠 1 位错、同时检 2 位错的扩展海明码(再加 1 位总校验位)所需的位数。
  • D 错8:以为每个数据位都要一个校验位。
卷 C · 14计组双符号位
采用双符号位的补码(变形补码)计算 x + y,x = 00.1011,y = 00.0110(定点小数,4 位数值位)。运算结果及溢出判断正确的是( )。
  • A. 00.0001,不溢出
  • B. 01.0001,正溢出
  • C. 10.0001,负溢出
  • D. 01.0001,不溢出
展开解析

考点2.4 溢出判断

B两个符号位不同就溢出:01 正溢出、10 负溢出。

  1. 00.1011 + 00.0110 = 01.0001。
  2. 符号位 01 → 正溢出(真值 11/16 + 6/16 = 17/16 ≥ 1)。
  • A 错只保留了一位符号位,把进到第二个符号位的 1 丢了,结果“看起来”很小。
  • B 对01.0001,正溢出。
  • C 错把两个符号位的位置看反了;第一个符号位是 0(真正的符号),第二个是 1。
  • D 错只看最高符号位 0 就认为是正数、不溢出;变形补码要比较两个符号位是否相同。
卷 C · 15计组非规格化数
实数 2−130 能否用 IEEE 754 单精度格式精确表示?若能,其机器数是( )。
  • A. 能,0008 0000H
  • B. 不能,会下溢为 0
  • C. 能,0000 0010H
  • D. 能,0080 0000H
展开解析

考点2.6 非规格化数

A最小的规格化正数是 2⁻¹²⁶,比它小的还可以用非规格化数(阶码全 0、指数按 −126、无隐藏位)表示。

  1. 2⁻¹³⁰ = 0.0001B × 2⁻¹²⁶ → 阶码全 0,尾数 0001 000…0(小数点后第 4 位为 1)。
  2. 尾数字段的值 = 2²³⁻⁴ = 2¹⁹ = 80000H → 机器数 0008 0000H(Python struct 解码核对)。
  • A 对0008 0000H。
  • B 错以为比最小规格化数 2⁻¹²⁶ 小就一定下溢;非规格化数一直能表示到 2⁻¹⁴⁹。
  • C 错0000 0010H = 2⁻¹⁴⁵:把“小数点后第 4 位”数成了尾数字段从右往左的第 4 位。
  • D 错0080 0000H 是最小的规格化数 2⁻¹²⁶,比 2⁻¹³⁰ 大 16 倍。
卷 C · 16计组DRAM 引脚
一片容量为 64K × 1 位的 DRAM 芯片,采用行、列地址分时复用(地址线复用)。该芯片的地址引脚和数据引脚至少分别为( )。
  • A. 16 根,1 根
  • B. 8 根,8 根
  • C. 16 根,8 根
  • D. 8 根,1 根
展开解析

考点8.2 DRAM 地址复用

D64K = 2¹⁶,行、列地址分两次送,引脚减半。

  • A 错16 根:忘了 DRAM 行、列地址复用,地址引脚可以减半。
  • B 错数据引脚 8 根:把 64K × 1 位看成了 64K × 8 位。
  • C 错既没有考虑地址复用,又把位宽看错了。
  • D 对地址 16 ÷ 2 = 8 根(配合 RAS、CAS),数据 1 根。
卷 C · 17计组替换算法
某 Cache 共 4 行,采用全相联映射和 LRU 替换算法,初始为空。依次访问的主存块号为 2, 5, 7, 2, 9, 5, 4, 2, 7, 9, 5, 2,命中的次数为( )。
  • A. 5
  • B. 6
  • C. 8
  • D. 4
展开解析

考点8.3 替换算法

DLRU:淘汰最久没被访问的块;命中时也要更新“最近访问时间”。

  1. 2、5、7 缺失;2 命中;9 缺失(装满);5 命中;4 缺失,淘汰 7。
  2. 2 命中;7 缺失,淘汰 9;9 缺失,淘汰 5;5 缺失,淘汰 4;2 命中。
  3. 共命中 4 次(程序模拟核对)。
  • A 错5 是 FIFO 的命中次数(命中时不更新顺序)。
  • B 错6 是 OPT 的命中次数(淘汰以后最晚用到的块)。
  • C 错8 是 LRU 的缺失次数,问的是命中次数。
  • D 对4 次。
卷 C · 18计组TLB
某系统虚拟地址 32 位,页面 4KB。TLB 共 32 项,采用 4 路组相联映射。TLB 中每一项的标记字段至少为( )。
  • A. 20 位
  • B. 15 位
  • C. 17 位
  • D. 18 位
展开解析

考点8.5 快表 TLB8.3 组相联的地址划分

CTLB 用虚页号查找:虚页号 = 组号 + 标记。

  1. 虚页号 32 − 12 = 20 位。
  2. 组数 32 ÷ 4 = 8 → 组号 3 位;标记 20 − 3 = 17 位。
  • A 错20 位是全相联 TLB 的标记(整个虚页号)。
  • B 错15 位:把 32 项都当成组(直接映射),组号取了 5 位。
  • C 对17 位。
  • D 错18 位:把“4 路”当成 4 组,组号只取了 2 位。
卷 C · 19计组相对寻址
某机器按字节编址,转移指令长 2 字节,采用相对寻址,偏移量为 8 位补码。一条位于 3000H 的转移指令,能够转移到的目标地址范围是( )。
  • A. 2F80H ~ 307FH
  • B. 2F82H ~ 3081H
  • C. 2F81H ~ 3080H
  • D. 3002H ~ 3101H
展开解析

考点3.2 相对寻址

B目标 = (PC) + 偏移,PC = 3000H + 2 = 3002H,偏移 −128 ~ +127。

  • A 错以本条指令的地址 3000H 为基准,忘了 PC 已经指向下一条。
  • B 对3002H − 80H = 2F82H,3002H + 7FH = 3081H。
  • C 错PC 只加了 1,没按指令长 2 字节更新。
  • D 错把偏移量当成 0 ~ 255 的无符号数,只能向前跳。
卷 C · 20计组微指令格式
某微程序控制器的控制存储器有 512 个单元,微指令字长 32 位,采用断定方式形成后继微地址。共有 4 个判断测试条件,每个条件在微指令中用 1 位表示。微指令中操作控制字段最多可以有( )。
  • A. 23 位
  • B. 21 位
  • C. 19 位
  • D. 20 位
展开解析

考点4.3 微指令格式

C32 位 = 操作控制字段 + 判断测试字段 + 下地址字段。

  1. 下地址字段:512 = 2⁹ → 9 位;判断测试字段 4 位。
  2. 操作控制字段 = 32 − 9 − 4 = 19 位。
  • A 错23 = 32 − 9:漏了判断测试字段。
  • B 错21:把 4 个条件编码成 2 位;题目规定每个条件用 1 位。
  • C 对19 位。
  • D 错20:下地址字段算成 8 位;512 个单元要 9 位地址。
卷 C · 21计组中断隐指令
在中断响应过程中,由中断隐指令(硬件)完成的操作是( )。
Ⅰ. 关中断 Ⅱ. 保存断点 Ⅲ. 保存通用寄存器的内容 Ⅳ. 引出中断服务程序(把入口地址送 PC)
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅱ、Ⅲ、Ⅳ
  • C. Ⅰ、Ⅱ、Ⅲ、Ⅳ
  • D. 仅Ⅰ、Ⅱ、Ⅳ
展开解析

考点5.4 硬件与软件的分工

D硬件只做“必须一气呵成、软件没法做”的几步。

  • Ⅰ 对必须先关中断,否则保存断点时又来新中断会把现场弄乱。
  • Ⅱ 对断点(PC)只能由硬件保存:一进入服务程序 PC 就变了。
  • Ⅲ 错通用寄存器由中断服务程序用指令保存(保护现场),属于软件;需要保存哪些由服务程序自己决定。
  • Ⅳ 对按中断类型号找到向量,把入口地址送 PC,由硬件完成。

干扰项从哪来:A 是漏了“引出服务程序”,以为转到服务程序是软件跳转;B 是漏了关中断,又把保护现场当成硬件的事;C 是把保存通用寄存器也算给硬件

卷 C · 22计组周期挪用
某磁盘的数据传输率为 4MB/s(M = 106),采用 DMA 方式以周期挪用方式与主存交换数据,每传送一个 32 位字挪用一个主存存取周期,存取周期为 100ns。磁盘传输期间,CPU 访存的时间被挪用的比例为( )。
  • A. 10%
  • B. 40%
  • C. 10.5%
  • D. 1%
展开解析

考点9.4 周期挪用

A每秒挪用的周期数 × 每个周期的时间 = 每秒被占用的时间。

  1. 每秒传送 4 × 10⁶ ÷ 4 = 10⁶ 个字,挪用 10⁶ 个存取周期。
  2. 10⁶ × 100ns = 0.1s → 每秒中有 10% 的主存时间给了 DMA。
  • A 对10%。
  • B 错40%:以为每传 1 个字节挪用一次,次数多算了 4 倍。
  • C 错10.5%:把 4MB/s 按 4 × 2²⁰ 算;题目说明 M = 10⁶。
  • D 错1%:存取周期按 10ns 算,单位换算错了。

一、单项选择题 · 操作系统(第 23 ~ 32 题,每题 2 分,共 20 分)

卷 C · 23操作系统操作系统引导
计算机开机后,下列操作的正确顺序是( )。
① CPU 执行 ROM 中的 BIOS 程序,进行硬件自检
② 读入磁盘第一个扇区的主引导记录(MBR),执行其中的引导程序
③ 根据分区表找到活动分区,读入其分区引导记录(PBR)
④ 引导程序把操作系统内核装入内存并转去执行
  • A. ②①③④
  • B. ①②③④
  • C. ①③②④
  • D. ①②④③
展开解析

考点1.7 操作系统引导

BROM 里的程序最先执行;分区表在 MBR 里,所以要先读 MBR 才能找分区。

  • A 错先读 MBR 再自检:开机时只有 ROM 里的 BIOS 可以执行,读磁盘本身也要 BIOS 先完成初始化。
  • B 对自检 → MBR → PBR → 装入内核。
  • C 错分区表就存放在 MBR 里,没读 MBR 就无法知道哪个是活动分区。
  • D 错内核要由活动分区的引导程序(PBR 中的)装入,必须先读 PBR。
卷 C · 24操作系统进程状态
下列事件中,会使进程从运行态转为就绪态的是( )。
  • A. 进程请求 I/O 操作
  • B. 进程执行 P 操作后信号量值小于 0
  • C. 进程的时间片用完
  • D. 进程被其他进程的 V 操作唤醒
展开解析

考点6.2 状态转换

C运行 → 就绪:进程还能运行,只是 CPU 被收回了。

  • A 错请求 I/O 要等设备完成,进程由运行态转为阻塞态。
  • B 错P 操作后 value < 0 表示资源不够,进程阻塞在该信号量的队列上。
  • C 对时间片用完(或被更高优先级抢占)时 CPU 被收回,进程回到就绪队列。
  • D 错被唤醒是由阻塞态转为就绪态,它原来并不在运行。
卷 C · 25操作系统优先级调度
三个进程的到达时刻、运行时间和优先数依次为:P1(0, 5, 1)、P2(1, 3, 3)、P3(2, 2, 2),优先数越大优先级越高。采用抢占式优先级调度,平均等待时间为( )。
  • A. 3.33
  • B. 5.67
  • C. 3.0
  • D. 2.33
展开解析

考点6.7 优先级调度

D每当有进程到达就比较优先级,更高的抢占。

  1. 0 ~ 1:P1;1 时 P2 到(优先数 3 最大)抢占,运行到 4;4 ~ 6:P3(2);6 ~ 10:P1 运行完。
  2. 等待时间:P1 = 10 − 0 − 5 = 5,P2 = 0,P3 = 6 − 2 − 2 = 2 → 平均 7 ÷ 3 ≈ 2.33(程序模拟核对)。
  • A 错3.33 是非抢占式的结果(P1 先运行完,等待 0、4、6)。
  • B 错5.67 = (10 + 3 + 4) ÷ 3 是平均周转时间,不是等待时间。
  • C 错3.0:把“优先数越大优先级越高”看反了,P1 一直不被抢占。
  • D 对约 2.33。
卷 C · 26操作系统互斥的实现
下列关于互斥实现方法的叙述中,正确的是( )。
Ⅰ. 关中断法只适用于单处理机,常用于内核中很短的临界区
Ⅱ. TestAndSet 指令的执行过程不会被中断
Ⅲ. Peterson 算法满足让权等待准则
Ⅳ. 用记录型信号量实现互斥时,进不了临界区的进程会阻塞而不忙等
  • A. 仅Ⅰ、Ⅱ、Ⅳ
  • B. 仅Ⅰ、Ⅱ
  • C. 仅Ⅱ、Ⅲ、Ⅳ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

考点7.2 软件方法7.2 硬件方法7.3 记录型信号量

A让权等待 = 进不去就让出 CPU;只有阻塞式的方法能做到。

  • Ⅰ 对多处理机上关一个 CPU 的中断挡不住别的 CPU;而且把关中断交给用户很危险。
  • Ⅱ 对TestAndSet 是一条原子指令,执行期间不响应中断(多处理机还要锁总线)。
  • Ⅲ 错Peterson 进不去时在 while 循环里空转(忙等),不满足让权等待。
  • Ⅳ 对记录型信号量的 P 操作在资源不够时调用 block 阻塞自己,让出 CPU。

干扰项从哪来:B 是漏了记录型信号量能做到让权等待;C 是以为 Peterson 满足全部四条准则,又漏了关中断法;D 是把 Peterson 也当成满足让权等待

卷 C · 27操作系统银行家算法
系统有某类资源 14 个,三个进程的最大需求和已分配数依次为:P0(9, 4)、P1(5, 3)、P2(8, 3)。下列资源请求中,按银行家算法可以立即分配的是( )。
  • A. P0 请求 3 个
  • B. P2 请求 3 个
  • C. P1 请求 2 个
  • D. P2 请求 6 个
展开解析

考点7.7 银行家算法

CAvailable = 14 − 10 = 4,Need = (5, 2, 5);逐个试分配后做安全性检查。

  1. P1 请求 2:试分配后 Available = 2,P1 的 Need = 0 → P1 完成,Available = 2 + 5 = 7 → P0(Need 5)完成 → 11 → P2 完成 → 安全,可以分配(程序核对)。
  • A 错P0 请求 3:试分配后 Available = 1,三个进程的 Need 分别为 2、2、5,没有一个能满足 → 不安全,不能分配。
  • B 错P2 请求 3:试分配后 Available = 1,Need 为 5、2、2,也找不到能完成的进程 → 不安全。
  • C 对可以分配。
  • D 错P2 的 Need 只有 5,请求 6 个超过了它声明的最大需求,直接出错拒绝。
卷 C · 28操作系统分页地址变换
某分页系统逻辑地址 16 位,页面大小 1KB,按字节编址。某进程的页表为:页 0 → 页框 5,页 1 → 页框 9,页 2 → 页框 3。逻辑地址 0A3CH 对应的物理地址是( )。
  • A. 0A3CH
  • B. 5A3CH
  • C. 323CH
  • D. 0E3CH
展开解析

考点8.5 分页的地址变换

D1KB 页 → 页内偏移 10 位。

  1. 0A3CH = 0000 10 | 10 0011 1100B:页号 2,偏移 23CH。
  2. 页框 3 → 物理地址 = 3 × 1024 + 23CH = 0C00H + 23CH = 0E3CH。
  • A 错0A3CH:没有做地址变换,直接把逻辑地址当物理地址。
  • B 错5A3CH:按 4KB 页切地址(页号 0、偏移 A3CH),再用页 0 的页框 5 拼接。
  • C 错323CH:页框号左移了 12 位(按 4KB 页拼接),偏移却按 10 位取。
  • D 对0E3CH。
卷 C · 29操作系统页表项
请求分页系统的页表项中,下列说法正确的是( )。
Ⅰ. 修改位供置换时判断是否需要把该页写回外存
Ⅱ. 访问位由操作系统在每次访存时设置
Ⅲ. 外存地址指出该页在外存中的位置,供调页时使用
Ⅳ. 存在位为 0 时访问该页会产生缺页异常
  • A. 仅Ⅰ、Ⅲ
  • B. 仅Ⅰ、Ⅲ、Ⅳ
  • C. 仅Ⅱ、Ⅳ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

考点8.7 请求分页的页表项

B每次访存都要做的事只能由硬件做。

  • Ⅰ 对没被修改过的页换出时不必写回,省一次磁盘 I/O。
  • Ⅱ 错访问位由硬件(MMU)在访存时自动置 1,操作系统只在置换算法里读它、清它;每次访存都让操作系统介入根本来不及。
  • Ⅲ 对缺页时操作系统按外存地址找到这一页,把它读入内存。
  • Ⅳ 对存在位 = 0 表示不在内存,MMU 产生缺页异常。

干扰项从哪来:A 是只记得修改位和外存地址,漏了存在位为 0 正是产生缺页的条件(Ⅳ);C 是把置位访问位当成操作系统的工作,又漏了修改位和外存地址;D 是以为访问位也由操作系统设置

卷 C · 30操作系统文件链接
用户 A 创建了文件 F,用户 B 为 F 建立了一个硬链接,用户 C 为 F 建立了一个软链接(符号链接)。之后 A 删除了 F(删除 A 目录中的目录项)。此时 B、C 能否访问该文件?F 的索引结点中链接计数为多少?( )
  • A. B 能,C 不能,计数为 1
  • B. B 能,C 能,计数为 2
  • C. B 不能,C 能,计数为 0
  • D. B 能,C 不能,计数为 2
展开解析

考点10.5 硬链接与软链接

A硬链接共享索引结点、计入链接计数;软链接只记路径名。

  • A 对建立硬链接后计数为 2;A 删除后减为 1,索引结点和数据还在,B 能访问;C 的软链接里记的是 A 目录下的路径,路径已不存在,找不到。
  • B 错以为软链接也指向同一个索引结点、也计入计数。
  • C 错把硬链接与软链接的特点弄反了。
  • D 错能否访问判断对了,但计数没有随 A 的删除减 1(或把软链接也计进去了)。
卷 C · 31操作系统磁盘调度
磁道号 0 ~ 199,磁头当前在 100 号磁道,正向磁道号减小的方向移动。请求序列为 150、32、105、61、178、20、133、90。采用 LOOK 算法,磁头移动的总磁道数为( )。
  • A. 278
  • B. 248
  • C. 311
  • D. 238
展开解析

考点10.2 磁盘调度

DLOOK:沿当前方向服务到最远的请求就回头,不走到磁盘端点。

  1. 向下:90、61、32、20(移动 100 − 20 = 80)。
  2. 回头向上:105、133、150、178(移动 178 − 20 = 158)。
  3. 共 80 + 158 = 238(模拟器核对)。
  • A 错278 是 SCAN:先走到 0 号磁道再回头(多走了 20 × 2 = 40)。
  • B 错248 是 SSTF 的结果(先到 105)。
  • C 错311 是 C-LOOK:向下服务到 20 后跳回 178 再向下服务。
  • D 对238。
卷 C · 32操作系统设备分配
下列关于设备分配的叙述中,正确的是( )。
Ⅰ. 在有通道的系统中,分配设备后还要依次分配控制器和通道,都分配成功才能启动 I/O
Ⅱ. 独占设备可以通过 SPOOLing 技术改造成可供多个进程共享的虚拟设备
Ⅲ. 采用动态分配方式时不可能发生死锁
Ⅳ. 采用安全分配方式时,进程发出 I/O 请求后便进入阻塞态,直到该 I/O 完成
  • A. 仅Ⅰ、Ⅱ
  • B. 仅Ⅱ、Ⅳ
  • C. 仅Ⅰ、Ⅱ、Ⅳ
  • D. Ⅰ、Ⅱ、Ⅲ、Ⅳ
展开解析

考点9.8 设备分配

C静态分配(一次分配全部设备)才不会死锁;动态分配要靠安全分配方式避免。

  • Ⅰ 对设备、控制器、通道三级都要分配到,I/O 才能真正进行。
  • Ⅱ 对这正是 SPOOLing 的作用:把打印机等独占设备虚拟成共享设备。
  • Ⅲ 错动态分配是“用时才申请”,进程可能占着一台设备再申请另一台,可能死锁;不会死锁的是静态分配。
  • Ⅳ 对安全分配方式让进程一发出 I/O 请求就阻塞,不会“占着设备再申请”,从而避免死锁,但进程推进较慢。

干扰项从哪来:A 是不了解安全分配方式“一请求就阻塞”的特点,漏选 Ⅳ;B 是漏了设备、控制器、通道三级分配(Ⅰ);D 是把“动态分配”当成“静态分配”,以为都不会死锁

二、综合应用题(第 43 ~ 46 题,共 38 分)

做完四道题后再展开答案,按评分细则给自己打分,把分数填进每题答案末尾的“自评得分”框;点上方的“交卷”时,选择题自动判分,综合题按你填的自评分计入,计组、操作系统分别统计。

卷 C第 43 题计组 · 11 分 · 涉及题型 ⑦

某计算机主频 500MHz,I/O 端口采用独立编址,用 IN、OUT 指令访问。键盘接口有数据端口 60H 和状态端口 64H(读 64H 时第 0 位为 1 表示有新字符;写 64H 可以向接口发控制命令)。接口中只有 1 个字节的数据缓冲,新字符到来时若上一个字符还没被取走就会丢失。

(1) 独立编址与统一编址相比有什么特点?为什么状态端口和控制端口可以共用地址 64H?(2 分)

(2) 采用定时查询方式:每次查询并在有字符时读取,共需 200 个时钟周期。用户最快每秒输入 20 个字符。为不丢失字符,查询间隔最长为多少?此时 CPU 用于查询的时间占比是多少?(3 分)

(3) 改用中断方式,键盘的中断类型号为 09H,中断向量表从地址 0 开始、每个中断向量占 4 字节。键盘的向量地址是多少?中断响应时 CPU 硬件完成哪些操作?键盘中断服务程序主要做哪些事?(3 分)

(4) 系统中还有时钟(L0)和磁盘(L1)两个中断源,键盘为 L2。硬件的响应优先级为 L1 > L0 > L2,现要求处理优先级为 L0 > L2 > L1。写出 L0、L1、L2 的中断服务程序中应设置的屏蔽字(1 表示屏蔽,从左到右对应 L0、L1、L2)。正在执行 L1 的服务程序时键盘中断到来,会怎样?(3 分)

做完再点开:满分答案与评分细则

考点9.3 I/O 接口与端口编址9.4 程序查询与中断5.4 向量地址5.5 屏蔽字

(1) 独立编址
答题卡这样写

独立编址:I/O 端口有自己的地址空间,不占主存地址,要用专门的 I/O 指令(IN / OUT)访问,端口地址位数少、程序中一眼能区分访存与 I/O;缺点是指令种类少、灵活性差。(统一编址则用访存指令访问端口,端口占用部分主存地址。)

状态端口只读、控制端口只写,读写方向不同,接口可以用读 / 写控制信号区分,所以能共用 64H。

(2) 定时查询
答题卡这样写

为不丢字符,两次查询之间最多只能来 1 个字符:间隔 ≤ 1 ÷ 20 = 50ms(每秒至少查询 20 次)。

CPU 占比 = 20 × 200 ÷ (500 × 10⁶) = 4000 ÷ 5 × 10⁸ = 0.0008%。

对慢速设备,定时查询的开销很小;真正浪费 CPU 的是“一直循环查询、什么也不干”的忙等方式。

(3) 中断方式
答题卡这样写

向量地址 = 09H × 4 = 24H(24H ~ 27H 中存放服务程序入口)。

中断响应(硬件,中断隐指令):关中断;保存断点(PC)和 PSW;按类型号从 24H 取出入口地址送 PC,转去执行服务程序。

服务程序:保护现场(保存要用到的寄存器)→(多重中断时设屏蔽字、开中断)→ 用 IN 指令从 60H 读出字符,放入键盘缓冲区,必要时唤醒等待输入的进程 → 恢复现场 → 中断返回。

(4) 屏蔽字
答题卡这样写

处理优先级 L0 > L2 > L1:每个服务程序屏蔽自己和处理优先级比自己低的。

L0:屏蔽 L0、L2、L1 → 111;L1(最低):只屏蔽自己 → 010;L2:屏蔽自己和 L1 → 011。

执行 L1 服务程序时(屏蔽字 010,且已开中断),键盘 L2 的请求不被屏蔽,CPU 会暂停 L1 的服务程序,转去处理键盘中断,处理完再回到 L1——实现了“键盘比磁盘先处理”。

屏蔽字按处理优先级设,与硬件的响应优先级无关。

小问得分点分值
(1)独立编址的特点1
共用 64H 的理由(读写方向不同)1
(2)间隔 50ms1
占比 0.0008%(列式正确)2
(3)向量地址 24H1
硬件响应的操作1
服务程序的主要步骤1
(4)三个屏蔽字2
L2 能打断 L11
合计11

I/O 题四件套:端口怎么编址、查询要多频繁、中断向量在哪、屏蔽字怎么设。

卷 C第 44 题计组 · 12 分 · 涉及题型 ⑥

某计算机按字节编址,虚拟地址 32 位,物理地址 30 位,页面大小 8KB,采用二级页表。TLB 共 32 项,采用 2 路组相联映射。L1 数据 Cache 的数据区容量 16KB,采用 2 路组相联映射,块大小 64B,用物理地址的标记进行比较。设计者让 CPU 在查 TLB 的同时就用虚拟地址的低位去选择 Cache 组。TLB 第 13 组(左)和 Cache 第 87 组(右)的内容如下:

路有效位标记页框号
01502H0F11AH
11520H1C3A5H
路有效位标记
011C3A5H
110C3A5H

(1) 虚拟地址中虚页号、页内偏移各几位?TLB 的标记、组号各几位?物理地址中 Cache 的标记、组号、块内地址各几位?(3 分)

(2) 为什么本设计可以在 TLB 完成地址变换之前就用虚拟地址选择 Cache 组?若把 Cache 容量改为 32KB(仍为 2 路组相联、块 64B),这种做法会出现什么问题?怎样调整 Cache 的组织才能继续这样做?(3 分)

(3) 访问虚拟地址 0A41 B5C8H:TLB 是否命中?物理地址是多少?Cache 是否命中?(3 分)

(4) 设查 TLB 需 1ns,读一组 Cache 并比较需 2ns;TLB 命中率 99%,缺失时查二级页表需 160ns;Cache 命中率 95%,缺失时再访问主存需 80ns。分别求“TLB 与 Cache 并行查找”和“先查 TLB 再查 Cache”两种方式的平均访存时间。(3 分)

做完再点开:满分答案与评分细则

考点8.8 一次访存的全过程8.5 TLB8.3 地址划分11.7 虚存 + TLB + Cache

(1) 字段
答题卡这样写

页面 8KB → 页内偏移 13 位,虚页号 32 − 13 = 19 位。TLB:32 ÷ 2 = 16 组 → 组号 4 位,标记 19 − 4 = 15 位。

Cache:块 64B → 块内 6 位;组数 16KB ÷ 64B ÷ 2 = 128 → 组号 7 位;标记 30 − 7 − 6 = 17 位(正好就是页框号)。

(2) 为什么能并行
答题卡这样写

Cache 的组号 + 块内地址共 7 + 6 = 13 位,恰好落在页内偏移里;页内偏移在地址变换中原样不变,所以虚拟地址的低 13 位就是物理地址的低 13 位,可以先用它选组,同时查 TLB,拿到页框号后再与该组各行的标记比较。

改为 32KB、2 路:组数 256 → 组号 8 位,组号 + 块内 = 14 位 > 13 位,组号的最高位来自虚页号,变换前后可能不同,用虚拟地址选的组可能不对(同一物理块可能出现在两个组里)。调整:把相联度提高到 4 路(32KB ÷ 64B ÷ 4 = 128 组,仍是 7 位组号),或者减小 Cache 容量。拓展:这种 Cache 叫“虚拟地址索引、物理地址标记”。

(3) 一次访存
答题卡这样写

0A41 B5C8H:页内偏移 = 低 13 位 = 15C8H,虚页号 = 520DH;TLB 组号 = 虚页号低 4 位 = 1101B = 13,标记 = 520H。第 13 组第 1 路有效且标记为 520H → TLB 命中,页框号 1C3A5H。

物理地址 = 1C3A5H × 2¹³ + 15C8H = 3874 B5C8H(30 位)。Cache 组号 = (15C8H >> 6) 的低 7 位 = 87,标记 = 1C3A5H;第 87 组第 0 路有效且标记相同 → Cache 命中。

常见错误:TLB 组号取了页内偏移的位、或把第 0 路的 502H 看成 520H。

(4) 平均访存时间
答题卡这样写

并行:命中时间 = max(1, 2) = 2ns;平均 = 2 + 0.01 × 160 + 0.05 × 80 = 7.6ns。

串行:命中时间 = 1 + 2 = 3ns;平均 = 3 + 1.6 + 4 = 8.6ns。并行查找每次访存省 1ns。

小问得分点分值
(1)虚页号 19、偏移 131
TLB 标记 15、组号 41
Cache 标记 17、组号 7、块内 61
(2)说明组号与块内地址都在页内偏移中1
32KB 时组号超出页内偏移、可能选错组1
提高相联度等调整1
(3)TLB 命中及组号、标记1
物理地址 3874B5C8H1
Cache 命中1
(4)7.6ns2
8.6ns1
合计12

TLB 与 Cache 能不能“同时查”,只看一个条件:Cache 的组号 + 块内地址是否不超过页内偏移的位数。

卷 C第 45 题操作系统 · 7 分 · 涉及题型 ⑬

单处理器系统采用非抢占式(只在当前进程阻塞、结束或时间片用完时才调度)的时间片轮转调度。开始时进程 P 在用户态运行,进程 Q 处于就绪态。之后依次发生:

t1:P 执行 read 系统调用读键盘,此时键盘缓冲区为空;
t2:用户按下一个键,键盘控制器发出中断请求(此时 Q 正在用户态运行);
t3:Q 的时间片用完,时钟中断到来;
t4:P 再次运行,read 把字符复制到用户缓冲区后返回;
t5:P 访问的数据所在页不在内存。

(1) t1 ~ t5 中,哪些时刻 CPU 由用户态进入内核态?分别由什么事件引起(系统调用、外部中断还是异常)?(3 分)

(2) 写出 P 在 t1 ~ t5 期间的状态变化。t2 时刻 P 被唤醒后为什么不立即运行?(2 分)

(3) t1 ~ t5 中,哪些时刻会发生进程切换?t5 之后 P 的状态如何变化?(2 分)

做完再点开:满分答案与评分细则

考点9.6 一次 scanf 的全过程5.2 中断和异常6.2 状态转换11.14 过程分析题

(1) 进入内核的原因
答题卡这样写

t1:P 执行陷入指令,系统调用(自陷)→ 进入内核态。t2:键盘外部中断 → Q 从用户态进入内核态执行中断服务程序。t3:时钟中断(外部中断)→ 进入内核态,调度程序运行。t5:缺页异常(故障)→ 进入内核态执行缺页处理。t4 不是“进入”内核:P 在内核态中继续执行 read,结束后返回用户态。

(2) P 的状态
答题卡这样写

t1:运行 → 阻塞(等键盘输入);t2:中断服务程序把 P 唤醒,阻塞 → 就绪;t3 之后:被调度,就绪 → 运行(t4 时在运行);t5:缺页需要读盘 → 阻塞。

t2 时 P 只是进入就绪队列:调度是非抢占式的,当前运行的 Q 时间片还没用完,中断返回后继续运行 Q。

(3) 进程切换
答题卡这样写

t1:P 阻塞,调度程序选中 Q → 切换(P → Q)。t3:Q 时间片用完回到就绪队列,调度选中 P → 切换(Q → P)。t2 的中断处理完回到 Q,不切换;t4 是 P 自己从内核态返回用户态,不切换。

t5 之后:缺页处理程序启动磁盘读页,P 由运行态变为阻塞态,调度 Q 运行(又一次切换);读完后磁盘中断唤醒 P 进入就绪态,再次被调度时重新执行引起缺页的指令。

“进入内核态”和“进程切换”是两回事:中断处理完回到原进程,只有模式切换、没有进程切换。

小问得分点分值
(1)t1 系统调用、t2 外部中断、t3 时钟中断、t5 缺页异常(每错 1 个扣 1 分)3
(2)状态序列正确1
非抢占、Q 继续运行1
(3)t1、t3 切换,t2、t4 不切换1
t5 之后阻塞 → 就绪的过程1
合计7

过程分析题列一张表:每个时刻“谁在用 CPU、CPU 什么态、为什么进内核、各进程什么状态、有没有切换”,五列填完就不会漏分。

卷 C第 46 题操作系统 · 8 分 · 涉及题型 ⑧

某渡口有一条渡船和若干乘客。规则:① 渡船最多载 3 人,必须坐满 3 人才开船;② 到对岸后,乘客才能下船;③ 3 名乘客都下船后,船才能返回此岸接下一批乘客。乘客进程的动作为“上船、下船”,船夫进程的动作为“开船到对岸、返回”。

(1) 请用信号量机制实现乘客进程和船夫进程的同步与互斥,说明信号量的含义和初值。(6 分)

(2) 若船夫不等乘客下完就返回并重新放出 3 个座位,会出现什么问题?(2 分)

做完再点开:满分答案与评分细则

考点7.4 PV 五步法7.4 新场景:屏障11.9 PV 训练营

(1) 信号量与代码
答题卡这样写
semaphore seat = 3;    // 船上的空座位
semaphore full = 0;    // 坐满 3 人,通知船夫开船
semaphore land = 0;    // 到岸,允许乘客下船
semaphore off = 0;     // 已下船的乘客数
semaphore mutex = 1;   // 保护 count
int count = 0;         // 已上船人数

乘客() {                          船夫() {
    P(seat);                         while (1) {
    P(mutex);                            P(full);           // 等满 3 人
    上船; count++;                       开船,到对岸;
    if (count == 3) V(full);             V(land); V(land); V(land);
    V(mutex);                            P(off); P(off); P(off);  // 等 3 人都下船
    P(land);                             返回;
    下船;                                P(mutex); count = 0; V(mutex);
    V(off);                              V(seat); V(seat); V(seat);
}                                    }
                                 }

思路:seat 控制上船人数(计数信号量);第 3 个上船的乘客负责 V(full) 通知船夫(凑满 3 人是一个“屏障”);land 让乘客等到岸;off 让船夫等 3 人都下船;返回后重置 count 并放出 3 个座位。程序验证:6 名乘客、船夫往返 2 次的状态空间穷举,船上不超过 3 人、开船时恰好 3 人、未到岸不下船、下完船才返回,且无死锁(verify/08/p13/mock/c46_pv.py)。

count 的重置要在放出座位之前,否则下一批第一个人上船时 count 可能还是 3。

(2) 不等下船就返回
答题卡这样写

船夫若不执行三次 P(off) 就返回并放出座位:船还在对岸、上一批乘客可能还没下船,下一批乘客就能“上船”,船上人数可能超过 3,而且新乘客可能在船没开时就收到 land 而下船。穷举程序找到了这类违例。

小问得分点分值
(1)信号量含义与初值(seat、full、land、off、mutex)2
乘客进程:限 3 人、第 3 人通知开船、等到岸再下船2
船夫进程:等满员、通知到岸、等下完、重置后放座位2
(2)指出人数超限或时序错乱2
合计8

“凑满 N 个才行动”用计数 + 第 N 个负责通知;“全部完成才继续”用 N 次 P 或计数信号量。

答案速查(卷 C)

计组:12 C 13 A 14 B 15 A 16 D 17 D 18 C 19 B 20 C 21 D 22 A

操作系统:23 B 24 C 25 D 26 A 27 C 28 D 29 B 30 A 31 D 32 C

综合题分值:第 43 题 11 分,第 44 题 12 分,第 45 题 7 分,第 46 题 8 分

13.8全书知识点细目表(满分清单)

这张表把全书压成 178 条“能直接拿来做题的结论”,按考纲顺序排列(中断、异常与系统调用单独成一组,因为它同时属于计组和操作系统)。考前一周逐条过:能说出结论、能说出理由、能举一个例子,就勾上;勾不上的点链接回原文。勾选只保存在你这台电脑的浏览器里。右列是直接考过这一条的真题(2009—2026 年计组、操作系统全部 451 道都已挂在表中),点题号可以弹出原题和讲解。

总进度:

计组 · 计算机系统概述 ()

✓知识点(结论)讲解真题
二进制、位、字节与“开 / 关”的对应;408 只考这些基本单位的换算去看2018-12
进制转换:除基取余(整数)、乘基取整(小数);二、八、十六进制按位分组互换去看—
2 的幂与单位:容量的 K/M/G = 2¹⁰/2²⁰/2³⁰,速率的 k/M/G = 10³/10⁶/10⁹去看—
地址位数 = log₂(单元数);按字节 / 按字编址时单元数不同去看—
冯·诺依曼机:五大部件、存储程序、指令和数据同等存放、按地址访问;CPU 靠指令周期的不同阶段区分指令与数据去看2009-11 2019-12
主机 = CPU + 主存;辅存、I/O 设备属于外设去看—
PC、IR、MAR、MDR、ACC 的作用;MAR 位数决定可寻址单元数,MDR 位数 = 存储字长去看—
对汇编程序员透明的寄存器(MAR、MDR、IR、微指令寄存器)与可见的寄存器(PC、通用寄存器、PSW、基址 / 变址寄存器)去看2010-18 2021-17
机器字长、指令字长、存储字长、数据总线宽度的区别与换算去看2016-18 2020-12
一条指令的执行:取指(PC → MAR → 读 → IR,PC + 1)→ 译码 → 取数 → 执行 → 写回;每条指令至少访存一次去看2011-19
计算机系统的层次:微程序机器 → 传统机器 → 操作系统 → 汇编语言 → 高级语言;软硬件逻辑功能等价去看—
编译、解释、汇编三种翻译程序:解释程序不生成目标程序去看2015-12 2016-12 2024-13
体系结构(ISA,程序员可见:指令集、寄存器、寻址方式)vs 组成(实现:乘法器、流水线、Cache)去看2022-18 2025-16 2026-12 2026-16
系列机与兼容:同一 ISA 多种实现,新机向后兼容旧机的软件去看—
CPU 时间 = 指令条数 × CPI × 时钟周期;MIPS = 主频 ÷ (CPI × 10⁶)去看2010-12 2014-12 2022-12 2023-12 2025-18
MIPS 不能比较指令集不同的机器;MFLOPS / GFLOPS / TFLOPS 数的是浮点运算次数去看2011-12 2013-12 2021-12
吞吐量、响应时间、基准程序;Amdahl 定律(拓展)去看2012-12 2017-12 2019-16

计组 · 数据的表示和运算 ()

✓知识点(结论)讲解真题
逻辑运算与位运算:与、或、异或、取反;用掩码取出 / 清除某几位去看—
原码、反码、补码、移码的定义;补码比原码多表示一个数(−2ⁿ⁻¹)去看2018-13 2021-13 2023-13
n 位定点整数、定点小数在四种编码下的表示范围去看2015-13 2022-13
补码与真值、[x]补 与 [−x]补、补码与移码(符号位取反)的速算去看—
C 语言类型转换:同宽有↔无符号位串不变;短→长按源类型扩展(符号扩展 / 零扩展);长→短截断去看2009-12 2024-12
有符号与无符号混合运算 / 比较时按无符号进行,比较结果可能“反常”去看2011-43 2012-13 2016-13 2019-13 2025-12
int → float 可能丢精度(24 位有效位),float → int 向 0 截断,int → double 精确去看—
逻辑移位补 0;算术移位:补码右移补符号位、左移可能溢出(移出位与新符号位不同)去看2013-14 2018-16 2026-13
原码、反码、补码各自的算术移位补位规则;循环移位(大循环 / 小循环)去看—
全加器、串行进位与先行进位,ALU 是组合逻辑;补码减法 A − B = A + B̄ + 1去看—
OF = Cₙ ⊕ Cₙ₋₁、SF、ZF、CF = Cout ⊕ Sub;CF 管无符号、OF 管有符号;双符号位 01 正溢出、10 负溢出去看2011-17 2014-13 2018-19 2023-16 2025-14
乘法、除法的原理与电路结构(移位 + 加减,计数器控制次数)去看2024-15
原码一位乘(符号单独异或)、补码一位乘 Booth(拓展)去看—
乘法溢出:2n 位积的高 n 位不全等于低 n 位的符号;除法:除数为 0 或 −2ⁿ⁻¹ ÷ −1 溢出去看2010-13 2020-43 2025-44
恢复余数法与加减交替法(不恢复余数法)去看—
IEEE 754 单精度 1 + 8 + 23、偏置 127,双精度 1 + 11 + 52、偏置 1023;规格化数隐含最高位 1去看—
编码四步:十进制 → 二进制 → 规格化 → 拼 S、E、M;解码反过来去看2013-13 2017-43 2020-13 2022-14 2023-14 2025-13 2026-14
阶码全 1:±∞ / NaN;阶码全 0:±0 与非规格化数(指数 −126、无隐藏位)去看2012-14 2018-14
浮点加减五步:对阶(小阶向大阶)→ 尾数加减 → 规格化(左规 / 右规)→ 舍入 → 判溢出(看阶码)去看2009-13 2015-14
就近舍入到偶数;float 间距 = 2^(指数 − 23),大数吃小数、加法不满足结合律去看2010-14 2011-13 2014-14 2021-14 2024-14
大端(高字节在低地址)/ 小端;结构体成员按自身大小对齐,总长按最大成员对齐去看2012-15 2016-14 2018-15 2019-15 2020-14 2025-15
BCD、ASCII;奇偶校验只能发现奇数个错;海明码 2ᵏ ≥ n + k + 1;CRC 模 2 除法去看2013-15

计组 · 存储器层次结构 ()

✓知识点(结论)讲解真题
时间局部性与空间局部性;存储层次“Cache—主存”管速度、“主存—辅存”管容量去看2017-14
RAM / ROM、SRAM(触发器、不刷新)/ DRAM(电容、破坏性读出、要刷新)、Flash;存取时间 < 存取周期去看2011-14 2012-16
DRAM 集中 / 分散 / 异步刷新,按行刷新;行列地址复用使地址引脚减半去看2010-16 2010-22 2014-15 2015-17 2018-17 2021-15 2022-17 2023-15
位扩展、字扩展、字位同时扩展:芯片数 = 总位数 ÷ 单片位数去看2009-15 2010-15 2011-15 2016-16 2017-13 2026-15
线选法与译码片选法;由片选位求某芯片的地址范围去看—
低位交叉(低位作体号、可流水,T + (m − 1)r)与高位交叉;单体多字、双端口 RAM(同一单元一写一读才冲突)去看2015-18
直接映射、全相联、组相联:组号 = 块号 mod 组数,全相联没有组号去看2009-14 2016-15 2026-18
地址划分:标记 + 组号 + 块内地址;Cache 总容量 = 行数 × (数据 + 标记 + 有效位 + 脏位 + 替换位)去看2010-44 2015-15 2016-45 2020-44 2021-16 2021-44 2022-16
命中率、平均访问时间的两种口径(先查后访 / 同时访问)去看2009-21 2014-16 2023-43 2025-43
替换算法 FIFO、LRU(位数 = log₂ 路数)、LFU、随机去看2012-17
写回法(脏位)+ 写分配,全写法 + 非写分配 + 写缓冲去看2015-16
多级 Cache、指令 / 数据 Cache 分离;Cache 行、TLB 项、页表项各存什么去看—
磁盘结构(盘面、磁道、扇区、柱面);访问时间 = 寻道 + 半圈旋转 + 传输;传输率 = 每秒转数 × 每道容量去看2012-32 2013-21 2015-20 2019-20 2022-44
RAID 0(条带)、RAID 1(镜像)、RAID 5(分布式校验,容量 (n − 1)C、容错 1 块)去看2013-20
SSD:页写块擦、闪存翻译层、磨损均衡(由 SSD 控制器做)、随机读快去看—

计组 · 指令系统 ()

✓知识点(结论)讲解真题
指令格式:操作码 + 地址码;零 / 一 / 二 / 三地址指令;定长与变长去看2017-16 2026-43
扩展操作码:短码不能是长码的前缀,逐级用剩余编码扩展去看2010-43 2014-17 2020-16 2021-43 2022-19
指令种类与条件码:cmp / test 设标志,条件转移看标志(无符号看 CF、有符号看 SF ⊕ OF)去看2026-17
十种寻址方式与有效地址:立即、直接、间接、寄存器、寄存器间接、基址、变址、相对、堆栈、隐含去看2023-17
基址寄存器面向系统(OS 设定)、变址寄存器面向用户(数组);相对寻址的 PC 已指向下一条去看2009-16 2011-16 2013-17 2013-44 2016-17 2017-15 2018-18
堆栈寻址:push / pop 时 SP 的变化与访存次数去看—
IA-32 常用指令与 Intel / AT&T 格式;读反汇编:循环骨架、条件转移、偏移量 = 目标 − 下一条地址去看2014-44 2024-44
过程调用:call 压返回地址、ret 弹出;栈帧中参数在 ebp + 8 起、局部变量在 ebp − 4 起去看2017-44 2019-45 2019-46 2023-44
CISC(变长、多寻址方式、微程序)vs RISC(定长、Load/Store、寄存器多、硬布线、利于流水)去看2009-17 2011-18 2025-17

计组 · 中央处理器 ()

✓知识点(结论)讲解真题
指令周期 ⊃ 机器周期 ⊃ 时钟周期;取指、间址、执行、中断四个周期的数据流去看—
单周期(CPI = 1、周期按最慢指令)、多周期、流水线 CPU 的比较去看2016-20 2020-17 2026-20
单总线、多总线、专用数据通路;单总线上一拍只能传一个数据去看2019-17 2025-19
单总线微操作序列:寄存器 → 总线 → 寄存器、读写主存、ALU 运算(Y、Z 暂存器)去看2009-44 2015-43 2015-44 2017-19 2021-18 2022-43 2024-43 2026-44
寄存器堆两读一写;组合逻辑元件 vs 时序逻辑元件去看2023-18
硬布线控制器:控制信号由组合逻辑直接产生,快、难改,RISC 多用去看—
微程序控制器:一条机器指令对应一段微程序;控制存储器(ROM)在 CPU 内;水平型 / 垂直型去看2009-19 2012-18 2014-18 2017-18
微命令编码:直接、字段直接(互斥的放同一字段、留一个不发命令的编码)、字段间接;下址字段与断定方式去看—
流水线时间 (k + n − 1)Δt;吞吐率、加速比、效率;周期 = 最慢段 + 寄存器延迟去看2009-18 2018-20
结构冒险(资源冲突,指令 / 数据 Cache 分离)、数据冒险(RAW,转发、load-use 阻塞 1 拍)、控制冒险(分支预测、延迟转移)去看2010-19 2012-44 2013-18 2016-19 2019-18 2023-19 2024-19
实际 CPI = 1 + 平均阻塞周期;数阻塞拍数的三种约定去看—
超标量、超流水线、VLIW;动态流水线:乱序执行、按序提交去看2017-17
Flynn 分类 SISD / SIMD / MISD / MIMD;向量处理器属于 SIMD去看—
硬件多线程(细粒度、粗粒度、SMT)、多核、SMP(共享单一物理地址空间)、UMA / NUMA去看2022-22

融合 · 中断、异常与系统调用(计组 + 操作系统) ()

✓知识点(结论)讲解真题
内核态 / 用户态:特权指令只能在内核态执行;用户态 → 内核态只能经中断或异常去看2011-24 2014-25 2021-23 2026-23
模式切换:越权执行特权指令引起异常;内核态 → 用户态用特权指令(中断返回)去看2022-27 2026-21
中断(外中断,来自 CPU 外)vs 异常(内中断,由当前指令引起)去看2012-23 2013-28 2015-24
故障(缺页,回本条;除 0 不可恢复)、自陷(trap,回下一条)、终止;可屏蔽 / 不可屏蔽中断去看2009-22 2015-22 2016-22 2019-14 2020-18 2020-20 2021-21 2023-21 2025-22
中断响应条件:有请求、开中断、一条指令执行结束;DMA 请求在存取周期结束时就能响应去看2018-29
中断隐指令(硬件):关中断、保存断点(PC、PSW)、引出服务程序;通用寄存器由服务程序保存去看2010-21 2012-22 2012-24 2015-23 2020-25 2026-22
中断判优(链式排队 / 软件查询);类型号 → 向量地址 → 中断向量(入口地址)去看2022-24 2023-24
多重中断:保护现场后开中断;屏蔽字(1 表示屏蔽)改变的是处理优先级,响应优先级由硬件决定去看2017-22 2020-21 2021-22 2024-21
画多重中断时间线:响应顺序 ≠ 处理顺序去看2011-21
系统调用:用户态发出(陷入指令)、内核态执行、返回下一条;参数经寄存器 / 内存传递去看2012-28 2017-24 2019-25 2021-32 2022-31 2023-26 2024-23 2024-29
系统调用 vs 库函数 vs 普通函数调用去看—

计组 · 总线与输入 / 输出 ()

✓知识点(结论)讲解真题
片内、系统(数据 / 地址 / 控制)、通信总线;总线宽度指数据线位数去看2010-20 2011-20 2012-21 2013-19 2017-20
总线带宽 = 每次传送字节数 × 每秒传送次数;复用省引脚不提速去看2009-20 2012-20 2014-19 2016-21 2018-21 2019-19 2020-19 2024-20 2025-20
总线事务:申请、仲裁、寻址、传送、释放;突发传送只送一次地址去看2013-43 2018-22 2023-20
同步定时、异步定时(不互锁 / 半互锁 / 全互锁 4 次握手)、半同步、分离式去看2012-19 2014-20 2015-19 2021-19
集中仲裁:链式 3 根、计数器 ⌈log₂n⌉ + 2 根、独立请求 2n + 1 根(拓展)去看—
I/O 接口:数据、状态、控制端口(状态与控制可共用地址);统一编址(访存指令)vs 独立编址(IN / OUT)去看2014-21 2015-21 2017-21 2021-20
程序查询(忙等、串行)、中断(每个数据中断一次)、DMA(每块中断一次);CPU 占用率 = 每秒次数 × 每次周期 ÷ 主频去看2014-22
DMA 控制器组成与传送过程:预处理 → 数据传送(不需 CPU)→ 后处理(中断);停止 CPU / 周期挪用 / 交替访存去看2009-43 2012-43 2013-22 2017-32 2018-43 2019-22 2020-22 2023-22 2024-22 2025-21
四种 I/O 控制方式(含通道)的比较去看2011-22 2016-44 2019-21 2022-21

操作系统 · 概述与程序运行环境 ()

✓知识点(结论)讲解真题
操作系统的定义、功能(处理机、存储器、设备、文件管理 + 接口)与目标去看2009-23
四个特征:并发(同一时间间隔)、共享(互斥共享 / 同时访问)、虚拟(时分 / 空分复用)、异步;并发与共享互为条件去看2018-23
命令接口(联机 / 脱机)、程序接口(系统调用)、GUI去看2010-23
单道 / 多道批处理、分时(及时、交互)、实时(硬实时 / 软实时)、网络与分布式 OS去看—
多道程序设计提高 CPU 利用率的计算去看2016-23 2017-28 2022-23
编译 → 汇编 → 链接 → 装入(编译器、汇编器、链接器的作用);静态链接、装入时动态链接、运行时动态链接去看2011-30 2022-20
绝对装入、静态重定位(装入时改地址)、动态重定位(运行时 + 重定位寄存器)去看—
进程地址空间:代码段、数据段、堆、栈、共享库、内核区;变量放在哪个段去看2023-28 2025-46
分层、模块化、宏内核、微内核(频繁切换、可靠)、外核(拓展)去看—
内核功能:时钟管理、中断处理、原语、进程 / 存储器 / 设备管理的数据结构去看2023-23
开机引导:BIOS 自检(POST)→ MBR → 活动分区 PBR → 引导程序 → 内核去看2013-29
虚拟机:第一类 VMM(裸金属)vs 第二类(宿主)去看2025-24

操作系统 · 进程与线程、调度 ()

✓知识点(结论)讲解真题
进程 = 程序段 + 数据段 + PCB;PCB 是进程存在的唯一标志;进程的特征与进程 vs 程序去看2020-29 2025-25
五状态转换:就绪 ↔ 运行 → 阻塞 → 就绪;阻塞 → 运行、就绪 → 阻塞不可能去看2014-26 2015-25 2018-27 2019-24 2022-28 2023-27
七状态:就绪挂起、阻塞挂起,中级调度去看—
创建(空白 PCB → 分配资源 → 初始化 → 就绪队列)、终止、阻塞(主动)、唤醒(被动)原语去看2010-24 2021-24
进程通信:共享存储(自己同步)、消息传递(直接 / 信箱)、管道(半双工、满写阻塞、空读阻塞)去看2014-31
信号:内核置位,返回用户态前处理;默认 / 忽略 / 捕获,SIGKILL 不能捕获;同种不排队去看—
fork 调用一次返回两次(子进程 0、父进程子 PID),循环 n 次得 2ⁿ 个进程(拓展)去看2024-24
线程是调度单位、进程是资源分配单位;线程共享地址空间,栈与寄存器私有去看2011-25 2024-28
用户级线程(内核不可见、一个阻塞全阻塞)vs 内核级线程;多对一、一对一、多对多去看2012-31 2019-23 2026-25
上下文切换:保存 / 恢复 PC、PSW、寄存器到 PCB;模式切换 ≠ 进程切换去看2021-25 2024-25 2025-23
三级调度;调度时机;中断处理、原语、内核临界区中不能调度,普通临界区可以去看2012-30 2021-27
抢占与非抢占;周转、带权周转、等待、响应时间去看—
闲逛进程、多处理机调度(负载均衡、处理器亲和性)去看—
FCFS、SJF(平均等待最短、可能饥饿)、HRRN(不饥饿)、优先级、RR(时间片过大退化为 FCFS)、多级反馈去看2009-24 2010-26 2011-23 2012-29 2013-31 2014-23 2016-24 2016-46 2017-23 2017-27 2020-26 2022-25 2026-45
画甘特图求周转、带权周转、等待时间;RR 中新到达的排在刚用完时间片的前面去看2018-24 2019-27 2023-29 2024-30

操作系统 · 同步互斥与死锁 ()

✓知识点(结论)讲解真题
临界资源与临界区;同步(直接制约)与互斥(间接制约);四准则:空闲让进、忙则等待、有限等待、让权等待去看2011-32 2016-30 2018-25 2020-32 2026-27
单标志法违反空闲让进、双标志先检查违反忙则等待、双标志后检查可能饥饿;Peterson 不满足让权等待去看2010-27
关中断(只适合单处理机内核)、TestAndSet、Swap(原子、忙等);互斥锁与自旋锁去看2016-27 2021-45 2023-45
记录型信号量:P 先减 1 再判 < 0 阻塞,V 先加 1 再判 ≤ 0 唤醒;值为负时绝对值 = 等待进程数去看2010-25 2018-32 2026-26
整型信号量忙等,记录型信号量让权等待去看—
PV 五步法:找关系 → 设信号量(互斥 1、同步 0、资源 n)→ 放 P / V → 同步 P 在前、互斥 P 在后 → 检查(V 顺序可换)去看2009-25 2009-45 2011-45 2013-45 2015-45 2017-46 2020-45 2022-46 2024-46 2025-45
经典模型:生产者—消费者、读者—写者(读优先 / 写优先)、哲学家、吸烟者、理发师去看2014-47 2019-43
多类资源 + 容量、屏障(凑满再出发)等新场景去看—
管程:任一时刻只有一个进程在管程内;条件变量 wait / signal(没有值,无等待者时 signal 无效)去看2016-32 2018-28
死锁四条件:互斥、不剥夺、请求并保持、循环等待;死锁 vs 饥饿 vs 死循环去看2014-24
资源分配图:单实例有环 ⇔ 死锁,多实例有环不一定去看—
不死锁的最少资源数 = Σ(各进程最大需求 − 1) + 1去看2016-25 2021-31
预防(破坏条件)、避免(银行家)、检测(化简资源分配图、死锁定理)、解除(剥夺、撤销、回退)去看—
银行家算法:Need = Max − Allocation;安全状态一定不死锁、不安全不一定死锁;安全序列可不唯一去看2011-27 2012-27 2013-32 2018-26 2019-30 2020-27 2022-26
死锁检测与解除去看2015-26

操作系统 · 内存管理(含计组虚存) ()

✓知识点(结论)讲解真题
逻辑地址 / 物理地址、重定位、内存保护(上下限 / 重定位 + 界地址寄存器)、覆盖与交换去看—
单一连续、固定分区(内部碎片)、动态分区(外部碎片、紧凑需动态重定位)去看2010-28 2017-25 2019-32
首次、最佳(小碎片多)、最坏、邻近适应;回收的四种合并情况;伙伴算法去看2009-26 2024-27
分页:页号 = A ÷ L、偏移 = A mod L;页表项只存页框号;物理地址 = 页框号 ∥ 偏移去看2022-15
TLB(快表,相联存储):EAT = tTLB + tm + (1 − h)tm去看2009-46 2013-16 2024-17 2026-19
多级页表:每级页表占一页,每级位数 = log₂(页大小 ÷ 表项大小);反置页表(拓展)去看2010-29 2013-46 2014-32 2015-46 2017-45 2018-45 2019-31 2020-46 2021-29 2024-45 2026-28
分段(二维地址、便于共享保护、外部碎片)vs 分页(一维);段页式访存三次去看2009-27 2016-28 2019-28 2023-30 2026-30
段式 / 页式 / 段页式虚拟存储器去看—
虚拟内存:多次性、对换性、虚拟性;最大容量受地址位数限制去看2010-17 2014-28 2024-18 2025-27
请求分页页表项:存在位、访问位、修改位、外存地址;缺页是故障,一条指令可多次缺页去看2011-28 2012-45 2013-30 2014-30 2014-45 2016-26 2020-28 2022-29 2022-30 2025-26 2026-24
OPT(不可实现)、FIFO(Belady 异常)、LRU、CLOCK、改进型 CLOCK去看2010-46 2015-27 2015-30 2019-29 2021-28
驻留集、固定 / 可变分配与局部 / 全局置换;抖动;工作集去看2011-29 2012-25 2016-29 2026-29
页面缓冲算法、预调页与请求调页;对换区 vs 文件区去看—
内存映射文件、写时复制去看2025-30
一次访存全过程:TLB → 页表 → Cache → 主存 → 缺页;TLB 与 Cache 命中互不决定,缺页时两者都不命中去看2011-44 2018-44
虚存性能:EAT 由缺页率主导;页面大小、页框数、置换算法、程序局部性;页缓冲只降缺页代价去看—
Cache—主存与主存—辅存对照:交换单位、谁管理、映射、写策略(虚存只能写回)去看2020-15 2024-16

操作系统 · 文件管理 ()

✓知识点(结论)讲解真题
FCB 与索引结点(inode 使目录项变小、检索快);文件属性去看2009-30 2017-30 2025-29
文件操作:create、open(读入 FCB、建表项、返回 fd)、read / write、close、delete去看2009-31 2014-29 2017-31 2020-23 2021-30 2023-31
逻辑结构:流式 / 记录式;顺序、索引、索引顺序、散列文件去看—
打开文件表:系统表一个文件一项(打开计数)、进程表记读写指针,fd 是进程表下标去看—
单级、两级、树形(绝对 / 相对路径)、无环图目录;目录的实现(线性表 / 哈希)去看2010-31 2013-23 2020-31 2022-45
硬链接(共享 inode、链接计数,不能跨文件系统)vs 软链接(存路径,原文件删除后失效)去看—
连续分配(随机访问、外部碎片)、隐式链接(只能顺序访问)、显式链接 FAT(在内存)、索引分配去看2015-31 2017-26
混合索引:N = 块大小 ÷ 地址项大小,最大文件 = (直接 + N + N² + …) × 块大小;访问某偏移的读盘次数去看2009-28 2010-30 2011-46 2012-46 2013-24 2013-26 2014-46 2015-29 2016-47 2018-46 2020-24
空闲表、空闲链表、位示图(块号 = 字号 × 字长 + 位号)、成组链接(超级块中的空闲块号栈)去看2014-27 2019-26 2023-25 2024-26 2025-31
文件系统布局:引导块、超级块、空闲空间管理、inode 区、根目录;内存中的挂载表、目录缓存、打开文件表去看2026-46
文件保护:口令、加密、访问控制矩阵(按列 ACL、按行权限表)去看—
文件系统层次;虚拟文件系统 VFS(统一接口);挂载去看2025-28
一次 open + read 的全过程:路径解析 → inode → 打开文件表 → 读盘块去看—

操作系统 · 输入 / 输出管理与磁盘 ()

✓知识点(结论)讲解真题
I/O 软件层次:用户层 → 设备独立性软件(逻辑名映射、缓冲、分配)→ 驱动程序(与硬件相关)→ 中断处理程序去看2011-26
块设备、字符设备、网络设备接口;阻塞、非阻塞、异步 I/O去看2022-32 2026-31
I/O 核心子系统:I/O 调度、缓冲、设备分配、假脱机、错误处理去看2012-26
一次 scanf / read 的全过程:系统调用 → 阻塞 → 中断 → 唤醒 → 复制数据 → 返回去看2010-32 2011-31 2023-46 2024-31 2026-32
单缓冲 max(C, T) + M、双缓冲 max(C + M, T);循环缓冲与缓冲池去看2013-27 2015-28 2018-31
设备分配:SDT → DCT → COCT → CHCT;静态 / 动态、安全 / 不安全分配去看2009-32 2020-30 2023-32
SPOOLing:输入井 / 输出井(磁盘)、输入 / 输出进程,把独占设备变共享设备去看2016-31
FCFS、SSTF(可能饥饿)、SCAN(到端点回头)、LOOK、C-SCAN(返回不服务,返回距离计入)、C-LOOK去看2009-29 2010-45 2013-25 2015-32 2018-30 2019-44 2021-26 2024-32
低级格式化 → 分区 → 高级格式化;引导块;扇区、块、簇;坏块(标记 / 扇区备用);交替编号去看2017-29 2021-46 2025-32