计算机操作系统这门课在考研408里占35分,加上计算机组成原理、数据结构、计算机网络,四门凑成150分,操作系统这一块的分值比重接近四分之一。很多人一开始觉得OS比数据结构好啃,翻到第二章进程同步就被信号量按在地上摩擦,第三章银行家算法算到怀疑人生,第五章页面置换又栽在Belady异常上。我自己当年备考时用的主教材就是汤小丹老师那本《计算机操作系统》慕课版,配合课后习题一道一道过,最后OS部分拿了比较理想的分数。这篇内容就把我当年整理课后习题答案的思路、章节知识骨架的搭法、以及复试阶段怎么把教材知识往深里拓展,完整地摊开讲一遍。不管你是刚开始第一轮、还是已经刷到第三轮在抠细节,都能从里面挑到能直接用的东西。
1. 教材选定与整体复习路线设计
1.1 为什么汤小丹慕课版值得作为主线教材
选教材这件事,很多人的误区是贪多。市面上操作系统教材不少,有偏理论的、有偏工程实践的、有直接照搬国外经典结构的。汤小丹这本慕课版的特点是章节编排和国内考研大纲的贴合度比较高,从操作系统引论、进程描述与控制,一路到处理机调度、进程同步、存储器管理、虚拟存储器、输入输出系统、文件管理、磁盘管理,主干顺序基本和408考纲一致。
我当年的做法是把这本教材当作唯一的“主干”,其他资料只作为补充。理由很简单:考研复习最怕的是知识点在几本书之间来回横跳,最后脑子里没有一棵完整的树。慕课版的每一章开头有学习目标,结尾有本章小结和习题,这个结构天然适合做“读完一章—做课后题—回头补漏”的循环。
还有一个容易被忽略的点:慕课版在部分章节里加了配套的线上课程资源提示,章节顺序和慕课视频基本能对上。我当时是一边看教材一边对着慕课过一遍,遇到讲得快的部分(比如调度算法那一节)就暂停自己推一遍公式,讲得慢的部分(比如文件系统结构)就倍速。这个节奏比纯啃书要快不少。
提醒:教材版本一定要和你报考院校指定的版本对齐。慕课版和第四版在部分章节的顺序、例题上有差异,尤其是虚拟存储器和磁盘管理两章,个别院校自命题会直接按指定版本的表述出题,别用错版本。
1.2 三轮复习的时间分配与阶段目标
我把OS的复习切成三轮,每一轮的目标非常明确,不做重复劳动。
第一轮的核心目标是“建骨架”。这一轮不追求做难题,只求把每一章的概念、算法名称、基本流程搞清楚。进程有哪几种状态、状态怎么转换、调度算法有哪几种、各自的优缺点是什么,这些先记牢。第一轮我大概花了三周,每天两到三小时,重点是通读教材加做课后选择题和概念题。
第二轮的核心目标是“补血肉”。这一轮开始动手算,银行家算法的安全性检查、页面置换的缺页率计算、磁盘调度的磁道移动数,都必须自己上手算,不能只看答案。这一轮也是整理课后习题答案的主要阶段。我当时的做法是每章准备一个单独的笔记本,左边抄题干,右边自己写答案,写完之后再和教材、资料对照,把错的、写得不完整的用红笔补上。
第三轮的核心目标是“抓漏洞”。这一轮不按章节顺序走,而是按题型走:把所有关于信号量的题集中做一遍,把所有关于页面置换的题集中做一遍。集中轰炸的好处是能快速发现自己在哪一类题上反复出问题。比如我发现自己每次遇到“多级反馈队列”的调度顺序题都会漏掉队列降级的条件,集中做了十几道之后这个坑就补上了。
三轮的时间比例我建议是 4:4:2。第一轮和第二轮各占四成,第三轮占两成。很多人第一轮拖得太久,导致后面没时间集中突破,这是很亏的。
1.3 配套资料的取舍原则
主线教材定了,配套资料要克制。我的配置是:一本教材、一份课后习题答案(自己整理为主)、一套历年真题、一个错题本。就这四样东西。
历年真题的作用是校准难度。教材课后题里有一些偏理论的推导题,真题未必考;反过来,真题里有些结合具体场景的综合题,教材课后题里也未必有。所以我建议在第二轮的中后期开始穿插做真题,用真题来反推哪些知识点是高频的。
错题本这个东西,我强烈建议用纸质本子而不是电子文档。原因是我试过用文档记错题,结果记完就再也没打开过。纸质本子放在桌上,每天翻两页,记忆效果完全不一样。错题本上只记三样东西:题干的关键条件、我当时错在哪一步、正确思路的关键转折点。不抄完整解答,抄了也不会看。
2. 课后习题的分层处理与答案整理方法
2.1 把课后题分成三类,别平均用力
汤小丹慕课版每章的课后习题量不小,如果每一道都同等对待,时间根本不够。我的做法是把课后题分成三类,投入的时间比例大概是 2:5:3。
第一类是概念辨析题,比如“进程和程序的区别是什么”“分页和分段的区别是什么”。这类题的答案在教材里基本能找到原话,处理方式是快速过一遍,把关键词圈出来,记住三到四个核心差异点即可。这类题不需要花太多时间,因为考试里这类题的分值不高,而且答案相对开放。
第二类是计算题,比如周转时间计算、银行家算法、页面置换、磁盘调度。这类题是必须动手算的,投入时间最多。我的经验是每道计算题至少自己独立算两遍,第一遍按自己的理解算,第二遍对照标准步骤算,把两次的差异记下来。
第三类是综合设计题,比如“设计一个生产者消费者问题的同步方案”“设计一个文件系统的目录结构”。这类题在初试里出现的频率低于前两类,但在复试里非常常见。所以我把它放在第二轮后期和复试准备阶段集中处理。
| 题型分类 | 典型代表 | 初试权重 | 处理策略 | 建议用时占比 |
|---|---|---|---|---|
| 概念辨析 | 进程vs程序、分页vs分段 | 中 | 抓关键词,快速过 | 20% |
| 计算分析 | 银行家算法、LRU、SCAN | 高 | 独立手算两遍 | 50% |
| 综合设计 | 同步方案设计、文件系统设计 | 低(初试)/高(复试) | 后期集中突破 | 30% |
2.2 答案整理的正确姿势:先自己写,再对照补
这是我最想强调的一个点。很多人整理课后习题答案是直接抄标准答案,抄完感觉很充实,但一到考试还是写不出来。原因是抄写这个动作几乎不调用你的思考,你的大脑处于被动接收状态。
正确的做法是:合上书,自己先写一遍答案,哪怕写得很难看、很不完整。写完再翻开书对照,找出漏掉的点,用不同颜色的笔补上。这个过程虽然慢,但记忆效率高得多。
举个具体的例子。第三章有一道关于银行家算法的经典题,给出若干进程对各类资源的最大需求、已分配量和系统可用资源,问是否存在安全序列。我第一次自己写的时候,漏掉了“检查完一个进程后要把它的已分配资源回收加到Work向量里”这个关键步骤,导致后面算不下去。对照答案补上这一步之后,我在错题本上专门写了一句“Work = Work + Allocation[i],别忘回收”。后来再做这类题就再没漏过。
注意:安全序列往往不唯一。自己算出来一条和答案不一样的序列,不代表算错了。判断标准是每一步都能满足 Need ≤ Work,只要步骤合法,序列不同是正常的。
2.3 一道计算题的完整拆解示范
拿一道典型的周转时间题来走一遍流程。假设有四个进程,到达时间和需要服务时间如下:
| 进程 | 到达时间 | 服务时间 |
|---|---|---|
| P1 | 0 | 7 |
| P2 | 2 | 4 |
| P3 | 4 | 1 |
| P4 | 5 | 4 |
先看先来先服务(FCFS)。按到达顺序执行,P1从0到7,P2从7到11,P3从11到12,P4从12到16。
周转时间 = 完成时间 − 到达时间:P1是7,P2是9,P3是8,P4是11。平均周转时间 = (7+9+8+11)/4 = 8.75。
带权周转时间 = 周转时间 / 服务时间:P1是1.0,P2是2.25,P3是8.0,P4是2.75。平均带权周转时间 = 14/4 = 3.5。
再看短作业优先(SJF,非抢占)。0时刻只有P1,先跑P1到7。7时刻P2、P3、P4都到了,按服务时间排序:P3(1)、P2(4)、P4(4)。P3从7到8,P2从8到12,P4从12到16。
周转时间:P1是7,P3是4,P2是10,P4是11。平均 = 8。
对比两组数据能看出,SJF的平均周转时间确实更短,这是SJF的理论优势。但SJF的问题是长作业可能饿死,而且实际系统中服务时间难以预知,所以真实系统里用的是基于历史预测的近似方案。
这道题我建议你至少做三遍,分别用FCFS、SJF、时间片轮转各算一遍,然后横向对比三组平均周转时间。这样算下来,你对调度算法的理解会比死记硬背强得多。
2.4 答案整理的归档方式
整理好的答案要能复用。我的归档方式是按章节建文件夹,每个文件夹里放三样东西:原始题干(剪贴或手抄)、我的初版答案、补充修订后的答案。这样到第三轮复习的时候,我不需要重新做题,直接看初版和修订版的差异就行,差异点就是我的薄弱点。
3. 六大核心章节的知识骨架与高频考点
3.1 进程与处理机调度:状态图是根
这一章的所有内容都挂在进程状态转换图上。三态模型(就绪、执行、阻塞)、五态模型(加了新建和终止)、七态模型(再加挂起就绪和挂起阻塞),这些状态之间的转换边,每一条都要能说清楚触发条件。
高频考点集中在调度算法上。FCFS、SJF、HRRN、时间片轮转、优先级调度、多级反馈队列,这六种算法要能从五个维度对比:是否抢占、是否考虑等待时间、是否考虑服务时间、对长作业是否友好、是否会产生饥饿。
多级反馈队列是难点也是高频点。它的核心规则是:新进程进入最高优先级队列,按时间片执行;如果没执行完就降到下一级队列;只有高优先级队列为空时才调度低优先级队列。做题时最容易错的是“降级时机”和“抢占时机”这两个判断。我的记忆口诀是“用完时间片就降级,高优先级来了就抢占”。
3.2 进程同步与信号量:把PV当工具用
进程同步这一章是OS里最考验思维的部分。核心工具是信号量,核心操作是P(wait)和V(signal)。P操作是申请资源,信号量减一,如果减完小于零就阻塞;V操作是释放资源,信号量加一,如果加完小于等于零就唤醒一个等待进程。
// 信号量的典型定义 typedef struct { int value; struct process *L; // 等待队列 } semaphore; void P(semaphore *S) { S->value--; if (S->value < 0) { // 将当前进程加入S->L,并阻塞 block(S->L); } } void V(semaphore *S) { S->value++; if (S->value <= 0) { // 从S->L中唤醒一个进程 wakeup(S->L); } }三大经典问题必须闭着眼睛都能写出来:生产者消费者、读者写者、哲学家进餐。这三个问题的变体在考试里出现的概率极高。
生产者消费者的关键是设置三个信号量:mutex初值为1(互斥访问缓冲区)、empty初值为n(空缓冲区数量)、full初值为0(满缓冲区数量)。顺序必须是先P资源信号量再P互斥信号量,反过来会死锁。
// 生产者 P(empty); P(mutex); // 放入产品 V(mutex); V(full); // 消费者 P(full); P(mutex); // 取出产品 V(mutex); V(empty);读者写者问题的关键是理解“第一个读者负责加锁,最后一个读者负责解锁”这个逻辑。哲学家进餐的关键是打破循环等待,方案有:最多允许四个人同时拿叉子、奇偶编号不同顺序拿叉子、一次拿两只叉子。
心得:信号量题的通用解法是先找“互斥资源”和“同步关系”,互斥资源配mutex,同步关系配资源信号量。找到这两类关系,代码框架基本就出来了。
3.3 内存管理与虚拟内存:三种置换算法的取舍
内存管理部分,从连续分配到分页、分段、段页式,核心是理解“地址转换”这条主线。逻辑地址怎么拆成页号和页内偏移,页表怎么查,快表怎么加速,多级页表怎么省空间,这一串问题要能连成一条线讲清楚。
虚拟存储器的考点集中在页面置换算法上。OPT(最佳置换)、FIFO(先进先出)、LRU(最近最久未使用)、Clock(时钟)这四种。
| 算法 | 是否可实现 | 是否产生Belady异常 | 命中率 | 实现开销 |
|---|---|---|---|---|
| OPT | 不可实现,仅作基准 | 否 | 最高 | 无 |
| FIFO | 可实现 | 是 | 较低 | 最低 |
| LRU | 可实现 | 否 | 较高 | 高(需栈或链表) |
| Clock | 可实现 | 否 | 接近LRU | 中等 |
Belady异常是FIFO独有的坑:增加物理块数,缺页次数反而增加。经典例子是访问序列 1,2,3,4,1,2,5,1,2,3,4,5,物理块为3时缺页9次,为4时缺页10次。这个例子我建议亲手算一遍,算完就再也不会搞混。
Clock算法是LRU的低成本近似,用一个访问位代替完整的时间戳。淘汰时扫描访问位,为1则置0继续扫描,为0则淘汰。改进型Clock还加了修改位,优先淘汰“访问位为0且修改位为0”的页,因为这样的页被淘汰时不需要写回磁盘。
3.4 文件系统与磁盘管理:从逻辑结构到物理结构
文件管理的主线是“逻辑结构”和“物理结构”的对应关系。逻辑结构是用户看到的(有结构文件、无结构文件),物理结构是磁盘上真实存放的(连续分配、链接分配、索引分配)。
索引分配是重点。UNIX系统的混合索引结构(直接块、一级间接、二级间接、三级间接)经常考,考法是给定索引节点大小、块大小、地址项大小,问最大文件大小是多少。计算方法是:直接块数 × 块大小 + 一级间接能索引的块数 × 块大小 + 二级间接 + 三级间接,逐层算。
假设一个索引节点有12个直接地址项、1个一级间接、1个二级间接、1个三级间接,每个地址项4字节,磁盘块大小4KB。
- 每个块能存放的地址项数 = 4KB / 4B = 1024
- 直接块:12 × 4KB = 48KB
- 一级间接:1024 × 4KB = 4MB
- 二级间接:1024 × 1024 × 4KB = 4GB
- 三级间接:1024³ × 4KB = 4TB
磁盘调度算法有FCFS、SSTF、SCAN、C-SCAN、LOOK、C-LOOK。考试里最常考的是SSTF和SCAN,给出一串磁道访问请求和一个初始磁头位置,让你算总移动磁道数。这里最容易错的是SCAN的方向判断,一定要看清楚题目给的初始移动方向。
3.5 输入输出系统:缓冲、SPOOLing与设备分配
I/O系统的考点相对分散,但有两个高频点必须拿下:缓冲技术和SPOOLing技术。
缓冲技术的目的是缓解CPU和I/O设备之间的速度差异。单缓冲、双缓冲、循环缓冲,各自的处理时间计算是常考的。单缓冲下处理一块数据的时间 = max(输入时间, 处理时间) + 传送时间;双缓冲下 = max(输入时间, 处理时间)。
SPOOLing技术(假脱机)是虚拟设备技术的典型应用,核心是用磁盘上的缓冲区模拟独占设备,让多个进程共享。打印机是经典例子:多个进程的打印请求先写入磁盘缓冲区,再由守护进程依次送打印机输出。这样就实现了“独占设备共享化”。
设备分配的数据结构是DCT(设备控制表)、COCT(控制器控制表)、CHCT(通道控制表)、SDT(系统设备表),这四张表的层次关系要理清楚:一个通道可以控制多个控制器,一个控制器可以控制多个设备。
3.6 死锁:四个必要条件与银行家算法
死锁的四个必要条件是互斥、占有并等待、不可剥夺、循环等待。这四个条件必须同时成立才可能死锁,破坏其中任何一个就能预防死锁。
银行家算法是这一章的绝对重点。算法的执行流程是:对每个进程检查其Need是否小于等于Work,如果满足就假设它执行完并回收资源,继续检查下一个,直到所有进程都能执行完。如果找不到这样的序列,系统处于不安全状态。
// 银行家算法安全性检查的伪代码 bool safetyCheck() { Work[] = Available[]; // 初始可用资源 Finish[] = false; // 所有进程未完成 while (存在 i 满足 !Finish[i] && Need[i] <= Work) { Work = Work + Allocation[i]; // 回收资源 Finish[i] = true; } if (所有 Finish[i] == true) return true; // 安全 else return false; // 不安全 }这里有个容易忽略的细节:题目问“系统是否安全”和“能否分配请求的资源”,是两个不同的问题。后者要先假设分配,再检查安全性,如果不安全就要撤销这次分配。我见过不少人直接跳到安全性检查,忘了先试分配这一步。
4. 高频错题排查与避坑对照表
这一节的内容几乎全部来自我自己的错题本和后来带学弟学妹时观察到的高频错误。整理成表,方便对照自查。
| 出错场景 | 典型错误表现 | 错误根源 | 正确做法 |
|---|---|---|---|
| 周转时间计算 | 把等待时间等同于周转时间 | 概念混淆 | 周转时间=完成-到达,等待时间=周转-服务 |
| 信号量PV顺序 | 先P(mutex)再P(empty) | 未理解死锁成因 | 资源信号量在前,互斥信号量在后 |
| 银行家算法 | 忘记回收已分配资源 | 步骤遗漏 | 每完成一个进程就 Work += Allocation |
| 页面置换FIFO | 忽略Belady异常 | 未验证块数增加的情形 | 块数增加时重新算缺页次数 |
| 磁盘调度SCAN | 方向判断反了 | 没看清初始移动方向 | 先确定磁头当前移动方向再排序 |
| 索引文件大小 | 忘记乘块大小 | 只算了块数 | 块数 × 块大小才是字节数 |
| 多级反馈队列 | 抢占与降级时机搞混 | 规则记忆不牢 | 用完时间片降级,高优先级到达抢占 |
| 死锁判断 | 把不安全状态等同于死锁 | 概念扩大化 | 不安全状态可能死锁,安全状态一定不死锁 |
再补充几个细节上的坑。
第一个坑是带权周转时间的分母。带权周转时间 = 周转时间 / 服务时间,分母是服务时间不是到达时间。这个看起来很低级,但确实有人搞错。
第二个坑是页面置换算法的初始状态。有些题目默认物理块初始为空,有些默认已经装满,一定要看清题干。初始为空时,前几次访问必然缺页,这会影响最终计数。
第三个坑是信号量的取值范围。信号量为负时,其绝对值表示等待队列中的进程数。这个性质在分析题里经常用到,题目问“此时有几个进程在等待”,答案就是信号量绝对值的当前值。
第四个坑是关于时间片轮转的调度顺序。如果时间片用完时刚好有新进程到达,通常的规定是新进程先入就绪队列,然后被换下的进程再入队尾。这个细节不同教材可能有细微差异,按你报考院校指定教材的规定来。
5. 复试拓展:从教材知识走向面试现场
5.1 复试问答的常见延伸方向
初试考的是“你会不会算”,复试考的是“你懂不懂为什么”。同样一个知识点,复试老师更倾向于追问背后的设计动机和实际应用。
比如初试可能问你“LRU算法的实现方式”,复试就可能问你“真实操作系统里为什么很少用严格的LRU”。这时候你需要答出:严格LRU需要为每个页面维护精确的访问时间戳,硬件开销大;实际系统多用Clock之类的近似算法,用访问位代替时间戳,在命中率和开销之间取平衡。
再比如初试考“进程和线程的区别”,复试可能追问“线程切换比进程切换快在哪里”。答案的关键是:同一进程内的线程共享地址空间,切换时不需要切换页表,TLB不需要刷新,所以开销小得多。
我整理了几个复试高频追问方向,都是我在准备阶段模拟过的:
- 调度算法:为什么Linux用CFS而不是简单的时间片轮转
- 内存管理:为什么现代系统普遍用多级页表而不是单级页表
- 文件系统:日志文件系统解决了什么问题
- 并发控制:自旋锁和互斥锁各自适用的场景
- I/O模型:阻塞I/O、非阻塞I/O、I/O多路复用的区别
这些问题的答案不需要背,需要你理解教材里的基础机制之后,自己推导出来。比如多级页表,本质是用时间换空间,通过分级减少常驻内存的页表项数量。
5.2 用教材知识回答开放性问题
复试里有一类问题是“谈谈你对某某技术的理解”,看起来没有标准答案,实际上是有答题框架的。我的框架是四步:先说这个技术解决什么问题,再说它的核心机制,然后说它的代价和局限,最后说它的典型应用或演进方向。
拿虚拟内存举例。它解决的问题是物理内存不够用;核心机制是把不常用的页换出到磁盘,用页表标记有效位,访问时触发缺页中断按需调入;代价是引入了缺页中断开销和地址转换开销,置换算法选择不当会引发抖动;典型应用就是现代所有通用操作系统。
这个框架答下来,逻辑完整,老师能看出你是有系统理解的,而不是背了几段话。
5.3 动手实践部分的补充
有些院校的复试会问到实践经历。如果你简历上写了操作系统相关的项目,老师很可能会追问细节。这时候教材知识就不够用了,需要你真正在Linux环境下动过手。
几个低成本但有效的实践方向:
第一个是用C语言实现一个简单的进程调度模拟器,把FCFS、SJF、时间片轮转都实现一遍,输入一组进程数据,输出平均周转时间和带权周转时间。这个项目代码量不大,但能让你对调度算法的理解从纸面变成可运行的逻辑。
第二个是观察真实系统的行为。比如用top或ps命令看进程状态,用vmstat看内存和交换分区的使用情况,用iostat看磁盘I/O。这些工具的输出字段含义,教材里都有对应概念。
# 查看系统内存和交换分区使用情况 free -h # 每秒刷新一次,查看进程状态 top -d 1 # 查看虚拟内存统计 vmstat 1 5 # 查看磁盘I/O统计 iostat -x 1 3第三个是用信号量实现一个生产者消费者的多线程程序。用pthread库,开两个线程分别做生产和消费,用信号量控制缓冲区的存取。跑通之后再尝试把信号量改成条件变量,对比两种写法的差异。这个练习对理解同步机制帮助极大。
提醒:实践项目的描述要诚实。做过就说做过,没做过就别往上写。复试老师追问两三个细节就能判断出你是真做过还是只看了教程。
6. 一些没人告诉你但很关键的经验
复习到后期,真正拉开差距的往往不是难题,而是这些看起来不起眼的细节。
错题本的用法要科学。我建议每道错题只写三行:第一行写题干的核心条件,第二行写我当时错在哪,第三行写正确的关键思路。不要抄完整解答,抄了等于没记。每周固定翻两次错题本,考试前一周只翻错题本,不再做新题。
教材上的图要自己画一遍。进程状态转换图、银行家算法的资源分配图、多级页表的地址转换图,这些图只看不画,考场上很容易画错箭头方向。我的做法是拿一张A4纸,合上书默画,画完再对照教材补漏。
关于刷题量,我的建议是课后计算题至少刷两遍,真题至少刷三遍。第一遍真题按年份做,第二遍按题型做,第三遍只做错过的题。三遍下来,你对高频考点的敏感度会明显提升。
时间管理上,我个人体会最深的一点是不要在第一轮追求完美。第一轮遇到不懂的地方,先标记,继续往下推,等整章过完再回头解决。卡在一个点上三天不动,是最亏的复习方式。很多知识点是前后关联的,后面的内容看完了,前面自然就通了。
最后说一个心态上的经验。操作系统这门课的知识密度确实大,最开始觉得乱是正常的。我当年的转折点是在第二轮中段,某天突然发现自己能把进程管理的整条线索从头到尾讲下来,那种感觉是前面所有零散积累突然串起来了。在那之前,你要做的就是相信这个过程,把每道题、每个概念老老实实过一遍,不跳步,不投机。
这个内容后续还可以往两个方向扩展:一是把每章的课后题挑出最有代表性的十道,做成一份带完整解答的速查清单;二是把复试常见的追问整理成问答对,配合教材章节索引。这两个方向我后面会陆续整理出来。