死锁
死锁的定义
- 一组进程处于死锁状态是指:每一个进程都在等待被另一个进程所占有的、不能抢占的资源
死锁的产生
- 原因:允许多个进程并发执行共享系统资源时,系统必须提供同步机制和进程通信机制。然而,对这种机制使用不当的话,可能会出现进程永远被阻塞的现象。
- 两个进程分别等待对方占有的一个资源,于是两者都不能执行而处于永远等待,这种现象称为“死锁”。例如:
- 打印机与读卡机
- PV操作使用不当产生死锁。
- 同类资源分配不当引起死锁。若系统中有m个资源被n个进程共享,当每个进程都要求K个资源,而 m < n × ( K − 1 ) + 1 m<n\times(K-1) + 1 m<n×(K−1)+1时,如果分配不当就可能引起死锁。
- 对临时性资源使用不加限制引起死锁。在进程通信时使用的信件可以看作是一种临时性资源,如果对信件的发送和接受不加限制的话,可能会引起死锁。(循环等待信件到来才能发送信件
)
死锁的解决
产生死锁的因素不仅与系统拥有的资源数量有关,而且与资源分配策略,进程对资源的使用要求以及并发进程的推进顺序有关。可以从三个方面解决死锁问题:
- 死锁防止
- 死锁避免
- 死锁检测和修复
死锁的防止
死锁产生的四个必要条件
- 互斥条件:进程应互斥使用资源,任一时刻一个资源仅为一个进程独占
- 占有和等待条件:一个进程请求资源得不到满足而等待时,不释放已占有的资源
- 不剥夺条件:任一进程不能从另一进程那里抢夺资源
- 循环等待条件:存在一个循环等待链,每一个进程分别等待它前一个进程所持有的资源
死锁的防止
破坏四个必要条件之一,死锁就可防止。
- 破坏第一个条件,把独占性资源改造成共享性资源,使资源可同时访问而不是互斥使用。(简单但对许多资源往往不能做到)
- 采用剥夺式调度方法可以破坏第三个条件,但剥夺式调度方法目前只适用于对主存资源和处理器资源的分配,而不适用于所有资源
- 采用**静态分配(预分配)**后,进程在执行中不再申请资源,因而不会出现占有了某些资源再等待另一些资源的情况,即破坏了第二个条件
- 所谓静态分配是指一个进程必须在执行前就申请它所要的全部资源,并且直到它所要的资源都得到满足之后才开始执行。所有并发执行的进程要求的资源总和不超过系统拥有的资源数
- 采用层次分配策略将阻止第四个条件的出现。在层次分配策略下,资源被分成多个层次。
- 一个进程得到某一层的一个资源后,它只能再申请在较高层的资源
- 当一个进程要释放某层的一个资源时,必须先释放所占用的较高层的资源
- 当一个进程获得了某一层的一个资源后,它想再申请该层中的另一个资源,那么必须先释放该层中的已占资源
死锁的避免
- 当不能防止死锁的产生时,如果能掌握并发进程中与每个进程有关的资源申请情况,仍然可以避免死锁的发生
- 只需在为申请者分配资源前先测试系统状态,若把资源分配给申请者会产生死锁的话,则拒绝;否则接受申请,为它分配资源
银行家算法
银行家算法:借钱给有偿还能力的客户
- 系统首先检查申请者对资源的最大需求量,如果现存的资源可以满足它的最大需求量时,就满足当前的申请
- 换言之,仅仅在申请者可能无条件地归还它所申请的全部资源时,才分配资源给它
例
假设系统有三个进程P、Q、R,系统只有一类资源共10个,目前分配情况如下:
进程 | 已占资源 | 还需要申请数 |
---|---|---|
P | 4 | 4 |
Q | 2 | 2 |
Q | 2 | 2 |
P或者R再申请资源时,不能分配,因为现在只剩下两个资源,不能满足它们的最大需求
安全状态
安全状态是指系统能按照某种进程顺序,来为每一个进程分配其所需要的资源,直到满足每个进程对资源的最大需求,使每个进程都可顺利地完成。
若系统无法找到这样一个安全序列,则称系统处于不安全状态
死锁的检测
解决死锁问题的另一条途径时死锁检测方法。这种方法对资源的分配不加限制,但系统定时运行一个“死锁检测”程序,判断系统内是否已出现死锁,若检测到死锁则设法加以解除。
检测的一种方法
-
可设置两张表格来记录进程使用资源的情况。等待资源表记录每个被阻塞进程等待的资源,占用资源表记录每个进程占有的资源
-
进程申请资源时,先查该资源是否为其它进程所占用
- 若资源空闲,则把该资源分配给申请者且登入占用资源表
- 否则,登入进程等待资源表
-
死锁检测程序定时检测这两张表
- 若有进程 P i P_i Pi等待资源 r k r_k rk,且 r k r_k rk被进程 P j P_j Pj占用,则说 P i P_i Pi和 P j P_j Pj具有“等待占用关系”,记为 W ( P i , P j ) W(P_i, P_j) W(Pi,Pj)
- 死锁检测程序反复检测这两张表,可以列出所有的“等待占用关系”
- 如果出现 W ( P i , P j ) , W ( P j , P k ) , … … , W ( P m , P n ) , W ( P n , P i ) W(P_i, P_j), W(P_j, P_k), ……, W(P_m, P_n), W(P_n, P_i) W(Pi,Pj),W(Pj,Pk),……,W(Pm,Pn),W(Pn,Pi)时,显然,系统中存在一组循环等待资源的进程,也就是说出现了死锁
死锁检测的数据结构
把两张表格中记录的进程使用和等待资源的情况用一个矩阵A来表示
![20191221192959](https://hglo.top/upload/2019/12/20191221192959-43e061c7b94642f6b46f7151f08b6d88.png)
死锁检测的算法
死锁检测程序可用Warshall的传递闭包算法检测是否有死锁发生,对矩阵A构造传递闭包 A ∗ [ b i j ] A^*[b_{ij}] A∗[bij]
A ∗ [ b i j ] A^*[b_{ij}] A∗[bij]中的每个 b i j b_{ij} bij是对 A [ b i j ] A[b_{ij}] A[bij]执行如下算法
for k:=1 to n do
for i:=1 to n do
for j:=1 to do
bij:= bij | (bik & bkj)
死锁检测后的解决办法
- 可以采用重新启动进程执行的办法,恢复工作应包含重启动一个或全部进程,以及从哪一个点开始重新启动
- 全部卷入死锁从头开始启动,但这样的代价是相当大的
- 在进程执行过程中定时设置校验点,从校验点开始重新执行
- 中止一个卷入死锁的进程,以后重执行