目录
资源分配图(Resource Allocation Graph)
“死锁检测”与银行家算法中“安全状态检测”有 何联系和区别?
死锁概念
基本概念
一组进程处于死锁状态,如果它们中每个 进程都在等待只有组内其他进程才能引发的事件
在多线程编程中,为了防止多线程竞争共享资源而导致数据错乱,都会在操作共享资源之前加上互斥锁,只有成功获得到锁的线程,才能操作共享资源,获取不到锁的线程就只能等待,直到锁被释放。
那么,当两个线程为了保护两个不同的共享资源而使用了两个互斥锁,那么这两个互斥锁应用不当的时候,可能会造成两个线程都在等待对方释放锁,在没有外力的作用下,这些线程会一直相互等待,就没办法继续运行,这种情况就是发生了死锁。
必要条件
只有同时满足以下四个条件才会发生:
- 互斥条件
-
- 资源只能由一个进程使用
- 持有并等待条件;
-
- 需要其他资源时不(完全)释放自己的资源
- 不可剥夺条件
-
- 只能由自己主动释放
- 环路等待条件
-
- 可以找到一个进程-资源的循环链
互斥条件
互斥条件是指多个线程不能同时使用同一个资源。
比如下图,如果线程 A 已经持有的资源,不能再同时被线程 B 持有,如果线程 B 请求获取线程 A 已经占用的资源,那线程 B 只能等待,直到线程 A 释放了资源。

持有并等待条件
持有并等待条件是指,当线程 A 已经持有了资源 1,又想申请资源 2,而资源 2 已经被线程 C 持有了,所以线程 A 就会处于等待状态,但是线程 A 在等待资源 2 的同时并不会释放自己已经持有的资源 1。

不可剥夺条件
不可剥夺条件是指,当线程已经持有了资源 ,在自己使用完之前不能被其他线程获取,线程 B 如果也想使用此资源,则只能在线程 A 使用完并释放后才能获取。

环路等待条件
环路等待条件指的是,在死锁发生的时候,两个线程获取资源的顺序构成了环形链。
比如,线程 A 已经持有资源 2,而想请求资源 1, 线程 B 已经获取了资源 1,而想请求资源 2,这就形成资源请求等待的环形图。

处理死锁的四种方法
- 预防死锁
- 避免死锁
- 检测死锁
- 解除死锁

预防死锁 — 破坏必要条件
互斥条件不能被破坏
破坏“请求和保持”条件

破坏“不可抢占”条件

破坏“循环等待”条件

避免策略
为什么提出避免策略?

(不)安全状态
基本概念
- 已知每个进程请求的资源的数量的最大值
- 一个状态称为安全状态当且仅当存在一个安全序列 (P_1, P_2, ..., P_n)使得
-
- 对于任意进程(P_i),(P_i)处于请求的最大资源数不超过系统剩余的资源加上所有在(P_i)前有的资源(j<=i)
- 注意
-
- 可找到一个资源的序列即可
- 如果不能找到此序列,则为不安全状态
安全状态举例

银行家算法 — 判断资源申请是否可以允许
- 目标:判断资源申请是否可以允许
- 核心思想:尝试分配,检测是否是安全状态
数据结构
- 为了实现银行家算法,设置这样四个数据结构,分别用来描述系统中:
-
- 可利用的资源
- 所有进程对资源的最大需求
- 系统中的资源分配
- 所有进程还需要多少资源的情况
- 可利用资源向量
Available:
-
- 是一个含有
m个元素的数组,Available[j] = k表示系统中现有Rj类资源k个。
- 是一个含有
- 最大需求矩阵
Max:
-
- 是一个
n * m的矩阵,Max[i, j] = k表示进程i需要Rj类资源的最大数目为k。
- 是一个
- 分配矩阵
Allocation:
-
- 是一个
n * m的矩阵,如果Allocation[i, j] = k表示进程i当前已分得Rj类资源的数目为k。
- 是一个
- 需求矩阵
Need:
-
- 即为
Max - Allocation。
- 即为
算法的描述
设 Request_i 是进程 Pi 的请求向量,如果 Request_i[j] = K,表示进程 Pi 需要 K 个 Rj 类资源。当 Pi 发出资源请求后,系统按下述步骤进行检查:
- 如果
Request_i[j] <= Need[i, j],便转向步骤(2);否则认为出错,因为它所需要的资源数已超过它所宣布的最大值。 - 如果
Request_i[j] <= Available[j],便转向步骤(3);否则,表示尚无足够资源,Pi须等待。 - 系统试探着把资源分配给进程
Pi,并修改下面数据结构中的数值:
-
Available[j] = Available[j] - Request_i[j]Allocation[i, j] = Allocation[i, j] + Request_i[j]Need[i, j] = Need[i, j] - Request_i[j]
- 系统执行安全性检测,检查此次资源分配后系统是否处于安全状态。
-
- 若安全,才正式将资源分配给进程
Pi,以完成本次分配; - 否则,将本次的试探分配作废,恢复原来的资源分配状态,让进程
Pi等待。
- 若安全,才正式将资源分配给进程
算法核心:安全状态检测
- 设置两个向量:
-
Work: 系统当前资源数目,Work = AvailableFinish: 进程是否顺利运行结束,Finish[i] = false
- 寻找到一个能满足下述条件的进程
i:
-
Finish[i] = false; Need[i, j] <= Work[j]
- 如找到,则:
-
Finish[i] = true; Work[j] = Work[j] + Allocation[i, j]- 继续执行2.
- 若所有进程均已结束,则安全;否则不安全
银行家算法举例1

最大需求(Max)和已分配资源(Allocation)
| 进程 | Max | Allocation | Need = Max - Allocation |
| P0 | 7 5 3 | 0 1 0 | 7 4 3 |
| P1 | 3 2 2 | 2 0 0 | 1 2 2 |
| P2 | 9 0 2 | 3 0 2 | 6 0 0 |
| P3 | 2 2 2 | 2 1 1 | 0 1 1 |
| P4 | 4 3 3 | 0 0 2 | 4 3 1 |
安全性检查
- 初始化
-
- Work = Available = [3, 3, 2]
- Finish = [False, False, False, False, False]
- 检查哪个进程可以满足当前的需求
-
- P1的需求 [1, 2, 2] 小于等于 Work [3, 3, 2],因此可以分配资源给 P1。
- 分配资源给 P1 并更新 Work 和 Finish
-
- Work = Work + Allocation[P1] = [3, 3, 2] + [2, 0, 0] = [5, 3, 2]
- Finish[P1] = True
- Finish = [False, True, False, False, False]
- 继续检查其他进程
-
- P3的需求 [0, 1, 1] 小于等于 Work [5, 3, 2],因此可以分配资源给 P3。
- Work = Work + Allocation[P3] = [5, 3, 2] + [2, 1, 1] = [7, 4, 3]
- Finish[P3] = True
- Finish = [False, True, False, True, False]
- 继续检查其他进程
-
- P0的需求 [7, 4, 3] 小于等于 Work [7, 4, 3],因此可以分配资源给 P0。
- Work = Work + Allocation[P0] = [7, 4, 3] + [0, 1, 0] = [7, 5, 3]
- Finish[P0] = True
- Finish = [True, True, False, True, False]
- 继续检查其他进程
-
- P2的需求 [6, 0, 0] 小于等于 Work [7, 5, 3],因此可以分配资源给 P2。
- Work = Work + Allocation[P2] = [7, 5, 3] + [3, 0, 2] = [10, 5, 5]
- Finish[P2] = True
- Finish = [True, True, True, True, False]
- 继续检查其他进程
-
- P4的需求 [4, 3, 1] 小于等于 Work [10, 5, 5],因此可以分配资源给 P4。
- Work = Work + Allocation[P4] = [10, 5, 5] + [0, 0, 2] = [10, 5, 7]
- Finish[P4] = True
- Finish = [True, True, True, True, True]
- 最终检查
-
- 所有进程的 Finish 均为 True,因此当前状态是安全的。
假设 P1 申请 [1, 0, 2]
- 检查需求是否满足
-
- P1的需求 [1, 0, 2] <= Need[P1] [1, 2, 2],满足条件。
- P1的需求 [1, 0, 2] <= Available [3, 3, 2],满足条件。
- 试探分配资源给 P1
-
- Available = Available - Request = [3, 3, 2] - [1, 0, 2] = [2, 3, 0]
- Allocation[P1] = Allocation[P1] + Request = [2, 0, 0] + [1, 0, 2] = [3, 0, 2]
- Need[P1] = Need[P1] - Request = [1, 2, 2] - [1, 0, 2] = [0, 2, 0]
- 安全性检查
-
- Work = Available = [2, 3, 0]
- Finish = [False, False, False, False, False]
- 继续检查
-
- P1的需求 [0, 2, 0] <= Work [2, 3, 0],满足条件。
- Work = Work + Allocation[P1] = [2, 3, 0] + [3, 0, 2] = [5, 3, 2]
- Finish[P1] = True
- Finish = [False, True, False, False, False]
- 继续检查
-
- P3的需求 [0, 1, 1] <= Work [5, 3, 2],满足条件。
- Work = Work + Allocation[P3] = [5, 3, 2] + [2, 1, 1] = [7, 4, 3]
- Finish[P3] = True
- Finish = [False, True, False, True, False]
- 继续检查
-
- P0的需求 [7, 4, 3] <= Work [7, 4, 3],满足条件。
- Work = Work + Allocation[P0] = [7, 4, 3] + [0, 1, 0] = [7, 5, 3]
- Finish[P0] = True
- Finish = [True, True, False, True, False]
- 继续检查
-
- P2的需求 [6, 0, 0] <= Work [7, 5, 3],满足条件。
- Work = Work + Allocation[P2] = [7, 5, 3] + [3, 0, 2] = [10, 5, 5]
- Finish[P2] = True
- Finish = [True, True, True, True, False]
- 继续检查
-
- P4的需求 [4, 3, 1] <= Work [10, 5, 5],满足条件。
- Work = Work + Allocation[P4] = [10, 5, 5] + [0, 0, 2] = [10, 5, 7]
- Finish[P4] = True
- Finish = [True, True, True, True, True]
- 当前状态是安全的。
- P1可以申请[1, 0, 2],并且之后P0可以申请[0, 2, 0]。
银行家算法举例2


检测/解除策略
死锁的检测
资源分配图(Resource Allocation Graph)

- 进程表示为圆圈节点
- 资源类型表示为方块节点;其中每个点表示一个实例
- 请求表示为从进程到资源类型的有向边
- 分配表示为从资源实例到进程的有向边
有环是死锁的必要条件

有环不是充分条件

死锁定理 — 化简资源分配图
- 在资源分配图中,找出一个既不阻塞又非独立的进程结点Pi。在顺利的情况下,Pi可获得所需资源而继续运行, 直至运行完毕,再释放其所占有的全部资源,这相当于消去Pi的请求边和分配边,使之成为孤立的结点
- Pi释放资源后,便可使Pj获得资源而继续运行,直至Pj完成后又释放出它所占有的全部资源,即将Pj的所有请求边和分配边消去
- 在进行一系列的简化后
-
- 若能消去图中所有的边使所有的进程结点都成为孤立结点,则称该图是可完全简化的;
- 若不能通过任何过程使该图完全简化,则称该图是不可完全简化的。
死锁的充分必要条件 — 完全化简的资源分配图中有环
死锁检测时机
- 当进程由于资源请求不满足而等待时,检测死锁
- 定时检测
- 有进程在等待,但系统资源利用率下降时检测
“死锁检测”与银行家算法中“安全状态检测”有 何联系和区别?
- 死锁是不安全状态的最终目的
- 思想是相同的
课堂练习


死锁的解除
- 剥夺资源
-
- 从其它进程剥夺足够数量的资源给死锁的进程,以解除死锁
- 撤消进程
-
- 撤销全部死锁进程
- 或者按照某种顺序逐个地撤消进程,直至有足够的资源可用,死锁状态消除为止
- 有一些优化策略,如为解除死锁状态所需要撤消的进程数目最小;或者撤消进程所付出的代价最小
思考题
考虑由4个相同类型资源组成的系统,系统中有3 个进程,每个进程最多需要2个资源,该系统是否会发生死锁?为什么?
假设系统由相同类型的m个资源组成,有n个进程 ,每个进程至少请求一个资源,且单个进程请求 资源总数不大于m。证明:当n个进程最多需要的资源数之和小于m+n时,该系统无死锁

230

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



