操作系统之死锁
文章目录
操作系统的几种资源
可重用性资源和消耗性资源
-
可重用性资源
-
消耗性资源
- 定义:与可重用性资源相对,消耗性资源是在进程运行过程中,由进程动态的创建和消耗的资源,又称为临时性资源
- 性质:
- 在进程运行过程中,消耗性资源的单元数目是可以不断变化的
- 进程运行过程中,可以创造消耗性资源
- 进程运行过程中,进程可以消耗一定数目的消耗性资源,而不再将该资源返回给资源类
可抢占性资源和不可抢占性资源
- 可抢占性资源
- 定义:某进程在获得这类资源后,该资源可以随时被其他进程或系统抢占
- 不可抢占性资源
- 定义:与不可抢占性资源相对,某进程获得这类资源后,该资源不能被其他进程或系统抢占,只有等进程用完之后自行释放该资源。
死锁的起因
死锁的定义、必要条件及处理方法
死锁的定义
如果一组进程中的每一个进程都在等待仅有该组进程中的其他进程才能引发的事件,则称该组进程是死锁的。
产生死锁的四个必要条件
产生死锁的原因有多种,但都是具备了一定的条件才形成死锁,产生死锁必须满足四个条件:
- 互斥条件:进程对分配到的资源进行排他性使用,不共享
- 请求和保持条件:进程在请求被其他进程所占有的资源,成阻塞状态时,保持自己所占有的资源不放
- 不可抢占条件:进程的资源不可抢占,只能等执行完毕后自行释放
- 循环等待条件:进程组每一个进程都在等待另一个进程所占用的资源,形成一个循环链
处理死锁的方法
四种方法从上到下对于死锁的防范程度不断减弱,但资源利用率不断提高
预防死锁
预防死锁通常是通过破坏死锁的四个必要条件中的后三个来避免产生死锁。
破坏“请求和保持”条件
可通过两个不同的协议来保证:当一个进程在请求资源时,该进程不能持有不可抢占资源。从而破坏“请求和保持”条件
- 第一种协议
- 内容:所有进程在运行之前,必须一次性申请其整个运行过程中需要的所有资源
- 优点:简单易行、安全
- 缺点:
- 资源被严重浪费
- 使进程经常出现饥饿现象
- 第二种协议
- 内容:允许进程在只获得运行初期所需要的资源时,便开始运行。在运行过程中逐步释放自己所占有的,且使用完毕的资源。然后再去请求下一运行阶段所需要的资源
- 优点:提高了资源的利用率,减少发生饥饿现象的几率
破坏“不可抢占”条件
通过一种协议来破坏“不可抢占条件”
- 协议:
- 协议内容:当一个已经保持了某些不可抢占资源的进程,提出新的资源请求而得不到满足时,该进程必须释放它保持的所有资源,待以后需要时再申请。可以理解为这些资源被抢占了
- 优点:破坏了不可抢占条件
- 缺点:
- 方法比较复杂,实现代价大
- 可能造成前一阶段工作的浪费,延长了进程的周转时间,降低系统的吞吐量。
破坏“循环等待”条件
对系统的各类资源类型进行线性排序,并赋予不同的序号。
- 协议:
- 内容:规定每个进行必须按序号递增的顺序请求资源。如果需要多个同类资源,必须一起请求。假如某进程申请到了序号较高的资源又想申请序号较低的资源时,必须先释放序号较高的资源,再去申请序号较低的资源
- 优点:
- 避免资源分配途中出现环路,从而破坏了“循环等待”条件
- 提高了资源利用率,系统的吞吐量
- 缺点:
- 各类资源的序号必须相对稳定,从而限制了新类型设备的增加
- 在实际中,可能会出现某些作业使用各类资源的顺序与系统规定的顺序不同,从而造成资源的浪费。
- 按固定次序去申请资源,限制了用户自主得进行编程。
避免死锁
与预防死锁相比,避免死锁也属于事前预防的措施,但并不是通过破坏形成死锁的必要条件来实现,而是通过在资源动态分配过程中,防止系统进入不安全状态,来避免死锁。
安全状态与不安全状态:
- 安全状态:安全状态,指系统能按某种进程推进顺序 ( P 1 , P 2 , . . . , P n ) (P_1,P_2,...,P_n) (P1,P2,...,Pn)为每个进程分配所需的资源,直至满足进程的所有需求,使每个进程都可顺利完成。该推进顺序称为安全序列
- 不安全状态:与安全状态相对,如果不存在进程推进顺序,使得每个进程都可顺利完成,则称系统处在不安全状态中。
- 需要注意的是:系统处于安全状态,则一定不会出现死锁。而如果系统处于不安全状态,可能会出现死锁,并不是一定会出现死锁。
避免死锁的方法即为,在系统进行资源分配之前,先计算此次资源分配的安全性,如果此次分配不会导致系统进入不安全状态,则执行分配,而如果此次分配会导致系统进入不安全状态,则不执行分配。
银行家算法(避免死锁算法)
银行家算法:当一个新进程进入系统时,该进程必须申明在运行过程中所需要的每种资源的最大数目,且该数目不能超过系统拥有的资源总量。当进程请求某组资源时,系统必须先确定系统中是否有足够的该类资源供以分配给该进程。若有,通过安全性算法来计算,将资源分配给该进程后,是否会使系统处于不安全状态,如果不会处于安全状态,则将资源分配给该进程,否则让进程等待。若没有,进程等待。
-
数据结构
实现银行家算法,必须有四种数据结构,用以描述系统中可使用的资源数目、所有进程对于各类资源的最大需求、系统中已分配资源的情况、各进程还需要多少资源的情况
- 可利用资源向量 A v a i l a b l e Available Available, A v a i l a b l e [ j ] = K Available[j]=K Available[j]=K 表示系统中现有可利用的 R j R_j Rj类资源 K K K个
- 最大需求矩阵 M a x Max Max, M a x [ i , j ] = K Max[i,j]=K Max[i,j]=K 表示进程 i i i需要 R j R_j Rj类资源的最大数目为 K K K个
- 分配矩阵 A l l o c a t i o n Allocation Allocation, A l l o c a t i o n [ i , j ] = K Allocation[i, j]=K Allocation[i,j]=K 表示进程 i i i已分得 R j R_j Rj类资源 K K K个
- 需求矩阵 N e e d Need Need, N e e d [ i , j ] = K Need[i,j]=K Need[i,j]=K 表示进程 i i i还需要 R j R_j Rj类资源 K K K个
有关系:
N e e d [ i , j ] = M a x [ i , j ] − A l l o c a t i o n [ i , j ] Need[i,j]=Max[i,j]-Allocation[i,j] Need[i,j]=Max[i,j]−Allocation[i,j] -
银行家算法描述
设 R e q u e s t i [ j ] Request_i[j] Requesti[j]是进程 i i i的请求向量, R e q u e s e t i [ j ] = K Requeset_i[j]=K Requeseti[j]=K表示进程 i i i请求K个 R j R_j Rj类资源。当进程发出资源请求后,算法执行步骤如下:
-
如果 R e q u e s t i [ j ] ≤ N e e d [ i , j ] Request_i[j]\leq Need[i,j] Requesti[j]≤Need[i,j],跳至步骤2;否则,认为出错,因为请求的超过它所需要的。
-
如果 R e q u e s t i [ j ] ≤ A v a i l a b l e [ i , j ] Request_i[j]\leq Available[i,j] Requesti[j]≤Available[i,j],跳至步骤3;否则,认为系统暂时满足不了该进程的需求,进程等待
-
系统尝试着将请求的资源分配给该进程,并修改数据结构中的数值:
A v a i l a b l e [ i , j ] = A v a i l a b l e [ i , j ] − R e q u e s t i [ j ] A l l o c a t i o n [ i , j ] = A l l o c a t i o n [ i , j ] + R e q u e s t i [ j ] N e e d [ i , j ] = N e e d [ i , j ] − R e q u e s t i [ j ] Available[i,j]=Available[i,j]-Request_i[j] \\ Allocation[i,j]=Allocation[i,j]+Request_i[j] \\ Need[i,j]=Need[i,j]-Request_i[j] Available[i,j]=Available[i,j]−Requesti[j]Allocation[i,j]=Allocation[i,j]+Requesti[j]Need[i,j]=Need[i,j]−Requesti[j] -
执行安全性算法,判断如此分配后,系统是否处于安全状态,如果处于安全状态,则执行分配;否则,本次尝试分配作废,进程等待。
-
安全性算法描述:
-
设置两个向量
- 工作向量 W o r k Work Work,表示系统可提供给进程继续运行的各类资源数目,算法执行之前,令 W o r k = A v a i l a b l e Work=Available Work=Available
- F i n i s h Finish Finish:表示系统是否有足够的资源分配给进程,使进程完成。开始时令 F i n i s h [ i ] = F a l s e Finish[i]=False Finish[i]=False,如果有足够的资源分配给进程,则令 F i n i s h [ i ] = T r u e Finish[i]=True Finish[i]=True
-
从进程集中寻找满足下列条件的进程:
- F i n i s h [ i ] = F a l s e Finish[i]=False Finish[i]=False
- N e e d [ i , j ] ≤ W o r k [ j ] Need[i,j]\leq Work[j] Need[i,j]≤Work[j]
若找到,跳至步骤3,否则执行步骤4
-
更新数据结构的数值。进程 i i i获得资源后,执行,直至完成进程,并释放他占有的资源,即:
W o r k [ j ] = W o r k [ j ] + A l l o c a t i o n [ i , j ] F i n i s h [ i ] = T r u e g o t o s t e p 2 Work[j]=Work[j]+Allocation[i,j] \\ Finish[i] = True \\ go\quad to\quad step 2 Work[j]=Work[j]+Allocation[i,j]Finish[i]=Truegotostep2 -
如果所有的进程都满足 F i n i s h [ i ] = T r u e Finish[i]=True Finish[i]=True,表示分配资源后系统将处于安全状态,执行分配。否则,尝试分配作废,进程等待。
-
死锁的检测与解除
如果系统中并没有预防死锁和避免死锁的措施,那么很可能会出现死锁,这时就需要利用死锁检测算法和死锁解除算法来解除死锁。
死锁的检测
资源分配图
资源分配图:由一个结点集 N N N和一个边集 E E E组成的对偶 G = ( N , E ) G=(N,E) G=(N,E),它具有下述定义:
- 将 N N N划分为两个互斥的结点集,进程结点集 P = { P 1 , P 2 , . . . , P n } P=\{P_1,P_2,...,P_n\} P={P1,P2,...,Pn}以及资源结点集 R = { R 1 , R 2 , . . . , R n } R=\{R_1,R_2,...,R_n\} R={R1,R2,...,Rn},满足 N = P ∪ R N=P\cup R N=P∪R
- 边集
E
E
E中的每一条边都连接
P
P
P中的一个结点与
R
R
R中的一个结点,
- e = { P i , R j } e=\{P_i,R_j\} e={Pi,Rj}为资源请求边,由进程结点指向资源结点,表示进程 P i P_i Pi请求一个单位的资源 R j R_j Rj
- e = { R j , P i } e=\{R_j,P_i\} e={Rj,Pi}为资源分配边,由资源结点指向进程结点,表示把一个单位的资源 R j R_j Rj分配给进程 P i P_i Pi
资源分配图如图所示:
死锁定理
死锁定理:系统处于S状态,S为死锁状态的充分条件为 当且仅当S状态的资源分配图为不可完全简化的。
- 资源分配的简化方法如下:
- 在资源分配图中,找出一个既不阻塞也非独立的进程结点 P 1 P_1 P1。如果在顺利的情况下, P i P_i Pi可获得所需资源,完成执行,则去掉 P i P_i Pi的所有请求边和分配边,使之成为一个孤立结点
- 按照步骤1,不断简化资源分配图
- 如果可以通过一系列简化,消去资源分配图中的所有边,使所有进程结点孤立,则称该图是可完全简化的。即该状态不为死锁状态
死锁中的数据结构
- 可利用资源向量 A v a i l a b l e Available Available,表示系统中每类资源的可用数目
- 把不占用资源的进程记入 L L L表中,即 L = L ∪ P i L=L\cup P_i L=L∪Pi
- 从进程集合中找到一个满足
R
e
q
u
e
s
t
i
≤
W
o
r
k
Request_i\leq Work
Requesti≤Work的进程,做以下处理:
- 将资源分配图简化,释放资源,令 W o r k = W o r k + A l l o c a t i o n i Work=Work+Allocation_i Work=Work+Allocationi
- 将该进程记入 L L L表中
- 如不能将所有的进程都记入 L L L表中,则表明此状态下资源分配图不可简化,即系统处于死锁状态。
死锁的解除
当检测出死锁时,需要采取一定措施,来解除死锁状态。常用的方法有:
定措施,来解除死锁状态。常用的方法有:
所以说,预防是最好的治疗。