什么是死锁
如果一组进程(或者线程)中的所有进程(或者线程)都在等待该组中其他进程(或者线程)才能引发的事件,那么这组进程(或者线程)就是死锁的
死锁出现的情况举例
(1)线程自锁,自己将自己锁住
当进程(线程)第一次访问一个资源时,对该资源进行加锁;如果该进程(线程)又对该资源试图使用而进行加锁时,由于锁之前已被自己加上,便会等待锁的释放。这就永远处于挂起状态了,便产生了死锁。
(2)多个线程抢占资源
例如当线程A获得资源1时,线程B获得资源2时;然后此时,线程A又对2资源进行请求,线程B又对1进行请求,这样线程A,B都将等待另一个线程释放资源,于是线程A,B又永远处于挂起状态了,这又是死锁产生的一种情形。
系统中资源的分类
<1>按照重用和消耗类型
(1)可重用资源
特点:
可以让用户多次使用的资源
性质:
1、每一个资源只可以让一个进程(或者线程)进行占用,不可以让多个进程(或者线程)共享
2、使用方法:请求资源---使用资源---释放资源
3、系统中每一个可重用资源中的数目是相对固定的,在进程(或者线程)运行期间不可以进行删除和创建
(2)可消耗资源
特点:
在运行期间创建和消耗
性质:
1、每一类可消耗的单元资源数目是变化的,可以为0
2、进程(或线程)在运行过程中可以不断的创建可消耗性资源的单元,将它们放入该资源类的缓冲区中,用来增加该资源类的单元数目。
3、进程(或线程)在运行过程中可请求若干个可消耗性资源,用于自身的消耗不再将它们返回给该资源类中。
4、可消耗性资源通常是由生产者进程(或线程)创建,由消费者进程(或线程)消耗。
<1>按照是否可以抢占类型
(1)不可抢占资源
当一个进程(或者线程)占用一个资源时,其他资源必须等到该资源进行释放才可以使用。
例如打印机和磁带
(2)可抢占资源
当一个进程(或者线程)占用一个资源时,其他进程(或者线程)、或系统可以对该资源进行抢占。
例如内存和CPU
死锁产生的必要条件
(1)互斥条件,资源在同一时刻只能由一个进程或者线程进行占用
(2)请求和保持,一个进程或者线程占用着一定数量的资源(至少一个),但又发出了新的资源请求,而新的资源此刻被其他进程或者线程占用着
(3)不可抢占资源,进程或线程在使用完占用的资源前,不可以被其他进程或者线程抢占
(4)循环等待,发生死锁时,必然存在死锁链
引起死锁的原因
(1)竞争不可抢占资源
假设系统中拥有两个进程p1和p2,它们都准备写两个文件F1和F2。
而这两者都属于可重用和不可抢占性资源。
这种情况下,如果进程p1在打开f1的同时,p2进程打开f2文件,当p1想打开f2时由于f2已结被占用而阻塞,当p2想打开f1时由于f1已结被占用而阻塞,此时就会无线等待下去,形成死锁。
(2)竞争可消耗资源
我们假设:系统中拥有三个进程p1、p2和p3以及三个可消耗资源m1、m2、m3。
进程p1一方面产生消息m1,将其发送给p2,另一方面要从p3接收消息m3。
而进程p2一方面产生消息m2,将其发送给p3,另一方面要从p1接收消息m1。
类似的,进程p3一方面产生消息m3,将其发送给p1,另一方面要从p2接收消息m2。
这种情况下,如果三个进程都先发送自己产生的消息后接收别人发来的消息,则可以顺利的运行下去不会产生死锁。
但要是三个进程都先接收别人的消息而不产生消息则会永远等待下去,便会产生死锁。
(3)进程推进顺序不当产生死锁
假设两个进程p1和p2,若p1保持了资源r1,p2保持了资源r2,则系统处于不安全状态,因为这两个进程再向前推进,便可能发生死锁。
例如,当P1运行到P1:Request(R2)时,将因R2已被P2占用而阻塞;
当P2运行到P2:Request(R1)时,也将因R1已被P1占用而阻塞,于是发生进程死锁。
处理死锁的办法
(1)预防死锁
具体方法:破坏死锁必要条件中的一个或者多个
注意:互斥是必要条件,不能破坏,否则将造成不可再现性
(2)避免死锁
具体方法:在资源分配过程中,防止系统进入不安全的区域
(3)检测死锁
具体方法:通过检测机制发现死锁,并可以采取适当措施解决死锁
(4)解除死锁
具体方法:当检测机制发现死锁后,采用相应的算法解决它
利用银行家算法解决死锁
1、在银行家算法中,主要的数据结构
(1)可利用资源向量Available[m]。m为系统中的资源种类数,如果向量Available[j] = K,则表示系统中Rj类资源由K个。
(2)最大需求矩阵Max[n][m]。m为系统中的资源种类数,n为系统中正在运行的进程(线程)数,如果Max[i][j] = K,则表示进程i需要Rj类资源的最大数目为K个。
(3)分配矩阵Allocation[n][m]。m为系统中的资源种类数,n为系统中正在运行的进程(线程)数,如果Allocation[i][j] = K,则表示进程i当前已分得Rj类资源的数目为K个。
(4)需求矩阵Need[n][m]。m为系统中的资源种类数,n为系统中正在运行的进程(线程)数,如果Need[i][j] = K,则表示进程i还需要Rj类资源K个。
以上三个矩阵间的关系:
Need[i][j] = Max[i][j] - Allocation[i][j]
2、银行家算法的具体步骤
设Request( i)是进程Pi的请求向量,如果Request(i) [j] = K,表示进程Pi需要K个Rj类型的资源。
(1)如果Request(i) [j] <= Need[i][j],转向步骤(2)。
(2)如果Request(i) [j] <= Available[j] ,转向步骤(3)。
(3)系统尝试着把资源分给进程Pi。
Available[j] = Available[j] - Request(i) [j];
Allocation[i][j] = Allocation[i][j] + Request(i) [j];
Need[i][j] = Need[i][j] - Request(i) [j];
(4)系统执行安全性算法,检查此次资源分配后系统是否处于安全状态。
3、银行家算法中的安全性
(1)设置两个向量:
1、工作向量Work[m],它表示系统可提供给进程继续运行所需要的各类资源数目,初始值Work = Available。
2、Finish:它表示系统是否有足够的资源分配给进程,使其运行完成。开始时Finish[i] = false,当有足够的资源分配给进程时Finish[i] = true。
(2)从进程(线程)集合中找到一个能满足下述条件的进程(线程)。
1、Finish[i] = false
2、Need[i][j] <= Work[j],如果找到转到步骤3》,没找到转到步骤4》。
3、Work[j] = Work[j] + Allocation[i][j] ;
Finish[i] = true;
go to step 2;
4、如果所有进程(线程)的Finish[i] = true都满足,表示系统处于安全状态,反之系统处于不安全状态。