操作系统系列 -- 死锁 & 银行家算法

1,086 阅读5分钟

前言

转载自:www.jianshu.com/p/355f138ea…www.jianshu.com/p/f80349e56…

死锁是多线程环境中由于对资源竞争分配不合理而产生的阻塞行为,银行家算法是一种动态避免死锁的策略

1. 死锁

1.1 死锁定义

如果一个线程集合中的每一个线程都在等待这个集合中另一个线程的执行结果才能继续执行下去,若无其他外力,它们都无法推进,这就形成了死锁

1.2 死锁的四个必要条件

  1. 互斥条件:一个资源在某时刻内只能允许一个线程进行访问;
  2. 一旦占有则不释放资源:一个线程A占有一部分资源,此时去申请另外的资源,但申请的资源被线程B占有,此时线程A的请求阻塞,但是也不对自己本来的资源进行释放。
  3. 不可剥夺别的线程资源:线程对已获得的资源,在未完成使用之前,不可剥夺,只能使用完成后才释放。
  4. 循环等待:若干个线程之间形成了一种头尾相连的循环等待资源关系。

1.3 死锁的三种避免方法:

1、 预防死锁发生:通过对死锁产生的四个必要条件进行限制;

2、 检测与拆除死锁:这种方式使允许死锁发生,检测死锁产生,然后解除死锁;
检测的具体实施可以维护两个资源矩阵,对可用资源和需要资源进行比较;
解除死锁的方式主要可以实施抢占剥夺;kill 掉进程;回滚系统等;

3、 动态避免:在资源分配过程中,确保资源请求批准后系统不会进入死锁或潜在的死锁状态。如银行家算法

银行家算法

银行家算法是仿照银行发放贷款时采用的控制方式而设计的一种死锁避免算法,该算法的策略是实现动态避免死锁

银行家算法思想

银行家算法的基本思想是:分配资源之前,判断系统是否安全,如果安全才会进行资源分配。

我们把操作系统看做是银行家,操作系统管理的资源相当于银行家的资金,线程向操作系统请求分配资源就是用户向银行家要贷款。

银行家算法实例

假定系统中有5个进程:P0,P1,...,P4,有3个资源A、B、C。某一时刻资源分配情况是:

其中有 4 个矩阵:

  • Max:表示每个进程声明对于某个资源的最大使用量。例如第一行[7,5,3]表示进程P0对于A、B、C三个资源的需求量不超过7、5、3个
  • Allocation:表示当前时刻这些进程已经分到的资源数目。例如第一行[0,1,0]表示进程P0已经分配到0个A、1个B以及0个C
  • Need:表示这些资源还需要多少资源。例如第一行[7,4,3]表示进程P0还需要7个A、4个B以及3个C
  • Available:表示系统现在还能分配的资源数。[3,2,2]表示A、B、C分别还有3个、2个、2个尚可分配。

(问题1)如何判断系统当前是否安全?——这里引入安全性算法:

  1. 构造两个向量Work和Finish。Work[i]表示第i个资源还能分配多少,Finish[i]表示第i个进程是否可以安全运转结束。初始时,Work=Available,而Finish全部为false。

  2. 现在,选出一个进程Pi,它必须满足:Finish[i]=false,并且Need[i]≤Work(即对应项进行比较),即选出一个尚不确定是否能安全运行结束的进程,并且这个进程还需要的资源数不能多于系统可以提供的资源数目,在这里,进程P1、进程P3都是可以的

    注意到这两个进程的Need是小于Work的且它们的Finish都是false

  3. 这里不妨选P1(选择进程P3也是可以的)。然后为其分配所需资源。我们总假定为其分配最大量,确保它能安全运行到结束。因为根据P1的Need可知它需要1个A、2个B和2个C,那么就从Work中减去相应的量:Work-Need[1] 。这样Allocation[1]==Max[1] ,并且它能够安全运行结束:Finish[1]==true

    Work-Need[1]从而Allocation[1]==Max[1]且Finish[1]==true

  4. 然后,设P1运行结束,它要释放所持有的资源(3个A、2个B、2个C)还给系统,由此变更Work向量:Work=Work+[3 2 2] 。并淘汰P1,之后不予考虑。

以上 4 个步骤其实可以合成为:Work=Work+Allocation[i]

  1. 重复以上 4 个步骤,可以逐渐淘汰掉 P1、P3、P2、P4(顺序无关,只要能分配即可),但是最终 P0 无法淘汰掉

因此得出结论:当前时刻的系统的状态就是一个不安全状态,可能会发生死锁。所以不会给这些进程分配资源。但如果初始的 Available 的值是[3,3,2]则是安全状态,则可以进行资源分配。

安全性算法流程图:

(问题2)若安全/当前无进程,此时有新的进程请求资源,怎么判断是否允许分配?

按照如下流程判断:

  1. Request是否小于等于它自己的Need? 如果大于显然这是不允许的(客观上的),因为它请求的资源不能大于它事先声明的需求数。请求被拒绝。
  2. Request是否小于等于它自己的Available? 如果大于这是不允许的,因为当前没有足够资源可够分配。此时一般不是拒绝,而是将该进程挂到等待队列中,等待其他进程释放资源后再行分配。
  3. 若上面两条都通过,则开始进行试探分配先假设将资源全都分配了出去,此时改写Need、Allocation、Available的数据,这样就又达到了一个状态,运行安全性算法判断该状态是否安全,若是安全状态,就进行分配;否则可能会引发死锁,拒绝分配。