死锁预防
死锁预防的思路极其简单:四个必要条件是死锁的前提,那就在系统设计时把其中一条彻底废掉。
因为是”必要条件”,废掉任意一条,死锁就从原理上不可能发生。代价是这些限制都相当粗暴,资源利用率会明显下降。
机制
① 破坏互斥条件
把资源改造成可以同时被多个进程使用的。
典型手段是 SPOOLing 技术:把独占的打印机改造成共享的”虚拟打印机”,各进程把输出送到磁盘上的输出井,由专门的守护进程排队实际打印。这样进程之间不再直接争抢打印机。
但这条路通常走不通。 互斥性往往是资源的物理属性决定的——磁带机、键盘这类设备就是没法同时给两个进程用。强行共享会破坏数据完整性。
结论:破坏互斥条件的可行性最低,一般不作为通用方案。
② 破坏不可剥夺条件
规定:进程申请新资源得不到满足时,必须立即释放自己已持有的全部资源,将来需要时再重新申请。
也可以由操作系统主动剥夺:当高优先级进程需要某资源时,直接从低优先级进程手中夺走。
缺点(其中一条常被误读,见下方边界段):
- 实现复杂
- 释放已获得的资源可能造成前一阶段工作失效——已经算到一半的中间结果作废
- 反复申请和释放导致系统开销大,延长周转时间
- 只适用于状态易于保存和恢复的资源(如 CPU 寄存器、内存),对打印机这类”打到一半不能中断”的资源不适用
③ 破坏请求并保持条件
采用静态分配(预先分配):进程在开始运行前一次性申请它所需要的全部资源,在整个运行期间不再提出任何新请求。
只要资源没配齐,进程就不投入运行;一旦运行起来,它绝不会因为等资源而阻塞。“边持有边等待”的状态从此不存在。
优点:简单、易于实现、安全。
缺点:
- 资源浪费严重。进程可能在最后一分钟才用到打印机,却从第一秒起就独占着它。
- 可能导致饥饿。需要资源种类多的进程,很难等到所有资源同时空闲,可能长期得不到运行机会。
④ 破坏循环等待条件
采用顺序资源分配法:
- 给系统中的各类资源编号。
- 规定每个进程必须按编号递增的顺序申请资源。
- 同类资源(编号相同的)必须一次申请完。
换句话说,一个进程只有在已经占有小编号资源的前提下,才有资格申请更大编号的资源;已持有大编号资源的进程,不可能再回头申请小编号资源。
为什么这样就一定不会有环:
假设存在一个环 P₁ → P₂ → … → Pₖ → P₁,其中每个 Pᵢ 持有资源 rᵢ、并正在申请 Pᵢ₊₁ 持有的 rᵢ₊₁。
按规则,一个进程申请的资源编号必须严格大于它已持有的全部资源编号,因此:
缺点:
- 编号必须相对稳定,不便于增加新类型的设备
- 进程实际使用资源的顺序可能与编号顺序不一致,造成资源被提前占用而浪费
- 给用户编程带来麻烦——必须按规定次序申请
边界
”最后一个资源不够”算不算死锁
疑问点:顺序资源分配法下,若多个进程最终都在申请同一个最大编号的资源,而该资源数量不足,是否会陷入僵局
不会构成死锁,这属于普通的资源等待。 这个区分很重要,它正好检验了对死锁定义的把握。
关键在于:顺序资源分配法保证的是不出现循环等待,而不是”任何进程都不用等”。等待是完全正常的。
假设多个进程都持有小编号资源,都在申请那个数量不足的最大编号资源。此时的状态是:所有等待者都在等同一个资源,而这个资源的持有者不在等待任何人(因为它已经拿到了编号最大的资源,按规则它不可能再申请更小编号的东西)。
没有环。 持有者会正常执行完毕并释放资源,等待队列中的下一个随即被唤醒。整个系统始终能向前推进。
这里体现的正是 2.4.1 的那条区分:“等待”不等于”死锁”,死锁要求等待关系构成闭环。 若这个最大编号资源始终被别的进程抢走导致某进程长期等不到,那叫饥饿,仍然不是死锁。
“剥夺次数过多”指的是什么
疑问点:教材"死锁处理策略比较"表中两处表述的含义
死锁预防的缺点列有”剥夺次数过多”;死锁避免的缺点列有”进程可能长时间阻塞”。这两处应如何理解?
“剥夺次数过多”专指破坏”不可剥夺条件”这一种方法的代价。
回顾该方法的规则:进程只要有一次新请求得不到满足,就要把手里所有资源全部吐出来。 在资源紧张的系统里,这种情况会频繁发生:
进程申请 → 失败 → 释放全部已有资源 → 重新申请全部 → 又失败 → 再释放……
每一轮都要付出两笔代价:一是之前的计算成果作废(因为资源没了,中间状态可能无法保持),二是重新申请全部资源的开销。反复循环下来,进程的周转时间被大幅拉长,系统吞吐量显著下降。
这也解释了为什么表格里同时列着”效率低”和”初始化时间延长”——那是破坏”请求并保持”(静态分配)的代价,两者说的不是同一种方法。表 2.5 的”主要缺点”一栏是把破坏四个条件的各种代价合并列出的,读的时候要拆开对应到具体方法上。
“进程可能长时间阻塞”则是死锁避免(银行家算法)特有的代价,见 2.4.3:资源明明在系统里空闲着,但因为分配后会进入不安全状态,请求被拒绝,进程只能干等。它等的不是资源本身,而是算法的放行。
预防 vs 避免:都在”事前”,区别在哪
两者常被混,判据是限制的是”规则”还是”具体请求”:
- 死锁预防是静态的:在系统设计阶段就定死一条规则(比如”必须按编号递增申请”),所有进程一律遵守,运行时不做任何判断。
- 死锁避免是动态的:不预设规则,而是在每一次资源请求发生时临时计算,判断这次分配会不会导致系统进入不安全状态。
一句话:预防是”立法”,避免是”每次执法时现算”。 因此预防更粗暴、利用率更低,避免更灵活但需要额外信息和计算。
对照速查
| 破坏哪条 | 方法 | 主要缺点 | 可行性 |
|---|---|---|---|
| ① 互斥 | SPOOLing 等改造为共享 | 多数资源的互斥性由物理属性决定 | 最低 |
| ② 不可剥夺 | 申请失败即释放全部已有资源 | 剥夺次数过多,前期工作作废,开销大 | 中 |
| ③ 请求并保持 | 静态分配,运行前一次性申请全部 | 资源浪费严重,可能饥饿 | 较高 |
| ④ 循环等待 | 顺序资源分配法(编号递增申请) | 编号难变更,使用顺序与编号不符则浪费 | 较高 |
考点
- 四种破坏方法各自对应哪个条件,以及各自的缺点
- 破坏互斥条件通常不可行(物理属性决定)
- 顺序资源分配法为什么一定无环(
矛盾) - 静态分配的两个缺点:资源浪费 + 可能饥饿
- 预防是静态立法,避免是动态判断
- 顺序分配法下”都在等最后一个资源”是等待/饥饿,不是死锁
链接
- 🏠 返回总览:操作系统第 2 章:进程与线程总览
- ⬅️ 上一节:2.4.1 死锁的概念
- ➡️ 下一节:2.4.3 死锁避免
- 📖 名词库:第 2 章名词库