一、什么是死锁?
在多任务系统下,当一个或者多个进程等待系统资源,而资源又被进程本身或者其他进程所占用时,就会形成死锁。
由于资源占用是互斥的,当某个资源提出申请资源后,使得有关进程在无外力协助下永远分配不到必须的资源而无法继续运行。
二、死锁产生的四个必要条件
(1)互斥条件
一个资源每次只能被一个进程所占有
(2)不可抢占资源
进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(而且只能是主动释放)
(3)请求和保持条件
一个进程因为请求资源而阻塞时,对已获得的资源保持不放
(进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源被其他进程所占有,此时请求进程被阻塞,但对自己已有的资源保持不放)
(4)循环等待条件
若干进程之间形成一种头尾相接的循环(eg:存在一个等待状态的进程集合{P1,P2,P3……Pn},其中Pi等待的资源被P(i+1)所占有(i=0、1、2…..n-1),Pn等待的资源被P0所占有)
三、死锁
1、产生死锁的原因
* (1)竞争不可抢占性资源
* (2)竞争可消耗资源
* (3)进程推进顺序不当:进程在运行过程中,请求和释放资源的顺序不当,也会同样导致产生进程死锁
2、死锁的处理方法(消防的例子:在发生火灾之前就预防之)
(1)死锁预防:确保系统永远不会进入死锁状态
(2)死锁避免:在使用之前预先判断,只允许不会出现死锁的进程请求资源
(3)死锁检测与恢复:在检测到运行系统出现死锁状态之后,进行恢复
* 一般而言由应用程序处理死锁,通常由操作系统忽略死锁
3**、死锁预防**:限制申请方式
预防是采用某种策略,限制并发进程对资源的请求,使系统在任何时刻都不满足死锁的必要条件
(1)互斥:把互斥的共享资源封装成可同时访问(以前打印机)
(2)持有并等待:进程申请资源时,要求它不持有任何其他资源(贷款:一次性申请全部贷款);仅允许进程在开始执行时,一次请 求所有需要的资源,资源利用率低(比如好几年才能建成的高楼大厦,在刚动工时就申请完全部资金,然后并不会一次性使用完,这样就会导致资金的利用率降低,不能实现利润的最大化)
(3)非抢占:如进程请求不能立即分配的资源,则释放已占有的资源(过独木桥问题,两人相遇,需要后退);只在能够同时获得所有需要资源时,才执行分配操作
(4)循环等待:对资源排序,要求进程按顺序申请资源(可能出现先申请的资源后用,这样会造成资源的利用率低的问题)
缺点:限制条件多,资源利用率没有死锁避免的高
4、死锁避免
利用额外的先验信息,在分配资源的时候判断是否会出现死锁,只在不会出现死锁的时候分配资源(比如银行贷款的例子:去银行贷款的时候,银行会先查看你的个人信用度,然后考虑是否会借钱给你)
死锁避免的三种方式:
* (1)要求进程声明需要资源的最大数目
* (2)限定提供与分配资源的数量,确保满足进程的最大需求
* (3)动态检查资源的分配状态,确保不会出现环形等待
优点:比起死锁预防来,它的限制条件少,可以获得较好的系统性能 缺点:不好确定系统分配安全
5、系统资源分配的安全状态
(1)当进程请求资源的时候,系统判断分配后是否处于安全状态
(2)当系统处于安全状态时:针对所有已占用的进程,存在安全序列
(3)当序列{P1,P2,P3……Pn}是安全时
a:Pi要求的资源<=当前可用资源+所有Pj持有资源,其中j<i
b:如Pi的资源请求不能立即分配,则Pi等待所有的Pj(其中j<i)完成
c:Pi完成后,Pi+1可得到所需资源,执行并释放所分配的资源
系统处于安全状态时,一定没有死锁
系统处于不安全状态时,可能会出现死锁
避免死锁就是确保系统不会进入不安全状态
五、死锁的检测与恢复
1、死锁的检测
* 允许进程进入死锁状态
* 维护系统的资源分配图
* 定期调用死锁检测算法来搜索上图是否存在死锁
* 出现死锁时利用死锁恢复机制进行恢复
2、死锁的恢复
(1)进程终止
* 终止所有死锁进程
* 一次只终止一个死锁进程直至死锁状态消失
* 终止进程的顺序:a、进程的优先级 b、进程已运行的时间以及还需运行的时间<终止运行时间长的> c、进程已占用资源 d、进程完成需要的资源 e、终止进程数目 f、进程是前台交互还是后台批处理<通常保留前台交互>
(2)资源抢占
* a、选择被抢占进程:最小成本目标
* b、进程回退:返回到一些安全状态,重启进程到安全状态
* c、可能出现饥饿:同一进程可能一直被同一个进程所抢占
六、常见算法及原理
(1)银行家算法
算法原理:
银行家算法是一个避免死锁产生的算法。以银行借贷分配策略为基础判断并保证系统处于安全状态
(1)客户在第一次申请贷款时,声明所需最大资金量,在满足所有贷款要求并完成项目时,及时归还
(2)在客户贷款数量不超过银行拥有的最大值时,银行家尽量满足客户需求
当进程首次申请资源时,需要测试该进程对资源的最大需求量,倘若系统现存的资源满足它的最大需求量则按当前的社清凉分配资源,否则就推迟分配。
当进程在执行中继续申请资源时,先测试该进程本次申请的资源数是否超过了该资源剩余的总量。若超过则拒绝分配资源,若能满足按当前的申请量分配资源,否则也要推迟分配。
1)数据结构:
n=线程数量,m=资源类型数量
* Max(总需求量):n*m矩阵
线程Ti最多请求类型Rj的资源Max[i,j]个实例
* Available(剩余空闲量):长度为m的向量
当前有Available[i]个类型Rj的资源实例可用
* Allocation(已分配量):n*m矩阵
线程Ti当前分配了Allocation[i,j]个Rj的实例
* Need(未来需要量):n*m矩阵
线程Ti未来需要Need[i,j]个Rj资源实例
2)安全状态判断
* (1)work和finish分别是长度为m和n的向量初始化:
work = Available // 当前资源剩余空闲余量
finish[i] = false for i:1,2,3......n // 线程i没结束
* (2)寻找线程i:
(a)finish[i] = false // 接下来找出Need比work小的线程i
(b)Need[i] <=work
没有找到满足条件的Ti,转(4)
* (3)work = work + Allocation[i] // 线程i的资源需求量小于当前剩余空闲资源量,所以配置给它再回收
finish[i] = true
转(2)
* 如所有线程Ti满足finish[i] == true // 所有线程的finish为true,表明系统处于安全状态
则系统处于安全状态
3)算法
初始化:Requesti线程Ti的资源请求向量
Requesti [j] 线程Ti请求资源Rj的实例
循环:
* (1)如果Requesti <= Need[i],转到步骤(2)。否则,拒绝资源申请。因为线程已经超过了其最大要求
* (2)如果Requesti <= Available,转到步骤(3)。否则,Ti必须等待,因为资源不可用
* (3)通过安全状态判断来确定是否分配资源给Ti:
生成一个需要判断状态是否安全的资源分配环境
Available = Available - Requesti;
Allocation[i] = Allocation[i] + Requesti;
Need[i] = Need[i] - Requesti;
调用安全状态判断
如果返回结果是安全,将资源分配给Ti
如果返回结果是不安全,系统会拒绝Ti的资源请求