死锁预防

死锁预防的思路极其简单:四个必要条件是死锁的前提,那就在系统设计时把其中一条彻底废掉。

因为是”必要条件”,废掉任意一条,死锁就从原理上不可能发生。代价是这些限制都相当粗暴,资源利用率会明显下降。

机制

① 破坏互斥条件

把资源改造成可以同时被多个进程使用的。

典型手段是 SPOOLing 技术:把独占的打印机改造成共享的”虚拟打印机”,各进程把输出送到磁盘上的输出井,由专门的守护进程排队实际打印。这样进程之间不再直接争抢打印机。

但这条路通常走不通。 互斥性往往是资源的物理属性决定的——磁带机、键盘这类设备就是没法同时给两个进程用。强行共享会破坏数据完整性。

结论:破坏互斥条件的可行性最低,一般不作为通用方案。

② 破坏不可剥夺条件

规定:进程申请新资源得不到满足时,必须立即释放自己已持有的全部资源,将来需要时再重新申请。

也可以由操作系统主动剥夺:当高优先级进程需要某资源时,直接从低优先级进程手中夺走。

缺点(其中一条常被误读,见下方边界段):

  • 实现复杂
  • 释放已获得的资源可能造成前一阶段工作失效——已经算到一半的中间结果作废
  • 反复申请和释放导致系统开销大,延长周转时间
  • 只适用于状态易于保存和恢复的资源(如 CPU 寄存器、内存),对打印机这类”打到一半不能中断”的资源不适用

③ 破坏请求并保持条件

采用静态分配(预先分配):进程在开始运行前一次性申请它所需要的全部资源,在整个运行期间不再提出任何新请求。

只要资源没配齐,进程就不投入运行;一旦运行起来,它绝不会因为等资源而阻塞。“边持有边等待”的状态从此不存在。

优点:简单、易于实现、安全。

缺点:

  • 资源浪费严重。进程可能在最后一分钟才用到打印机,却从第一秒起就独占着它。
  • 可能导致饥饿。需要资源种类多的进程,很难等到所有资源同时空闲,可能长期得不到运行机会。

④ 破坏循环等待条件

采用顺序资源分配法:

  1. 给系统中的各类资源编号。
  2. 规定每个进程必须按编号递增的顺序申请资源。
  3. 同类资源(编号相同的)必须一次申请完。

换句话说,一个进程只有在已经占有小编号资源的前提下,才有资格申请更大编号的资源;已持有大编号资源的进程,不可能再回头申请小编号资源。

为什么这样就一定不会有环:

假设存在一个环 P₁ → P₂ → … → Pₖ → P₁,其中每个 Pᵢ 持有资源 rᵢ、并正在申请 Pᵢ₊₁ 持有的 rᵢ₊₁。

按规则,一个进程申请的资源编号必须严格大于它已持有的全部资源编号,因此:得到 ,矛盾。所以环根本不可能形成。

缺点:

  • 编号必须相对稳定,不便于增加新类型的设备
  • 进程实际使用资源的顺序可能与编号顺序不一致,造成资源被提前占用而浪费
  • 给用户编程带来麻烦——必须按规定次序申请

边界

”最后一个资源不够”算不算死锁

疑问点:顺序资源分配法下,若多个进程最终都在申请同一个最大编号的资源,而该资源数量不足,是否会陷入僵局

不会构成死锁,这属于普通的资源等待。 这个区分很重要,它正好检验了对死锁定义的把握。

关键在于:顺序资源分配法保证的是不出现循环等待,而不是”任何进程都不用等”。等待是完全正常的。

假设多个进程都持有小编号资源,都在申请那个数量不足的最大编号资源。此时的状态是:所有等待者都在等同一个资源,而这个资源的持有者不在等待任何人(因为它已经拿到了编号最大的资源,按规则它不可能再申请更小编号的东西)。

没有环。 持有者会正常执行完毕并释放资源,等待队列中的下一个随即被唤醒。整个系统始终能向前推进。

这里体现的正是 2.4.1 的那条区分:“等待”不等于”死锁”,死锁要求等待关系构成闭环。 若这个最大编号资源始终被别的进程抢走导致某进程长期等不到,那叫饥饿,仍然不是死锁。

“剥夺次数过多”指的是什么

疑问点:教材"死锁处理策略比较"表中两处表述的含义

死锁预防的缺点列有”剥夺次数过多”;死锁避免的缺点列有”进程可能长时间阻塞”。这两处应如何理解?

“剥夺次数过多”专指破坏”不可剥夺条件”这一种方法的代价。

回顾该方法的规则:进程只要有一次新请求得不到满足,就要把手里所有资源全部吐出来。 在资源紧张的系统里,这种情况会频繁发生:

进程申请 → 失败 → 释放全部已有资源 → 重新申请全部 → 又失败 → 再释放……

每一轮都要付出两笔代价:一是之前的计算成果作废(因为资源没了,中间状态可能无法保持),二是重新申请全部资源的开销。反复循环下来,进程的周转时间被大幅拉长,系统吞吐量显著下降。

这也解释了为什么表格里同时列着”效率低”和”初始化时间延长”——那是破坏”请求并保持”(静态分配)的代价,两者说的不是同一种方法。表 2.5 的”主要缺点”一栏是把破坏四个条件的各种代价合并列出的,读的时候要拆开对应到具体方法上。

“进程可能长时间阻塞”则是死锁避免(银行家算法)特有的代价,见 2.4.3:资源明明在系统里空闲着,但因为分配后会进入不安全状态,请求被拒绝,进程只能干等。它等的不是资源本身,而是算法的放行。

预防 vs 避免:都在”事前”,区别在哪

两者常被混,判据是限制的是”规则”还是”具体请求”:

  • 死锁预防是静态的:在系统设计阶段就定死一条规则(比如”必须按编号递增申请”),所有进程一律遵守,运行时不做任何判断。
  • 死锁避免是动态的:不预设规则,而是在每一次资源请求发生时临时计算,判断这次分配会不会导致系统进入不安全状态。

一句话:预防是”立法”,避免是”每次执法时现算”。 因此预防更粗暴、利用率更低,避免更灵活但需要额外信息和计算。

对照速查

破坏哪条方法主要缺点可行性
① 互斥SPOOLing 等改造为共享多数资源的互斥性由物理属性决定最低
② 不可剥夺申请失败即释放全部已有资源剥夺次数过多,前期工作作废,开销大中
③ 请求并保持静态分配,运行前一次性申请全部资源浪费严重,可能饥饿较高
④ 循环等待顺序资源分配法(编号递增申请)编号难变更,使用顺序与编号不符则浪费较高

考点

  • 四种破坏方法各自对应哪个条件,以及各自的缺点
  • 破坏互斥条件通常不可行(物理属性决定)
  • 顺序资源分配法为什么一定无环( 矛盾)
  • 静态分配的两个缺点:资源浪费 + 可能饥饿
  • 预防是静态立法,避免是动态判断
  • 顺序分配法下”都在等最后一个资源”是等待/饥饿,不是死锁

链接