Linux中的死锁

来源:互联网 发布:剑三大师捏脸数据 编辑:程序博客网 时间:2024/06/06 02:52

一、死锁的引入

顾名思义,死锁肯定与锁有关,我们直到引入又是为了解决多进程或者多进程之间的同步与互斥问题,那么到底怎样的情形才会产生死锁呢?

典型的两种死锁情形:

(一)线程自己将自己锁住

一般情况下,如果同一个线程先后两次调用lock,在第二次调用时,由于锁已经被占用,该线程会挂起等待占用锁的线程释放锁,然而锁正是被自己占用着呢,该线程又被挂起而没有机会释放锁,因此就永远处于挂起等待状态了,于是就形成了死锁(Deadlock)。

(二)多线程抢占锁资源被困

又如线程A获得了锁1,线程B获得了锁2,这时线程A调用lock试图获得锁2,结果是需要挂起等待线程B释放锁2,而这时线程B也调用lock试图获得获得锁1,结果是需要挂起等待线程A释放锁1,于是线程A和B都永远处于挂起状态了,死锁再次形成。


二、计算机系统中的死锁

1、资源分类

(一)可重用性资源和消耗性资源

可重用资源:可供用户重复使用多次的资源

性质:

1》每一个可重用性资源中的单元只能分配给一个进程(或线程)使用,不允许多个进程(或线程)共享

2》可重用性资源的使用顺序:

请求资源-->使用资源-->释放资源

3》系统中每一类可重用性资源中的单元数目是相对固定的,进程(或线程)在运行期间既不能创建也不能删除它。


可消耗性资源:又称临时性资源,是有进程或线程在运行期间动态的创建和消耗的。

性质:

1》每一类可消耗性资源的单元数目在进程(或线程)运行期间是可以不断变化的,有时可能为0

2》进程或线程在运行过程中可以不断的创建可消耗行资源的单元,将它们放入该资源累的缓冲区中,以增加该资源类的单元数目

3》进程或线程在运行过程中可请求若干个可消耗性资源,用于进程(或线程)自己的消耗不再将它们返回给该资源类中。

可消耗性资源通常是有生产者进程或线程创建,有消费者进程消耗。

(二)可抢占性资源和不可抢占性资源

可抢占性资源:某进程或线程在获得该类资源后,该资源可以被其他进程或线程或系统抢占。

不可抢占性资源:系统一旦把某资源分配该进程或线程之后,就不能强行收回,只能在进程或线程用完之后自行释放

磁带机、打印机等都属于不可抢占性资源

2、引起死锁的原因

(一)竞争不可抢占资源引起死锁

如:共享文件时引起死锁

系统中拥有两个进程P1和P2,它们都准备写两个文件F1和F2。而这两者都属于可重用和不可抢占性资源。如果进程P1在打开F1的同时,P2进程打开F2文件,当P1在打开F2时由于F2已经被占用而阻塞,当P2想打开F1时由于F1已经被占用而阻塞,此时就会无线等待下去,形成死锁。


(二)竞争可消耗资源引起死锁

如:进程通信时引起死锁

系统中拥有三个进程P1、P2和P3,m1、m2、m3是3个消耗资源。进程P1一方面产生消息m1,将其发送给P2,另一方面从P3接受消息m3,而进程P2一方面产生消息m2,将其发送P3,另一方面要从P1接收消息m1。类似的,进程P3一方面产生消息m3,将其发送给P1,另一方面要从P2接收消息m2。 
如果三个进程都先发送自己产生的消息后接收别人发来的消息,则可以顺利的运行下去不会产生死锁,但要是三个进程都先接收别人的消息而不产生消息则会永远等待下去,产生死锁。


(三)进程推进顺序不当引起死锁


上图中,如果按曲线1的顺序推进,两个进程可顺利完成;如果按曲线2的顺序推进,两个进程可顺利完成;如果按曲线3的顺序推进,两个进程可顺利完成;如果按曲线4的顺序推进,两个进程将进入不安全D中,此时P1保持了资源R1,P2保持了资源R2,系统处于不安全状态,如果继续向前推进,则可能产生死锁。

三、死锁的定义、必要条件和处理方法

1、死锁的定义:如果一组进程(或线程)中的每一个进程(或进程)

2、产生死锁的必要条件 
1)互斥条件。进程(线程)所申请的资源在一段时间内只能被一个进程(线程)锁占用。 
2)请求和保持条件。进程(线程)已经占有至少一个资源,但又提出了新的资源请求,而该资源却被其他进程(线程)占用。 
3)不可抢占条件(不可剥夺条件)。进程(线程)已获得的资源在未使用完之前不能被抢占。 
4)循环等待条件(环路等待条件)。在发生死锁时,必然存在一个进程(线程)—-资源的循环链。 
3、处理死锁的方法 
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需要KRj类型的资源。 
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 
2Finish:它表示系统是否有足够的资源分配给进程,使其运行完成。开始时Finish[i] = false,当有足够的资源分配给进程时Finish[i] = true 
2)从进程(线程)集合中找到一个能满足下述条件的进程(线程)。 
1Finish[i] = false 
2Need[i][j] <= Work[j],如果找到转到步骤3》,没找到转到步骤4》。 
3Work[j] = Work[j] + Allocation[i][j] ; 
Finish[i] = true; 
go to step 2; 
4》如果所有进程(线程)的Finish[i] = true都满足,表示系统处于安全状态,反之系统处于不安全状态



原创粉丝点击