在多道程序环境中,多个进程可以竞争有限数量的资源。当进程申请资源时,如果没有可用资源,那么这个进程进入等待状态。有时,如果所申请的资源被其它等待进程占有,那么该进程可能再也无法改变状态。这种情况称为死锁。
一、系统模型
资源分为多种类型,每种类型有一定数量的实例。
- 资源类型R1, R2, . . ., Rm, 如CPU, 内存空间, I/O 设备,文件
- 每个资源类型Ri有Wi个实例
正常操作模式下,进程只按如下顺序使用资源:
- 申请:进程请求资源
- 使用:进程对资源进行操作
- 释放:进程释放资源
当一组进程中的每个进程都在等待一个事件的发生,而这一事件只能由这一组进程的另一进程引起,那么这组进程就处于死锁状态
二、死锁特征
1.必要条件
如果在一个系统如下四个条件同时成立,就会引起死锁:
- 互斥(mutual excusive):至少有一个资源只能由一个进程占用(非共享)
- 占用并等待:一个进程应占用至少一个资源,并请求/等待另一个资源
- 非抢占:资源不能被抢占
- 循环等待:有一组等待进程{P0, P1, …, Pn},P0等待的资源被P1所占用,P1等待的资源被P2 所占用,…,Pn–1等待的资源被Pn所占用,Pn等待的资源被P0所占用
2.资源分配图
通过系统资源分配图的有向图可以更精确的描述死锁。
资源分配图由一个节点集合V和一个边集合E组成。
1.节点集合V分为进程集合P和资源集合R
- 进程节点集合:P = {P1, P2, …, Pn}
- 资源节点集合:R = {R1, R2, …, Rm}
2 边集合E
- Pi → Rj,表示进程Pi 已经申请使用资源类型Rj的一个实例,并等待这个资源
- Rj → Pi,表示资源类型Rj的一个实例已经分配给进程Pi
上图一个成环是死锁,一个成环不是死锁
根据资源分配图的定义,可以证明:
- 如果分配图没有环,就没有死锁
- 如果分配图有环,就有可能发生死锁如果每个资源类型只有一个实例,就肯定会发生死锁
如果每个资源类型有多个实例,就有可能处于死锁
三、死锁处理方法
一般来说,处理死锁问题有三种方法:
预防或避免(prevention or avoidance)
- 预防死锁:确保⾄少⼀个必要条件不成⽴
- 避免死锁:利⽤事先得到进程申请资源和使⽤资源的额外信息,判断每当发⽣资源请求时是否会发⽣死锁
发生死锁,检测并恢复。确定死锁是否确实发⽣, 并提供算法从死锁中恢复
忽视死锁问题,认为死锁不会发生
四、死锁预防
发生死锁有4个必要条件,所以只要确保至少一个必要条件不成立,就能预防死锁发生
1.互斥
通常不能通过否定互斥条件来预防死锁
可共享的资源不要求互斥访问,如只读⽂件;但不可共享的资源本身就是非共享的,必须要确保互斥,如打印机,一个互斥锁
2.占用并等待
为否定这一条件,应保证:当一个进程请求一个资源时,它不能占有其他资源。
实现的方法如下:
- 每个进程在执⾏前申请并获得所有资源
- 另一种是进程只有在不占⽤资源时, 才允许申请资源
注意:让互斥和占⽤并等待不成⽴、两种⽅法的缺点是资源利⽤率低和可能发⽣饥饿问题
3.非抢占
为了确保这一条件不成立,可以是使用如下协议:
如果一个进程占有资源并申请另一个不能分配的资源(也就是说,这个进程应该等待),那么其现已分配的资源可被抢占。
如果一个进程申请一些资源,那么首先检查它们是否可用。
- 如果可⽤,就分配。
- 如果不可⽤,检查这些资源是否已分配给其他等待额外资源的进程,如果是,那么就抢占。
- 如果不可⽤,且也没有被其他等待额外资源的进程占有,那么就等待
4.循环等待
确保其不成立地一个方法:对所有资源类型进行完全排序,且要求每个进程按递增顺序来申请资源。
假设资源类型集合R={R1,R2…Rn},为每个资源类型分配一个唯一的整数,形式地,定义一个函数F:R → N(N为自然数集合)
可采用如下协议预防死锁:
每个进程只能按递增顺序来申请资源,即一个进程开始可以申请任何数量的资源类型Ri的实例,之后,仅当F(Rj)>F(Ri)时,进程才能申请资源类型Rj的实例
五、死锁避免
采用上述死锁预防中的的方法来预防死锁有副作用:设备使用率低和系统吞吐率低。
避免死锁的另一种方法,需要额外信息,即如何申请资源。在获悉每个进程的请求和释放的完整顺序后,系统可以决定,在每次请求时是否应该等待以避免未来可能的死锁。
需要掌握的额外信息包括:
- 当前可用资源
- 已分配给每个进程的资源
- 每个进程将来要申请或释放的资源
- 每个进程可能申请的每种资源类型实例的需求
针对每次申请,系统在做决定时考虑现有可用资源、现已分配给每个进程的资源、每个进程将来申请和释放的资源来申请与释放资源
鉴于这些先验信息,可构造算法确保系统不会进入死锁状态。避免死锁是动态的方法,它根据进程申请资源的附加信息决定是否申请资源
1.安全状态
如果系统按一定顺序(存在一个顺序)为每个进程分配资源(不超过它的最大需求),且能避免死锁,那么系统状态就是安全的,如果没有,系统状态就是非安全的
避免死锁指的是确保系统不进入不安全状态
2.资源分配图法
适用于每个资源具有单个实例。
由上一节的资源分配图的变形可以避免死锁
除原来的申请边和分配边,引入了新的类型边叫需求边。需求边用虚线Pi - - > Rj,表示进程Pi可能在将来某个时刻申请资源Rj;当进程Pi申请资源Rj时,需求边变成申请边(虚线变成实线);当进程Pi释放资源Rj时,分配变成需求边
算法规则:只有在将申请边变成分配边而不会导致资源分配图形成环时,才允许申请资源。
假设进程Pi申请资源Rj,对资源的申请,把申请边变成分配边后,如果没有环就允许申请,如有环就不允许申请
下图中如果将R2分配给P2,就会创建一个环,表示系统处于非安全状态。
3.银行家算法
适用于每个资源具有多个实例
当一个新进程进入系统时,它应声明可能需要的每种类型资源实例的最大数量,当然这不能超过系统资源总和。当用户申请一组资源时,系统应确定这些资源的分配是否会使系统处于安全状态,如果会,就可以分配;否则进程应等待,直到某个进程释放了做够多的资源为止。
为实现这一算法,引入一些数据结构,这些数据结构都资源分配系统的状态进行了记录
Available ( vector 向量): 表示可分配的资源数。Available[j] = k 表示资源Rj 的可用资源数是k个
Max(n x m 矩阵): 表示资源最大需求数。Max[i,j] = k 表示进程Pi可能请求的资源Rj的实例个数是k.
Allocation(n x m 矩阵): 表示占有的资源数。Allocation[i,j] = k 表示Pi已经占有的资源Rj.实例的个数是k.
Need(n x m 矩阵): 为完成任务可能仍然需要的资源。Need[i, j] = k, 表示进程Pi可能需要的资源Rj实例数是k.
安全性算法:确定计算机系统是否处于安全状态的算法。
//Work 和Finish 分别为长度m 和n的向量
Work = Available;//可用资源数
For all i, Finish[i] = false;//初始化,各个进程状态
//这里并不是简单的遍历一遍,而是不断查找
For all i do
//表示当前进程可以获得资源,并执行
IF ( Finish[i] == false && Need i<= Work)
Work = Work + Allocation i//释放进程i原来的占有资源
Finish[ i ] = true//表示执行完成
End IF
End For
//所有进程都执行完
IF for all i, Finish[i] == true
Then the system is safety
End IF
STEP 1:
Work 和Finish 分别为长度m 和n的向量, 分别初始化为:
Work = Available //可分配的资源数
Finish [ i ] = false for i = 0, 1, …, n- 1
STEP 2:
查找i使其满足:Finish [ i ] = false && Need i <= Work
如果没有满足以上条件的i, 那么就跳转到STEP 4
STEP 3:
Work = Work + Allocation i
Finish[ i ] = true
返回到STEP 2
STEP 4:
如果对所有i, Finish[i] == true 那么系统处于安全状态,如果不是就处于不安全状态
资源请求算法:判断是否可安全允许请求的算法。
每次进程请求资源的时候,运行资源请求检测算法,确认是否允许请求
IF ( Request i <= Need i) //检测资源的请求是否合法
IF (Request i <= Available i){ //检测可用资源能否满足请求
Available i = Available i– Request i ;
Allocation i = Allocation i + Request i ;
Need i = Need i– Request i ;
do Safety Check Algorithm; //检测分配资源后是否安全
ELSE
waiting;
End IF
ELSE
error
End IF
设Request i 为进程P i 的请求向量,当进程Pi作出资源请求时,采取如下操作:
STEP 1:
如果Requesti <= Need i , 那么转到STEP 2. 否则,产生出错条件,这是因为进程Pi 已超过了其最大请求
STEP 2:
如果Requesti <= Available, 那么转到STEP 3. 否则Pi必须等待,这是因为没有可用资源
STEP 3:
假定系统可以分配给进程Pi所请求的资源,则修改资源分配状态,进入安全性检查
- 如果所产生的资源分配状态是安全的,那么Pi 可分配到其所需要资源.
- 如果所产生的资源分配状态是不安全的,那么Pi 必须等待并恢复到原来资源分配状态
4.银行家算法实例
假设一个系统,有5个进程:P0、P1、P2、P3、P4。3 种资源类型: A (10个实例), B (5个实例),C (7个实例)。
假定在时间T0,系统资源分配状态如下:
执行算法过程:
查找满足如下条件的i :Finish [ i ] = false && Needi <= Work
发现P1满足以上条件,则Finish[1] 设置为1,
Work = Work + Allocationi;
Work = 【3,3,2】+ 【2,0,0】
继续往下查找,
发现P3 满足条件,则Finish[3]设置为1,
Work = 【5,3,2】+ 【2,1,1】= 【7,4,3】;
继续往下查找,
发现P4 满足条件,则Finish[4]设置为1,
Work = 【7,4,3】+ 【0,0,2】= 【7,4,5】;
继续往下查找,
发现P0满足条件,则Finish[0]设置为1,
Work = 【7,4,5】+ 【0,1,0】= 【7,5,5】;
继续往下查找,
发现P2满足条件,则Finish[2]设置为1,
Work = 【7,5,5】+ 【3,0,2】= 【10,5,7】;
此时Finish都为true,说明分配资源后,系统仍出于安全状态,安全顺序为【P1,P3,P4,P0,P2】
对上述例子及银行家算法和安全状态的理解:
银行家算法算的是是否可以分配给某个进程某些资源,所以假设系统把资源分配后,去判断系统是否仍处于安全状态。上面例子T0时刻的资源状态,其实就是某个进程请求了某些资源后的一个状态,算法需要去判断这个状态是否安全,如果安全便可以分配。
上文提到了什么是安全状态,即在当前资源分配下,存在一个序列使所有进程运行不死锁。关键在于如何知道存在,银行家算法模拟了最坏情况,即进程每次都请求最大数量的Need(显然实际环境中进程可能只请求一个实例),如果这样分配资源仍然能使所有进程都完成,那显然这个序列就是存在的,系统就是安全的
六、死锁检测
如果一个系统既不采用死锁预防算法也不采用死锁避免算法,那死锁可能出现,这种环境下系统可以提供:
- 检测算法:确定系统是否进入死锁
- 恢复算法:从死锁状态中恢复
需要从如下两个方面分别考虑这个问题:
- 第一个方面,每个资源类型有单个实例
- 另一个方面,每个资源类型有多个实例
1.每种资源类型只有单个实例
检测算法:用等待图,它是资源分配图的一个变形,从资源分配图中删除所有资源类型节点,合并适当边,就能得到等待图。
- 每个节点是进程
- Pi → Pj 意味着进程Pi等待进程Pj释放一个Pi所需的资源
如等待图中有环,系统中存在死锁。
为检测死锁,系统需要维护等待图,并周期性的调用在图中进行搜索环的算法
2.每种资源类型可有多个实例
类似于银行家算法
3.应用检测算法
何时调用算法取决于:
- 死锁发生的频率
- 死锁发生时,有多少进程会受影响
每次资源请求调用检测算法
每次资源请求不被允许时调用检测算法
七、死锁恢复
当检测算法确定已有死锁是,需要打破。打破死锁有两个选择,一个是简单的终止一个或多个进程来打破循环等待;另一个是从一个或多个进程那里抢占一个或多个资源
1.终止进程
两种方法:
- 终止所有死锁进程
- 一次只终止一个进程直到取消死锁循环为止
如果采用部分终止,我们应该终止造成最小代价的进程,许多因素影响了选择哪个进程:
- 进程的优先级
- 进程已计算了多久,进程在完成指定任务之前还需要多久
- 进程使用了多少数量的何种类型的资源
- 进程需要多少资源以完成
- 多少资源需要被终止
- 进程是交互的还是批处理的
2.资源抢占
通过抢占资源以取消死锁,逐步从进程中抢占资源给其他进程使用,直到死锁被打破为止。
需要处理三个问题
- 选择牺牲品:抢占哪些资源和哪个进程
- 需要考虑饥饿:避免同一个进程总成为牺牲品
- 回滚(Rollback):必须把不能正常运行的进程,回滚到某个安全状态,以便重启进程
-
cpu
+关注
关注
68文章
10858浏览量
211657 -
内存
+关注
关注
8文章
3023浏览量
74028 -
死锁
+关注
关注
0文章
25浏览量
8077 -
程序
+关注
关注
117文章
3786浏览量
81018
发布评论请先 登录
相关推荐
评论