☰
操作系统408复习笔记:三层笔记法攻克进程内存与PV操作
2026/9/30 3:21:51 网站建设 项目流程

操作系统这门课,我前后完整过了三遍,前两遍都属于"眼睛会了,手不会"的状态。第一遍跟着王道讲义划重点,第二遍抱着教材啃概念,直到第三遍自己动手把整套知识点重新整理成一份能带进考场、也能应付课程设计的笔记,才算真正摸到门道。这份笔记的底子是王道《计算机操作系统》讲义加配套习题,但绝对不是照抄——凡是我在做题时卡住的地方、讲义一笔带过却暗藏坑点的地方,我都补上了自己的推导和索引。它适合正在准备考研408的同学,也适合正在上操作系统课、需要应付期末和课设的本科生。核心解决三件事:知识点记不住、题目看不懂、跨章节的题串不起来。下面我把这份笔记从设计思路到具体做法,完整拆一遍,你可以直接照着搭一份属于自己的版本。

1. 笔记的定位与整体框架:为什么要在现成讲义上再动手一遍

很多人复习操作系统的第一反应是买一本口碑好的讲义,从头翻到尾,画满荧光笔,然后合上书感觉空空如也。我第一遍就是这么干的,结果做题时发现,能认出来的知识点和能写出来的答案完全是两回事。所以第二遍我换了个思路:讲义的结论只当"参考答案",真正要留下来的是自己走一遍推导的过程。

1.1 只刷讲义踩到的三个坑

第一个坑是把"看懂"当成了"记住"。进程状态转换图这种东西,讲义上画得清清楚楚,看一遍觉得简单到不需要记。可真正做题时,题目问"进程从运行态转为就绪态的原因是什么",我脑子里第一反应是"时间片用完",但选项里同时有"等待I/O完成"和"被高优先级进程抢占",瞬间就懵了。问题的根源在于我只记住了箭头,没有把每个转换背后的触发条件单独拎出来做对比。

第二个坑是没有自己的索引。操作系统这门课的知识点天然是网状结构,虚拟内存里的页面置换算法和文件系统里的缓冲区管理共享同一套"容量有限、需要替换"的思维模型;进程调度和磁盘调度在算法设计上高度同构。如果笔记是按章节线性抄下来的,这些跨章节的联系就永远浮不出来,遇到综合题只能干瞪眼。

第三个坑是讲义给的结论没有推导过程。比如"FIFO算法会出现Belady异常,而LRU不会",讲义就这一句结论。可为什么FIFO会有,LRU不会?这背后涉及栈式算法(stack algorithm)的定义——一个算法的页框数从n增加到n+1时,缺页集合一定是前者包含于后者。把这一点想透,再遇到类似的判断题就能秒答。

1.2 我的三层笔记结构:骨架层、推导层、索引层

想清楚上面三个坑之后,我把笔记拆成三层,每层用不同的载体记录。

骨架层是整个操作系统的一张总图,用一张A3纸手绘,中心是"资源管理"四个字,向外伸出进程管理、内存管理、文件管理、I/O管理四条主干,每条主干上挂核心概念和它们之间的依赖关系。这张图不抄任何细节,只画名词和箭头,画的时候强迫自己去想"这个模块在解决什么问题,它依赖谁,谁依赖它"。

推导层是笔记本的主体,按算法和公式组织,而不是按章节组织。每一个调度算法、每一个页面置换算法、每一个计算公式,我都要求自己在笔记上完整写一遍推导过程,包括参数的含义、边界条件、以及最容易出错的特殊情形。比如计算有效访问时间(EAT)时,TLB命中和未命中两条路径要分别列式,再合并成一个通式。

索引层是一份错题编号表。每道做错的题,我不抄题干,只记三样东西:题目来源编号、卡住的知识点、我当时想歪的方向。比如"习题集第3章第12题,卡在页面置换的命中判定,我把命中当成了缺页"。这份索引越到后期越值钱,冲刺阶段基本就靠它。

1.3 载体和工具的选择:纸质、Markdown、卡片各管一段

三个载体我分了工。手绘骨架图、算法推导过程用纸质笔记本,原因是手写时的停顿本身就是思考,键盘打字太快,容易变成无脑录入。跨章节的对比表格、公式速查用Markdown,方便随时增删改,也方便打印成小册子。高频易错点做成卡片,正面写问题,反面写答案和推导要点,利用碎片时间反复过。

有些同学喜欢把全部笔记都放进电子文档,我个人不太推荐。电子化最大的问题是"搜索太方便",你不会去记,遇到问题就Ctrl+F。而考场上是没有搜索的,逼着自己把关键内容背下来的唯一办法,就是让记录本身变得"不经济"——手写一遍的时间成本,恰好就是记忆的成本。

提示:骨架图建议用不同颜色区分四个模块,但颜色不要超过四种,否则视觉上会互相干扰,反而记不住重点。

2. 核心模块拆解:把厚书读薄的四个抓手

操作系统教材动辄五六百页,但真正的考点骨架其实不到两百页。我的做法是每个大模块先找一个"主线问题",所有知识点都往这条线上挂。挂了不上去的,要么是超纲,要么是我理解有偏差。

2.1 进程与线程:状态转换是根,调度算法是枝

进程管理的主线问题是"CPU只有一个,怎么让多个任务看起来同时在跑"。把这个问题想透,进程状态、上下文切换、调度算法就全串起来了。

进程的五个状态里,最容易混淆的是三对转换:就绪到运行(被调度器选中)、运行到就绪(时间片用完或被抢占)、运行到阻塞(主动请求资源或等待I/O)。我在笔记里给每对转换都标注了"谁触发"和"是否主动"。记住一个要点:进程绝对不会从阻塞态直接变成运行态,必须先回到就绪态排队,这是因为调度器只从就绪队列里挑人。

调度算法部分,我整理了一张对照表,把各算法的评价指标摆在一起看,比单独背定义有效得多。

算法选择依据是否抢占优点典型缺点
先来先服务 FCFS到达顺序否公平、实现简单短作业等待时间长
短作业优先 SJF估计运行时间最短否平均等待时间最优长作业可能饥饿
高响应比优先 HRRN响应比最高否兼顾长短作业需计算响应比
时间片轮转 RR就绪队列顺序是响应及时时间片大小敏感
多级反馈队列队列优先级是综合性能好参数设置复杂

响应比的计算公式是响应比 =(等待时间 + 要求服务时间)/ 要求服务时间,也就是 1 + 等待时间/服务时间。这个式子的意义是:等待越久,响应比越高,天然防止长作业被无限推迟。

线程部分我重点记了一句话:线程是调度的基本单位,进程是资源分配的基本单位。这句话几乎每年都会以某种变体出现在选择题里。用户级线程和内核级线程的映射关系有三种(多对一、一对一、多对多),考的是各自的优缺点,比如用户级线程切换快但一个线程阻塞会导致整个进程阻塞,内核级线程相反。

2.2 内存管理:从连续分配到虚拟内存的一条主线

内存管理的主线问题是"程序比内存大,或者多个程序要共享内存,怎么安排"。这条线从连续分配一路走到虚拟内存,逻辑是层层递进的。

连续分配阶段,重点是四种放置策略。我这里用一句话概括它们的缺陷:首次适应(从头找第一个够大的)低地址会堆积碎片;最佳适应(找最小够用的)会留下大量难以利用的小碎片;最坏适应(找最大的)把大块切碎;邻近适应(从上次结束位置继续找)会让高地址也碎掉。这四个策略的对比是选择题常客,我建议用同一个空闲分区序列做一遍模拟,眼见为实。

进入非连续分配之后,核心概念是地址转换。逻辑地址到物理地址的映射分两步:先用页号 = 逻辑地址 / 页大小取整算出页号,再用页内偏移 = 逻辑地址 % 页大小算出偏移,然后查页表得到物理块号,最后拼成物理地址 = 物理块号 × 页大小 + 页内偏移。

举个具体例子。假设页大小为4KB,即 2^12 字节,逻辑地址是 0x3A2F。页号 = 0x3A2F / 0x1000 = 3,页内偏移 = 0x3A2F % 0x1000 = 0xA2F。查页表若第3页对应物理块号 7,则物理地址 = 7 × 0x1000 + 0xA2F = 0x7A2F。整个过程的关键就是"页大小是2的整数次幂时,页号和偏移在二进制上正好是切分的关系",页号是高若干位,偏移是低12位。

多级页表的计算也是必考题。以32位逻辑地址、4KB页大小、每个页表项4字节为例:页内偏移占12位,每个页表占4KB,可以放1024个页表项,也就是2^10项,所以每级页号占10位。32 - 12 - 10 = 10,正好再分一级,于是两级页表刚好覆盖。如果换成64位地址、8字节页表项,每页8192/8 = 512项,即9位,64 - 12 - 9 = 43位,需要大约5级页表。这类题的解题套路就是把地址位数拆成"各级页号位数 + 页内偏移位数"。

2.3 文件系统与磁盘:从逻辑结构到物理落地的链路

文件管理的主线问题是"一堆字节怎么变成用户眼里的文件,又怎么落到磁盘上"。这条链路上有三个关键环节:逻辑结构、目录结构、物理结构。

逻辑结构分顺序文件、索引文件、索引顺序文件。顺序文件适合批量读取,随机访问要扫一遍前半部分;索引文件随机访问快,但索引块本身占空间;索引顺序文件是折中,每若干条记录建一个索引项,兼顾两者。

物理结构(文件分配方式)的重点是三种:连续分配、链接分配、索引分配。连续分配支持随机访问但在文件增长时会遇到"后面已经有别的文件"的问题;链接分配没有外部碎片但不能随机访问,且指针占空间;索引分配用索引块存所有块号,随机访问支持好,缺点是索引块本身的开销。

目录结构里,重点是FCB(文件控制块)和索引节点(inode)的区别。采用了inode之后,目录项里只保留文件名和inode号,检索速度大幅提升,这是必考的概念点。

磁盘调度部分我用一个统一例子来对比,磁头初始位置在53磁道,请求序列是98、183、37、122、14、124、65、67,磁道范围0到199:

算法服务顺序总移动磁道数
FCFS53→98→183→37→122→14→124→65→67640
SSTF53→65→67→37→14→98→122→124→183236
SCAN(向大号方向)53→65→67→98→122→124→183→199→37→14331
LOOK(不回端点)53→65→67→98→122→124→183→37→14299

SSTF的磁道数最少,但会导致远端请求饥饿;SCAN和LOOK解决了饥饿问题,代价是平均寻道时间变长。LOOK和SCAN的差别就在于是否移动到磁盘端点,这一点经常被出题人拿来设置陷阱。

2.4 同步、互斥与死锁:把PV操作写成肌肉记忆

这一块的主线问题是"多个进程同时访问共享资源,怎么保证不出错"。生产者消费者、读者写者、哲学家进餐这三个经典模型,我建议全部默写到能闭着眼睛写出来的程度。

生产者消费者的标准写法是这样的:

semaphore mutex = 1; // 缓冲区互斥访问 semaphore empty = n; // 空闲缓冲区数量 semaphore full = 0; // 已填充缓冲区数量 // 生产者 while (1) { P(empty); // 先申请空位,再拿锁 P(mutex); // 把数据放入缓冲区 V(mutex); V(full); } // 消费者 while (1) { P(full); // 先申请数据,再拿锁 P(mutex); // 从缓冲区取数据 V(mutex); V(empty); }

这里最经典的坑是两个P操作的顺序不能颠倒。如果先执行 P(mutex) 再执行 P(empty),当缓冲区满时,生产者持有锁在等空位,消费者想取出数据却拿不到锁,双方永久等待,直接死锁。同理,V操作的顺序也不需要严格对称,但资源信号的V一般放在互斥V之后更安全。

死锁部分要牢记四个必要条件:互斥、占有并等待、不可抢占、循环等待。这四个条件是"同时成立"才可能死锁,破坏任何一个就能预防死锁。处理策略分四类:预防、避免、检测、解除。其中银行家算法属于避免策略,是考试计算题的高频考点。

我给银行家算法准备了一个标准案例:系统有A、B、C三类资源,总量(10,5,7),五个进程的已分配情况和最大需求如下。

进程Allocation (A,B,C)Max (A,B,C)Need (A,B,C)
P00,1,07,5,37,4,3
P12,0,03,2,21,2,2
P23,0,29,0,26,0,0
P32,1,12,2,20,1,1
P40,0,24,3,34,3,1

已分配总量是(7,2,5),所以初始 Available = (3,3,2)。找安全序列的过程是:先挑Need不超过Available的进程,假设它执行完并释放资源,再继续找下一个。这里可以依次选 P1(Need 1,2,2)→ P3(Need 0,1,1)→ P4(Need 4,3,1)→ P0(Need 7,4,3)→ P2(Need 6,0,0),每一步都满足条件,最终Available回到(10,5,7),所以安全序列 P1→P3→P4→P0→P2 成立。做这类题的关键是每执行一个进程就把它的Allocation加回Available,不要漏加或加错类别。

3. 从零到一的整理流程:我实际是怎么做的

知道要记什么只是第一步,真正决定效率的是整理顺序。我试过"边看书边抄笔记",结果速度极慢而且抄完就忘。后来改成三轮制,每一轮只干一件事,效率和留存率都明显提升。

3.1 第一轮:只搭骨架,定义先别抄

第一轮的目标只有一个——建立全局地图。这一轮我完全不抄任何定义的完整表述,只画结构:每章的标题、核心概念名、概念之间的箭头。比如内存管理这一章,我在纸上只写"内存管理"→"连续分配"→"非连续分配"→"虚拟内存",每个节点下再挂三五个关键词。

为什么第一轮不抄定义?因为定义的记忆本质上是"精确复述",而精确复述需要以理解结构为前提。如果连这个知识点在整个体系里的位置都不知道,抄再多定义也只是死记硬背,几天就忘。等结构和逻辑通了,定义反而会自己"长"出来。

这一轮我大概花了十天,每天两小时,只做骨架图。做完之后的效果是,随便指一个知识点,我能说出它属于哪个模块、解决什么问题、和哪些概念相邻。这个"地图感"在后面做题时价值极大,很多选择题的错误选项一看位置就知道不对。

3.2 第二轮:用题目反推,把错题变成笔记的一部分

第二轮是核心投入期,方法是用题目反推。具体做法是:先做一章的题,做题时不会的直接跳过,不查书;做完对答案,凡是错的、蒙对的、虽然做对但没想清楚原因的,全部标记;然后带着这些问题回到讲义和教材,只精读对应的段落。

这个顺序的意义在于,问题先出现,阅读才有靶子。如果你先通读讲义再做题,阅读时没有具体的困惑,注意力会自动滑过那些"看起来懂了其实没懂"的地方。而带着题目去读,你会发现自己原来把某个概念理解成了另一个意思,这种认知冲突恰恰是记忆最深的地方。

每道错题我会在笔记上做三步处理。第一步写清楚"我错在哪",不是抄正确答案,而是描述自己当时的错误逻辑。第二步写"正确思路的关键点",用一句话概括。第三步找一道同类型的题做验证,如果还是错,说明这个知识点要重新整理。这样处理下来,一道错题的产出往往比做十道对题还多。

3.3 第三轮:压缩与默写,把一本书收成一页纸

第三轮是冲刺期的压缩。目标是把我整理的所有推导层和索引层内容,压缩成一页A4纸的"最后看一眼"。压缩的原则是只留"最容易忘的"和"最容易错的",凡是已经形成肌肉记忆的公式和步骤全部删掉。

压缩的过程本身就是检验。你会发现有些知识点写不进一页纸里,因为它其实需要一大段解释才能说清楚——这说明你还没真正掌握,只是在复述。真正的掌握是能把复杂的东西压成一个关键词或者一个箭头,看到它就能展开全部内容。

默写是压缩之后的验证手段。我每周会挑一个模块,白纸一张,不看书,把该模块的骨架图、核心公式、典型例题的解法全部写出来。写完再对照笔记补缺。这个过程很痛苦,但效果极好,考场上基本就是这种"白纸默写"的状态,提前练过就不会慌。

注意:三轮之间不是严格的串行关系。第二轮遇到卡壳时完全可以回头补第一轮的骨架,第三轮默写发现漏洞也要回到第二轮重新整理错题。把三轮当成三个工具,按需取用,而不是三段必须走完的路。

4. 高频易错点与排查实录

复习到后期,你会发现错题其实反复集中在几个点上。把这些高频坑点提前梳理出来,比盲目刷题有效得多。

4.1 计算题:页面置换、银行家算法、磁盘寻道

页面置换是失分重灾区。我用的经典例题是引用串 7,0,1,2,0,3,0,4,2,3,0,3,2,1,2,0,1,7,0,1,共20次访问,物理块3个。手工模拟的结果是:FIFO缺页15次,LRU缺页12次,OPT缺页9次。对应的缺页率分别是75%、60%和45%。

模拟时最容易出错的是命中判定。FIFO只需要检查当前页是否在页框里;LRU不仅要检查是否在,还要更新它的"最近使用"顺序;OPT则要向后扫描,找出未来最晚才被访问的那一页替换掉。很多同学LRU算错,就是因为命中的页没有及时调整到最新位置,导致后面替换时选错了对象。

Belady异常是另一个坑。用一个专门的例子:引用串 1,2,3,4,1,2,5,1,2,3,4,5,物理块3个时FIFO缺页9次,物理块4个时FIFO缺页10次——页框增加反而更差。这个反常现象只在FIFO这类非栈式算法上出现,LRU和OPT都是栈式算法,永远不会出现。考试如果给出"增加页框后缺页次数必然减少"的选项,直接判错。

磁盘寻道的计算相对机械,但要注意三个细节。第一,移动磁道数是相邻请求之间的距离绝对值之和,不是跨度;第二,SCAN和C-SCAN要不要走到端点,取决于题目表述,如果题目说"扫描到磁盘端点"就必须算到0或199;第三,初始方向的判断,有些题目会明确"当前正在向磁道号增大的方向移动",有些则需要你根据SSTF之外的信息推断。

4.2 概念辨析速查表

下面这些概念对,几乎年年以各种形式出现,我把它们整理成一张对照表,方便考前扫一遍。

概念A概念B关键区别
进程线程进程是资源分配单位,线程是调度单位
并发并行并发是宏观同时微观交替,并行是真正的物理同时
死锁饥饿死锁是循环等待,饥饿是长期得不到资源但可能解除
分页分段分页对用户透明、大小固定;分段对用户可见、大小可变
内部碎片外部碎片内部碎片在分配单元内部,外部碎片在分配单元之间
缓冲缓存缓冲用于速度匹配,缓存用于提高访问效率
文件逻辑结构文件物理结构逻辑是用户视角的组织,物理是磁盘上的存放方式

这张表的用法是"遮盖法":盖住右边一列,看左边的概念自己复述区别。如果复述不出来,说明这两个概念在你的脑子里还是糊的。

4.3 复习过程中反复出现的几个问题

第一个问题:公式记不住。我的解决办法是只背推导思路,不背最终形式。比如有效访问时间的公式,我记的是"命中走一条路,不命中走另一条路,加权求和",具体形式现场推。这样即使考场上紧张忘了一部分,也能推出来。

第二个问题:算法步骤会背但不会用。这通常是因为你背的是文字描述,而不是具体的操作序列。解决办法是在草稿纸上用同一个例子把算法走一遍,走的时候把每一步的状态都写出来,包括队列内容、指针位置、计数器数值。

第三个问题:跨章节的题无从下手。这类题的破题点是找到两个章节的"共同抽象"。比如"虚拟内存的页面置换"和"文件系统的缓冲区替换"共享同一个模型:空间有限、访问有序、需要淘汰。把共同模型抽出来,两边的知识就可以互相迁移。我在笔记里专门开了一页"跨模块同构",把所有能找到的这种对应关系都列出来,做题时先判断它属于哪个模型,再套对应的方法。

提示:错题索引建议每周更新一次,把已经彻底搞懂的条目划掉,只保留仍然会犯错的。考前只看剩下的那一小撮,心理压力会小很多。

5. 不同目标下的复习节奏与延伸

同样一份笔记,考研、期末、课程设计三种目标下的用法完全不同。我在不同阶段用过这三套节奏,效果都不错。

5.1 考研408与期末考的区别

考研408的操作系统部分,分值大概在35分左右,特点是范围固定、题型稳定、计算量大。复习重心应该放在计算题和概念辨析上,尤其是页面置换、银行家算法、磁盘寻道、PV操作这四类,几乎每年必考。复习节奏上,基础阶段可以慢,强化阶段必须以题为主线,冲刺阶段反复默写公式。

期末考则灵活得多。不同学校的侧重点差异极大,有的老师喜欢考PV操作的变体,有的偏爱文件系统的计算。最有效的办法是拿到历年真题,统计一下高频考点,按出现频次排序复习。期末考的另一个特点是概念题的表述和教材高度绑定,所以教材上的原话反而要背准。

5.2 课程设计与实验环节怎么补

如果你同时在做课程设计,比如实现一个简单的进程调度模拟器或者页面置换模拟器,那复习笔记可以直接拿来当设计文档。我的做法是把笔记里的算法步骤直接翻译成代码框架:进程调度对应一个就绪队列和调度循环,页面置换对应一个页框数组和一个替换策略函数,PV操作对应信号量的封装。

写模拟器有一个额外好处:它会强迫你把算法里的每个边界条件都想清楚。比如实现LRU时你会立刻发现,"更新最近使用顺序"这个动作在数组实现和链表实现下成本差别巨大,这种体感是看书得不到的。如果你想进一步深入,可以试着用真实的操作系统接口做实验,比如在Linux环境下观察进程的内存映射(查看 /proc/[pid]/maps)、用系统调用实现共享内存和信号量、观察文件系统的inode信息。这些实验不要求你写内核代码,但能让你把课本概念和真实系统对上号。

关于学习环境,我建议至少装一个主流的Linux发行版来练手,命令行操作、进程管理、文件权限这些内容在真实环境里过一遍,比看截图印象深得多。市面上常见的发行版都可以,图形界面友好一些的适合新手,服务器向的适合练命令。国内也有几款自主演进的操作系统发行版,底层同样遵循POSIX规范,命令和内核机制与主流发行版基本一致,用来做课程实验完全够用。

5.3 现代操作系统里的延伸

课本上的操作系统模型相对简化,真实系统还有不少值得了解的延伸。比如多核环境下的缓存一致性、NUMA架构下的内存分配策略、容器技术用到的命名空间和cgroups机制,这些都是课本知识在现代工程中的自然延伸。你不需要为考试去啃这些,但在做课程设计或者写简历项目时,能把课本概念和现代工程挂上钩,会显得理解更深入。

还有一个容易被忽略的点:操作系统的历史演进。从批处理到分时,从单核到多核,从物理机到虚拟化,每一次变化都是在解决特定的资源矛盾。理解这条演进线,很多设计决策就变得理所当然,比如为什么要有虚拟内存、为什么要有进程隔离。复习到后期,我建议花两个小时把这条线捋一遍,它会让你对整个学科的框架有质的提升。

最后分享一个我自己踩过的小坑。我前两轮复习时总想着"等我把这章完全搞懂再开始下一章",结果卡在某个难点上好几天,进度严重滞后。后来改成"允许带着未解决的问题往前走",把卡住的地方记进索引层,第二轮再集中攻。事实证明,很多当时觉得过不去的坎,在学完后面对应知识后回头看,自然就通了。复习是一个网络逐渐连通的过程,不要指望任何一条线一次就能拉直。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询