操作系统之死锁

news/2024/5/20 15:56:40 标签: 操作系统, 死锁

操作系统死锁

文章目录

      • 操作系统死锁
        • 操作系统的几种资源
          • 可重用性资源和消耗性资源
          • 可抢占性资源和不可抢占性资源
        • 死锁的起因
        • 死锁的定义、必要条件及处理方法
        • 预防死锁
          • 破坏“请求和保持”条件
          • 破坏“不可抢占”条件
          • 破坏“循环等待”条件
        • 避免死锁
          • 安全状态与不安全状态:
          • 银行家算法(避免死锁算法)
        • 死锁的检测与解除

操作系统的几种资源

可重用性资源和消耗性资源
  • 可重用性资源

    • 定义:可供用户重复使用的资源
    • 性质:
      1. 可重用性资源的单元只能同一时间只能分配给一个进程,不允许共享
      2. 进程使用可重用性资源,需要一定顺序使用1)请求资源2)使用资源3)释放资源
      3. 操作系统中每一类可重用性资源的单元数目是固定的,进程在使用资源的过程不可创建或删除重用性资源
    • 操作系统中的大多数资源都属于可重用性资源
  • 消耗性资源

    • 定义:与可重用性资源相对,消耗性资源是在进程运行过程中,由进程动态的创建和消耗的资源,又称为临时性资源
    • 性质:
      1. 在进程运行过程中,消耗性资源的单元数目是可以不断变化的
      2. 进程运行过程中,可以创造消耗性资源
      3. 进程运行过程中,进程可以消耗一定数目的消耗性资源,而不再将该资源返回给资源类
可抢占性资源和不可抢占性资源
  • 可抢占性资源
    • 定义:某进程在获得这类资源后,该资源可以随时被其他进程或系统抢占
  • 不可抢占性资源
    • 定义:与不可抢占性资源相对,某进程获得这类资源后,该资源不能被其他进程或系统抢占,只有等进程用完之后自行释放该资源。

死锁的起因

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

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

死锁的定义

如果一组进程中的每一个进程都在等待仅有该组进程中的其他进程才能引发的事件,则称该组进程是死锁的。

产生死锁的四个必要条件

产生死锁的原因有多种,但都是具备了一定的条件才形成死锁,产生死锁必须满足四个条件:

  • 互斥条件:进程对分配到的资源进行排他性使用,不共享
  • 请求和保持条件:进程在请求被其他进程所占有的资源,成阻塞状态时,保持自己所占有的资源不放
  • 不可抢占条件:进程的资源不可抢占,只能等执行完毕后自行释放
  • 循环等待条件:进程组每一个进程都在等待另一个进程所占用的资源,形成一个循环链
处理死锁的方法
  • 预防死锁:破坏必要条件
  • 避免死锁:防止进程进入不安全状态
  • 检测死锁:可通过检测出死锁,从而采取一定措施
  • 解除死锁:若检测到死锁,便采取措施解除死锁,通常是撤销一些进程

四种方法从上到下对于死锁的防范程度不断减弱,但资源利用率不断提高

预防死锁

预防死锁通常是通过破坏死锁的四个必要条件中的后三个来避免产生死锁

破坏“请求和保持”条件

可通过两个不同的协议来保证:当一个进程在请求资源时,该进程不能持有不可抢占资源。从而破坏“请求和保持”条件

  • 第一种协议
    • 内容:所有进程在运行之前,必须一次性申请其整个运行过程中需要的所有资源
    • 优点:简单易行、安全
    • 缺点:
      1. 资源被严重浪费
      2. 使进程经常出现饥饿现象
  • 第二种协议
    • 内容:允许进程在只获得运行初期所需要的资源时,便开始运行。在运行过程中逐步释放自己所占有的,且使用完毕的资源。然后再去请求下一运行阶段所需要的资源
    • 优点:提高了资源的利用率,减少发生饥饿现象的几率
破坏“不可抢占”条件

通过一种协议来破坏“不可抢占条件”

  • 协议:
    • 协议内容:当一个已经保持了某些不可抢占资源的进程,提出新的资源请求而得不到满足时,该进程必须释放它保持的所有资源,待以后需要时再申请。可以理解为这些资源被抢占了
    • 优点:破坏了不可抢占条件
    • 缺点:
      1. 方法比较复杂,实现代价大
      2. 可能造成前一阶段工作的浪费,延长了进程的周转时间,降低系统的吞吐量。
破坏“循环等待”条件

对系统的各类资源类型进行线性排序,并赋予不同的序号。

  • 协议:
    • 内容:规定每个进行必须按序号递增的顺序请求资源。如果需要多个同类资源,必须一起请求。假如某进程申请到了序号较高的资源又想申请序号较低的资源时,必须先释放序号较高的资源,再去申请序号较低的资源
    • 优点:
      1. 避免资源分配途中出现环路,从而破坏了“循环等待”条件
      2. 提高了资源利用率,系统的吞吐量
    • 缺点:
      1. 各类资源的序号必须相对稳定,从而限制了新类型设备的增加
      2. 在实际中,可能会出现某些作业使用各类资源的顺序与系统规定的顺序不同,从而造成资源的浪费。
      3. 按固定次序去申请资源,限制了用户自主得进行编程。

避免死锁

与预防死锁相比,避免死锁也属于事前预防的措施,但并不是通过破坏形成死锁的必要条件来实现,而是通过在资源动态分配过程中,防止系统进入不安全状态,来避免死锁

安全状态与不安全状态:
  • 安全状态:安全状态,指系统能按某种进程推进顺序 ( 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类资源。当进程发出资源请求后,算法执行步骤如下:

  1. ​ 如果 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;否则,认为出错,因为请求的超过它所需要的。

  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;否则,认为系统暂时满足不了该进程的需求,进程等待

  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]

  4. 执行安全性算法,判断如此分配后,系统是否处于安全状态,如果处于安全状态,则执行分配;否则,本次尝试分配作废,进程等待。

  • 安全性算法描述:

    1. 设置两个向量

      • 工作向量 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
    2. 从进程集中寻找满足下列条件的进程:

      1. F i n i s h [ i ] = F a l s e Finish[i]=False Finish[i]=False
      2. N e e d [ i , j ] ≤ W o r k [ j ] Need[i,j]\leq Work[j] Need[i,j]Work[j]

      若找到,跳至步骤3,否则执行步骤4

    3. 更新数据结构的数值。进程 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

    4. 如果所有的进程都满足 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),它具有下述定义:

  1. 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=PR
  2. 边集 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状态的资源分配图为不可完全简化的。

  • 资源分配的简化方法如下:
    1. 在资源分配图中,找出一个既不阻塞也非独立的进程结点 P 1 P_1 P1。如果在顺利的情况下, P i P_i Pi可获得所需资源,完成执行,则去掉 P i P_i Pi的所有请求边和分配边,使之成为一个孤立结点
    2. 按照步骤1,不断简化资源分配图
    3. 如果可以通过一系列简化,消去资源分配图中的所有边,使所有进程结点孤立,则称该图是可完全简化的。即该状态不为死锁状态
死锁中的数据结构
  • 可利用资源向量 A v a i l a b l e Available Available,表示系统中每类资源的可用数目
  • 把不占用资源的进程记入 L L L表中,即 L = L ∪ P i L=L\cup P_i L=LPi
  • 从进程集合中找到一个满足 R e q u e s t i ≤ W o r k Request_i\leq Work RequestiWork的进程,做以下处理:
    • 将资源分配图简化,释放资源,令 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表中,则表明此状态下资源分配图不可简化,即系统处于死锁状态。
死锁的解除

当检测出死锁时,需要采取一定措施,来解除死锁状态。常用的方法有:

  1. 抢占资源。从若干个进程中抢占足够的资源,分配给死锁进程,以解除死锁状态
  2. 终止进程。终止若干个死锁进程,使系统从死锁状态中摆脱出来。
    • 终止进程的方法有:
      • 终止所有死锁进程:进程运行周期变长
      • 逐个终止进程:执行死锁检测算法代价大,不易于实际操作。
    • 付出代价最小的死锁解除算法:从所有逐个终止进程的序列中找出代价最小的。是一种不太实际的算法。

定措施,来解除死锁状态。常用的方法有:

  1. 抢占资源。从若干个进程中抢占足够的资源,分配给死锁进程,以解除死锁状态
  2. 终止进程。终止若干个死锁进程,使系统从死锁状态中摆脱出来。
    • 终止进程的方法有:
      • 终止所有死锁进程:进程运行周期变长
      • 逐个终止进程:执行死锁检测算法代价大,不易于实际操作。
    • 付出代价最小的死锁解除算法:从所有逐个终止进程的序列中找出代价最小的。是一种不太实际的算法。

所以说,预防是最好的治疗。


http://www.niftyadmin.cn/n/1051272.html

相关文章

JAVA基础篇二(Java,C++中的面向对象)

面向对象技术中的对象就是现实世界中某个具体的实体在计算机逻辑中的映射和体现,而类则是同种对象的集合与抽象。C和Java是当今两种主流的面向对象语言,所有的Java程序都是由类或者说是类的定义组成的。在我们的学习过程中应该自始至终地牢记这一点&…

第一次作业:深入源码分析进程模型(Linux kernel 2.6.32)

1.前言 本文基于Linux 2.6.32分析其进程模型,包括进程的概念、组织、转换、调度等内容,帮助对操作系统课程及Linux相关知识的理解和学习。 附Linux Kernel 2.6.32源码下载地址:https://mirrors.edge.kernel.org/pub/linux/kernel/v2.6/linux-…

Hibernate连接池配置

Hibernate支持第三方的连接池,官方推荐的连接池是C3P0,Proxool,以及DBCP。在配置连接池时需要注意的有三点: 一、Apche的DBCP在Hibernate2中受支持,但在Hibernate3中已经不再推荐使用,官方的解释是这个连接池存在缺陷。如果你因为…

JAVA基础篇三(Java,C++中的异常机制)

由于C和JAVA有很多相似之处,又有很多细微的差别,所以在学习JAVA的过程中对两种语言进行对比学习。 1、C的异常机制 C中处理异常的过程是这样的:在执行程序发生异常,可以不在本函数中处理,而是抛出一个错误信息&#…

Leetcode 142 Linked List Cycle Ⅱ

**Leetcode 142 Linked List Cycle Ⅱ 题目描述 Given a linked list, return the node where the cycle begins. If there is no cycle, return null. To represent a cycle in the given linked list, we use an integer pos which represents the position (0-indexed) i…

Java LinkedList 和 ArrayList

Java 手册 java.util 类 ArrayList<E> java.lang.Object java.util.AbstractCollection<E> java.util.AbstractList<E> java.util.ArrayList<E>所有已实现的接口&#xff1a;Serializable, Cloneable, Iterable<E>, Collection…

JAVA基础篇四(Java,C++中的数组)

JAVA里数组的内存分配是在堆里面的&#xff0c;必须用new来分配&#xff0c;而C里面是在栈里面分配的&#xff08;除利用指针new出的数组&#xff09;&#xff0c;定义的时候会自动分配。 1、JAVA中的数组 &#xff08;1&#xff09;数组不是集合&#xff0c;它只能保存同种类型…

python中利用队列asyncio.Queue进行通讯详解

python中利用队列asyncio.Queue进行通讯详解 本文主要给大家介绍了关于python用队列asyncio.Queue通讯的相关内容&#xff0c;分享出来供大家参考学习&#xff0c;下面话不多说了&#xff0c;来一起看看详细的介绍吧。 asyncio.Queue与其它队列是一样的&#xff0c;都是先进先出…