一、死锁的避免
避免死锁同样属于事先预防策略,是在资源动态分配过程中,防止系统进入不安全状态,以避免发生死锁。
1、系统安全状态
在避免死锁方法中,把系统的状态分为安全状态和不安全状态。当系统处于安全状态时可避免发生死锁,而当系统处于不安全状态时,则可能会进入死锁状态
2、安全序列
所谓安全序列,就是指如果系统按照这种序列分配资源,则每个进程都能顺利完成。只要能找出一个安全序列,系统就是安全状态。当然,安全序列可能有多个。
3、由安全状态进入不安全状态
如果分配了资源之后,系统中找不出任何一个安全序列,系统就进入了不安全状态。这就意味着之后可能所有进程都无法顺利的执行下去。
如果系统处于安全状态,就一定不会发生死锁。如果系统进入不安全状态,可能会发生死锁。(不安全状态未必就是发生了死锁,但发生死锁时一定是在不安全状态)
因此可以在资源分配之前预先判断这次分配是否会导致系统进入不安全状态,以此决定是否答应资源分配请求。这也是“银行家算法”的核心思想。
那么问题来了,什么是银行家算法呢?
先举个例子,便于理解:
此时你是一位成功的银行家,手里有100亿资金…
此时有三个企业想找你贷款,分别是企业B,企业A,企业T
B:“大哥,我最多要借70亿”
A:“大哥,我最多要借40亿”
T:“大哥,我最多要借50亿”
有个规矩:借给企业的钱达不到企业提出的最大要求,那么你借的钱就打水漂了
当然你也不想你的钱打水漂,那么就要考虑 怎么借才能保证自己的100亿不打水漂
最大要求 | 已经借走 | 最多再借 | |
---|---|---|---|
B | 70 | 20 | 50 |
A | 40 | 10 | 30 |
T | 50 | 30 | 20 |
此时你手里还有40亿…
分析借钱的安全序列
此时B想跟你借30亿,你敢借吗?
假如你答应了:借给了B 30亿,那么你的手里还有10亿,上面的图稍作修改,如下图:
最大要求 | 已经借走 | 最多再借 | |
---|---|---|---|
B | 70 | 20+30=50 | 50-30=20 |
A | 40 | 10 | 30 |
T | 50 | 30 | 20 |
如果其他企业再提出借20亿,那你巴比Q了,显然你借不了,你的钱打水漂了,所以这个钱不能借。不安全
此时A想跟你借20亿,你敢借吗?
假如你答应了:借给了A 20亿,那么你的手里还有20亿,上面的图稍作修改,如下图:
最大要求 | 已经借走 | 最多再借 | |
---|---|---|---|
B | 70 | 20 | 50 |
A | 40 | 10+20=30 | 30-20=10 |
T | 50 | 30 | 20 |
接下来你可以把这20亿都借给T企业。等他把钱全部还回来了,手里就有50亿,再把这些钱借给B企业。等他把钱全部还回来了,手里就有70亿,最后再借给A企业。这样你的钱就全回来了。
所以此借钱序列(安全序列):T->B->A
根据上述思路自行验证这个序列:A->T->B
4、银行家算法
银行家算法是荷兰学者Dijkstra为银行系统设计的,以确保银行在发放现金贷款时,不会发生不能满足所有客户需要的情况。后来该算法被用在操作系统中,用于避免死锁。
***核心思想:***在进程提出资源申请时,先预判此次分配是否会导致系统进入不安全状态。如果会进入不安全状态,就暂时不答应这次请求,让该进程先阻塞等待。
1)数据结构:
①可利用资源向量Available。长度为m的一维数组,Available表示还有多
少可用资源。
②最大需求矩阵Max。表示各进程对资源的最大需求数,nxm矩阵。
③分配矩阵Allocation 。表示已经给各进程分配了多少资源,n x m矩阵
④需求矩阵Need。矩阵表示各进程最多还需要多少资源,
Max -Allocation=Need
⑤进程P的请求向量。用长度为m的一位数组,表示进程此次申请的各种资源数。
2)算法步骤:
设Request是进程Pi的请求向量,如果Requesti[j]=K,表示进程Pi需要K个Rj类型的资源。当Pi发出资源请求后,系统按下述步骤进行检查:
①如果Requesti[j]≤Need[i,j]门便转向步骤②;否则认为出错,因为它所需要的资源数已超过它所宣布的最大值。
②如果Requesti[j]≤Available[j],便转向步骤③;否则,表示尚无足够资源Pi 须等待。
③系统试探着把资源分配给进程Pi,并修改下面数据结构中的数值
Available[j]=Available[j]-Requesti[j];
Allocation[i,j]=Allocation[i,j]+Requesti[j];
Need[i.j]=Need[i,j]-Requesti[j];
④系统执行安全性算法,检查此次资源分配后系统是否处于安全状态。若安全,才正式将资源分配给进程Pi,以完成本次分配;否则,将本次的试探分配作
废,恢复原来的资源分配状态,让进程Pi等待。
3)安全性算法:
系统所执行的安全性算法可描述如下:
①设置两个向量:
a.工作向量Work,它表示系统可提供给进程继续运行所需的各类资源数目,它含有m个元素 在执行安全算法开始时,Work=Available;
b.Finish:它表示系统是否有足够的资源分配给进程,使之运行完成。开始时先做Finish[i]=false;当有足够资源分配给进程时,再令Finish[i]=true。
②从进程集合中找到一个能满足下述条件的进程
a.Finish[i]=false;
b. Need[i,j]≤Work[j];
若找到,执行步骤③,否则,执行步骤④。
③当进程Pi获得资源后,可顺利执行,直至完成,并释放出分配给它的资源,故应执行:
Work[j]=Work[j]+Allocation[i,j];
Finish[i]=true;
返回步骤②。
④如果所有进程的Finish[i]=true都满足,则表示系统处于安全状态;否则,系统处于不安全状态。
4)安全算法举例
算法:银行家算法(做题过程)
问题:
解析:
假设0,6,2,如下不安全:
假设1,1,1,如下不安全:
假设0,4,7,如下不安全:
假设1,4,0,如下安全:
二、死锁的检测与解除
如果系统中既不采用预防死锁也不采用避免死锁的措施,系统就很有可能发生死锁。这种情况下,系统应当提供两个算法:
1)死锁检测算法:用于检测系统状态,已确定系统中是否发生了死锁。
为了能对死锁进行检测,在系统中必须:
①用某种数据结构来保存资源的请求和分配信息。
②提供一种算法,利用上述信息来检测系统是否进入死锁状态。
资源分配图
两种结点:进程结点和资源结点(资源结点可能有多个)
两种边:
进程结点->资源结点:代表一个进程结点请求一类资源。
资源结点->进程结点:代表这类资源已经分配给了指向的进程结点。
死锁定理:如果某时刻系统的资源分配图是不可简化的,那么此时系统死锁。
2)死锁解除算法:当使用这种算法的时候该算法可将系统从死锁中解脱出来。一旦检测出死锁的发生就应该立即解除死锁。
补充:并不是系统中所有的进程都是死锁状态,用死锁检测算法分配图后,还连着边的进程就是死锁进程。
解除死锁的主要方法:
①资源剥夺法。
将某些死锁进程锁占有的资源剥夺并分配给其他进程。但是应该防止被挂起的进程长时间得不到资源饿死。
②撤销进程法。强制撤销部分死锁进程或者全部死锁进程,简单粗暴但是付出的代价较大。
③进程回退法。让一个或多个死锁进程回退到足以避免死锁的地步。这个就要求记录进程的历史消息,设置还原点。
还有一类题:
俺的理解:就是不能让两个一起运行,运行一个一个来,不然生成的东西别的地方用的话不知道,用哪个都会导致死锁。
再练一个吧!!!
例:某系统中有三个并发进程都需要四个同类资源,则该系统必然不会发生死锁的最少资源是(B)
A.9 B.10 C.11 D.12