死锁的检测与解除 (Deadlock Detection and Recovery)。
这种策略的态度是“乐观主义”的:它不像“预防”那样因噎废食,也不像“避免”那样步步为营。它认为死锁是小概率事件,平时不用管,让大家自由申请资源。但是,它会定期或在必要时启动一个“侦探程序”,来检查系统里是否已经形成了死锁。如果发现了,再采取“紧急预案”来打破僵局。
我们用一个非常形象的比喻:城市交通管理。
- 进程:路上的汽车。
- 资源:一个个十字路口的通行权。
- 死锁:多辆车在十字路口互相卡住,谁也动不了,形成了交通大瘫痪。
1. 死锁检测:交通指挥中心的“鹰眼”系统
交通指挥中心(操作系统)需要一个“鹰眼”系统来实时监控路况,判断是否发生了交通瘫痪。这个“鹰眼”系统就是死锁检测算法,它依赖于一张资源分配图 (Resource-Allocation Graph)。
资源分配图的构成
这张图就像一个简化的城市地图,上面只有两种东西:
-
节点 (Nodes):
- 进程节点 (Process Nodes):用圆形表示,代表一辆辆汽车(
P1, P2, ...)。 - 资源节点 (Resource Nodes):用矩形表示,代表一个个十字路口(
R1, R2, ...)。矩形里的小圆点,代表这个路口有几个车道(该类资源的实例数)。
- 进程节点 (Process Nodes):用圆形表示,代表一辆辆汽车(
-
有向边 (Directed Edges):
- 请求边 (Request Edge):从进程节点指向资源节点的边 (
Pi -> Rj)。表示“汽车Pi正在等待通过路口Rj”。 - 分配边 (Assignment Edge):从资源节点指向进程节点的边 (
Rj -> Pi)。表示“路口Rj的一个车道已经被汽车Pi占用了”。
- 请求边 (Request Edge):从进程节点指向资源节点的边 (
基于资源分配图的死锁检测算法(简化版的银行家算法)
这个算法的思路非常直观,就像一个虚拟的交警在图上进行“疏导推演”:
-
寻找“不受阻”的汽车:交警首先在图上寻找一辆“不受阻”的汽车。什么样的车不受阻?就是它当前没有发出任何请求,或者它请求的路口还有空闲车道。这种车可以被认为是能顺利通过路口、完成自己路程的。
-
模拟通行并回收路权:找到这样一辆不受阻的汽车(比如P1)后,交警就假装它已经顺利开走了。这意味着:
- P1占用的所有路口(所有从资源指向P1的分配边)都被释放了。
- P1的所有请求(所有从P1指向资源的请求边)也消失了。
- 在图上,就是把与P1相连的所有边都消除掉。
-
循环往复:P1“开走”后,它释放的路口资源可能会让之前被它堵住的其他汽车(比如P2)变得“不受阻”。交警就继续这个过程,寻找新的不受阻汽车,消除它的边,直到...
-
最终判断:
- 如果图上所有的边最终都被消除掉了,说明所有汽车都能找到一条通路开走。这张图就是可完全简化的,系统没有发生死锁。
- 如果在某个阶段,交警再也找不到任何一辆不受阻的汽车,但图上仍然存在边,那就说明剩下的这些汽车和路口已经形成了无法解开的“死疙瘩”。这张图就是不可完全简化的,系统发生了死锁。那些无法被消除的进程,就是死锁进程。
这就是死锁定理的直观体现。
2. 死锁解除:交警的“强力清场”手段
一旦“鹰眼”系统确认发生了死锁,就必须采取强硬手段来打破僵局。主要有以下几种“清场”方案:
1. 资源剥夺法 (Resource Preemption)
- 思路:强行把某个路口(资源)从一辆死锁的车(进程)那里“抢”过来,分配给另一辆死锁的车,以此来打破循环等待。
- 执行:选择一个“牺牲品”进程,挂起它,并将其占有的资源分配给其他死锁进程。
- 难点:
- 选谁牺牲? 这是个复杂的问题(后面会讲)。
- 如何回退? 资源被抢走后,牺牲品进程的状态如何恢复是个大问题。
- 防止饥饿:不能总抢同一个进程的资源,否则它会被“饿死”。需要一个机制来保证它最终能完成。
2. 撤销进程法 (Process Termination)
- 思路:这是最简单粗暴的方法——直接把一辆或多辆车“拖走”。
- 执行:
- 终止所有死锁进程:简单直接,保证能打破死锁,但代价巨大,所有进程之前的工作都白费了。
- 逐个终止死锁进程:一次只拖走一辆车,然后重新运行死锁检测算法,看看死锁是否解除。如果没解除,再拖走一辆。这种方式代价较小,但检测开销大。
3. 进程回退法 (Process Rollback)
- 思路:让某些死锁的汽车“倒车”,回到某个之前的、未发生死锁的“安全点”。
- 执行:这需要系统维护进程的历史状态信息(设置检查点 Checkpoints)。一旦发生死锁,就让一个或多个进程回退到最近的安全点,并从那里重新开始执行。
- 难点:系统需要巨大的开销来记录所有进程的状态历史,实现起来非常困难和不切实际。
3. 选择“牺牲品”的智慧
在需要“剥夺资源”或“撤销进程”时,我们不能随便选。就像交警不会随便拖走一辆救护车一样。选择牺牲品时通常会综合考虑以下因素:
- 进程优先级:优先干掉优先级低的。
- 已执行时间:优先干掉刚开始执行不久的,这样损失小。
- 还需执行时间:优先干掉还需要很久才能完成的。
- 占有的资源:优先干掉占有资源多的,或者它的资源能解救更多其他进程的。
- 进程类型:优先干掉“批处理式”的后台任务,而不是正在和用户交互的“交互式”任务。
必会题与详解
题目一:请解释资源分配图中的“请求边”和“分配边”的含义,并说明如何通过简化资源分配图来判断是否存在死锁。
答案详解:
-
边的含义:
- 请求边 (Request Edge):是一条从进程节点Pi指向资源节点Rj的有向边。它表示进程Pi正在请求Rj类资源中的一个实例,但尚未获得,处于等待状态。
- 分配边 (Assignment Edge):是一条从资源节点Rj中的一个实例指向进程节点Pi的有向边。它表示Rj类资源中的一个实例已经被分配给了进程Pi,Pi当前正持有该资源。
-
简化与判断:
- 判断是否存在死锁的过程,就是一个尝试简化资源分配图的过程。简化的基本思想是模拟进程的执行和资源的释放。
- 简化步骤:
- 首先,在图中寻找一个不被阻塞的进程。一个进程不被阻塞,意味着它的所有请求边所指向的资源类当前都有空闲的资源实例可供分配。(在简化图中,这意味着该进程节点没有发出任何请求边)。
- 找到这样的进程后,就认为它可以执行完毕并释放其占有的所有资源。在图上表现为消除该进程所有的分配边和请求边(即与该进程节点相连的所有边)。
- 重复以上步骤,直到找不到任何可以被简化的进程为止。
- 死锁定理:
- 如果最终图中所有的边都被消除了,则称该图是可完全简化的,系统没有发生死锁。
- 如果最终图中仍然存在边,则称该图是不可完全简化的,系统发生了死锁。图中那些无法被消除的进程就是死锁进程。
题目二:死锁的解除策略中,“撤销进程法”和“资源剥夺法”有什么主要区别?哪一种的实现代价通常更高?
答案详解:
-
主要区别:
- 撤销进程法 (Process Termination):是一种“釜底抽薪”的策略,它通过终止一个或多个死锁进程的生命周期来回收其所有资源。被终止的进程会从系统中消失,其之前所做的所有计算都将丢失。
- 资源剥夺法 (Resource Preemption):是一种相对“温和”的策略,它只是暂时挂起一个死锁进程,并抢占它所持有的部分或全部资源,将这些资源分配给其他死锁进程。被挂起的进程并没有被杀死,理论上它未来还有机会重新获得资源并继续执行。
-
代价比较:
- 撤销进程法的直接代价(丢失工作量)可能非常高,但实现起来相对简单。
- 资源剥夺法的实现代价通常更高。因为它涉及到非常复杂的问题:
- 如何选择剥夺哪些资源?
- 如何处理被剥夺资源后进程的状态?(比如一个打印到一半的文件,如何恢复?)
- 如何防止被剥夺的进程发生饥饿?
- 这些问题使得资源剥夺法的系统开销和实现复杂度远高于简单的撤销进程法。
题目三:一个系统采用死锁检测与解除策略。当检测到死锁后,系统决定撤销一个进程。在选择要撤销的进程时,为什么通常会优先选择“批处理进程”而不是“交互式进程”?
答案详解:
优先选择撤销“批处理进程”是基于用户体验和代价的考虑。
-
用户体验:
- 交互式进程通常是前台进程,它直接与用户进行交互(例如,你正在使用的Word文档、浏览器)。如果终止这类进程,用户会立刻感知到,他们的工作会被中断,数据可能会丢失,导致非常差的用户体验。
- 批处理进程通常是后台进程,它在没有用户干预的情况下默默运行(例如,系统在后台进行文件索引、数据备份)。终止这类进程,用户通常不会有直接的感觉,对用户体验的影响最小。
-
恢复代价:
- 批处理任务通常被设计为可重新启动的。即使被终止,大不了就是从头再运行一次这个任务。
- 而交互式进程的状态可能非常复杂,用户可能已经输入了大量未保存的数据。终止它会导致这些即时状态的永久丢失,恢复的代价极高甚至不可能。
因此,为了最小化对用户的影响和数据丢失的风险,系统在选择牺牲品时,会优先选择那些对用户透明、恢复代价低的后台批处理进程。
2729

被折叠的 条评论
为什么被折叠?



