尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

操作系统复习笔记:以小林Coding为主线串讲进程、内存、文件系统与并发

发布时间:2026/9/29 8:55:41

资讯中心
01
ARTICLE

操作系统复习笔记:以小林Coding为主线串讲进程、内存、文件系统与并发

操作系统复习笔记:以小林Coding为主线串讲进程、内存、文件系统与并发
很多人学操作系统的时候习惯从教材第一章操作系统的定义开始一句一句往下啃啃到进程调度就开始犯困翻到内存管理基本就放弃了。我自己也是这样反复学了三遍操作系统前面两遍都是看得懂、记不住、用不上直到后来我换了个方式——把小林Coding的操作系统笔记当成复习主线配合教材和实验来回对照才真正把散落的知识点串成了一张网。这篇就是我整理这份读书笔记的全过程记录包括我是怎么选的复习顺序、每个模块我踩过哪些理解上的坑、以及那些面试和考试里反复出现的核心概念我是怎么用自己的话重新讲明白的。不管你是在准备期末、考研复试还是校招笔试面试又或者是工作几年后想把操作系统底子重新捡起来这篇内容都能给你一套可以直接抄作业的整理思路。1. 为什么我把小林Coding的操作系统笔记当成主线来读1.1 操作系统这门课真正的难点在哪操作系统最劝退的地方不是概念难而是概念之间的关联太多、太隐蔽。你单独看进程能看懂单独看虚拟内存也能看懂但一旦问进程切换的时候页表发生了什么变化很多人就答不上来了。这说明知识是散着学的没连成线。我第一遍学的时候用的是某本经典教材从头到尾划重点笔记记了整整一本结果合上书脑子里只剩下几个词进程、线程、死锁、分页。第二遍我试着看视频课程老师讲得很清楚但我发现一个更严重的问题——听懂不等于会答。面试官问为什么线程切换比进程切换开销小我能隐约知道点意思但组织不成一句有逻辑的话。后来我想明白了一件事操作系统的知识点本质上是围绕几个核心问题展开的比如CPU怎么高效地被多个任务共享内存不够用怎么办多个任务抢同一份数据怎么办。如果我能把知识点挂到这些核心问题下面记忆就有了钩子。小林Coding的笔记之所以适合当主线是因为它的组织方式就是问题驱动的。它不会一上来给你一堆定义而是先抛出一个为什么再去拆解背后的机制。这种结构和人的认知习惯是对上的所以用它来打骨架再用教材去填细节效率会高很多。1.2 我的笔记整理思路从抄到问我把整个复习过程分成了三种笔记这是我自己摸索出来比较好用的方式骨架笔记只写大标题和核心问题比如进程切换开销大在哪几个部分不写答案逼自己回忆。细节笔记针对骨架里的每个问题把能想到的机制、参数、例子写下来。错题笔记每次做题或面试被问倒的单独记一页标注当时卡在哪一步。前两遍学习我都是只做细节笔记结果笔记很厚但复习的时候根本不知道从哪看起。第三遍改成先搭骨架笔记我发现复习成本一下降下来了——因为骨架只有几页几分钟就能过一遍然后针对薄弱的环节再翻细节笔记。提示骨架笔记一定要用问句来写。虚拟内存是个名词看完就忘没有虚拟内存会怎样是个问题能逼你回忆服务器实际场景。这一套方法的核心逻辑是记忆的本质是提取不是存储。你反复抄写是在存储效果很差你合上书自己问自己答是在提取效果极好。这也是为什么很多人觉得自己看了很多遍还是记不住——因为你一直在做低效的存储动作。2. 进程与线程把任务这个词拆到内核结构里2.1 进程到底是什么一个被调度的实体学操作系统的第一道坎就是进程这个概念。教材上会告诉你进程是程序的一次执行这句话没错但它没说清楚进程在内核里到底是由什么组成的。我一开始就是卡在这个抽象上。后来我换了个理解方式把进程想象成一个正在干活的工人。这个工人手里有他的工作台账代码段我要执行的指令。他的记事本数据段、堆栈我算到哪了、现在有哪些中间变量。他的工位牌PCB进程控制块内核靠这块牌子来认识他、管理他。所以进程 代码 数据 内核里描述它的一份结构PCB。真正被操作系统调度的是PCB不是代码本身。理解这一点后面很多问题就顺了比如进程切换为什么要保存寄存器——因为工人要换班了得先把当前进度记在记事本上等下次回来接着干。我在笔记本上专门画了一张表把PCB里我理解得比较透的字段列了出来PCB字段作用类比进程ID、状态唯一标识与当前所处状态工号 是否在岗程序计数器下一条要执行的指令地址干到哪一页了寄存器集合切换时保存/恢复现场手上的半成品内存管理信息页表、段表基址能进哪几个仓库打开文件列表当前持有的资源手里拿着的钥匙这张表是我自己整理的教材不一定这么列但列完之后我对进程的理解从一个词变成了一堆具体的东西。2.2 线程与进程的区别为什么面试老爱问线程这个问题被问烂了但真正能答出层次的人不多。大多数人只会背进程是资源分配的基本单位线程是调度的基本单位。这句话没毛病但面试官接着问那具体省在哪就答不上来了。我的理解是这样进程之间切换要换一整套房子线程之间切换只换房间里的椅子和桌子。具体来说进程切换时除了保存寄存器操作系统还要切换地址空间——也就是切换页表、刷新TLB。这两件事都很贵。而线程是同一个进程里的兄弟它们共享地址空间所以切换时不用换页表开销自然小。我在笔记里对比了一下两者的开销来源进程切换开销保存/恢复寄存器 切换页表更贵 可能刷新TLB 可能切换内核栈。线程切换开销保存/恢复寄存器 切换栈指针地址空间不动。这里有个容易搞混的点内核级线程的切换仍然是需要陷入内核的所以它也不是零成本。真正零成本的是用户级线程的切换因为它在用户态就能完成但它有个致命问题——一个线程阻塞会拖垮整个进程因为内核根本不知道进程里还有别的线程。注意面试里如果被问到协程为什么快答案基本就是这个逻辑——协程的切换在用户态完成不经过内核所以省去了内核态与用户态来回切换的开销。但代价是调度器要靠用户程序自己实现。2.3 进程状态与上下文切换的真实开销进程状态转换图就绪、运行、阻塞是必考内容但我一开始只是把它当图背没理解背后的调度逻辑。后来我把它和上下文切换绑在一起看才明白为什么这张图这么重要。一个进程从运行态切出去无非就是几种情况时间片用完回就绪态、等待IO去阻塞态、被更高优先级抢占回就绪态。这几种情况触发的切换代价是不一样的但都会做一件事——保存现场。我做过一个不太严谨的估算用来理解切换开销的构成。假设一次上下文切换要保存30个左右的通用寄存器加上切换页表导致的TLB失效一次切换的代价大概在几微秒这个量级。听起来很小但如果你每秒切换几万次那CPU有相当一部分时间就花在换班上而不是干活上了。这也是为什么操作系统的调度器要尽量做减少切换的优化比如批量处理、尽量减少不必要的唤醒。搞清楚这一点你再看那些调度算法时间片轮转、多级反馈队列就不会觉得它们是孤立的公式了它们都是在公平和开销之间找平衡。3. 内存管理虚拟地址、分页和页表是怎么串起来的3.1 为什么需要虚拟内存内存管理这一块我觉得是整个操作系统里最抽象、也最容易学崩的部分。抽象的原因在于——地址这个东西你看不见摸不着。我的破题方法是先回答一个很朴素的问题如果没有虚拟内存会发生什么答案有这么几条程序之间会互相踩内存A程序想写地址0x100B程序的变量刚好也在那直接就被改坏了。程序必须自己知道自己会占多少内存写程序的人得操心物理内存布局太难用了。内存碎片没法收拾程序退出后留下的空洞别的大程序可能用不上。虚拟内存就是为了解决这些问题的。它做的事本质上是在程序和物理内存之间加了一层翻译。程序看到的地址是虚拟地址CPU真正访问的时候由MMU内存管理单元把它翻译成物理地址。我在笔记里用了一个快递的类比你寄快递写的是某小区某栋某号虚拟地址快递员要送到实际的地方中间需要查一个地址映射表页表才知道小区的那一栋楼实际在城市的哪个位置。搬家了物理内存换了你写的地址不用改只需要更新映射表。这个类比帮我记住了整个虚拟内存的核心思想。3.2 分页与页表多级页表解决什么问题虚拟地址翻译的方式有两种分段和分页。现在主流用的是分页因为它简单、不产生外部碎片虽然会产生内部碎片。分页的核心思想是把虚拟地址空间和物理地址空间都切成固定大小的块虚拟的叫页物理的叫页框大小一样通常是4KB。这样一来映射就以页为单位进行而不是以字节为单位。但问题来了如果虚拟地址空间是32位页大小4KB那么一共有2的20次方约100万个页。每个页需要一个页表项来记录它映射到哪个物理页框一个页表项假设4字节那就是4MB。这还只是32位64位下这个数字会爆炸。所以有了多级页表。它的核心思路是没必要为没用到的那部分地址空间维护页表。二级页表把原来的一大张表拆成了页目录 各个页表。如果某段虚拟地址根本没被用到对应的页表根本不用创建省下来的空间非常可观。我用一个表格对比了一下方案空间占用缺点单级页表固定与地址空间成正比空间浪费严重浪费在没用的地址上多级页表按需分配实际使用量小查表要多次访存需要TLB加速倒排页表只与物理内存大小相关查找复杂实现难度高这里有个关键补充多级页表虽然省空间但查一次地址要多次访问内存二级页表是两次三级是三次还没算最后访问数据那次。为了缓解这个问题硬件上引入了TLB也就是一块专门缓存页表项的小缓存。命中的话就不用走多级查表了这是性能的关键。3.3 缺页中断与置换算法虚拟内存还有一个强大的能力——让程序用的内存比实际物理内存还大。靠的就是按需调页页表项里有个标志位表示这一页是不是在物理内存里。如果不在CPU访问时就会触发缺页中断操作系统负责把这一页从磁盘读进来。缺页中断的处理流程我背过很多遍但真正理解它是做了这几件事检查这个虚拟地址是否合法不合法直接报错段错误。找一个空闲的物理页框如果没有就得淘汰一个页。把需要的页从磁盘读进内存。更新页表项重新执行那条触发中断的指令。第三步淘汰哪个页就是置换算法的用武之地。常见的算法有FIFO、LRU、Clock。面试常考的是为什么LRU比FIFO好但实际系统里又不用纯LRU——因为纯LRU实现成本太高需要维护访问顺序所以实际用的Clock算法是LRU的一个近似。提示有一类经典陷阱题叫Belady异常说的是FIFO算法在某些访问序列下物理页框越多缺页次数反而越多。这个反直觉的现象用栈式算法来区分——LRU是栈式算法不会有Belady异常FIFO不是。我自己调试缺页问题时有个体会不要一上来就追求理论最优先看工作集。一个程序如果在短时间内集中访问一小块地址这就是局部性原理那么无论是LRU还是Clock都能表现很好。反过来如果程序访问模式是随机的那什么算法都救不了。所以性能优化的大方向其实是写出有局部性的程序而不是指望操作系统帮你兜底。4. 文件系统与IO一次read调用背后发生了什么4.1 文件系统抽象层如果说进程和内存是算的部分那文件系统和IO就是存的部分。这一块我一开始学得特别被动因为教材里全是i节点、目录项、位图这种名词看起来很枯燥。直到我把它们和一个具体操作绑在一起——打开并读取一个文件——才觉得活了过来。文件系统的核心任务是把一堆磁盘块这种物理资源抽象成文件和目录这种人类能理解的结构。为了实现这个抽象它需要维护几样东西超级块描述整个文件系统的元信息比如总大小、块大小、空闲块信息。i节点描述一个文件的元数据比如大小、权限、数据块在磁盘上的位置。注意i节点里通常不存文件名。目录项把文件名和i节点编号对应起来。所以目录本质上就是文件名到i节点的映射表。数据块真正存文件内容的地方。这里有个我踩过的坑我一直以为文件名是文件的一部分直到理解了i节点和目录项的分工才明白——文件名其实属于目录不属于文件本身。这也是为什么硬链接能实现——多个目录项指向同一个i节点文件本身只有一份。4.2 缓冲、页缓存与零拷贝理解了抽象层再来看一次read调用就会发现它比想象中复杂得多。调用read(fd, buf, size)用户拿到的数据其实经过了这几步系统调用进入内核态。根据文件描述符找到对应的打开文件表项再找到i节点。计算要读的逻辑块号检查页缓存里有没有。不在的话发起磁盘IO把数据读进页缓存。把数据从页缓存拷贝到用户缓冲区。注意第5步这里是关键——数据被拷贝了一次。如果读的是大文件、又追求高吞吐这一次拷贝就是浪费。于是就有了零拷贝技术核心思路是让数据在内核里直接送到要用的地方不经过用户缓冲区。我给零拷贝整理过一个对比表方式数据拷贝次数典型场景普通readwrite4次含两次内核态内拷贝一般文件读写mmapwrite3次读大文件sendfile2次文件传输服务sendfileSG-DMA2次无CPU拷贝高性能传输这里我要补充一个很多人会错的理解零拷贝不是一次拷贝都没有而是减少了CPU参与的数据拷贝次数。DMA负责的部分不算CPU开销。搞清楚这一点你才不会被零拷贝这个名字误导。注意页缓存的存在让读文件和读内存在性能上其实是一回事——只要命中缓存速度就是内存级别。所以调优文件IO时第一件事往往是看缓存命中率而不是急着换硬盘。5. 并发与同步锁、信号量与死锁5.1 竞态条件从哪来并发问题我觉得是操作系统里最实战化的一块因为它和你在写多线程程序时踩的坑是同一批坑。理解并发的前提是理解为什么会出问题。问题的根源在于多个执行流共享了同一份数据而且它们的操作不是原子的。比如经典的i看起来是一行代码实际拆开是三步读i、加1、写回i。如果两个线程同时执行就可能都读到旧值最后只加了1次。我把这个问题的本质总结成一句话竞态条件的根源是检查和使用之间被打断了。你检查了某个条件成立准备使用结果在这中间别人把它改了。这种检查后使用TOCTOU的问题是所有并发bug的母亲。解决竞态的核心思想就一个把检查使用变成一个不能被打断的原子操作。围绕这个思想才有了互斥锁、信号量、条件变量、原子变量这些工具。5.2 信号量与互斥锁的选择很多人搞不清信号量和互斥锁的区别我来把它们的适用场景说清楚。互斥锁保证同一时刻只有一个线程进入临界区。它的值只有0和1本质是我给这段代码上了把锁。信号量一个计数器用来控制有几个资源可以被同时访问。互斥锁其实是信号量在值为1时的特例。我用一个更贴近场景的例子来区分洗手间。互斥锁就像只有一间洗手间进去就锁门出来解锁。信号量就像是有3个工位可以同时干活来了第4个人就得等谁走了谁喊一声。我在笔记里做了一个决策表方便自己选工具场景推荐工具原因保护一段临界区代码互斥锁语义简单开销小控制并发数量如连接池信号量需要计数语义等待某个条件成立条件变量避免忙等节省CPU单变量自增/比较交换原子操作无锁性能最好这里我要提醒一个很多人忽略的点锁的粒度。锁太粗并发度上不去锁太细加锁解锁的开销和死锁风险又上来了。这是个需要根据实际业务来权衡的问题不存在标准答案。我的经验是——先保证正确性用大锁跑通再用性能分析工具找出真正的热点针对性地减小锁粒度。5.3 死锁的四个条件与排查死锁是并发里最让人头疼的问题因为它不会立刻报错而是让程序卡住。理解死锁从它的四个必要条件入手最清晰互斥资源同一时刻只能被一个线程占用。持有并等待拿着一个资源还在等另一个资源。不可剥夺资源不能被强行抢走。循环等待存在一个环形的等待链。这四个条件必须同时满足才会死锁所以破坏任意一个就能预防死锁。实际工程里最常用的是破坏循环等待——给所有锁编号规定必须按顺序加锁。这样就不可能形成环。排查死锁我一般按这个顺序来先确认是不是真死锁可能只是卡在慢IO上看CPU占用率和线程栈。看线程栈如果多个线程都在等锁且等待链构成环基本就是死锁了。定位加锁顺序找出是哪几把锁、谁先谁后不一致。统一顺序或改用超时锁tryLock带超时是一种实用的兜底手段。提示死锁和活锁饥饿是三件不同的事别搞混。活锁是线程一直在动但没进展互相让路饥饿是某个线程长期拿不到资源优先级太低。死锁是彻底不动了。我在实际写并发代码时的体会是能不用锁就不用锁。能用消息队列传递数据、能用不可变对象、能用一个原子操作解决的尽量别引入锁。因为锁带来的复杂度、死锁风险和调试成本往往比它解决的问题本身还要大。6. 我的笔记复习节奏和输出方法6.1 怎么用费曼法检验理解复习到后期我发现能背不等于能懂。检验自己是否真的理解一个知识点我用的是费曼法的变体——假装给别人讲一遍。具体做法是挑一个概念比如虚拟内存然后对着空气或者对着录音讲三分钟。要求是不能看笔记不能用术语糊弄。如果卡壳了说明这里就是你的理解盲区。我第一遍讲页表的时候就卡在为什么多级页表省空间上回去重新啃了一遍才讲顺。这个方法之所以有效是因为它强行暴露了知识里看起来懂、其实模糊的部分。你脑子里觉得我懂了但一旦要输出就发现说不清楚。输出是最严格的检验。我用这个方法把操作系统过了一遍之后最大的收获是——以前我遇到问题会先想标准答案是什么现在我会先想这个机制是为了解决什么问题而存在的。从背答案到理解动机这可能是学操作系统这一路上最重要的转变。6.2 复习节奏和刷题策略说点具体的节奏安排。我当时的做法是把操作系统拆成几个模块每个模块两天第一天过笔记骨架把核心问题和答案自己能讲一遍。第二天做题。选择填空题练基础简答题练表达再挑几道大题模拟笔试。刷题的时候我有个习惯——不追求刷题量追求错题的复盘深度。每道错题我都会问自己三个问题这题考的是哪个知识点我为什么错下次遇到类似的题我怎么快速识别把这三个问题答清楚比多刷十道题有用。另外那些反复出现的高频考点我会单独整理成一个必背清单比如进程与线程的区别进程通信的几种方式虚拟内存的作用缺页中断的处理流程死锁的四个条件常见的IO模型这份清单我每周过一遍直到能脱稿复述。这份清单的价值在于它能保证你在有限的时间里把最重要的东西反复强化。最后分享一个我自己觉得最有用的心得操作系统不是背出来的是想出来的。你每看到一个机制就问一句如果不用它会怎样。当你习惯了这种反问式的学习你会发现那些曾经零散的知识点会自己慢慢连成线。到那时候不管是考试还是面试你脑子里浮现的不再是一段段孤立的文字而是一幅完整的、能随时调出来的系统全景图。我自己从第三遍系统复习到现在最大的变化不是记住了多少概念而是遇到一个性能问题或者并发问题时会本能地往操作系统那一层去想。这种能下探到底层的思维方式才是我觉得这份读书笔记带给我最实在的东西。如果你也在啃操作系统不妨试试先搭骨架、再填细节、最后用讲给别人听来检验的这套流程比我前两遍那种从头抄到尾的方式效率至少高出一倍。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。