“操作系统这门课,最劝退的地方就在第二章——进程。”这不是我吓唬你。很多人前面学第一章“引论”还好好的,一到“进程的描述与控制”,概念一个接一个:进程、PCB、状态转换、原语、同步、通信、线程……尤其是慕课版的课程节奏快,课后题又喜欢考细节,稍不注意就翻车。但这章恰恰是整个操作系统的地基,后面的调度、死锁、内存管理全都要用到进程这套框架。地基没打牢,后面越学越虚。
这篇内容我用“复习笔记 + 课后典型题拆解 + 实操验证 + 避坑清单”的形式,把第二章从头到尾捋一遍。重点说清楚三个问题:进程到底是什么、操作系统怎么管进程、进程之间怎么协作。适合正在刷慕课版教材、准备期中期末、以及考研复习第一轮的同学参考。你不需要死记硬背,跟着思路走一遍,很多问题能自己推理出来。
1. 第二章到底在讲什么:一份“进程”的完整说明书
1.1 为什么说进程是操作系统的“心脏”
先想一个问题:为什么操作系统不直接管程序,非要引入“进程”这个概念?因为程序是静态的,躺在磁盘上就是一串指令;但你双击运行它之后,它要占内存、要抢CPU、要打开文件、要等待I/O,这期间程序的状态一直在变。操作系统要管理的是“运行中的程序”,不是“躺在磁盘上的文件”。于是就用“进程”来描述“一个正在运行的程序及其所有活动状态”。
操作系统的四大核心功能——处理机管理、存储器管理、设备管理、文件管理——里面最关键的处理机管理,管的对象就是进程。CPU调度调的是进程,资源分配分给的是进程,程序间的隔离和协作也以进程为边界。这一章把进程讲透了,后面的调度算法、死锁、内存分页、文件系统,都是在“进程”这个大舞台上演戏。
从考试角度来说,本章属于“必须拿分”的章节。题型覆盖选择、填空、简答、综合题,分值占比通常排在全书前三。最常见考法包括:进程和程序的区别、进程状态转换合法性判断、PCB的作用、同步互斥概念、IPC方式的比较。这些都是可以在考前短期突击出来的硬分数。
1.2 章节知识地图与考点权重
我复习的时候习惯先画一张知识地图,把这章拆成五块:进程概念、进程状态、进程控制、进程同步、进程通信。然后给每个模块标上“考频”和“难度”,复习时就有的放矢。
| 模块 | 核心内容 | 常见题型 | 考频 |
|---|---|---|---|
| 进程概念 | 定义、特征、进程与程序区别 | 选择、填空、简答 | 极高 |
| PCB | 作用、内容、组织方式 | 选择、简答 | 高 |
| 进程状态 | 三态/五态/七态、状态转换 | 选择、综合题 | 极高 |
| 进程控制 | 创建/终止/阻塞/唤醒原语 | 选择、简答 | 中高 |
| 进程同步 | 临界区、信号量、P/V操作 | 选择、综合题 | 高 |
| 进程通信 | 共享存储、消息传递、管道等 | 选择、简答 | 中 |
| 线程 | 线程概念、进程与线程区别 | 选择、简答 | 极高 |
看完这张表你就知道,本章没有真正意义上的“冷门考点”。每一块都值得认真对待,但优先级有差别:状态转换和进程线程区别几乎是年年考,通信方式更多是选择题区分。
2. 程序到进程:从“菜谱”到“做菜”的转变
2.1 进程的定义与特征:四个词背下来只是第一步
教材上的标准定义是:进程是程序在某个数据集合上的一次执行过程,是系统进行资源分配和调度的一个独立单位。这个定义信息量很大,我习惯用“菜谱做菜”来类比:程序是菜谱,数据集合是食材,进程就是“按照菜谱,用这些食材,在锅里炒出菜来”的整个过程。
从这定义能推出进程的五大特征,这是选择题的重灾区:
- 动态性:进程是“正在执行”的,它有时而暂停、时而继续的生命周期;程序是静态的,永远躺在那不会自己变。
- 并发性:多个进程可以同时存在于内存中,并在一段时间内交替执行。注意这里说的是“并发”,不是“并行”,单核CPU上同一时刻只能跑一个进程。
- 独立性:进程是资源分配和调度的独立单位。每个进程独立拥有地址空间、打开的文件等资源,互不干扰。
- 异步性:各进程按各自独立的、不可预知的速度向前推进。这是导致程序执行结果不确定性的根本原因,也因此才需要同步机制。
- 结构性:进程实体由程序段、数据段、进程控制块(PCB)三部分组成。
有一个很容易忽略的坑:选择题如果问“进程和程序的主要区别”,你要答动态性和并发性,不要答什么“大小不同”“数量不同”。凡是把程序当动态的选项,直接排除。
2.2 PCB(进程控制块):操作系统眼里的“身份证”
进程本身是动态的、不断变化的,操作系统怎么精确掌握每一个进程的状态?答案是PCB。PCB是操作系统为了管理进程而设置的一个数据结构,里面记录了这个进程的所有关键信息。它是进程存在的唯一标志。
PCB里面有什么?按功能可以拆成四类:进程标识符(PID、父进程号PPID)、处理机状态信息(通用寄存器值、程序计数器PC、程序状态字PSW,也就是进程被中断时现场保存的内容)、进程调度信息(进程状态、优先级、等待原因等)、进程控制信息(程序和数据地址、打开文件列表、I/O设备等)。
这里有一个很深的考点:为什么说“创建进程”本质上是“创建PCB”?因为PCB是进程在操作系统眼里的唯一凭证。撤销进程,第一步也是回收PCB。后面学Linux的时候,Linux第一个进程是系统启动时创建的,之后的每一个进程都通过复制父进程的PCB来创建,这就是fork的本质。
PCB的组织方式也需要了解。最常见的是链接方式——把同一状态的PCB用指针链接成队列,比如就绪队列、阻塞队列;另一种是索引方式——按状态建索引表,表中记录PCB地址。选择题如果问“就绪进程的PCB放在哪里”,答案就是就绪队列。
2.3 进程与线程、程序的区别:一个表格理清三者的关系
讲线程之前,必须先理解一个角度翻转:进程是资源分配的基本单位,线程是调度运行的基本单位。同一个进程可以有多个线程,它们共享该进程的地址空间和资源,却各自保留独立的栈和寄存器上下文,能并行或并发执行不同的子任务。
很多同学会把“程序→进程”和“进程→线程”搞混。我的记忆方法是这样:程序是静态的菜谱,进程是按菜谱做菜的一次完整过程,线程则是做菜时“切菜的人”“掌勺的人”“递盘子的人”。同一个人可以同时干几件事吗?不行,但厨师团队(线程组)可以分工合作,共享同一口锅(进程地址空间)和同一个菜谱(程序代码)。
教材上常考的区别点,我整理成一个对比表。这部分我建议你直接背下来,因为简答题几乎必考:
| 对比维度 | 进程 | 线程 |
|---|---|---|
| 基本单位 | 资源分配的基本单位 | CPU调度(运行)的基本单位 |
| 地址空间 | 每个进程独立地址空间 | 同进程内线程共享地址空间 |
| 资源开销 | 创建/切换开销大 | 创建/切换开销小 |
| 通信方式 | 进程间通信(IPC)复杂 | 线程间可直接读写共享数据 |
| 系统开销 | 进程切换需保存/恢复大量现场 | 同进程线程切换保存现场少 |
| 健壮性 | 一个进程崩溃一般不影响其他进程 | 一个线程崩溃可能导致整个进程崩溃 |
另外注意,现代操作系统都是“多线程进程”模型。进程是资源容器,线程才是真正在CPU上跑的家伙。所以“进程是并发执行的单位”这个说法在某些教材里已经被代替成“线程是并发执行的单位”,做题时要以教材定义为准。多选题如果同时出现“进程是调度的基本单位”和“线程是调度的基本单位”两个选项,就要小心了,得看题目问的是传统操作系统还是现代操作系统。
3. 进程状态机与状态转换:就绪队列为何排长队
3.1 三态与五态:状态转换图要画到条件反射
进程的一生不是一条直线,而是在几个状态之间来回跳。最基本的模型是三态模型:就绪(Ready)、运行(Running)、阻塞(Blocked/Waiting)。
我当初学的时候,老搞不清“就绪”和“阻塞”的区别,后来找到一个特别好用的类比。你去食堂打饭,排队窗口有很多人,队伍前面的人还没打完饭,你排在后面只能等着,这叫“就绪”——万事俱备,只差CPU这个窗口空出来。可是如果你饭卡没钱了,跑去充卡,这时候就算窗口空着也不能打饭,这叫“阻塞”——你在等待一个外部事件(充卡完成),而不是等CPU。
三态模型的关键转换有四个:
- 就绪 → 运行:进程被调度程序选中,获得CPU(排队排到了)。
- 运行 → 就绪:时间片用完,或更高优先级进程抢占,被迫让出CPU(被打断,回去排队)。
- 运行 → 阻塞:进程主动请求I/O或等待某个事件(比如等待用户输入),即使有CPU也推进不了。
- 阻塞 → 就绪:等待的事件完成了(I/O结束、信号到达),重新回到就绪队伍排队。
五态模型是在三态基础上增加“创建态”和“终止态”。创建态:进程正在被创建,PCB还没完全建好,资源还没分配完,这个状态的进程还没资格进就绪队列。终止态:进程已结束运行,资源正在被系统回收,等PCB被回收后,进程才真正消失。
这里有一个高频考题:判断状态转换的合法性。我告诉你一个万能判断方法:两个状态之间能不能直接跳,看中间是否有必须经过的“手续”。比如“阻塞→运行”就是非法的,因为阻塞的进程被唤醒后,必须先进入就绪队列排队,由调度程序决定什么时候给它CPU,不能插队。同理,“就绪→阻塞”也是非法的,一个根本不运行的程序不会主动要求等待I/O。凡是非法转换,原因基本都跟“调度必须经过就绪队列”“只有运行态才能主动请求事件”这两条铁律有关。
3.2 挂起状态与七态模型:内存不够时的“临时请出场”
引入挂起(Suspend)状态的核心原因是内存资源紧张。一个进程如果暂时不运行,又占着大量内存,操作系统可以把它的进程实体和数据搬到磁盘上的外存交换区,让它从“活动就绪/阻塞”变成“静止就绪/静止阻塞”,等内存有空了再调回来。
七态模型就是三态加上创建、终止、挂起后的组合变体。挂起状态有两个分支:静止就绪(在外存上,随时可以调入内存)和静止阻塞(等待的事件还没完成,且在外存上)。挂起状态下,进程不参加CPU调度,相当于“被请出场休息”。
考点集中在:什么情况下会挂起?主要有四种——内存不够、系统出现故障、调试需要、进程长期不运行。另外要注意,挂起操作可能由进程自身发出,也可能由父进程或操作系统强制发出。判断题常说“挂起就是把进程置于等待状态”,这句话是错的,因为挂起不等于阻塞,挂起是进程实体在外存,阻塞是进程在主存中等待事件。
3.3 进程池与空进程:从热词看状态机的实际应用
你如果搜过“进程池”,会发现它跟状态机关系很密切。进程池的核心思想是:与其反复创建和销毁进程(每次都要走完创建态→就绪态→...→终止态的完整生命周期,开销很大),不如预先创建一批进程放在池里待命。这些进程处于“就绪态/阻塞态”等待分配任务,有任务来了直接从池里取一个用,用完了再还回来,而不是销毁。这比频繁调用创建原语节省大量系统开销,是服务器高并发场景下的标配做法。
课本里还有一个概念叫“空进程”,比如Windows系统里的System Idle Process,它表示CPU空闲时间的“占位进程”。它永远处于“运行态”,但它的作用不是干活,而是让系统知道CPU此刻有多少时间片是闲置的。你在任务管理器里看到它的CPU占用率很高,恰恰说明系统很闲,不用慌。这类进程可以理解为“操作系统自己给自己开的记账进程”,没有实际业务逻辑,这也是理解进程控制原语的一个辅助视角。
遇到“进程基础操作考察”这类实验题(像头歌平台上的任务),本质就是让你用命令或编程接口去操作状态,比如用ps查看进程状态、用kill发送信号让进程终止、用sleep挂起进程。这些实操我在第四章详细展开。
4. 进程控制与实操:用命令见证进程的一生
4.1 创建、终止、阻塞、唤醒:四个核心原语
进程控制是操作系统对进程全生命周期的管理,但是这种管理不是随随便便就能做的,必须通过“原语”完成。什么叫原语?你可以理解成“原子操作”——要么执行到底,要么不执行,中途不能被中断。为什么必须原子?因为进程控制涉及修改PCB、出入队列、分配资源等关键操作,如果执行一半被打断,比如PCB建了一半就去处理中断,状态队列就会乱套。
教材上重点掌握四个原语:
创建原语:分配PCB → 分配所需资源(内存) → 初始化PCB(填PID、状态设为就绪/静止就绪) → 插入就绪队列。触发条件通常是用户登录、作业调度、系统服务、父进程创建子进程(最常见的fork)。
终止原语:从队列中移除该进程 → 回收资源 → 撤销PCB。注意,终止不只是进程自己运行完,用户强制结束、父进程终止、系统故障都会导致终止。
阻塞原语:进程主动请求等待某事件 → 保存CPU现场 → 把PCB改成阻塞状态 → 插入阻塞队列 → 让出CPU转调度程序。关键点是“主动”。
唤醒原语:事件完成后,由相关进程(而非阻塞进程自己)执行 → 把阻塞PCB移出 → 改成就绪态 → 插入就绪队列。关键点是“被动”。
很多人搞混阻塞和唤醒,其实一句话就能分清:阻塞是“自己睡”,唤醒是“别人叫”。只要题目里说“进程因等待I/O而睡眠”,它一定调用了阻塞原语,而I/O完成后是由设备驱动或相关进程调用唤醒原语把它叫醒。
4.2 Linux下的进程管理三件套:ps、kill、top
学进程控制原语时,如果只看书不敲命令,很难形成肌肉记忆。Linux下最经典的命令组合就三个。
第一个是查看进程状态,用ps。最常用的是ps -ef或ps aux。STAT那一列就是进程状态代码:R表示运行,S表示可中断睡眠(对应阻塞态),D表示不可中断睡眠,Z表示僵尸态,T表示停止(可看作挂起)。彻底吃透这套代码,比背章节末的状态转换图还直观。
ps -efps aux第二个是实时监控,用top或htop。top默认按CPU占用排序,第一行能看load average,第二行能看进程总数和状态统计,后面每行就是一个进程的动态信息。用top -p PID可以只看某个进程,用top再按H可以切换线程视图。我做实验最爱用htop,开F4可以直接按名字过滤进程,比ps好使。
top -p 1234第三个是结束进程,用kill。kill本质是向进程发送信号,默认发送SIGTERM(15),让进程自己打扫完现场再退出;如果进程不理你,才用kill -9(SIGKILL)强制杀掉。这里要特别注意:kill -9是最后手段,它会绕过进程的清理逻辑,可能导致文件损坏或数据丢失。实际工作中,遇到“安全卫士进程无法中止、拒绝访问”之类的问题,往往是因为权限不足,命令前要加sudo,或者进程处于不可中断睡眠状态(D状态)——D状态下连kill -9都砍不动,只能等它自己结束。
如果想看某个进程的父子关系,用pstree;想按名字找PID,用pidof或pgrep。组合起来就是完整的一套:
pgrep -f python3 kill -9 1234遇到“u盘无法弹出,请先结束占用进程”这类问题,先别急着关U盘。用lsof | grep /media/your_usb找到占用U盘目录的进程,再决定是退出该进程还是kill掉。这其实就是进程管理的日常应用——找到进程、观察状态、结束进程,三步走。
4.3 Windows下的进程管理实操:任务管理器、tasklist、taskkill
Windows下可视化操作大家都熟:Ctrl+Shift+Esc打开任务管理器,在“详细信息”标签页能看到PID、状态、CPU/内存占用。右键可以结束任务、结束进程树、打开文件位置。比Linux直观很多。
但有些情况下界面操作不管用。比如你遇到“wechatappex进程怎么那么多”——微信小程序进程开了一大堆,手动一个个右键结束太累;或者遇到“目标进程已退出,但未引发 coreclr 启动事件”这种开发报错,第一步就是想办法定位进程状态。这时候用命令行更靠谱:
tasklisttasklist | findstr wechattaskkill /F /PID 1234tasklist不带参数列出所有进程;findstr相当于Linux的grep,用来过滤名字。taskkill /F是强制结束,/T可以连同子进程一起结束。想杀“微信的全部进程”,可以配合脚本一次性处理,不过普通场景手动就够了。
Windows下的进程状态没有Linux那么直观地分R/S/D,但“无响应”的状态其实就对应阻塞态——进程在等待某个系统调用或事件(比如等待网络、等待用户输入)却没来得及响应窗口消息。你在任务管理器里看到“未响应”,通常就相当于这个进程长时间停在阻塞状态没能回到就绪。理解了这个对应关系,排查问题就不会慌。
还有一个高频问题:“mate-indicators进程可关闭吗”“sangforpwex.exe是什么”。我的建议是:不认识的名字一律先不关,用搜索引擎查一下是哪个软件自带的组件。系统组件和杀毒软件相关的进程乱关了容易出问题。但“mate-indicators”这类桌面状态栏指示器,一般在“启动”里禁用或结束影响不大,因为它是用户态程序,重启后还会被桌面环境拉起来。判断标准就一条:进程的父进程是谁、属于哪个安装目录。路径在系统目录(C:\Windows\System32)且署名Microsoft的,基本都是系统进程,谨慎处理。
5. 进程同步与通信(IPC):进程协作的两座桥梁
5.1 临界区与互斥:为什么多进程跑起来会“打架”
多个进程并发执行时,如果它们只读各自独立的数据,互不相干,那是多好的世界。可现实是很多进程要共享资源:共享打印机、共享计数器、共享文件。一旦共享,就会出现“竞争条件”——两个进程同时读写同一个变量,执行顺序不同,结果就不同。
经典的例子是购票系统。两个售票进程同时读到余票为1,各自减1后各自写回,余票变成0还是-1?取决于谁后写。这就是数据不一致。解决问题的关键就是“互斥”:同一时间只允许一个进程进入临界区(访问共享资源的代码段)。
教材要求的临界区管理原则有四条:空闲让进、忙则等待、有限等待、让权等待。最后一条“让权等待”常被忽略——当进程进不了临界区时,应该释放CPU,而不是死循环等下去。这其实是“忙等待”和“阻塞等待”的分水岭。
后面的信号量机制,就是通过P操作(wait,申请资源)和V操作(release,释放资源)来实现互斥与同步的。P操作把信号量减1,如果结果小于0就阻塞;V操作把信号量加1,如果结果不大于0就唤醒一个阻塞进程。你可以把信号量理解成“管理员的计数器”:房间里有几个空位,来一个人就少一个(P),走一个人就多一个(V)。值为1时表示只有一个共享资源,就是互斥量(mutex)。
这里给一个用信号量实现互斥的最小示例骨架,考试常考的就是这类代码填空:
semaphore mutex = 1; void process() { P(mutex); // 进入区:申请资源 // 临界区:访问共享资源 V(mutex); // 退出区:释放资源 }重点是理解P、V的位置。P必须在临界区之前,V必须在临界区之后。如果临界区代码里面又调用了P,或者P和V嵌套顺序错了,就会出现死锁或无法互斥。
5.2 进程通信(IPC):进程之间怎么“传话”
进程间通信是操作系统提供给进程的“传话工具”。按教材的经典分类,主要有四种:共享存储、消息传递、管道通信和信号。考试喜欢让你区分它们的特点和适用场景。
共享存储:在内存中划出一块公共区域,多个进程直接读写。这是速度最快的方式,因为数据不搬移,谁都能直接访问。但它有个大前提:必须自己解决同步互斥问题(防止两个进程同时写)。所以共享存储常常和信号量配合使用,信号量负责“锁门”,共享区负责“开门搬数据”。例子:Linux的shmget+shmat系列函数,Redis的AOF缓冲、进程间大块数据交换都会用它。
消息传递:进程之间不直接读写共享空间,而是通过“发消息”和“收消息”完成通信。操作系统提供send和receive原语,消息在系统缓冲区里传递。它比共享存储慢(多一次拷贝),但安全性好,发送方和接收方不用互相知道对方数据结构。支持了分布式场景,两个通信的进程可以在不同机器上。常见实现是消息队列和邮箱机制。
管道通信:用一个“管道”连接两个进程,一个进程往管道写数据,另一个从管道读。数据是字节流,遵守先进先出。管道是半双工的——数据只能单向流动,如果要双向,就得建两根管道。最常见的例子就是Shell命令里的竖线:ps -ef | grep python,左边进程的输出就是右边进程的输入。
信号(Signal):不是传数据,而是传“通知”。比如Ctrl+C给前台进程发SIGINT,强制结束用SIGKILL。它适合通知进程“有事件发生了”,不适合传输大量数据。操作系统里很多“唤醒”机制底层就是发信号。
做题时如果想快速判断应该选哪种通信方式,我有个经验法则:题目强调“速度快”选共享存储;强调“数据量小、通知类型”选信号;强调“流水线、单向字节流”选管道;强调“可靠的进程间数据交换、支持不同机器”选消息传递。当然题目也可能让你比较相互关系,比如共享存储需要同步机制配合,消息传递本身由系统保证同步。
5.3 生产者与消费者问题:课后综合题的第一道门槛
同步与互斥的综合题里,生产者-消费者问题是最经典的,几乎每本教材都讲,每次考试都要考。它是一个“有一批生产者进程在产数据,有一批消费者进程在消费数据,两者共享一个有界缓冲区”的场景。需要满足两个条件:缓冲区没满时生产者才能放,缓冲区没空时消费者才能取,并且同时只能有一个进程操作缓冲区。
标准解法需要三个信号量:
semaphore mutex = 1; // 保护缓冲区互斥访问 semaphore empty = n; // 空位数,初值为缓冲区大小n semaphore full = 0; // 已占位数,初值为0 // 生产者 void producer() { while (1) { produce_item(); P(empty); // 申请一个空位 P(mutex); // 进入临界区 buffer[in] = item; in = (in + 1) % n; V(mutex); // 退出临界区 V(full); // 已占位数加1 } } // 消费者 void consumer() { while (1) { P(full); // 申请一个产品 P(mutex); // 进入临界区 item = buffer[out]; out = (out + 1) % n; V(mutex); // 退出临界区 V(empty); // 空位数加1 consume_item(); } }注意这里P操作的顺序不能颠倒。必须先P(empty)、再P(mutex),不能反过来。如果先P(mutex)再P(empty),万一缓冲区满了,生产者就会占用互斥锁后睡眠,消费者拿不到锁进不了临界区,也释放不了空位——死锁就来了。这个考点叫“检查死锁”,很多综合题就是让你分析这个代码会不会死锁,答案就是“会,因为违背了先申请资源再上锁的顺序”。掌握这一个细节,写同步题时能避开一半的坑。
6. 课后习题精解:把“答案”变成“思路”
6.1 高频选择题:排除法加原理推导
课后题里选择题占了很大比例。题目变化多,但核心考点就那么几个。我挑几类最常出现的,讲讲思路。
第一类,“以下哪个不是进程的特征?”选项目有动态性、并发性、独立性、顺序性。答案显然是“顺序性”。进程的特性是并发,不是顺序。但如果你只背了“四个特征”却没理解,可能看到“并发性”反而犹豫:并发不是多个程序一起跑吗?怎么成特征了?这里的关键是:并行是同时执行,并发是交替推进。进程并不是要求同时执行,而是允许在一段时间内交替执行,这一特性就是并发性。
第二类,“一个进程被唤醒意味着什么?”错误选项会说“该进程一定得到了CPU”或“该进程一定变为运行态”。正确理解是:唤醒只改变一个东西——“阻塞状态转为就绪状态”。它只是从“不能排队”变成“可以排队”,能不能被调度还需要看调度算法和就绪队列长度。这个点我见过很多同学错,就是因为在讲状态机时把“唤醒”与“调度”混淆了。
第三类,“进程和程序的根本区别是什么?”答案是动态性与静态性的区别。最容易选的干扰项是“进程是暂时的,程序是永久的”“进程有结构,程序没有结构”。其实进程和程序都有结构(程序段、数据段、PCB vs 指令、数据),说“程序没有结构”是错的。做题时牢牢抓住“动态vs静态”这个根本点,其余选项即使看起来有道理也要再斟酌。
第四类,“哪个不是进程控制原语?”选项有创建、终止、阻塞、调度。答案是“调度”。调度不是原语,调度是操作系统的功能模块,它负责从就绪队列中选一个进程,然后引发上下文切换。创建/终止/阻塞/唤醒都是原语。如果你把“进程切换”和“进程调度”搞混,这类题就容易翻车。
第五类,“进程从运行态到阻塞态,可能的原因是?”标准答案:请求I/O。一下就能选出。进程不可能因为“时间片到”而从运行到阻塞,时间片到只会让运行变就绪。“信号量减1结果为负”也分情况,如果是等待资源的信号量才会阻塞,如果是互斥锁则不一定。最稳妥的判断就是从运行态出发的转换只有两条合法路径:时间片到/被抢占→就绪,请求事件→阻塞。反向推导即可。
6.2 简答与综合题:答题框架比“字数”更重要
简答题最怕写成知识点堆砌,踩分点找不到。我建议按“概念→特点→联系→例子”四步框架答题。以“什么是进程,进程与程序的区别与联系”为例:
先写定义:进程是程序在某个数据集合上的一次执行过程,是系统进行资源分配和调度的独立单位。然后列区别:程序是静态的、进程是动态的;程序可以长期保存、进程是暂时的;进程具有并发性而程序不具备;两者存在一一对应关系,也可能一个程序对应多个进程,或者一个进程在生命周期中执行多个程序(通过exec)。最后补一句联系:进程是程序功能的动态表现形式,一个进程至少对应一个程序,程序的执行必须通过进程实现。
如果你的简答题只有技术细节而没有概念定义,会被当作“要点不全”扣分。定义、特征、区别、联系、例子,这五个层面都要照顾到,哪怕每个层面只写两行,也比只写一大段“区别”更容易得分。
综合题里,状态转换图是必考题。比如给你一个序列:创建→就绪→运行→阻塞→就绪→运行→终止,问你“该转换序列是否合法?”我的解题顺序是:先画一个七态状态图放在草稿纸上,然后逐一对照。上面的序列全部合法。但如果出现“运行→创建”“阻塞→运行”这种跳跃,直接用“调度前必须先等就绪队列”这条规则排除。
信号量综合题的核心是“把需求翻译成信号量”。我总结了三个步骤:找出资源共享关系(互斥就设互斥量,初值为1;计数就设资源量,初值为资源个数),找出同步关系(谁先谁后,用信号量初始值为0控制顺序),最后检查P/V顺序是否会死锁。这套方法在生产者消费者、读者写者、哲学家进餐这类题目中通用。读者写者问题尤其要注意:读者之间不互斥,写者和所有读者、写者互斥,所以通常需要两个信号量,一个保护读者计数,一个控制写者互斥,这就是它比生产者消费者更复杂的核心点。
7. 期末复习速查表与避坑经验
7.1 进程与线程对比表:考前背这一张就够了
我知道你考前可能没时间再过一遍全书,所以把最重要的对比和结论整理成速查表,直接抄在A4纸上当“小抄”用都行。
- 进程是资源分配的基本单位,线程是独立调度的基本单位。
- 进程拥有独立的地址空间,同进程里的线程共享地址空间。
- 进程切换开销大(需要切换地址空间、保存大量寄存器),线程切换开销小(同进程内不涉及地址空间切换)。
- 一个进程崩了系统一般还能撑住,一个线程崩了往往整个进程跟着崩。
- 进程间通信需要操作系统提供的IPC机制,线程间通信直接读写共享变量,但要靠同步机制保护。
- 多线程适合I/O密集型任务(等待时切换线程),多进程适合CPU密集型任务(并行计算且相互隔离)。
- 创建进程的开销远大于创建线程,所以高并发服务首选用线程池,而不是频繁fork进程。
这七条覆盖了选择题90%的考点。如果你能自己再把每条扩展成一句话解释,比如解释“为什么线程切换开销小——因为不需要切换页表、不刷新TLB、共享文件表”,那简答题也能应付。
7.2 高频考点与易错点盘点
最后把这些年学生最容易踩的坑列一遍:
- 就绪态和阻塞态的区别:就绪是“万事俱备,只欠CPU”,阻塞是“万事不备,就算有CPU也白搭”。
- 运行态到就绪态是“被动让出”,运行态到阻塞态是“主动等待”。题目如果写成“进程从运行态转为阻塞态是主动行为”,对。
- 只有运行态进程才能主动请求I/O进入阻塞,所以“就绪→阻塞”不存在。
- 创建原语结束后的进程状态通常是就绪态,而不是运行态。除非系统启动时第一个进程,否则刚创建完还要排队。
- 挂起不等于阻塞。挂起是“整件事被搬到外存”,阻塞是“在主存里等事件”。
- 进程是并发的单位,不是并行的单位。并发是宏观并行、微观交替。
- 唤醒由其他进程完成,阻塞由自身完成,两者是“他醒我睡”。
- 线程之间不独立拥有资源,但拥有独立的栈和寄存器上下文。
还有一个特别经典的坑:如果题目问“PCB属于进程还是系统?”答案既不是进程也不是程序,而是操作系统管理进程的数据结构。它不属于进程的程序段,也不属于用户数据段,是操作系统内核区里的数据。很多选择题干扰项就是“PCB是用户程序中的一部分”,直接排除。
亲测一个有效的复习路径:先拿一小时把状态转换图默画三遍,再花一小时把ps、kill、top、tasklist命令敲熟,然后做课后题和实验平台的进程操作题。状态图是理论的骨架,命令是理论的验证,两者配合,比抱着教材反复翻效率高得多。特别是头歌这类平台上“进程基础操作考察”的实验,本质就是让你用命令感知进程的创建、查看和终止,做完再回来看状态机,会有一种“原来课本说的就是它”的感觉。
最后分享一个我自己的小习惯。每章复习完我都会写一张“一句话总结”,第二章我写的是:进程是操作系统对“执行中程序”的抽象,进程的一切管理都围绕PCB展开,进程的一切协作都围绕同步和通信展开。你复习完毕之后,不妨也试着用一句话总结这章。如果你能一句话说清楚,那这章基本就过关了。