操作系统——死锁

资源问题

  • 重用性资源和可消耗性资源
  • 可抢占性资源和不可抢占性资源

计算机系统中的死锁

  • 竞争不可抢占性资源引起死锁
  • 竞争可消耗资源引起死锁
  • 进程推进顺序不当引起死锁

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

死锁的定义

一组阻塞的进程持有一种资源等待获取另一个进程所占有的一个资源。
操作系统——死锁_第1张图片
操作系统——死锁_第2张图片
操作系统——死锁_第3张图片
不 会 产 生 死 锁 不会产生死锁
操作系统——死锁_第4张图片
会 产 生 死 锁 , 形 成 了 一 个 环 状 的 结 构 ( 一 个 大 环 和 小 环 ) 会产生死锁,形成了一个环状的结构(一个大环和小环)
操作系统——死锁_第5张图片
有 环 状 的 资 源 分 配 图 没 有 死 锁 , 如 P 2 释 放 , P 1 获 得 资 源 R 1 , 可 释 放 P 2 , P 3 获 得 R 2 执 行 有环状的资源分配图没有死锁,如P2释放,P1获得资源R1,可释放P2,P3获得R2执行 P2P1R1P2P3R2
操作系统——死锁_第6张图片

死锁的特征

产生死锁必须同时具备下面四个必要条件,只有其中一个条件不成立,死锁就不会发生。

  • 互斥条件。进程对所分配的资源进行排他性使用。即该资源只允许一个进程使用,其他进程如果请求该资源只能等待,直到占有该资源的进程用毕释放。
  • 请求和保持条件。进程已经保持至少一个资源,但又提出了新的资源请求,而新资源已被其他进程占有,导致进程被阻塞,但对自己已获得的资源保持不放。
  • 不可抢占条件。 进程已获得的资源在未使用完之前不能被抢占,只有进程在使用完之后才能释放。
  • 循环等待条件。发生死锁时,必然存在一个进程——资源循环链,即进程集合 { P 0 , P 1 , P 2 , . . . , P n } \{P_0,P_1,P_2,...,P_n\} {P0,P1,P2,...,Pn}中的 P 0 P_0 P0正在等待一个 P 1 P_1 P1占有的资源, P 1 P_1 P1正在等待 P 2 P_2 P2占有的资源,……, P n P_n Pn正在等待已 P 0 P_0 P0占有的资源。
    操作系统——死锁_第7张图片

处理死锁方法

死锁的处理可以分为下面这四种:

  • 预防死锁。该方法是通过产生死锁的四个必要条件中的一个或者几个来预防产生死锁。
  • 避免死锁。该方法是在资源的动态分配的过程中,用某种方法预防系统进入不安全状态,从而可以避免发生死锁
  • 检测死锁。该方法允许进程发生死锁,但可以检测死锁的发生,然后通过适当的措施将进程解脱出来。
  • 解除死锁。当已经检测出死锁时,就采用相应的措施,将进程解脱出来,常用的方法是撤销一些进程,回收他们的资源。将他们分配给处于阻塞状态的进程。

上述的四种方法,从(1)到(4)对死锁的防范程度逐渐减弱,但对资源利用率逐渐提高,以及进程因资源因素而阻塞的频率下降(即并发程度提高)

死锁预防和死锁避免

预防死锁

互斥条件是非共享设备必须的,不能改变,主要是破坏产生死锁的后三个条件。

  • 占用并等待
    将条件变大,拿资源就拿全部的资源才去执行,否者不能资源去睡眠,这样就不会存在死锁。但是不同的执行过程中,需要的资源不同,导致一直占用资源但是没有使用,所以会导致系统资源的利用率低。
  • 不抢占
    直接将进程kill掉,也就将资源抢占过来了,但是手段比较的暴力,不合理。
  • 循环等待
    死锁的出现会出现一个环,打破这个环可以实现死锁的预防。如果对资源类型进行排序,进程按资源顺序进行申请,也就是资源只能往上进行申请,这样就不会形成循环的圈。但是前提是要讲资源排好序,但是资源利用还是不合理的。

破坏“请求和保持”条件

为了破坏“请求和保持”条件,可以通过以下两个不同的协议实现:

  • 第一种协议
    该协议规定,所有进程在开始运行之前,必须一次性的申请其整个运行过的所需要的全部资源,该进程在整个运行期间,便不会再提出资源请求,从而破坏了“请求”条件。系统在分配资源时,只要有一种资源不能满足进程的要求,即使其它所需的各资源都空闲也不分配给该进程,而让该进程等待,则破坏了“保持条件”。

    第一种协议简单,易行且安全,但缺点也比较明显:
    (1)资源被验证浪费,严重恶化了资源的利用率
    (2)使进程经常发生饥饿现象。可能个别资源长期被其他进程占用。

  • 第二种协议
    该协议是对第一种协议的改进,它允许一个进程只获得运行初期所需的资源后,便开始运行。进程运行过程中再逐步释放已分配给自己的、且已经用完的全部资源,再去请求新的所需资源。

    第二种协议相比于第一种协议而言,提高的资源的利用率,也减少了进程发生饥饿的几率。

破坏“不可抢占”条件

  • 当进程占有某些资源,并请求其他不能被立即分配的的资源,则释放已经占有的所有资源
  • 被抢占资源添加到资源列表中
  • 只有当它能够获得旧的资源以及它请求新的资源,进程可以得到执行

破话“循环等待”条件

对系统所有资源类型进行线性排序,并赋予不同的序号。对系统所有资源进行线性排序,每个进程必须按照序号的地址顺序来请求资源。一个进程在开始时,可以请求资源 R 1 R_1 R1的单元,以后,当且仅当 F ( R j ) > F ( R i ) F(R_j) >F(R _i) F(Rj)>F(Ri),进程才可以请求资源 R j R_j Rj的单元。如果需要多个同类资源单元,则必须一起请求。

优点:

资源利用率和系统吞吐量都有比较明显的改善。
缺点:

  • 系统中各类资源所规定的序号必须稳定,这就限制了新类型设备的增加
  • 可能会发生作业使用各类资源的顺序与系统规定的顺序不同,造成资源的浪费。
  • 为了方便用户,系统对用户在编程时所施加的限制条件应尽量少,然而这种按照规定次序申请资源的方法会限制用户简单,自主的编程

避免死锁

在资源动态分配过程中,防止系统进入不安全状态,以避免发生死锁,这种方法施加的限制条件较弱,可以获取较好的系统性能,目前常用此方法避免发生死锁。
操作系统——死锁_第8张图片

系统安全状态

把系统的状态分安全和不安全状态,安全状态可避免发生死锁,不安全状态可能进入到死锁状态。

避免死锁:确保系统进入不安全状态。

操作系统——死锁_第9张图片

安全序列:是指系统能按某种进程推进顺序 ( P 1 , P 2 , . . . , P n ) (P_1, P_2, ... ,P_n) (P1,P2,...,Pn)为每个进程 P i P_i Pi分配其所需资源,对于每个 P i P_i Pi P i P_i Pi仍然可以申请的资源数小于当前资源加上所有进程 P j P_j Pj ( j < i ) (jj<i所占用的资源,在这种情况下,进程 P i P_i Pi可以等待直到所有 P j P_j Pj释放资源。当他们完成时, P i P_i Pi可以得到所需的资源,完成给定的任务,返回分配的资源,最后终止。当 P i P_i Pi终止时, P i + 1 P_{i+1} Pi+1可得到需要的资源,如此进行。如果系统无法找到这样一个安全序列,则称系统处于不安全序列。

利用银行家算法避免死锁

最具有代表性避免死锁的算法就是Dijkstra的银行家算法。

基本思想:在每个新进程进入系统时,他必须声明在运行过程中,可能需要的每种资源类型的最大单元数目(数目不超过系统拥有的资源总量)。当系统请求一组资源时,系统必须首先在确定是否有足够的资源分配给该进程。若有,在进一步计算将这些资源分配给进程后,是否会使系统处于不安全状态。如果不会,才将资源分配给他。

1.银行家算法的数据结构
为实现银行家算法,在系统中必须要有这样四个数据结构,分别用来描述系统中可利用的资源,所有进程对资源的最大需求,系统中的资源分配,以及所有进程还需要资源的情况。

  • 可利用资源Available:这是一个含有m个元素的数组,其中每一个元素代表一类可利用的资源情况。其初始值是系统所配置的全部可用资源的数目。 如果Available[j] = k,则表示系统中现有 R j R_j Rj类资源K个。

  • 最大需求矩阵Max: 这是一个 n ∗ m n*m nm的矩阵。它定义了系统中n个进程每一个对m类资源的最大需求。如果Max[i,j] = K,则表示进程i需要 R j R_j Rj类资源的最大数目为K。

  • 分配矩阵Allocation:这也是一个n*m矩阵,它定义了系统中每一类资源当前已分配给每一类进程的资源。如果Allocation[i,j]= K ,则表示进程 i i i已分得 R j R_j Rj类资源的数目为K。

  • 需求矩阵Need:这也是n*m的矩阵,用来表示每一个进程尚需的各类资源数,如果Need[i,j]=K,则表示进程i还需要K个 R j R_j Rj类资源才能完成任务。
    三个矩阵关系:
    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]

2.银行家算法
R e q u e s t i Request_i Requesti P i P_i Pi进程的请求向量,如果 R e q u e s t i [ j ] = K Request_i[j]=K Requesti[j]=K则表示 P i P_i Pi进程需要K个 R j R_j Rj 类资源。当 P i P_i Pi发出资源请求后,系统按照下述步骤进行检查
(1)如果 R e q u e s t i [ j ] < = N e e d [ i , j ] Request_i[j]<=Need[i,j] Requesti[j]<=Need[i,j],便转向步骤(2),否则认为错误,因为它所需要的资源数已超过它所宣布的最大值。
(2)如果 R e q u e s t i [ j ] < = A v a i l a b l e [ i , j ] Request_i[j]<=Available[i,j] Requesti[j]<=Available[i,j],便转向步骤(3),否则认为错误,表示尚无足够资源, P i P_i Pi等待。
(3)系统试探把资源分配给进程 P i P_i Pi,并修改下面数据结构的值:

A v a i l a b l e p [ j ] = A v a i l a b l e [ j ] − R e q u e s t i [ j ] Availablep[j]=Available[j]−Requesti[j] Availablep[j]=Available[j]Requesti[j]
A l l o c a t i o n [ i , j ] = A l l o c a i o n [ i , j ] + R e q u e s t i [ j ] Allocation[i,j]=Allocaion[i,j]+Requesti[j] Allocation[i,j]=Allocaion[i,j]+Requesti[j]
N e e d [ i , j ] = N e e d [ i , j ] − R e q u e s t i [ j ] Need[i,j]=Need[i,j]−Requesti[j] Need[i,j]=Need[i,j]Requesti[j]
(4)系统执行安全性算法,检查此次分配是否处于安全状态,若属于安全状态,则将资源正式分配给进程 P i P_i Pi,以完成本次分配。否则将本次试探分配作废,恢复原来的资源分配状态,让进程 P i P_i Pi等待。

3.安全性算法
系统所描述的安全性算法可以描述为下:
数据结构
(1)设置两个变量:

  • 工作向量Work,它表示可以提供给进程继续运行所需要的各类的资源数目,它含有m个元素,在执行安全算法开始时,Work = Available;
  • Finish,它表示系统是否有足够的资源分配给进程,使之运行完成。开始先做Finish[i] = false.当有足够资源分配给进程时,再灵FInish[i] = true;

(2)从进程集合中找到一个满足下述条件的进程:

  • Finish[i] = true;

  • Need[i,j]<=Work.

    若找到执行步骤(3),否则执行步骤(4);

(3)当进程 P i P_i Pi获得资源后,可顺利执行,直到完成,并释放它的资源。故应执行:

Work[j] = Work[j] - Allocation[i,j];
Finish[j] =true;
go to step 2;

(4)如果所有进程的Finish[i] = true都满足,则表示系统处于安全状态;否则,系统处于不安全状态。

eg1
操作系统——死锁_第10张图片 p 2 可 以 满 足 情 况 , 执 行 后 可 返 回 其 所 占 有 的 资 源 p2可以满足情况,执行后可返回其所占有的资源 p2

操作系统——死锁_第11张图片 回 收 资 源 之 后 , 按 照 顺 序 , p 1 所 需 要 的 资 源 是 可 以 满 足 的 , 可 以 执 行 回收资源之后,按照顺序,p1所需要的资源是可以满足的,可以执行 p1
操作系统——死锁_第12张图片 p 1 执 行 后 , 对 资 源 进 行 回 收 , 剩 下 的 两 个 进 程 可 以 满 足 要 求 , 随 便 选 一 个 , 比 如 p 3 , 然 后 再 选 择 p 4 p1执行后,对资源进行回收,剩下的两个进程可以满足要求,随便选一个,比如p3,然后再选择p4 p1,便p3p4

操作系统——死锁_第13张图片
这样就已经找到了一个序列,如果按照p2-p1-p3-p4这个顺序去执行,就可以实现所以的进程都可以正常的执行并结束,其所需要的资源都可以得到满足。这个就是安全的执行序列。

eg2:
操作系统——死锁_第14张图片 如 果 一 开 始 p 1 提 出 了 一 个 101 请 求 , 执 行 之 后 如果一开始p1提出了一个101请求,执行之后 p1101
操作系统——死锁_第15张图片
此时系统所剩余的资源为011,此时不能满足任何的其他进程,会进入一个unsafe的状态。所以,一开始银行家算法是不会接受p1的101的请求的。

总结:
银行家算法的思路是判断当前的资源分配操作是否安全的,如果安全则可以执行,如果不安全就不能分配出去。

死锁检测与死锁恢复

死锁检测

基础

1.背景
死锁的检测又将条件放宽了一点。

  • 前面的死锁避免是既是不会导致死锁的现象方法,但是如果会出现不安全状态,也不会执行。
  • 这里的死锁检测允许系统进入unsafe状态,在某一个状态判断当前的系统是否出现死锁,如果是,就启动恢复机制;如果没有,就继续执行,将死锁的检测放在了系统运行中,更往后了。

2.死锁检测的大致思路

  • 允许系统进入死锁状态
  • 死锁检测算法
  • 恢复机制

3.检测原理

死锁检测中的数据结构类似于银行家算法中的数据结构:

操作系统——死锁_第16张图片

操作系统——死锁_第17张图片
将资源分配图中资源的节点简化,只留下进程。从而将资源分配图,变成进程等待图。然后再判断这个等待图是否具有环。有环就代表有可能死锁。
操作系统——死锁_第18张图片
操作系统——死锁_第19张图片
死锁检测算法,定期的执行对操作系统运行比较大,更多是起调试的作用。而已银行家算法需要提前知道进程未来所需要的资源,这个是比较难实现的,只能去预估。

例子

eg1:
操作系统——死锁_第20张图片操作系统——死锁_第21张图片
操作系统——死锁_第22张图片操作系统——死锁_第23张图片
操作系统——死锁_第24张图片
eg2:

操作系统——死锁_第25张图片操作系统——死锁_第26张图片

算法使用

操作系统——死锁_第27张图片

算法恢复

操作系统——死锁_第28张图片
都存在某种程度上的强制性和不合理性。所以死锁恢复是最后的手段。
操作系统——死锁_第29张图片

你可能感兴趣的:(操作系统)