总体结论

这些卷子的重点非常稳定。几乎每年都会围绕下面几类题展开:

  1. UNIX 进程与系统调用:forkwaitsleepopenreadwritedup、管道、I/O 重定向、文件描述符和打开文件表。
  2. 作业/进程调度与内存管理混合题:FCFS、SJF、优先级、RR、多级反馈队列,常和主存分配、设备静态分配一起考平均周转时间。
  3. 死锁:银行家算法、安全序列、资源请求能否满足、死锁检测、死锁必要条件。
  4. 请求分页:页表规模、二级页表地址划分、反置页表、页表项字段、缺页异常、NRU/LFU/LRU/Aging/Clock/FIFO。
  5. UNIX 文件系统:inode 直接/间接索引、目录项、open 内核路径、硬链接/软链接、文件描述符复制、逻辑块到物理块、读写物理块数。
  6. 磁盘调度与同步编程:FCFS/SSTF/SCAN/C-SCAN/电梯调度、读者写者、管程、信号量、匿名管道。

复习优先级建议:

优先级 内容 原因
必拿 fork 进程树、管道重定向、文件描述符共享 2016、2017、2018、2020、2021、2022 都考
必拿 银行家算法/死锁检测 2016、2017、2018、2019、2020、2021、2022 都考
必拿 请求分页和页面置换 2016、2017、2018、2020、2021、2022 都考
必拿 inode 索引计算、open/read/write/dup/link 2016、2017、2018、2020、2021、2022 都考
高频 作业调度、平均周转时间、RR 平分 CPU 2016、2017、2018、2020、2021、2022 都考
高频 磁盘调度与容量/旋转延迟 2016、2017、2018、2019、2020、2021、2022 都考
高频 管程/信号量编程 每年第二大题常考

一、进程调度

1. 基本指标

常用时间定义:

  • 到达时间:作业/进程到达系统或就绪队列的时间。
  • 开始时间:第一次获得 CPU 或进入主存创建进程的时间,题目要看语境。
  • 完成时间:运行结束时间。
  • 周转时间 = 完成时间 - 到达时间。
  • 带权周转时间 = 周转时间 / 实际运行时间。
  • 平均周转时间 = 所有作业周转时间之和 / 作业数。
  • 平均带权周转时间 = 所有带权周转时间之和 / 作业数。

易错点:

  • “进入主存时间”和“开始运行时间”不是同一个概念。多道程序设计系统中,作业进入主存后可能与其他作业 RR 平分 CPU。
  • RR 题如果说“时间片远小于各作业运行时间”,通常表示多个驻留作业近似平均分享 CPU。
  • 作业调度决定谁进入内存;进程调度决定内存中的进程谁占 CPU。卷子常把两层调度混在一题里。

2. FCFS

先来先服务,按到达顺序运行。

特点:

  • 实现简单。
  • 对长作业有利,对短作业不利。
  • 容易产生 convoy effect,短作业排在长作业后面会等待很久。

计算套路:

  1. 按到达时间排序。
  2. 当前时间小于下一作业到达时间时,CPU 空闲到该作业到达。
  3. 完成时间逐个累加运行时间。
  4. 计算周转时间和带权周转时间。

3. SJF/SPF

短作业优先,选择估计运行时间最短的作业。

特点:

  • 在所有作业同时到达、运行时间已知时,平均周转时间最短。
  • 可能导致长作业饥饿。
  • 实际系统难点是预测运行时间。

证明思路:

若两个相邻作业运行时间分别为 ab,且 a > b。把顺序从 a,b 交换为 b,a,后续作业完成时间不变,这两个作业总等待/周转时间减少 a-b。不断交换逆序长短作业,可得按运行时间升序时平均周转时间最小。

4. RR

时间片轮转,适合分时系统。

特点:

  • 公平,响应时间较好。
  • 时间片过短,调度开销大;时间片过长,退化为 FCFS。
  • 考题常问调度开销百分比。

例:时钟中断每秒 100 次,每次中断处理 1ms;RR 以 10 个时钟中断为 1 个时间片;一次调度 2ms,分派 CPU 1ms。时间片长度 10 * 10ms = 100ms,每个时间片内开销为 10 * 1 + 2 + 1 = 13ms,CPU 调度相关开销为 13%

5. 优先级调度

注意题目会说明“优先数越小优先级越高”或相反。不要按常识假设。

抢占式:

  • 高优先级进程到达后可抢占 CPU。
  • 响应紧急任务好。
  • 调度频繁,开销大,低优先级可能饥饿。

非抢占式:

  • 已运行进程不主动让出 CPU 前不会被抢占。
  • 开销较小。
  • 高优先级任务可能等待长作业结束。

6. MLFQ

多级反馈队列的核心思想:

  • 多个优先级队列,高优先级队列时间片短,低优先级队列时间片长。
  • 新进程通常先进高优先级队列。
  • 用完时间片仍未结束则降级。
  • I/O 密集型进程经常阻塞并让出 CPU,容易保持较高优先级;CPU 密集型进程会逐步降级。
  • 为避免饥饿,需要周期性提升优先级或 aging。

答题要点:

  • 它是对进程行为的动态预测:过去经常很快让出 CPU 的进程更可能是 I/O 密集型,应给予较好响应。
  • 设计关键是预测进程行为和避免长期饥饿。

二、请求分页与页面置换

1. 地址划分与页表大小

基本公式:

  • 页内偏移位数 = log2(页面大小)
  • 虚页号位数 = 逻辑地址位数 - 页内偏移位数。
  • 一级页表项数 = 逻辑地址空间 / 页面大小 = 2^虚页号位数
  • 一级页表最大大小 = 页表项数 * 页表项大小。
  • 页框号位数 = log2(物理内存 / 页面大小)

二级页表划分:

  1. 每个页表页能放的页表项数 = 页面大小 / 页表项大小。
  2. 页表页内索引位数 = log2(每页表项数)
  3. 页目录位数 = 虚页号位数 - 页表页内索引位数。

常见例子:

  • 32 位逻辑地址,4KB 页面,4B 页表项:偏移 12 位;一个页表页有 4096/4 = 1024 = 2^10 项;二级页表为 10 位页目录 + 10 位页表页索引 + 12 位偏移
  • 32 位逻辑地址,1KB 页面,4B 页表项:偏移 10 位;一个页表页有 1024/4 = 256 = 2^8 项;虚页号 22 位,所以二级页表为 14 位页目录 + 8 位页表页索引 + 10 位偏移
  • 32 位逻辑地址,2KB 页面,4B 页表项:偏移 11 位;一个页表页有 2048/4 = 512 = 2^9 项;二级页表为 12 位页目录 + 9 位页表页索引 + 11 位偏移

多级页表优缺点:

  • 优点:页表不需要连续存放;没有使用的虚拟地址范围不必分配页表页,节省内存。
  • 缺点:地址转换需要多次访存,若无 TLB 会明显变慢。

反置页表:

  • 表项数按物理页框数算:物理内存 / 页面大小。
  • 表项至少要包含进程号和虚页号,用来说明该物理页框属于哪个进程的哪个虚页。
  • 优点:全系统维护一张与物理内存大小相关的表,节省内存。
  • 缺点:地址转换查找复杂;未调入内存的页面仍需其他结构记录,缺页处理可能更慢。

2. 页表项字段

常考字段:

  • 页框号:该页在物理内存中的位置。
  • 存在位/驻留位/有效位:是否在内存中,不在则触发缺页异常。
  • 访问位/引用位 R:最近是否被访问,常由硬件置 1,由 OS 周期性清 0。
  • 修改位/脏位 W/M:是否被写过,脏页淘汰前必须写回磁盘。
  • 保护位:读/写/执行权限。
  • 锁定位:某些关键页面暂时不能被替换,2022 卷在 CALL 指令多次缺页中考过。
  • 外存地址:不在内存时页面所在的磁盘位置。

易错点:

  • R=0 且 W=1 是可能的:页面以前被写过,尚未写回磁盘;之后 OS 清零了 R 位,但不会因此清零 W 位。
  • 写访问一定会置 R 和 W;读访问只置 R。
  • 脏页不是不能淘汰,而是淘汰成本更高,需要写回。

3. 缺页异常处理

标准流程:

  1. CPU 访问虚拟地址,查页表/TLB。
  2. 有效位为 0,触发缺页异常,陷入内核。
  3. OS 检查地址是否合法、权限是否允许。
  4. 找空闲页框;若没有空闲页框,运行页面置换算法选牺牲页。
  5. 若牺牲页是脏页,先写回磁盘。
  6. 从外存调入目标页,更新页表项。
  7. 恢复被中断指令,重新执行。

4. 页面置换算法

FIFO:

  • 淘汰最早进入内存的页。
  • 实现简单。
  • 可能有 Belady 异常。

LRU:

  • 淘汰最长时间未被访问的页。
  • 理论效果好,但精确实现代价大,需要维护全序访问历史或特殊硬件。
  • 常用近似算法:NRU、Clock、Aging、NFU。

NRU:

  • 根据 R/M 位分类。
  • 优先淘汰 (R=0, M=0);其次 (0,1);再 (1,0);最后 (1,1)
  • 周期性清 R 位,才有“最近未使用”的信息。

Clock:

  • FIFO 环形队列 + R 位。
  • 指针扫描;遇到 R=1 清 0 并跳过;遇到 R=0 淘汰。
  • 增强版 Clock 会结合 M 位,优先干净页。

NFU:

  • 每个页面维护计数器,每次时钟中断把 R 位加到计数器。
  • 淘汰计数最小者。
  • 缺点:早期频繁访问的页面即使后来不用,计数仍很大。

Aging:

  • 每个页面维护 n 位寄存器。
  • 每次时钟中断右移一位,把当前 R 位放到最高位,然后清 R。
  • 寄存器值越小,越久未使用,优先淘汰。
  • 要接近 LRU:采样周期要合适,寄存器位数要足够,且每次采样后清 R 位。

OPT:

  • 淘汰未来最长时间不会被访问的页。
  • 实际不可实现,但可作为评价其他算法的理论下界。

5. 颠簸与工作集

颠簸:系统频繁缺页,大量时间用于换入换出,CPU 利用率下降。

避免条件:

  • 同时运行进程的工作集总大小不能超过可用物理内存。
  • 页面分配不能过少。
  • 页面置换算法应利用局部性,减少不必要淘汰。

三、死锁与银行家算法

1. 死锁四个必要条件

  1. 互斥:资源一次只能被一个进程使用。
  2. 占有并等待:进程已占有资源,同时还等待其他资源。
  3. 不可剥夺:资源不能被强制抢走。
  4. 循环等待:存在进程等待环。

破坏策略:

  • 静态分配所有资源:破坏“占有并等待”。
  • 资源有序申请:破坏“循环等待”。
  • 可抢占资源:破坏“不可剥夺”。

2. 银行家算法

常用矩阵:

  • Available:当前可用资源。
  • MaxClaim:最大需求。
  • Allocation:已分配。
  • Need = Max - Allocation:尚需资源。

安全性检测:

  1. Work = Available
  2. Finish[i] = false
  3. 找一个 Finish[i] == falseNeed[i] <= Work 的进程。
  4. 假设它能完成,令 Work = Work + Allocation[i]Finish[i] = true,加入安全序列。
  5. 重复直到所有进程完成,或找不到可完成进程。
  6. 全部完成则安全;否则不安全。

资源请求判断:

  1. 检查 Request[i] <= Need[i],否则超过最大声明,拒绝。
  2. 检查 Request[i] <= Available,否则暂不能分配。
  3. 试分配:
    • Available -= Request[i]
    • Allocation[i] += Request[i]
    • Need[i] -= Request[i]
  4. 再做安全性检测。
  5. 试分配后安全才真正分配;否则回滚并拒绝。

易错点:

  • “不安全”不等于“已经死锁”。不安全表示未来可能死锁。
  • 死锁检测用的是当前 Request,银行家算法用的是最大需求 Max/ClaimNeed
  • 题目中 Claim 有时表示最大需求,有时表头会同时给 Request。先看题干定义。
  • 向量比较是逐项比较,不能只比较总数。

3. 死锁检测

检测算法通常用当前请求矩阵:

  1. Work = Available
  2. 没有资源分配的进程可直接 Finish=true,因为它不参与死锁。
  3. Request[i] <= Work 的进程,假设它完成并释放资源。
  4. 最后仍 Finish=false 的进程就是死锁进程。

资源分配图:

  • 有环不一定死锁,若每类资源多个实例,需要继续检测。
  • 若每类资源只有一个实例,有环就是死锁。

四、进程管理与系统调用

1. API 与系统调用

系统调用:

  • OS 提供给用户程序进入内核态请求服务的接口。
  • 一定会陷入内核态。
  • 例:forkexecwaitopenreadwriteclosedup

API 函数:

  • 程序库提供的接口。
  • 可能直接在用户态完成,也可能封装一个或多个系统调用。
  • 例:printf 是 C 库函数,可能最终触发 writegetpid 可封装系统调用。

直接使用系统调用的弊端:

  • 代码复杂,可读性差。
  • 平台差异大,可移植性差。
  • 需要自己处理更多细节和错误码。

2. fork

核心语义:

  • 调用一次,返回两次。
  • 父进程返回子进程 pid。
  • 子进程返回 0。
  • 失败返回负数。
  • 父子进程拥有独立地址空间,普通变量互不影响。
  • 文件描述符表会复制,但指向同一个系统打开文件表项,因此共享文件偏移量。

进程数计算:

  • 遇到无条件 fork(),当前已有多少个进程,就会新增多少个子进程,总数翻倍。
  • 遇到条件分支,要分别追踪父子返回值。
  • wait 只改变执行顺序/回收子进程,不会创建进程。

输出不确定性:

  • 父子进程调度顺序不确定。
  • 多个进程 printf 的行顺序不确定。
  • 如果没有关闭缓冲或没有换行,输出还可能受缓冲影响。

3. exec

语义:

  • 用新程序替换当前进程的用户态地址空间。
  • pid 不变。
  • 成功后不返回到原程序;失败才返回错误。
  • 常和 fork 配合:子进程 exec 新程序,父进程 wait

4. wait/waitpid

作用:

  • 父进程等待子进程结束。
  • 回收子进程退出状态,避免僵尸进程。
  • 若没有已结束子进程,调用者进入等待态。

常考表述:

  • 内核把调用进程从运行态转为等待态。
  • 子进程结束后产生唤醒,父进程回到就绪态。

5. 管道与 I/O 重定向

管道:

  • Shell 先创建管道,再 fork 出命令进程。
  • 前一个命令的标准输出重定向到管道写端。
  • 后一个命令的标准输入重定向到管道读端。
  • 匿名管道常用于有亲缘关系的进程。
  • 有名管道可通过文件系统路径让无亲缘关系进程通信。

常见 IPC:

  • 管道
  • 消息队列
  • 共享内存
  • 信号
  • 套接字

I/O 重定向:

  • 本质是修改文件描述符表,让 fd 0/1/2 指向文件或管道。
  • dup/dup2 常用于把某个文件描述符复制到标准输入或标准输出。
  • > 是标准输出重定向到文件。
  • < 是标准输入从文件读取。
  • cmd1 | cmd2cmd1 的 stdout 到管道,cmd2 的 stdin 从管道。

五、文件系统结构

1. inode 与目录项

inode 保存:

  • 文件类型、权限、属主、时间戳、大小。
  • 链接计数。
  • 数据块地址索引:直接索引、一级间接、二级间接、三级间接。

目录文件保存:

  • 目录项:文件名 + inode 号。
  • 目录本身也是文件,内容是目录项。

普通文件与目录文件区别:

  • 普通文件内容是用户数据。
  • 目录文件内容是目录项,描述该目录下有哪些名字以及对应 inode。

2. 文件最大尺寸

若:

  • 物理块大小为 B 字节。
  • 每个索引项大小为 e 字节。
  • 直接索引项数为 d
  • 每个索引块可存索引项数 n = B/e

则最大文件大小:

(d + n + n^2 + n^3) * B

例:

  • inode 有 10 个直接索引,1KB 块,4B 索引项,n=256
  • 最大大小 = (10 + 256 + 256^2 + 256^3) * 1KB

2022 变体:

  • 12 个直接索引,1KB 块,4B 索引项。
  • 最大大小 = (12 + 256 + 256^2 + 256^3) * 1KB
  • inode 中文件物理结构信息占 15 * 4 = 60B,因为 12 个直接 + 1 个一级 + 1 个二级 + 1 个三级,共 15 个索引项。

3. 目录大小与磁盘空间

目录文件大小:

目录项数量 * 每个目录项大小

占用磁盘空间:

  • 至少按物理块取整。
  • 若目录数据块数超过直接索引范围,还要额外占用间接索引块。

例:

  • 每个目录项 16B,目录下 610 个文件 + 40 个子目录,共 650 项。
  • 目录大小 650 * 16 = 10400B
  • 物理块 1KB,则目录数据需要 11 个物理块。
  • 若直接索引只有 10 个,还需要 1 个一级间接索引块,所以共占 12 个物理块。

4. open 内核过程

标准答法:

  1. 用户态调用 open,陷入内核。
  2. 根据路径逐级查目录,找到目标文件目录项和 inode 号。
  3. 若 inode 已在活动 inode 表中,增加引用;否则从磁盘读入 inode,建立活动 inode 表项。
  4. 检查权限和打开方式。
  5. 建立系统打开文件表项,记录读写方式、当前文件偏移量、指向活动 inode 的指针、引用计数等。
  6. 在当前进程用户打开文件表中找空项,指向系统打开文件表项。
  7. 返回该用户打开文件表下标,即文件描述符 fd。

5. read/write 内核过程

read(fd, buf, n)

  1. 通过 fd 查当前进程用户打开文件表。
  2. 找到系统打开文件表项,获得当前偏移量和活动 inode。
  3. 检查打开方式是否允许读。
  4. 根据 inode 索引把逻辑块号转换为物理块号。
  5. 从磁盘或缓存读入数据到内核缓冲,再复制到用户缓冲区。
  6. 更新文件偏移量。
  7. 返回实际读入字节数。

write(fd, buf, n)

  1. 同样通过 fd 找系统打开文件表和 inode。
  2. 检查是否以写方式打开。
  3. 根据偏移量定位或分配数据块。
  4. 把用户数据写入缓存/磁盘。
  5. 更新偏移量、文件大小、inode 修改时间等。
  6. 若打开方式不允许写,返回错误码。

易错点:

  • fork 后父子进程复制用户打开文件表,但指向同一个系统打开文件表项,所以共享偏移量。
  • 分别 open 同一文件会产生不同系统打开文件表项,偏移量独立。
  • dup 复制的是文件描述符,使两个 fd 指向同一个系统打开文件表项,偏移量共享,引用计数增加。

6. 硬链接与软链接

硬链接:

  • 新目录项指向同一个 inode。
  • inode 链接计数加一。
  • 不新增目标文件 inode。
  • 通常不能跨文件系统,通常不链接目录。
  • 访问快,实现简单。

软链接/符号链接:

  • 新建一个特殊文件,内容是目标路径。
  • 有自己的 inode。
  • 可跨文件系统,可链接目录。
  • 访问时需要解析路径,开销较大;目标删除后可能悬空。

六、历年试卷考点总结

2016-2017

题型概况:

  • Q1 UNIX 代码:open("data.txt", O_RDONLY)、两次 forkread(fd, buffer, 2)getpidprintfwaitpid。考进程树、输出结果、open 系统调用过程。
  • Q2 调度理论:所有作业同时到达,FCFS 平均周转时间/平均带权周转时间公式;证明 SJF 平均周转时间最短;SJF 导致长作业饥饿。
  • Q3 死锁:给资源总量、已分配、申请、最大需求;用死锁检测判断是否死锁;讨论银行家算法实际困难。
  • Q4 请求分页:32 位、1GB 物理内存、2KB 页面、4B 页表项;一级页表项数、二级页表地址划分、反置页表项数;给地址序列和 3 个页框,用 LRU 求页号序列、页框变化和物理地址。
  • Q5 文件系统:目录树、目录项大小、目录文件块结构;按链接结构/顺序结构读取文件第 15 块的最少/最多启动磁盘次数。
  • Q6 磁盘:位图管理空闲块、磁盘容量、C-SCAN 调度、寻道时间/旋转延迟/传输时间。
  • 编程题:按进程间优先关系图,用信号量 P/V 写并发程序。

注意点:

  • fork 后共享打开文件表偏移,所以四个进程依次读到 abcdefgh,但输出行顺序由调度决定。
  • SJF 最优证明要写“相邻交换”或“短作业在前使加权累加项最小”,不能只写结论。
  • 死锁检测和银行家算法要区分:检测当前是否已死锁;银行家避免未来进入不安全状态。

2017-2018

题型概况:

  • Q1 Shell 管道:cat file1 file2 | sort,考输出、两个子进程关系、管道特点、IPC。
  • Q2 调度:两道批处理系统;作业调度按优先数,进程调度 RR;求进入主存时间、结束时间、平均周转和平均带权周转;比较批处理和分时系统。
  • Q3 银行家算法:判断安全序列;判断 P1 请求能否满足;若满足后再给一组请求,用死锁检测找死锁进程。
  • Q4 请求分页:32 位、1GB、4KB 页面、4B 页表项;一级/二级页表;访问逻辑地址 12300;NRU/LFU/Aging 选牺牲页;颠簸和最佳页面大小。
  • Q5 UNIX 文件系统:inode 直接/间接索引,open 过程磁盘读取次数,父子进程读文件物理块数,文件占用空间和最大文件大小,link 硬链接过程。
  • Q6 磁盘:容量、读写速率、平均旋转等待、位示图大小、FCFS/SSTF/电梯调度。
  • 编程题:管程实现读者写者问题。

注意点:

  • 二级页表计算必须写出页内偏移 12 位、页表页索引 10 位、页目录 10 位。
  • 12300/4096 = 3,所以访问 3 号页;题中驻留 1、2、4、5,因此缺页。
  • 最佳页面大小公式:f(p)=se/p+p/2,这里最优为 8KB,所以 4KB 不合理。

2018-2019

题型概况:

  • Q1 UNIX 代码:API 与系统调用区别;fork 实现;进程树;getpid/getppid/sleep/printf 输出。
  • Q2 作业调度与内存:Buddy 伙伴系统、FCFS 作业调度、RR 平分 CPU,列进入主存/结束时间和关键内存状态。
  • Q3 死锁:进程-资源分配图检测;线程使用锁是否可能死锁,OS 能否检测应用内部锁死锁。
  • Q4 请求分页:分页缺点、多级页表/TLB;反置页表;给逻辑地址序列和 3 页框,用 LRU 求页框变化和后续物理地址;LRU 实现困难、NRU/Aging 近似;OPT 意义。
  • Q5 文件系统:根目录最多文件数、inode 区最多文件数、目录文件占块数、硬链接/软链接、open 内核过程。
  • Q6 磁盘:CHS 请求、电梯调度、同柱面扇区优化、读请求和写请求区别。
  • 编程题:用管程实现匿名管道,容量 K 的环形队列,read/write 阻塞条件。

注意点:

  • 反置页表项数按物理页框算:2GB/4KB = 2^19
  • LRU 精确实现通常不可行,理由是维护精确最近访问顺序代价大。
  • 目录文件数据块超过直接索引数时,要额外算间接索引块。

2019-2020

题型概况:

  • Q1 中断与 RR 调度开销:时钟中断频率、中断处理、调度、分派 CPU 的时间比例。
  • Q2 SPOOLing:比较采用/不采用 SPOOLing 的总运行时间。
  • Q3 银行家算法:状态是否安全;P2 请求能否分配;证明满足某个最大需求条件时不会死锁。
  • Q4 可变分区:首次适应、最佳适应能否装入作业序列,画内存队列。
  • Q5 文件系统一致性:空闲块/分配块不一致、丢失盘块、重复分配等。
  • Q6 磁盘 I/O:SSTF、SCAN、C-SCAN 移动臂总量;用信号量或管程实现 SCAN 调度。
  • 编程题:猴子过峡谷问题,用信号量避免相反方向同时通过产生死锁。

注意点:

  • RR 调度开销题不要漏掉引起调度的多个时钟中断处理时间。
  • SPOOLing 的关键是预输入、缓输出和 CPU 处理可重叠。
  • 文件系统一致性题要区分“空闲表重复”“分配表重复”“既空闲又分配”“既不空闲也不分配”。

2020-2021

题型概况:

  • Q1 Shell:cat fileA.txt fileB.txt | sort > sorted.txt,考输出文件、进程数、管道、IPC、输入/输出重定向。
  • Q2 调度:作业调度 SJF,进程调度抢占式优先级;求进入主存/结束时间和平均周转;列常见调度算法;讨论 I/O 密集型和 CPU 密集型平衡;抢占/非抢占优缺点。
  • Q3 银行家算法:安全性、P1 请求、P4 请求。
  • Q4 请求分页:32 位、1GB、4KB 页面;二级页表划分;页表项字段;W 位从 1 到 0 的原因;访问地址 2000 缺页;LFU/Aging 选牺牲页;Aging 如何接近 LRU。
  • Q5 文件系统:inode 最大文件大小、目录文件大小和磁盘占用、用户打开文件表/系统打开文件表/活动 inode 引用关系、read 过程和物理块数。
  • Q6 磁盘调度:FCFS、电梯、SCAN、SSTF。
  • 编程题:Hoare/Mesa 风格管程语义转换,用信号量模拟。

注意点:

  • 地址 2000 在 4KB 页面下属于 0 号页,不是 2 号页。
  • 选择牺牲页时,若计数相同,优先淘汰干净页,避免写回脏页。
  • 管道和重定向题要指出:cat stdout 到管道,sort stdin 从管道,sort stdout 到 sorted.txt

2021-2022

题型概况:

  • Q1 UNIX 代码:产生 3 个进程,进程树,输出 Count: 20 多行;fork/sleep/printfsleep 使进程等待。
  • Q2 多道程序设计:可变分区、低地址优先、FCFS 作业调度、RR、设备静态分配;平均周转时间;列作业/进程调度算法;死锁四条件和静态分配破坏占有并等待。
  • Q3 银行家与死锁:安全序列;P3 请求能否满足;修改分配和可用资源后检测死锁进程。
  • Q4 请求分页:32 位、1GB、1KB 页面、4B 页表项;页表项理论最大数、页框号最大值、页表项信息;从页面大小合理性推断进程平均大小;R 位清零;NRU 替换;TLB 和缺页开销。
  • Q5 文件系统:inode 区最多文件数、目录文件不同 inode 号/大小/占用空间;write 因只读打开失败;read 涉及间接索引块,共读几个物理块。
  • Q6 磁盘调度:FCFS/SSTF/电梯顺序和移动量,SSTF 饥饿。
  • 编程题:读者写者问题变体,同时读进程编号总数不大于 N,用 PV 操作实现。

注意点:

  • 普通变量在 fork 后独立,所以每个进程自己的 count 不互相影响。
  • 静态设备分配不是破坏互斥,而是一次性申请资源,破坏占有并等待。
  • 最近未使用 NRU 题要结合 R 位和 W 位,优先淘汰未引用且干净的页。

2022-2023

题型概况:

  • Q1 UNIX 代码:8 个进程、进程树、多种输出可能;setbuf/printf 与 I/O;fork/wait/getpid/getppid 与进程控制/同步;直接系统调用的弊端。
  • Q2 可变分区:最优适应与最坏适应;作业创建/结束时间;内存用户区变化;内碎片和外碎片。
  • Q3 自旋锁:单核关中断为何可实现临界区互斥;多核为何失效;基于 XCHG 实现 lock/unlock
  • Q4 死锁检测:给 Allocation、Request、Available,判断当前死锁进程;讨论死锁避免需要何时决策和掌握最大需求。
  • Q5 请求分页:32 位、2GB、1KB 页面;页表理论最大尺寸;二级页表划分;多级页表优缺点;反置页表项数;R=0/W=1 原因;CALL 指令访问栈和目标页,FIFO/Clock/NRU 缺页次数;锁定位减少多次缺页。
  • Q6 文件系统:inode 中物理结构信息大小;最大文件尺寸;fd 值含义和能否为 0;dup 用途与引用计数;缓存下逻辑块到物理块转换和实际读块数;程序输出。
  • Q7 磁盘:FCFS/SSTF/电梯/SCAN 顺序和移动柱面数;同柱面请求归并和扇区循环排序。
  • 编程题:管程实现最多 K 个读者的读者写者问题。

注意点:

  • fd 一般从 0、1、2 之后开始分配,所以普通 open 常返回 3;如果先关闭 0,再 exec,新程序里 open 可能返回 0。
  • dup 后多个 fd 指向同一系统打开文件表项,引用计数增加,共享偏移。
  • 2022 分页题的二级划分是 14 + 8 + 10,不是常见的 10 + 10 + 12

七、高频易错点清单

调度

  • 优先数大小方向必须看题干。
  • RR 平分 CPU 时,要动态统计同一时间在内存中的作业数。
  • 平均周转时间用到达时间,不是进入主存时间,除非题目明确要求“作业驻留后”的指标。
  • SJF 最短平均周转时间成立的前提是运行时间已知,且同一批作业可重新排序。
  • 抢占式优先级中,新高优先级作业到达会打断当前进程。

分页

  • 地址页号 = floor(逻辑地址 / 页面大小),页内偏移 = 逻辑地址 mod 页面大小
  • 页表项大小影响二级页表划分,不影响页内偏移。
  • 反置页表按物理页框数算,不按虚拟页数算。
  • R 位会被 OS 周期性清零;W 位表示脏页,不会因为 R 清零而清零。
  • Aging 寄存器是数值比较,值越小越久未访问。
  • 脏页淘汰前要写回,所以同等条件下优先淘汰干净页。

死锁

  • Need = Max - Allocation,不要把 Request 当 Need。
  • 银行家算法的“请求可满足”必须试分配后仍安全。
  • 不安全不等于死锁。
  • 死锁检测最后未完成的进程才是死锁进程。
  • 静态分配破坏占有并等待,不是破坏互斥。

UNIX 进程/系统调用

  • fork 后普通变量各自独立,文件偏移可能共享。
  • printf 是 API,不是系统调用;底层可能触发 write
  • exec 成功不返回,pid 不变。
  • wait/sleep 会使调用进程进入等待态。
  • Shell 管道命令通常创建两个命令子进程,Shell 是它们的父进程。
  • 输出顺序不确定时,应说明由调度顺序决定。

文件系统

  • 文件最大尺寸要把直接、一级、二级、三级全部算上。
  • 目录文件大小按目录项数算,占磁盘空间按物理块向上取整。
  • 数据块超过直接索引范围时,读数据可能还要读间接索引块。
  • 硬链接不新增目标文件 inode,只新增目录项并增加链接计数。
  • 软链接新增 inode,文件内容是路径。
  • dupfork 都可能增加系统打开文件表项引用计数,但一般不新增活动 inode。

八、例题

例题 1:SJF 与 FCFS

题目:三个作业同时到达,运行时间分别为 J1=8, J2=4, J3=2。分别用 FCFS 和 SJF 求平均周转时间。

解:

  • FCFS 顺序 J1,J2,J3:完成时间分别为 8、12、14,平均周转时间 (8+12+14)/3 = 34/3
  • SJF 顺序 J3,J2,J1:完成时间分别为 2、6、14,平均周转时间 (2+6+14)/3 = 22/3

注意:

  • 同时到达时,完成时间就是周转时间。
  • 若题目还要带权周转时间,要再除以各自运行时间。

例题 2:二级页表划分

题目:32 位逻辑地址,页面大小 1KB,页表项 4B,求二级页表地址划分。

解:

  • 1KB = 2^10,页内偏移 10 位。
  • 虚页号位数 = 32 - 10 = 22 位。
  • 一个页表页能放 1KB/4B = 256 = 2^8 个页表项,所以页表页索引 8 位。
  • 页目录位数 = 22 - 8 = 14 位。
  • 划分:14 位页目录 + 8 位页表页索引 + 10 位页内偏移

注意:

  • 不要把 1KB 页面套成 4KB 的 10+10+12

例题 3:Aging 页面置换

题目:某时刻 4 个驻留页的 Aging 寄存器为:页 1=1100,页 2=0111,页 3=1001,页 4=0111。页 2 是脏页,页 4 是干净页。发生缺页时淘汰谁?

解:

  • Aging 值越小越久未访问。
  • 页 2 和页 4 都是 0111,同为最小。
  • 页 2 是脏页,淘汰需写回;页 4 干净。
  • 因此优先淘汰页 4。

注意:

  • 页面置换不只看算法主指标,参考答案常要求“结合 W 位考虑”。

例题 4:银行家算法请求能否满足

题目:某进程 P1 当前 Need=(1,2,0),系统 Available=(1,1,1)。若 P1 请求 (1,1,0),是否一定能分配?

解:

  • 先看请求是否超过 Need:(1,1,0) <= (1,2,0),通过。
  • 再看请求是否超过 Available:(1,1,0) <= (1,1,1),通过。
  • 但不能直接说可以。必须试分配后运行安全性算法。
  • 若试分配后仍存在安全序列,才可以;否则拒绝。

注意:

  • 很多题的坑就在“资源够”但“分配后不安全”。

例题 5:fork 与打开文件表

题目:父进程 fd=open("data.txt") 后执行两次 fork(),然后每个进程执行 read(fd, buf, 2)。文件内容为 abcdefghijkl。如果读操作依次由 pid 2、3、4、5 执行,各自读到什么?

解:

  • openfork 前执行,所有子进程复制 fd,但 fd 指向同一个系统打开文件表项。
  • 该系统打开文件表项的文件偏移量共享。
  • 第一个进程读 ab,偏移变 2。
  • 第二个读 cd,偏移变 4。
  • 第三个读 ef,偏移变 6。
  • 第四个读 gh,偏移变 8。

注意:

  • 读到的内容由实际调度顺序决定,不一定按 pid 顺序。
  • 如果每个进程分别 open 文件,则偏移量独立,结果不同。

例题 6:inode 最大文件大小

题目:inode 有 10 个直接索引项,一级/二级/三级间接索引各 1 个;物理块 512B;索引项 4B。求单个文件理论最大大小。

解:

  • 每个索引块可存 512/4 = 128 个索引项。
  • 直接索引可寻址 10 个数据块。
  • 一级间接可寻址 128 个数据块。
  • 二级间接可寻址 128^2 个数据块。
  • 三级间接可寻址 128^3 个数据块。
  • 最大大小 = (10 + 128 + 128^2 + 128^3) * 512B

注意:

  • 题目若问“占多少磁盘空间”,还可能要把间接索引块本身也算进去;若问“文件最大尺寸”,通常只算可存用户数据的大小。

例题 7:硬链接过程

题目:执行 link /test/demo.dat /demo.dat,文件系统主要做什么?

答题模板:

  1. 查找 /test/demo.dat,得到目标文件 inode 号。
  2. 查找新路径 /demo.dat 的父目录,即根目录。
  3. 在父目录中增加目录项:名字 demo.dat + 目标 inode 号。
  4. 将目标 inode 的链接计数 i_nlink 加一。
  5. 不复制文件数据,不新增目标文件 inode。

注意:

  • 硬链接是多个目录项指向同一个 inode。

九、考前速记

  • 32 位 + 4KB + 4B PTE:二级页表 10/10/12
  • 32 位 + 1KB + 4B PTE:二级页表 14/8/10
  • 32 位 + 2KB + 4B PTE:二级页表 12/9/11
  • 反置页表项数 = 物理内存 / 页面大小。
  • Need = Max - Allocation
  • 银行家:先检查 Request,再试分配,再安全性检测。
  • fork:变量独立,打开文件表偏移可能共享。
  • exec:成功不返回,pid 不变。
  • dup:复制 fd,指向同一系统打开文件表项。
  • 硬链接:同 inode,链接计数加一。
  • 软链接:新 inode,内容是路径。
  • SJF:平均周转时间优,但长作业可能饥饿。
  • RR:时间片太小开销大,太大退化 FCFS。
  • Aging:右移寄存器,R 放最高位,值越小越先淘汰。
  • NRU:优先 (R=0,M=0),再 (0,1),再 (1,0),最后 (1,1)