目录
死锁
什么是死锁?
死锁:在并发环境下,各进程因相互等待对方手里的资源而造成各个进程都无法向前推进的现象就是死锁。
死锁产生的必要条件
-
互斥条件:只有对必须使用互斥才能访问的资源进行争抢才会导致死锁。共享资源不存在争抢,也就不会导致进程无法向前推进。
-
不可剥夺条件:进程所持有的资源不能被其他进程强行抢走,只能等待该进程自己释放。
-
请求和保持条件:进程持有了一些资源并且还在申请资源,申请的资源被其他进程所持有,那么请求资源的进程进入阻塞状态,并且对所持有的资源保持不释放。
-
循环等待条件:存在一种资源的循环等待链,即链中进程所持有的资源刚好是下一个进程想要申请的资源。
注意:发生死锁一定有循环等待链,但是有循环等待链不一定发生死锁。
如果循环等待链中所有进程申请的资源都被其他进程所持有,就会发生死锁。但如果系统中还有额外的资源没被进程持有,并且至少能满足一个进程的请求,那么这条循环等待链将被化简,不会发生死锁。
什么时候会发生死锁?
-
对系统资源的竞争。各进程对不可剥夺资源的争抢可能会造成死锁,对可剥夺资源(如CPU)的争抢是不会造成死锁的。
-
进程推进顺序法。请求和释放的资源的顺序不当,也同样会导致死锁。
-
信号量的使用不当也会造成死锁。
总之,对系统资源分配不合理就会造成死锁。
死锁的处理策略
-
预防死锁:皮怀四个必要条件中的其中一个或者几个。
-
避免死锁:用某种方法阻止系统进入不安全状态。(银行家算法)
-
死锁的检测和解除:允许死锁的发生,但是操作系统会检测是否有死锁发生,如果有,则会采取某种措施解除死锁。
-
死锁忽略:就好像死锁没有发生一样。
如果把死锁比作一场火灾,那么预防死锁就是杜绝吸烟,再森林中杜绝生火;避免死锁就是检测到煤气含量超标自动切断电源;死锁的检测和解除就是发现火灾,呼叫119。死锁忽略就相当于太阳上面发生火灾,不用管它。
由于死锁发生的概率及其小,即使发生了死锁也可以通过重启等方式解决。在一些情况下(比如我们的电脑),使用这种策略代价反而很小。死锁忽略在windows和Linux都采用这种策略。但是在卫星等系统上,必须要解决死锁。
预防死锁
①破坏互斥条件
互斥条件:只有对必须使用互斥才能访问的资源进行争抢才会导致死锁。共享资源不存在争抢,也就不会导致进程无法向前推进。
方案:如果把只能互斥访问的资源变成共享资源,使得进程之间不再争抢该资源从而预防死锁。
操作系统可以采用SPOOLing技术把互斥访问的设备改造成逻辑上的共享设备。
SPOOLing技术:当进程访问临界资源时,用一个进程将这些请求进程按申请时间先后排序,逐次访问临界资源。在临界资源的角度上,这些进程依旧是互斥访问的,但是在各个进程的角度上,他们都访问到了临界资源,那么它们不会因此而阻塞,而是可以继续向前推进。
该策略的缺点:并不是所有的资源都可以改造成共享资源,并且为了系统安全性,一些资源必须保持互斥性,所以打破互斥的方式很难实现。
②打破不可剥夺条件
不可剥夺条件:进程所持有的资源不能被其他进程强行抢走,只能等待该进程自己释放。
方案一:当某个进程请求的资源暂时不能访问时,那么该进程就主动放弃自己所持有的所有资源,等到资源满足时再次申请。
方案二:当某个进程请求的资源被其他进程所占有时,在操作系统的帮助下,强行剥夺其他进程所持有的该进程的资源。这种方式一般要考虑进程的优先级。
破坏不可剥夺条件可以从俩个角度出发,在进程请求资源得不到满足时,要么主动放弃自己所持有的资源,格欧其他进程先用,要么直接剥夺求他进程的资源(操作系统协助),让自己先用。
该策略的缺点:
-
实现比较复杂。
-
释放已获得资源很可能造成之前的工作失效,因此这种方案比较适用于易保存、易回复的资源,如CPU。
-
反复的释放和申请资源,增达开销,降低了系统的吞吐量。
-
该方案可能造成饥饿现象,有进程可能会一直得不到资源,也有进程的资源总是没执行完就被剥夺了。
③破坏请求和保持条件
请求和保持条件:进程持有了一些资源并且还在申请资源,申请的资源被其他进程所持有,那么请求资源的进程进入阻塞状态,并且对所持有的资源保持不释放。
方案:采用静态分配方法,进程之前预估所需要的资源,然后一次性申请。如果不能满足他所有资源,那么就先不执行,一旦执行起来
就不再申请资源了。
该策略的缺点:
-
有些进程与用户的交互性较强,不好预估进程执行中的资源。
-
进程所申请的资源中,有些资源暂时用不到,但是它申请之后,其他进程就不能用这些资源。
-
该策略可能会导致饥饿。
④破坏循环等待条件
循环等待条件:存在一种资源的循环等待链,即链中进程所持有的资源刚好是下一个进程想要申请的资源。
方案:采用顺序分配法,将系统资源逐次排序,从小到大,进程申请资源时,一次性递增的申请,同类的资源(即相同的资源)一次性申请完。
一个进程只有持有小编号的资源时,才有资格申请编号大的资源,同样,已经申请到编号较大资源的进程不会反过头来在申请编号较小的资源,从而破坏了循环等待条件。
该策略的缺点:
-
不方便系统新增设备,因为可能需要重新编号。
-
进程申请资源的次序可能和使用次序不一样,造成资源的浪费。
-
程序员编程麻烦。
程序可能需要在不同的平台上运行,而不同平台的资源编号可能不同,因此程序员编程较麻烦。
避免死锁
安全序列:在系统资源有限的情况下,对进程之间资源分配的次序可能会导致死锁。按照一定的次序分配资源不会造成死锁的序列就是安全序列。显然,安全序列并不唯一。
不安全状态:系统给某个进程分配资源后,找不到安全序列,那么该系统就处于不安全状态。
如果系统处于安全状态,那么系统一定不会发生死锁;如果系统处于不安全状态,那么就可能发生死锁。
系统处于不安全状态,不一定会发生死锁,但是死锁一定发生在不安全状态下。
银行家算法------避免系统进入不安全状态
银行家算法是荷兰学者 Dijkstra 为银行系统设计的,以确保银行在发放现金贷款时,不会发生不能满足所有客户需要的情况。后来该算法被用在操作系统中,用于避免死锁。
核心思想:在进程提出资源申请时,先预判此次分配是否会导致系统进入不安全状态。如果会进入不安全状态,就暂时不答应这次请求,让该进程先阻塞等待。
死锁的检查和检测
如果系统中既不采取预防死锁的措施,也不采取避免死锁的措施,系统就很可能发生死锁。在这种情况下,系统应当提供两个算法:
①死锁检测算法:用于检测系统状态,以确定系统中是否发生了死锁。
②死锁解除算法:当认定系统中已经发生了死锁,利用该算法可将系统从死锁状态中解脱出来。
死锁的检测算法
为了能对系统是否已发生了死锁进行检测,必须:
①用某种数据结构来保存资源的请求和分配信息;
②提供一种算法,利用上述信息来检测系统是否已进入死锁状态。
如果系统剩余资源数能够满足进程的需求,那么该进程就可以向前推进,等到进程执行完毕后将资源归还给系统,那么系统的剩余资源数又可以满足其他进程,那么能够满足的进程就可以被唤醒,向前推进。相应地,这个进程执行完毕后又会将资源归还给系统...
按照上述过程,最终能消除所有边,就称这个图是可完全简化的,此时一定没有发生死锁。(相当于找到了安全序列)。如果最终不能消除所有边,就代表发生了死锁。
死锁的解除
资源分配图经过死锁检测法后不是孤立的进程(没有边的进程)就是死锁,那么系统就需要解除这些死锁。
解除死锁的方法有:
-
资源剥夺法:挂起某些死锁进程,并剥夺它们的资源,将这些资源分配给其他进程先用。但是要防止这些被挂起的进程饥饿。
-
撤销进程法:(终止进程法),将撤销部分甚至全部死锁进程,剥夺它们的资源。这种方式实现简单,但是代价很大,因为有些进程可能已经执行很长时间了,撤销将功亏一篑,以后还得重头再来。
-
进程回退法:将死锁进程回退到没有发生死锁的那一步即可。这需要操作系统记录进程的历史信息,设置还原点,不易实现。