zoukankan      html  css  js  c++  java
  • 预防死锁,检测死锁,避免死锁,解除死锁....

    什么是死锁

    在并发环境下,各个进程因竞争资源而造成的一种互相等待对方手里的资源,导致各个进程都阻塞,都无法向前推进的现象就是死锁。发生死锁后如果没有外力干涉就会导致任务无法向前推进。

    进程死锁、饥饿、死循环的区别

    死锁:各个进程互相等待对方手里的资源,导致个进程都阻塞,无法向前推进的现象。
    饥饿:由于长期得不到想要的资源,某进程无法向前推进的现象。比如在短进程优先算法中,如果有源源不断的短进程进入就绪队列的话,那么长进程就无法获得处理机,从而发生饥饿的现象。(孩儿们(相当于进程)都饿得吃不到奶(CPU))
    死循环:某个进程执行的过程中一直跳不出循环的现象。(这个大家应该都知道)

    三者的共同点:都是进程无法顺利向前推进的现象
    区别

    • 死锁一定是“循环等待对方资源导致的”,如果有死锁现象的发生,那么至少会有两个或两个以上的进程同时发生死锁。另外,发生死锁的进程一定处于阻塞态

    • 饥饿可能只有一个进程发生饥饿。发生饥饿的进程可能是阻塞态也有可能是就绪态。

    • 死循环的进程可以上处理机运行也就是说可以是运行态,只不过无法像期待的那样顺利执行。死锁和饥饿的问题是由操作系统分配资源策略不合理造成的,而死循环是代码逻辑导致的。

    • 死锁和饥饿是管理者(操作系统)的问题,死循环是被管理者的问题。

    死锁产生的必要条件

    产生死锁必须同时满足以下四个条件,只要其中任意一个条件不成立,死锁就不会发生。

    1. 互斥条件:只有对必须互斥使用的资源争夺才会造成死锁(例如哲学家问题,打印机设备)象内存、扬声器这样可以同时让多个进程使用的资源时不会导致死锁的(因为进程不用阻塞等待这种资源)。
    2. 不剥夺条件:进程获得的资源在未使用的时候不能被其他进程抢走,只能自己主动释放。
    3. 请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其他进程占有,此时请求资源的进程被阻塞,但是又对自己已有的资源保持不放。也就是说,自己想要其他人的资源,别人不给,自己也不会把现在拥有的资源主动给别人。
    4. 循环等待条件:存在一种进程资源的循环等待链,链子中的每一个进程已获得资源的同时被下一个进程所请求。

    如果发生了死锁,这时一定会有循环等待链,但是出现了循环等待的现象不一定是死锁状态(循环等待是死锁的必要不充分条件)

    如果同类资源数大于1 则即使有循环等待,也未必发生死锁。但如果每类资源都只有一个,那么循环等待的就是死锁的充分必要条件。

    何时才会发生死锁

    1. 对系统资源的竞争。各进程对不可剥夺的资源(如打印机)的竞争可能引起死锁,对可剥夺的资源的竞争是不会引起死锁的。
    2. 进程执行的顺序是不对的。例如:并发执行的进程P1和P2分别申请并占有资源R1和R2,之后进程P1又紧接着申请资源R2,而进程P2又申请R1两者会应为申请资源被对方占有而阻塞,从而发生死锁。
    3. 信号量的使用不当会发生死锁。例如消费者生产者问题,互斥信号量操作如果在同步信号量之前的话就会照成两个进程的死锁。

    发生死锁该怎样处理

    一、预防死锁。破坏死锁产生的四个条件中的任何一个或几个
    二、避免死锁。用某种方法防止系统进入不安全状态,从而避免死锁(银行家算法)
    三、死锁的检查和解除。允许死锁的发生,不过操作系统会负责检测出死锁的发生,然后才去某种措施解除死锁。

    回顾:死锁的产生必须满足四个必要条件,只要其中一个或者几个条件不满足,死锁就不会发生。

    静态策略

    预防死锁

    • 破坏互斥条件
    • 破坏不剥夺条件
    • 破坏请求和保持条件
    • 破坏循环等待条件

    破坏互斥条件

    如果把只能互斥访问的资源改造成共享的资源,那么就不会发生死锁。比如SPOOLing技术。
    举个例子,就比如说两个进程使用打印机的资源,在一个进程还没执行完的时候,另外一个进程是不能获取打印机的资源,只能阻塞。这就是互斥性,但是引入SPooling技术后,这时会出现第三个进程,叫做输入进程,进程1和进程2都将自己对打印机的请求交给输入进程,输入进程来决定什么时候让进程获取打印机资源,所以对于两个进程来说,自己的请求能够被立即接收并处理,不需要再阻塞等待。

    缺点:并不是所有的资源都可以改造成可共享资源。并且为了系统安全,很多地方必须保护这种互斥性。因此很多时候无法破坏互斥条件。

    破坏不剥夺条件

    不剥夺条件:也就是说进程在未使用完资源之前,不能由其它进程强行夺走,只能主动释放。

    如果破坏了不剥夺条件,
    方案一:即使某些资源尚未使用完,也必须主动释放,从而破坏了不可剥夺的条件。
    方案二:当某个进程需要的资源被其它进程占有的时候,可以由操作系统协助将想要的资源强行剥夺。这种方式需要考虑进程的优先级剥夺方式。

    缺点:

    1. 实现起来比较复杂
    2. 释放自己已经拥有的资源会造成前段时间的工作内容丢失。因此这种方法只适用于易保存,易恢复的资源(CPU)
    3. 反复的申请和释放资源会造成额外的系统开销,降低系统的吞吐量。
    4. 若采用方案一,意味着只要暂时得不到某个资源,之前获得的那些资源就需要全部放弃,以后再重新获取,如果一直发生这种情况就会导致进程饥饿。

    破坏请求和保持条件

    请求保持条件:进程持有至少一个资源,但是又请求新的资源,新的资源又被其他进程占有,此时请求进程被阻塞,但是又对自己已经持有的资源不放。

    可以采用静态分配的方法。当进程在运行前一次申请完他所需要的全部资源,在他的资源未满足前,不让他投入运行。一旦运行这些资源就一直由它持有。这样该进程就不会再请求其他资源(因为一次性全要了)

    该策略实现起来简单,但也有缺点:
    有些进程只需要某个资源很短的使用时间,但是如果一个进程在运行期间一直保持所有的资源不放,就会造成严重的资源浪费,资源利用率低下,另外也会导致某些进程饥饿的现象。

    破坏循环等待的条件

    循环资源等待条件:存在一种进程资源的循环等待链,链中的每个进程获得的资源同时被下一个进程所请求。

    破坏方法:给资源编号,必须按照编号从小到大的顺序申请资源
    缺点:不方便增加新设备;如果加了新设备,编号可能需要重新编;会导致资源浪费;用户编程麻烦。

    动态策略

    避免死锁

    什么是安全序列

    所谓安全序列:就是指如果系统按照这种序列分配资源,则每个进程都能够顺利完成,只要能找出一个安全序列,系统处于安全状态。当然,安全序列可能有多个。

    如果分配了资源之后,系统中找不出任何安全序列,系统就进入了不安全状态。这往往意味着之后所有进程都无法顺利执行下去。当然,如果有进程提前归还了一些资源,那系统也有可能重新回到安全状态,不过我们在分配资源之前总是要考虑到最坏的情况。

    什么是系统的不安全状态,与死锁有何关系

    对于一个系统找不到安全序列那他就是不安全的状态。
    如果系统处于安全状态,就一定不会发生死锁。如果系统进入了不安全状态。就可能发生死锁。
    因此可以在资源分配之前先判断这次分配是否会导致系统进入不安全状态,以此来决定是否答应资源分配请求。这就是银行家算法的核心思想。

    如何避免进入不安全状态----银行家算法

    银行家算法是荷兰学者 Dijkstra为银行系统设计的,以确保银行在发放现金贷款时,不会发生不能满足所有客户需要的情况。后来该算法被用在操作系统中,用于避免死锁核心思想:在进程提出资源申请时,先预判此次分配是否会导致系统进入不安全状态。如果会进入不安全状态,就暂时不答应这次请求,让该进程先阻塞等待。

    问题:
    你是一位成功的银行家,手里掌握着100个亿的资金有三个企业想找你贷款,分别是企业B、企业A、企业T,为描述方便,简称BAT。

    B表示:“大哥,我最多会跟你借70亿.” A表示:“大哥,我最多会跟你借40亿….” T表示:“大哥,我最多会跟你借50亿.” 然而…江湖中有个不成文的规矩:如果你借给企业的钱总数达不到企业提出的最大要求,那么不管你之前给企业借了多少钱,那些钱都拿不回来了 T_T

    用到的数据结构:
    长度为m的一维数组 Available,表示还有多少可用资源;
    n*m矩阵Max表示各进程对资源的最大需求数;

    Max- Allocation=Need矩阵表示各进程最多还需要多少资源
    用长度为m的一维数组 Request表示进程此次申请的各种资源数

    银行家算法步骤:
    ①检查此次申请是否超过了之前声明的最大需求数
    ②检査此时系统剩余的可用资源是否还能满足这次请求
    ③试探着分配,更改数据结构
    ④用安全性算法检查此次分配是否会导致系统进入不安全状态;

    安全性算法步骤:

    • 检査当前的剩余可用资源是否能满足某个进程的最大需求,如果可以,就把该进程加入安全序列,并把该进程持有的资源全部回收不断重复上述过程,看最终是否能让所有进程都加入安全序列。

    死锁的检测和解除

    如果系统中既不采用预防死锁也不采用避免死锁的措施,系统就很有可能发生死锁。这种情况下,系统应当提供两个算法。

    1. 死锁检测算法:用于检测系统状态,已确定系统中是否发生了死锁
    2. 死锁解除算法:当使用这种算法的时候该算法可将系统从死锁中解脱出来

    死锁的检测

    为了能对死锁进行检测

    1. 用某种数据结构来保存资源的请求和分配信息
    2. 提供一种算法,利用上述信息来检测系统是否进入死锁状态

    数据结构:资源分配图

    • 两种结点:进程结点和资源结点(资源结点可能有多个)
    • 两种边:
      • 进程结点—>资源结点他的含义就是一个进程结点请求一类资源
      • 资源结点---->进程结点他的含义是这类资源已经分配给了指向的进程结点
        在这里插入图片描述

    根据这个图怎样判断出系统是不是已经死锁呢?
    结论:最终能够消除所有边的图就代表这个系统是安全的,不会出现死锁

    死锁定理:如果某时刻系统的资源分配图是不可简化的,那么此时系统死锁。
    来看一个死锁的图
    在这里插入图片描述

    资源R2有两个资源一个给了P3一个给了P2,这时P1请求R2两个资源,因为R2这时没有资源了,所以P1阻塞;P2去请求R1资源,但是R1三个资源都已经分配出去了所以P2阻塞;P3使用完资源后又放回R2,但是P1请求资源R2的数量是2,现在空闲的R2只有一个所以还是阻塞状态,当所有的进程都进行不下去的时候,系统就处于死锁状态。

    死锁的解除

    一旦检测出死锁的发生就应该立即解除死锁。
    补充:并不是系统中所有的进程都是死锁状态,用死锁检测算法分配图后,还连着边的进程就是死锁进程。

    解除死锁的主要方法:

    • 资源剥夺法。将某些死锁进程锁占有的资源剥夺并分配给其他进程。但是应该防止被挂起的进程长时间得不到资源饿死。
    • 撤销进程法。强制撤销部分死锁进程或者全部死锁进程,简单粗暴但是付出的代价较大。因为有些进程已经快运行完了,你把人家撤销从头开始,比较残忍。
    • 进程回退法。让一个或多个死锁进程回退到足以避免死锁的地步。这个就要求记录进程的历史消息,设置还原点。

    这篇的篇幅的确有点太长了,希望各位谅解,能认真读完的都是好汉![赞]
    以上有理解不当的地方敬请指出,感激不尽!

  • 相关阅读:
    编写更好的API
    C,C++语法基础 | 字符串 | 05
    C,C++语法基础 | 变量,表达式与顺序语句 | 01
    业务数据分析 | 深入浅出数据分析入门 | 01
    linux全套 | 目录 | 00
    linux全套 | 组管理和权限管理 | 08
    linux全套 | crond任务调度 | 09
    linux全套 | linux磁盘分区,挂载 | 10
    linux全套 | 网络配置 | 11
    linux全套 | 进程管理 | 12
  • 原文地址:https://www.cnblogs.com/dataoblogs/p/14121947.html
Copyright © 2011-2022 走看看