第二章 进程与线程——王道操作系统 – Beatless
目录
四、死锁
死锁的概念
1、死锁的定义
死锁:多个进程因竞争资源而造成的一种僵局,使得各进程都被阻塞,若无外力干扰,这些进程都无法向前推进。
一组进程处于死锁状态是指组内的每一个进程都在等待一个事件,而该事件只可能由组内的另一个进程产生。
2、死锁与饥饿
饥饿:进程在信号量内的无穷等待情况,当等待时间给进程的推进带来明显的影响时称发生了饥饿。
产生饥饿的主要原因:分配策略的不公平,不能保证等待时间的上界存在。
饥饿并不表示当前系统一定发生了死锁,但至少有一个进程的执行被无限期地推迟。
死锁与饥饿:
共同点:都是进程无法向前顺利推进的现象。
差别:①发生饥饿的进程可能只有一个;发生死锁的基础至少有两个。②发生饥饿的进程可能处于就绪态,也可能处于阻塞态;发生死锁的进程一定处于阻塞态。
3、死锁产生的原因
(1)系统资源的竞争:对不可剥夺资源的竞争。
(2)进程推进顺序违法:①请求和释放资源的顺序不合理。②信号量的使用不当。
可剥夺资源:进程在获得这类资源之后,该资源可以被其他进程或系统剥夺。
不可剥夺资源:进程在获得这类资源之后,该类资源不可被其他进程或紫铜剥夺。
4、死锁产生的必要条件
死锁产生的必要条件:
(1)互斥条件:进程要求对资源进行排他性使用;
(2)不可剥夺条件:进程所获得的资源在未使用完之前不能被强行夺走,只能等待进程主动释放。
(3)请求并保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已经被其它进程所拥有,自身阻塞,但又对自己所获得的资源不放。
(4)循环等待:存在一种资源的循环等待链,链中的每一个进程已获得的资源同时被链中的下一个进程所请求。
循环等待不一定有死锁,但是发生死锁一定有循环等待。
资源分配图合圈又不一定有死锁的原因是,同类资源数大于一。但若系统中每种资源都只有一个,那么资源分配图有圈必有死锁。
注意,死锁和饥饿是由于OS资源分配不合理导致的,但是死循环是由代码逻辑的错误导致的。
5、死锁的处理策略
(1)设法破坏死锁产生的四个条件之一
(2)允许死锁的发生,但当死锁发生的时候能检测出死锁,并有能力恢复。
死锁预防:破坏死锁的四个必要条件之一。
避免死锁:在资源的动态分配的过程中,用某种方法防止系统进入不安全状态。
死锁的检测及解除:无需采取任何措施,允许发生死锁。通过系统的检测机构及时地检测出死锁的发生,然后采取某种措施解除死锁。
死锁的预防限制条件严格,实现简单,但系统效率低,资源利用率低。
避免死锁限制条件宽松,但是实现困难。
预防死锁
1、破坏互斥条件
操作系统可以采用SPOOLing技术,把独占设备在逻辑上改造成共享设备。
缺点:并不是所有的独占设备资源都可以改造成共享使用的资源。
为了系统安全,很多地方需要保护这种互斥性,因此很多时候都无法破坏互斥条件。
2、破坏不可剥夺条件
有两种实现方法:
(1)当进程请求资源但得不到满足时,立即释放它所拥有的所有资源,需要的时候再申请。
(2)请求的资源被占有的时候,借助操作系统,将资源强行剥夺。
缺点:
① 实现复杂;
② 释放已获得的资源可能造成前一阶段的工作失效;
③ 反复申请和释放资源会增加系统的开销,降低系统的吞吐量;
④ 采用(1)的时候可能发生饥饿现象。
3、破坏请求并保持条件
采用静态分配的方法,在进程运行前一次性申请完它所需要的资源,在资源满足之前不运行。(实现简单)
缺点:
① 有些资源可能只使用很小一段时间,资源利用率极低;
② 可能导致某些进程饥饿。
4、破坏循环等待条件
采用顺序资源分配法。首先给系统中的资源编号,规定每个进程必须按编号递增的顺序请求资源,编号相同的资源一次性申请完。
缺点:
① 不方便增加新设备,需要重新分配编号;
② 进程实际使用的资源顺序可能和编号递增的顺序不一致;
③ 必须按规定申请资源,用户编程麻烦。
死锁避免
根据”防止系统进入不安全状态“采取措施实现;结果是使进程的推进顺序合理。
1、系统安全状态
安全序列:指如果系统按这种序列分配资源,则每个进程都能顺利完成结束。
- 只要能找到一个安全序列,系统就是安全的状态。(安全序列可能有多个)
- 如果分配资源之后系统找不到任何一个安全序列,系统就进入了不安全状态。此时如果有一些进程提前归还了一些资源,那么系统也有可能重新回到安全状态。
- 如果系统处于安全状态,就一定不会发生死锁。如果系统进入不安全状态,就有可能发生死锁。
注意:处于不安全状态不一定发生了死锁,但有死锁一定处于不安全状态。
2、银行家算法
假设系统中有 n 个进程,有 m 个资源:
最大需求矩阵Max:Max[i, j] = k,表示进程 Pi 最多需要资源 Rj 共 k 个。
分配矩阵Allocation:表示对所有资源的分配情况。
需求矩阵Need:表示各进程最多还需要多少资源。
剩余资源矩阵Available:表示当前系统中还剩余多少资源可用。
请求资源矩阵Request:表示某进程本次申请的各种资源数量。
判断本次分配是否导致系统进入不安全状态:
① 若 Request[i, j] <= Need[i, j],jump to ②;否则出错——申请超出了允诺的最大值。
② 若 Request[i, j] <= Available[j],jump to ③;否则表示没有足够的资源,让 Pi 等待。
③ Available[j] -= Requesti[j]; Allocation[i, j] += Requesti[j]; Need[i, j] -= Requesti[j]; 更新数据并非分配。
④ OS继续执行安全性算法,检查资源分配后,系统是否处于安全状态:若安全,则允许本次分配;否则恢复相应的数据,让 Pi 阻塞等待。
死锁检测和接触
系统应提供两个算法:① 死锁检测算法; ② 死锁接触算法
1、死锁检测算法
(1)用某种数据结构保存资源的请求和分配图;
(2)提供算法,利用(1)检测系统是否进入死锁状态。
资源分配图:
- 两种结点:
- 进程结点;
- 资源结点。
- 两种边:
- 进程结点->资源结点:表示进程想要申请一个该类资源;
- 资源结点->进程结点:表示已经为该进程分配了一个该类资源。
算法内容:
(1)找出既不阻塞又不是孤点的进程 Pi。若该进程能够一直运行到结束(所有请求的资源都可以获取到),则消去它所有的请求边和分配边,使之成为孤立结点。重复执行(1)直到不能化简资源分配图。
(2)若(1)之后能够消除所有的边,那么称这个资源分配图是可完全化简的,此时一定没有死锁。如果最终没有消除所有的边,那么就是发生了死锁。
最终还连接着边的进程就是死锁进程。
死锁定理:若某时刻系统的资源分配图是不可完全化简的,那么此时系统死锁。
2、死锁的解除
(1)资源剥夺法:挂起某些死锁进程,并抢占它的资源,分配给其他进程。应防止挂起进程饥饿。
(2)撤销进程法:强制撤销部分、甚至全部死锁进程,并剥夺其所拥有的资源。实现简单,又称终止进程法
(3)进程回退法:让一个或多个死锁进程回退到足以避免死锁的地步。要求系统记录进程的历史信息,设置还原点。
对哪些进程剥夺资源或撤销:
- 进程优先级低的
- 执行时间短的
- 剩余执行时间长的
- 进程使用资源多的
- 优先考虑批处理进程(其次是交互式进程)