一、死锁的概念
1.定义
各进程互相等待对方手里的资源,导致各进程都阻塞,无法向前推进的现象。
2.死锁与饥饿的区分
(1)共同点
都是进程无法顺利向前推进导致的现象。
(2)不同点
- 死锁:循环等待对方手里的资源导致的死锁。至少有两个及以上进程同时发生死锁,且一定处于阻塞状态。
- 饥饿:可能只有一个进程发生饥饿。发生饥饿的进程既可能是阻塞态(长期得不到I/O设备),也可能是就绪态(长期得不到CPU)。
3.产生死锁的必要条件(四个条件同时成立)
-
(1)互斥条件。
- 只有对必须互斥使用的资源的争抢才会导致死锁。
-
(2)请求和保持条件。
- 进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其他进程占有,
- 此时请求进程被阻塞,但又对自己已有的资源保持不放。
-
(3)循环等待条件。
- 存在一种进程资源的循环等待链,链中的每一个进程已获得的资源同时被下一个进程所请求。
- 注意:发生死锁时一定有循环等待,但是发生循环等待时未必死锁。
-
(4)不剥夺条件。
- 进程所获得的资源在未使用完之前,不能由其他进程强行夺走,只能主动释放。
二、死锁的预防
1.定义
死锁的处理就是不允许死锁的发生,分为静态策略(预防死锁)和动态策略(避免死锁)。
2.静态策略(预防死锁)
-
(1)预防死锁。
- 破坏死锁产生的四个必要条件中的一个或几个。
-
(2)避免死锁。
- 用某种方法防止系统进入不安全状态,从而避免死锁(银行家算法)
-
(3)死锁的检测和解除。
- 允许死锁的发生,不过操作系统会负责检测出死锁的发生,然后采取某种措施解除死锁。
3.动态策略(避免死锁)
(1)破坏互斥条件
如果把只能互斥使用的资源改造为允许共享使用,则系统不会进入死锁状态。
- 比如:
SPOOLing 技术,操作系统可以采用SPOOLing技术把独占设备在逻辑上改造成共享设备。
- 缺点:
并不是所有的资源都可以改造成可共享使用的资源,并且为了系统安全,很多地方还必须
保护这种互斥性,因此很多时候都无法破坏互斥条件。
(2)破坏不剥夺条件
当某个进程请求新的资源得不到满足时,它必须立即释放保持的所有资源,待以后需要时再重新申请。也就是说,即使某些资源尚未使用完,也需要主动释放,从而破坏了不可剥夺条件。
缺点:
- 实现起来比较复杂。
- 释放已获得的资源可能造成前一阶段工作的失效。因此这种方法一般只适用于易保存和恢复状态的资源。
- 反复地申请和释放资源会增加系统开销,降低系统吞吐量。
- 这种破坏意味着只要暂时得不到某个资源,之前获得的那些资源都需要放弃,以后再重新申请。
- 如果一直发生这样的情况,就会导致进程饥饿。
(3)破坏请求和保持条件
可以采用静态分配的方法,即进程在运行前一次申请完它所需要的全部资源,在它的资源未满足前,不让它投入运行。一旦投入运行后,这些资源就一直归它所有,该进程就不会再请求别的任何资源了。
该策略实现起来简单,但也有明显的缺点:有些资源可能只需要用很短的时间,因此如果进程的整个运行期间都一直保持着所有资源,就会造成严重的资源浪费,资源利用率极低。另外,该策略也有可能导致某些进程饥饿。
(4)破坏循环等待条件
方法:
对系统的所有资源类型进行线性排序(顺序资源分配法)。首先给系统中的资源编号,规定每个进程必须按编号递增的顺序请求资源,同类资源(即编号相同的资源)一次申请完。
原理分析:
一个进程只有已占有小编号的资源时,才有资格申请更大编号的资源。
缺点:
- 为系统中各类资源规定的序号必须相对稳定,这限制了新类型设备的增加。
- 尽管在为资源的类型分配序号时,已经考虑到了大多数作业在实际使用这些资源时的顺序, 但也经常会发生作业使用各类资源的顺序与系统规定的顺序不同的情况,造成对资源的浪费。
- 为了方便用户,系统对用户在编程时所施加的限制条件应尽量少,然而这种按规定次序申请资源的方法必然会限制用户进行简单,自主的编程。
三、死锁的避免
1.定义
避免死锁同样属于事先预防策略,是在分配过程中,防止系统进入不安全状态,以避免发生死锁。
2.方法
(1)系统安全状态
在避免死锁方法中,把系统的状态分为安全状态和不安全状态。当系统处于安全状态时可避免发生死锁,而当系统处在不安全状态时,则可能会进入死锁状态。
(2)安全序列
所谓安全序列,就是指如果系统按照这种序列分配资源,则每个进程都能顺利完成。只要能找出一个安全序列,系统就是安全状态。当然,安全序列可能有多个。
(3)由安全状态进入不安全状态
如果分配了资源之后,系统中找不出任何一个安全序列,系统就进入了不安全状态。这就意味着之后可能所有进程都无法顺利的执行下去。
如果系统处于安全状态,就一定不会发生死锁。如果系统进入不安全状态,可能会发生死锁。 (不安全状态未必就是发生了死锁,但发生死锁时一定是在不安全状态) 。
因此可以在资源分配之前预先判断这次分配是否会导致系统进入不安全状态,以此决定是否答应资源分配请求。这也是“银行家算法”的核心思想。
(4)银行家算法
银行家算法是荷兰学者Dijikstra为银行系统设计的,以确保银行在发放现金贷款时,不会发生不能满足所有客户需要的情况。后来该算法被用在操作系统中,用于避免死锁。
核心思想:
在进程提出资源申请时,先预判此次分配是否会导致系统进入不安全状态。如果会进入不安全状态,就暂时不答应这次请求,让该进程先阻塞等待。
①数据结构:
- 可利用资源向量Available。长度为m的一维数组,Available表示还有多少可用资源。
- 最大需求矩阵Max。表示各进程对资源的最大需求数,nXm矩阵。
- 分配矩阵Alocation。表示已经给各进程分配了多少资源,n×m矩阵。
- 需求矩阵Need。矩阵表示各进程最多还需要多少资源,Max一Allocation=Need。
- 进程P的请求向量。用长度为m的一位数组,表示进程此次申请的各种资源数。
②算法步骤:
步骤一:如果ReguestiUj≤Need [i.j便转向步骤2;否则认为出错,因为它所需要的资源数已超过它所宣布的最大值。
步骤二:如果RequestiLj≤Available[j,便转向步骤3;否则,表示尚无足够资源,Pi须等待。
步骤三:系统试探着把资源分配给进程Pi,并修改下面数据结构中的数值
- AvailableU- AvailableUl-RequestiUl
- Allocation (ij- Allocation ij+ RequestiU]
- Need[ij]= Need [ij1-Requesti Ul
步骤四:系统执行安全性算法,检查此次资源分配后系统是否处于安全状态。若安全,才正式将资源分配给进程Pi,以完成本次分配;否则,将本次的试探分配作废,恢复原来的资源分配状态,让进程Pi等待。
③安全性算法
系统所执行的安全性算法可描述如下:
1设置两个向量:
a.工作向量Work,它表示系统可提供给进程继续运行所需的各类资源数目,它
含有m个元素,在执行安全算法开始时,Work=Available;
b.Finish:它表示系统是否有足够的资源分配给进程,使之运行完成。开始时先
做Finish[i= false;当有足够资源分配给进程时,再令Finish[i=true。
总结
本篇对死锁进行了讲解。部分内容源自网络,如有侵权请联系作者删除,谢谢!