死锁的定义:如果一组进程中的每一个进程都在等待仅由该组进程中的其他进程才能引发的时间,那么该组进程是死锁的。
产生死锁的必要条件:(产生死锁必须同时具备下面四个必要条件)
- 互斥条件:简单的说就是进程抢夺的资源必须是临界资源,一段时间内,该资源只能同时被一个进程所占有
- 请求和保持条件:当一个进程持有了一个(或者更多)资源,申请另外的资源的时候发现申请的资源被其他进程所持有,当前进程阻塞,但不会是放自己所持有的资源
- 不可抢占条件:进程已经获得的资源在未使用完毕的情况下不可被其他进程所抢占
- 循环等待条件:发生死锁的时候,必然存在一个进程—资源的循环链
这里所说的资源不仅包括硬件资源或者其他的资源,还包括锁,锁也是一种资源,锁的争用也会导致死锁
- 死锁的几个例子(为了好描述,这里用锁作为资源来描述死锁,这里的锁换成资源完全没问题)
自死锁,简单的说一个进程持有了一个锁之后,在临界区内又去申请该锁,它将不得不等待该锁被释放,但因为它本身在等待申请该锁,所以永远不会有机会释放锁并得到锁,最终结果就是死锁。因为很多锁都不是可递归锁,所以不要尝试在一个线程内多次申请同一个锁。
ABBA死锁(该死锁常发生于多个进程多个锁),简单的说就是每个进程都持有其他进程想要获得的锁,上图
这个最经典的例子当属,哲学家用餐问题(不再多说,基本原理就是上图和上面所说的ABBA死锁,可以百度一下)
死锁的处理方法又分为好几大类
- 预防死锁
预防死锁的办法就是破坏死锁的四个必要条件,只要破坏了条件,死锁自然就不会产生了,简单的描述一下破坏四个条件的思想
破坏请求和保持条件:1.所有进程在开始运行之前,必须一次性获得所有资源,如果无法获得完全,释放已经获得的资源,等待;2.所有进程在开始运行之前,只获得初始运行所需要的资源,然后在运行过程中不断请求新的资源,同时释放自己已经用完的资源。 相比第一种而言,第二种方式要更加节省资源,不会浪费(因为第一种可能出现一种资源只在进程结束用那么一小下,但却从头到尾都被占用,使用效率极低),而且,减少了进程饥饿的情况。
破坏不可抢占条件:说起来简单,只要当一个进程申请一个资源,然而却申请不到的时候,必须释放已经申请到的所有资源。但是做起来很复杂,需要付出很大的代价,加入该进程已经持有了类似打印机(或者其他的有必要连续工作的)这样的设备,申请其他资源的时候失败了,必须释放打印机资源,但是人家用打印机已经用过一段时间了,此时释放打印机资源很可能造成之后再次是用打印机时两次运行的信息不连续(得不到正确的结果)
破坏循环等待条件:设立一个规则,让进程获取资源的时候按照一定的顺序依次申请,不能违背这个顺序的规则。必须按照顺序申请和释放,想要申请后面的资源必须先把该资源之前的资源全部申请,想要申请前面的资源必须先把该资源之后的资源(前提是已获得)全部释放
破坏互斥条件:没法破坏,是资源本身的性质所引起的
- 避免死锁
最常听到的算法来袭!银行家算法来了!!
银行家算法需要的数据结构:1.可利用资源向量(Available);2.最大需求矩阵(Max);3.分配矩阵(Allocation);4.需求矩阵(Need)。
上述三个矩阵存在如下关系 Need[i,j]=Max[i,j]-Allocation[i,j];
说的看似很难懂的样子,下面详解一下就很好理解了。
可利用资源向量(Available):这么说吧,比如当前有三种资源A,B,C,可利用资源向量就是一个数组(为了在程序中使用),我们理解的话就说ABC各有多少个资源,例子
A B C
7 2 5 这里7,2,5三个资源的数目组合起来就是一个数组(向量)
最大需求矩阵(Max):说是矩阵完全是为了在编程时使用方便(就是一个二维数组),我们理解的话就说是进程工作需要的每个资源的总数目,例子
A B C
P1 1 1 1 这样一看就明白了,进程P1需要三种资源各一个(这个不是二维数组啊,是一维的?怎么可能只有一个进程这是一对进程思死锁的解决办法!),多加几个进程就二维数组了
分配矩阵(Allocation):和需求矩阵类似,只是数值的含义变成了,目前已经给进程某某(行值i),分配了多少的某某(列值j)资源
需求矩阵(Need):根据上面的公式就可以看出,这个是进程目前还需要多少资源才可以运行,和最大需求不一样,最大需求是一共需要的数目。
有了这些我们使用银行家算法就够了,我们使用该算法的目的是什么?是避免死锁,避免死锁的意思就是我们使用这些数据结构来推断如果按某种顺序执行进程队列,是否是安全的(是否会造成死锁)
上图是一个简单的图实例,为了计算机程序运行方便,我们一般假设从P0进程开始运行,那么就要给P0分配足够运行的资源(就是把NEED都给了,前提是当前Available资源数目足够该进程的Need),然后计算Available(new)=Available(old)+Allocation(P),其中Allocation就是执行的进程之前已经分配的资源执行完成之后自然要会收到Available里。
题目一般都会给上图的表,然后让你写出安全序列,用当前的Available看满足哪个进程的Need然后就先执行它,执行完后回收(加到Available中)该进程的Allocation就可以了,这样一步一步算到最后如果Available一直算下来没有亏损不够的情况证明这个序列就是一个安全队列了~!
- 死锁的检测和解除
使用类似银行家算法的方式就可以简单的检测死锁
死锁解除:1.终止进程(简单粗暴),就是字面上的,你们死锁了,我就把你们一起杀掉,缺点就是如果一个进程跑了很长时间,但是被杀了,还得从头来。
2.逐个终止进程,按照某种顺序,挨个杀死进程,每杀一个进程就去看看死锁解除了没有(每杀一个进程都会释放一些资源,如果释放好粗来的资源解决了死锁问题,就没必要再滥杀无辜了),没解除就继续杀。
第二种方式显然人性化了许多,但是按照某种顺序显得很朦胧,这里的某种顺序就是指死锁解除算法,有很多,这里不再赘述。
推荐书籍:《计算机操作系统(第四版)》第三章第五节,P104