回 Jason 主站

笔试速练

30+ 题分类练习,含答案+易错点。

用法:盖住答案做题,做完对照。每题答案后含易错点提示。 配套 考前 P0 必默写清单 一起用——quiz 是覆盖广度,cheatsheet 是 P0 大题深度。


Ch1 概念辨析(8 题)

Q1.1 OS 干啥识别题

下面 4 个场景,哪些是 OS 在做的事?

A. 决定哪个程序此刻用 CPU B. 微信把消息编码成 UTF-8 发给服务器 C. 把 read() 系统调用排进磁盘 I/O 队列 D. Word 计算文档分页

答案

A、C 是 OS(资源管理);B、D 是应用(业务逻辑)。 判断口诀:凡涉及”分配/协调/管理硬件资源”的都是 OS;凡是业务逻辑的都是应用。

Q1.2 并发 vs 并行

单核 CPU 上”同时”开 5 个微信窗口,是并发还是并行?

答案

并发(同一时间段内交替推进,不是同时刻)。并行需要多核 CPU 真正同时执行。

Q1.3 系统调用必经 ___ ?

系统调用的本质是从 ___ 切换到 ___ ,触发指令是 ___ 。

答案

用户态 → 核心态,触发指令 陷入指令(trap)。系统调用是用户程序获得 OS 服务的唯一途径。

Q1.4 CPU 利用率正向求

p = 0.5(50% 时间等 I/O),n = 4 道程序,CPU 利用率 = ?

答案

1 - 0.5⁴ = 1 - 0.0625 = 93.75%

Q1.5 CPU 利用率反向求

p = 0.8,要求 CPU 利用率不低于 80%,至少需要多少道?

答案

1 - 0.8ⁿ ≥ 0.8 → 0.8ⁿ ≤ 0.2

试值:0.8⁵ ≈ 0.328 > 0.2(不够);0.8⁸ ≈ 0.168 < 0.2(够)→ n ≥ 8

Q1.6 单内核 vs 微内核

为什么微内核稳定性比单内核好?

答案

微内核把 OS 服务拆成多个用户态进程,一个服务崩溃不影响其他服务和内核;单内核所有服务在同一内核态,一个 bug 整个 OS 崩(比如 Linux 早期某个驱动 BUG 让整机重启)。代价:微内核服务间走 IPC 比单内核函数调用慢。

Q1.7 多道程序设计的本质

多道程序设计为什么能提高 CPU 利用率?(一句话答)

答案

用 I/O 等待时间换 CPU 工作时间——一个程序等 I/O 时,OS 立即把 CPU 调度给另一个就绪程序。

Q1.8 并发四特性关系

OS 四大特性(并发/共享/虚拟/异步)的关系是?

答案

并发是前提,派生出共享、虚拟、异步——没有并发就不需要共享、不需要虚拟、不会有异步。


Ch2 进程/线程/调度(10 题)

Q2.1 进程 vs 程序

Chrome 任务管理器显示 8 个进程但你只装了一个 Chrome 程序,怎么解释?

答案

硬盘上 Chrome 程序文件只有一份(静态代码),OS 把它装载到内存中产生 8 个独立的运行实例(每个有自己的内存空间、PCB、寄存器现场)——这正是”程序 vs 进程”的本质:程序静态、进程动态;一个程序可以对应多个进程。

Q2.2 状态转换反例

为什么”阻塞 → 运行”这种状态转换不存在?

答案

阻塞结束后必须先回到就绪态重新排队,因为 OS 的统一规则是”所有想用 CPU 的进程必须先在就绪队列排队”。直接阻塞→运行会绕过调度公平性

Q2.3 PCB 的关键作用

如果一个进程的 PCB 被销毁,但程序段和数据段还在内存里,OS 还认它是进程吗?

答案

不认。PCB 是 OS 识别和管理进程的唯一标志——没有 PCB,OS 完全不知道有这个进程存在。”创建进程”=建立 PCB,”销毁进程”=删除 PCB。

Q2.4 进程切换 5 步

写出”进程从 A 切换到 B”的 5 步操作(必须点到 PCB)。

答案
1. 保存现场:CPU 寄存器、PC、PSW 保存到 A 的 PCB
2. 入队更新:A 的 PCB 状态改为"就绪"或"阻塞",挂到对应队列
3. 选择新进程:调度器从就绪队列选中 B
4. 恢复现场:从 B 的 PCB 读出寄存器/PC,写回 CPU
5. 上 CPU:B 的 PCB 状态改"运行",从 B 的 PC 继续执行

Q2.5 线程切换为什么快

“线程切换比进程切换快是因为不需要保存寄存器值”——这句话错在哪?

答案

线程切换也要保存寄存器值。真正快在于:同一进程内的线程共享地址空间,切换时不需要切换页表,TLB 不会失效——省掉了进程切换里最贵的一笔开销。

口诀:进程切寄存器 + 切地址空间,线程只切寄存器

Q2.6 ULT 整进程阻塞

为什么 ULT(用户级线程)”一个线程阻塞会导致整个进程都被阻塞”?

答案

4 步因果链: 1. 内核只看到进程,不知道有线程 2. 某线程调用阻塞 I/O,内核以为是”进程”在等 I/O 3. 内核把整个进程挂起到阻塞态 4. 同进程所有线程一起被冻结

Q2.7 抢占式调度的物理基础

什么是”抢占式调度”?它的物理基础是什么?

答案

抢占式 = OS 可以主动把进程从 CPU 上踢下来。物理基础是时钟中断——没有时钟中断,OS 一旦把 CPU 交给用户程序就拿不回来(除非进程主动等 I/O 或结束)。

Q2.8 SJF 致命缺点

SJF(短作业优先)有什么致命缺点?

答案

长作业可能饥饿——如果短作业源源不断到达,长作业永远排在最后,可能永远轮不到执行

解法:HRRF(最高响应比优先)——把”等待时间”也加入考量,等久的会因为响应比变高而被优先。

Q2.9 RR 反直觉

为什么 RR 时间片轮转的平均周转时间反而比 FCFS、SJF 都差?

答案

RR 频繁切换让每个进程都被反复打断,丧失了”快速结束短作业”的机会。RR 的真正价值不在平均周转,而在响应时间——每个进程在很短时间内就能拿到一次 CPU。适合分时系统不适合批处理

Q2.10 时间片大小选择

时间片 q 选得太大或太小会怎样?

答案
  • q 过大:退化为 FCFS(每个进程一上 CPU 就跑到底)
  • q 过小:切换开销过大(保存/恢复 PCB 的时间占比上升,CPU 实际工作时间下降)
  • 经验值:q 略大于一次典型交互响应所需的 CPU 时间

Ch3 同步/互斥/死锁(10 题)

Q3.1 PV 操作必须原子

为什么 P 和 V 操作必须是”原子的”?

答案

信号量 S 自己是共享变量,PV 操作就是访问临界资源。如果 PV 不原子,信号量自己就先竞态了——根本保护不了别的临界区。这是个套娃问题——信号量原子性靠硬件原子原语(LOCK/CAS/关中断)保证。

Q3.2 漏写 V 后果

P(mutex)
counter++
// 漏写 V(mutex)

会有什么后果?

答案

5 步链式死锁: 1. mutex 永远停在 0 2. A 自己下次 P(mutex) 也阻塞——把自己锁外面了 3. 其他进程 P(mutex) 全部进入等待队列 4. 没人执行 V(mutex) 5. 等待队列永远没人叫醒 → 死锁

Q3.3 同步信号量 V 谁来做

下面这段代码错在哪?

消费者:
  V(full)
  P(full)
  取数据
  ...
答案

消费者自己 V 自己等的信号量——绕过同步通知,在缓冲区还是空的时候就去取数据,读到垃圾值。

铁律:V(s) 必须由”触发那个事件的进程”来做——full 的语义是”缓冲区有数据” → 只有生产者放完数据才能 V(full)。

口诀:P 自己等的,V 别人等的

Q3.4 抱锁睡觉

下面这段代码会什么时候死锁?

empty = 3, full = 0, mutex = 1

生产者:
  P(mutex)
  P(empty)
  放入缓冲
  V(mutex)
  V(full)
答案

当缓冲区满(empty = 0)时死锁: 1. 生产者 A 先 P(mutex)(拿到锁) 2. 接着 P(empty) → 0 → -1,A 阻塞睡眠 3. A 抱着锁睡觉——没人能再 P(mutex) 4. 所有其他生产者全部阻塞,消费者也可能因为后续操作卡住 5. 死锁

铁律:同步 P 必须在互斥 P 之前——避免抱锁睡觉。

Q3.5 读者写者饥饿

经典读者写者算法,5 个读者+2 个写者同时到达,最坏情况下写者要等多久?

答案

情况 1(读者数固定):W 等当前所有读者完成。 情况 2(读者源源不断到来)W 可能永远等不到——readcount 永远 ≥ 1,rw 永远不释放。这就是”读者优先”算法的写者饥饿问题。

Q3.6 死锁四条件

死锁的 4 个必要条件是什么?

答案
  1. 互斥:资源一次只能被一个进程使用
  2. 不可抢占:资源只能由占用进程主动释放
  3. 占有并等待:进程已占有部分资源,请求新资源时不放弃已占
  4. 循环等待:存在进程链,每个等待下一个占用的资源

口诀:互斥/不抢/占等/循环——4 个全有才死锁,破坏任一个就避免死锁。

Q3.7 哲学家进餐避免死锁

写出 4 种避免哲学家进餐死锁的方法,分别破坏哪个条件?

答案
方法 破坏条件
资源序号法(先小后大) ④ 循环等待
限制最多 4 人同时拿叉 ④ 循环等待(变相)
同时拿两叉或一根都不要 ③ 占有并等待
每人一双自己的叉子(私有化) ① 互斥

Q3.8 银行家算法判断步骤

写出银行家算法的 3 步抽象描述(不能含具体数字)。

答案
1. 初始化:Work ← Available, Finish[i] ← false (所有 i)
2. 找 Finish=false 且 Need[i] ≤ Work 的进程:
   - 找到 → Work ← Work + Allocation[i], Finish[i] ← true,回 2
   - 找不到 → 跳 3
3. 检查 Finish 数组:
   - 全为 true → 安全
   - 有 false → 不安全

口诀:找得到就走、找不到就停、停下后看完没完

Q3.9 死锁 vs 不安全

死锁、不安全状态、安全状态之间是什么关系?

答案

死锁 ⊂ 不安全 ⊂ 全状态

  • 安全:存在安全序列让所有进程完成
  • 不安全:找不到安全序列,有死锁风险但不一定死锁
  • 死锁:实际发生进程互相等待

银行家算法的策略:只允许进入安全状态——不安全就拒绝分配。

Q3.10 PV 三步法

PV 题的通用解题三步法是什么?

答案
Step 1: 画关系
  - 哪些进程互斥关系?
  - 哪些进程同步先后关系?

Step 2: 设信号量
  - 互斥资源:mutex(初值 1)
  - m 个同类资源:S(初值 m)
  - 同步事件:S(初值 0)

Step 3: 写 PV
  - 互斥 P/V 在同一进程内成对
  - 同步 P/V 跨进程交叉
  - 铁律:同步 P 在前,互斥 P 在后

Ch4 存储管理(10 题)

Q4.1 内/外部碎片

下面 2 个场景分别属于哪种碎片?

① 固定分区把内存切成 100KB 一块,60KB 程序占了 100KB 分区,剩 40KB 没法用 ② 动态分区剩 3 块空闲:50/30/20 KB(不连续),来了 80KB 程序装不下

答案

内部碎片(分配单元内部浪费) ② 外部碎片(空闲单元间小到没法分)

口诀:被分了用不完=内部,分不出去=外部

Q4.2 分页地址转换

页大小 = 1KB,逻辑地址 A = 2500,页表显示页 2 → 页框 5。求物理地址。

答案
页号 p = ⌊2500 / 1024⌋ = 2
页内偏移 d = 2500 - 2 × 1024 = 452
物理地址 = 5 × 1024 + 452 = 5120 + 452 = 5572

⚠️ 必须算到具体数值(5572),不能停在 5×1024+452。

Q4.3 EAT 公式辨析

什么时候用 EAT = T_t + (2 - p) × T_m,什么时候用 EAT = (1 - f) × T_m + f × T_缺页

答案
  • 第一个:TLB 命中率分析(p 是 TLB 命中率)
  • 第二个:缺页率分析(f 是缺页率,T_缺页 是磁盘 I/O 级毫秒)

两个容易混淆——看清题面给的是哪种场景。

Q4.4 缺页率震撼数字

T_m = 100ns,T_缺页 = 10ms。缺页率 0.1% 时 EAT 多少?变慢几倍?

答案
EAT = 0.999 × 100 + 0.001 × 10⁷
    = 99.9 + 10000
    ≈ 10100 ns

变慢 = 10100 / 100 = 100 倍

反直觉:千分之一缺页率就慢 100 倍——所以 OS 拼命压低缺页率到 0.001% 以下。

Q4.5 Belady 异常

什么是 Belady 异常?哪个算法有?

答案

Belady 异常:FIFO 算法增加页框数,缺页率反而上升——反直觉。

只有 FIFO 有——OPT、LRU、CLOCK 都没有。原因:FIFO 只看进入时间不看活跃度,多一个页框可能让常用页晚被淘汰反而让”该淘汰的”提前出局。

OPT/LRU 因为驻留集随页框单调扩张,多一个页框只会”多保留有用的页”。

Q4.6 LRU vs FIFO 命中差异

LRU 和 FIFO 在”页面命中时”的处理有什么本质区别?

答案
  • FIFO:命中时不更新顺序(队列结构不变,淘汰仍按进入时间)
  • LRU:命中时把该页移到”最新”那端(淘汰最久没用的)

口诀:“FIFO 看进来,LRU 看用过”

Q4.7 段页式访存次数

段页式地址转换无 TLB 时需要几次访存?

答案

3 次: 1. 查段表(得页表起址) 2. 查页表(得页框号) 3. 访问数据

⚠️ 不是 2 次。

Q4.8 局部性原理

虚拟存储为什么可行?

答案

基于局部性原理: - 时间局部性:最近访问的,很可能马上又访问(循环、局部变量) - 空间局部性:访问某地址,很可能马上访问相邻地址(数组、顺序代码)

所以”只把当前用的页装内存,其他放磁盘”在大多数程序上都有效——磁盘 I/O 的高代价被局部性的高命中率摊销。

Q4.9 抖动

什么是抖动(Thrashing)?怎么避免?

答案

抖动:驻留集 < 工作集 → CPU 大量时间处理缺页中断而非用户代码 → 进程互相抢页 → CPU 利用率急剧下降。

3 种解法: 1. 工作集模型:根据进程的工作集大小动态分配页框 2. PFF 缺页频率控制:监测缺页率,超阈值分配更多页框 3. 挂起部分进程:降低多道程度,让剩下的进程都有足够页框

Q4.10 分页 vs 分段

分页和分段的 6 维对比是什么?

答案
维度 分页 分段
划分依据 物理大小 逻辑结构
大小 固定 可变
地址 一维 二维 (S, d)
用户可见
碎片 内部 外部
共享/保护

口诀:“分页死板省内存,分段灵活便共享”


Ch5 设备管理(5 题)

Q5.1 中断驱动 vs DMA 真区别

中断驱动 I/O 和 DMA 的核心区别?(不能答”中断频率不同”)

答案

核心区别是”搬数据的工作由谁做“: - 中断驱动:CPU 在每次中断时亲自把数据从设备控制器寄存器搬到内存 - DMA:DMA 控制器代替 CPU 完成搬运,CPU 只在传输开始和结束时介入

8 字金句:“中断驱动 = CPU 搬,DMA = 控制器搬”

Q5.2 中断风暴

为什么网卡不能用中断驱动 I/O?

答案

100 万包/秒 × 1KB/包 = 10 亿次中断/秒。CPU 时钟 3 GHz 也撑不住——每次中断处理至少几百个时钟,CPU 100% 都在处理中断,没时间执行用户代码——这叫中断风暴

现代网卡都用 DMA + 中断聚合(Linux NAPI)。

Q5.3 单缓冲为什么 +M

单缓冲处理一块的时间是 max(C, T) + M,为什么 M 是独立加在外面?

答案

M 期间缓冲区被占用,设备不能往里写新数据(会覆盖正在拷出的数据)——M 是被迫串行的”独木桥”,不能与任何动作并行。

双缓冲让 M 进入 max 是因为多一个缓冲区给设备用——M 用缓冲 1 时,T 用缓冲 2 工作,M 不再阻塞 T

Q5.4 SPOOLing 输入井 vs 输出井

打印 PDF 文件时,SPOOLing 把数据放在输入井还是输出井?

答案

输出井——打印是输出方向(用户进程 → 设备)。

判断口诀:“输入是数据进来(设备→进程),输出是数据出去(进程→设备)——按数据流向决定”

Q5.5 SSTF 饥饿

SSTF 磁盘调度有什么缺点?

答案

远端请求可能饥饿——如果近端请求源源不断到来,远端的请求永远轮不到。

SCAN/LOOK 通过”扫描方向”机制避免饥饿——每个请求都会被”扫到”。


综合大题(5 道,需要 30+ 分钟做)

Q-Big-1 调度 4 算法对比

作业表(同 #1-#3): | P1 | 0 | 4 | | P2 | 1 | 3 | | P3 | 2 | 5 | | P4 | 3 | 2 |

分别用 FCFS / SJF / RR(q=2) / HRRF 算法计算平均周转时间和平均带权周转时间,对比 4 个算法的优劣

答案

参考 cheatsheet #1-#4。结果:

FCFS SJF RR(q=2) HRRF
T 7.75 6.75 9.25 7.0
W 2.625 1.89 2.81 2.1

结论: - SJF 平均最优(但长作业饥饿) - RR 平均最差但响应时间最好 - HRRF 接近 SJF 且无饥饿 - FCFS 最简单但短作业不公

Q-Big-2 银行家算法多资源

系统 3 类资源(A=10, B=5, C=7),4 个进程:

进程 Max (A,B,C) Allocation (A,B,C)
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)

判断当前状态是否安全。

答案
Available = (10,5,7) - 各进程 Allocation 总和
         = (10,5,7) - (7,2,3) = (3,3,4)

Need:
  P0: (7,4,3)
  P1: (1,2,2)
  P2: (6,0,0)
  P3: (0,1,1)

推演:
  Work=(3,3,4), 找 Need ≤ Work:P3.Need=(0,1,1) ✓ → P3 完成
  Work=(3,3,4)+(2,1,1)=(5,4,5), 找:P1.Need=(1,2,2) ✓ → P1 完成
  Work=(5,4,5)+(2,0,0)=(7,4,5), 找:P0.Need=(7,4,3) ✓ → P0 完成
  Work=(7,4,5)+(0,1,0)=(7,5,5), 找:P2.Need=(6,0,0) ✓ → P2 完成

安全!安全序列 = P3 → P1 → P0 → P2

Q-Big-3 LRU 大题

序列:7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3, 2, 1, 2, 0, 1, 7, 0, 1

页框 m=3,求 LRU 算法的缺页次数和缺页率。

答案

完整推演:

t=1: 7 → F, [7]
t=2: 0 → F, [7,0]
t=3: 1 → F, [7,0,1]
t=4: 2 → F, 淘汰 7, [0,1,2]
t=5: 0 → H, [1,2,0]
t=6: 3 → F, 淘汰 1, [2,0,3]
t=7: 0 → H, [2,3,0]
t=8: 4 → F, 淘汰 2, [3,0,4]
t=9: 2 → F, 淘汰 3, [0,4,2]
t=10: 3 → F, 淘汰 0, [4,2,3]
t=11: 0 → F, 淘汰 4, [2,3,0]
t=12: 3 → H, [2,0,3]
t=13: 2 → H, [0,3,2]
t=14: 1 → F, 淘汰 0, [3,2,1]
t=15: 2 → H, [3,1,2]
t=16: 0 → F, 淘汰 3, [1,2,0]
t=17: 1 → H, [2,0,1]
t=18: 7 → F, 淘汰 2, [0,1,7]
t=19: 0 → H, [1,7,0]
t=20: 1 → H, [7,0,1]

缺页 = 12
缺页率 = 12/20 = 60%

Q-Big-4 磁盘调度多算法对比

磁头位置 50,方向向外。请求队列 [86, 147, 91, 177, 94, 150, 102, 175, 130]。

分别用 FCFS / SSTF / SCAN / LOOK 计算总移动柱面数。

答案

FCFS: 50→86→147→91→177→94→150→102→175→130 距离 = 36+61+56+86+83+56+48+73+45 = 544

SSTF: 每次选最近的 50→86→91→94→102→130→147→150→175→177 距离 = 36+5+3+8+28+17+3+25+2 = 127

SCAN(向外扫到 199 再反向): 50→86→91→94→102→130→147→150→175→177→199(边界)→ 没有更内的请求,停 距离 = (199-50) = 149(不计回程)

LOOK(不到边界,扫到最远请求 177): 50→86→91→94→102→130→147→150→175→177 距离 = 177-50 = 127

Q-Big-5 综合:进程切换的总开销

进程 A 正在运行,时钟中断到来,OS 决定切换到进程 B。请详细说明这次切换会触发哪些动作(按时序),并解释为什么这个开销不可忽略。

答案

时序

  1. 时钟中断信号触发,CPU 完成当前指令后跳转到中断处理程序入口
  2. 保护现场:CPU 自动把 PC、PSW 入栈;中断处理程序保存通用寄存器到 A 的 PCB
  3. CPU 切换到核心态
  4. 调度器查就绪队列选中 B(这一步本身可能涉及优先级计算、HRRF 响应比等)
  5. 状态切换:A 的 PCB 状态改”就绪”,B 的 PCB 状态改”运行”
  6. 切换地址空间:把 B 的页表基址(CR3 寄存器)加载到 MMU
  7. TLB 失效:因为页表换了,所有 TLB 条目作废,下次访存先 TLB miss
  8. 恢复现场:从 B 的 PCB 读出寄存器/PC,写回 CPU
  9. 返回用户态,CPU 从 B 的 PC 继续执行

为什么开销大: - 步骤 6-7:切换页表 + TLB 失效——之后一段时间访存全部 miss,性能掉一截 - 步骤 2、8:寄存器组保存恢复(x86-64 大约 16+ 通用寄存器 + 浮点 SIMD) - 总开销:现代 CPU 上1-10 微秒——所以频繁切换会浪费大量 CPU

对比线程切换: - 步骤 6-7 不需要(同进程内地址空间相同) - 这就是”线程切换比进程切换快”的根本原因


错题统计建议

每做一题: - ✅ 一次做对:略过 - ⚠️ 看答案后才懂:标记,第二天重做 - ❌ 看答案仍困惑:回笔记对应章节复习

目标:考前最后一天每题都能秒答 + 默写关键步骤。