详解Linux死锁概念、必要条件、预防方法---银行家算法
概述
今天主要介绍下死锁的一些原因及条件,银行家算法以及预防的方法。要想说银行家,首先得说死锁问题,因为银行家算法就是为了死锁避免提出的。那么,什么是死锁?简单的举个例子:两人吃饺子,一个人手里拿着酱油,一个人手里拿着醋,拿酱油的对拿着醋的人说:“你把醋给我,我就把酱油给你”;拿醋的对拿着酱油的人说:“不,你把酱油给我,我把醋给你。”
于是,这两个人这两份调料是永远吃不上了。这就是死锁。
01
死锁的原因和必要条件
1.死锁的概念
一般情况下,如果同一个线程先后两次调用lock,在第一次调用时,由于锁已经被占,该线程会挂起等待别的线程释放锁,然而锁正是被自己占着的,该线程又被挂起,没有机会释放锁,因此,就永远处于挂起等待状态了,这叫做死锁(Deadlock)。另种典型的死锁情形是这样:线程A获 得了锁1,线程B获得了锁2,这时线程A调用lock试图获得锁2,结果是需要挂起等待线程B释放锁2,而这时线程B也调用lock试图获得锁1,结果是需要挂起等待线程A释放锁1,于是线程A和B都永远处于挂起状态了。
死锁
所谓死锁,是指多个线程在运行过程中因争夺资源而造成的一种僵局(DeadlyEmbreace)即互相等待的现象,当线程处于这种僵持状态时,若无外力作用,它们都将无法向前推进。
2.产生死锁的原因
(1).竞争资源。当系统中供多个线程共享的资源如打印机,公用队列等,其数目不足以满足诸线程的需要的时候,会引起诸线程对资源的竞争而产生死锁。
(2).线程间推进顺序非法。线程在运行过程中,请求和释放资源的顺序不当,也同样会导致产生线程死锁。
下面详细分析产生死锁的原因:
(1)竞争资源引起死锁
1)可剥夺性和非剥夺性资源
可把系统的资源分成两类,一类是可剥夺性资源,是指某线程在获得这类资源后,该线程可以再被其它线程或系统剥夺。如优先级高的线程可以剥夺优先级低的线程的资源。又如线程间的切换。另一类是非剥夺性资源,当系统把这类资源分配给某个线程后,再不能强行收回,只能在其用完后自行释放,如磁带机、打印机等。
2).竞争非剥夺性资源
在系统所配置的非剥夺性资源,由于它们的数量不能满足诸线程的需要,会使线程在运行过程中,因争夺这些资源陷入僵局。
3).竞争临时性资源
打印机资源属于可顺序重复使用型资源,称为永久性资源。所谓的临时性资源,是指被线程使用一短暂时间后便无用的资源,故也称之为消耗性资源,它也可能引起死锁。
(2).线程推进顺序不当引起死锁
如开始所提线程A获 得了锁1,线程B获得了锁2,这时线程A调用lock试图获得锁2,结果是需要挂起等待线程B释放锁2,这时线程B也调用lock试图获得锁1,结果是需要挂起等待线程A释放锁1,于是线程A和B都将永远处于挂起状态了。
3.产生死锁的必要条件
虽然线程在运行过程中可能发生死锁,但死锁的发生也必须具备一定的条件。综上所述不难看出,死锁的发生必须具备下列四个必要条件。
(1).互斥条件:指线程对所分配的资源进行排它性使用,即在一段时间内某资源只由一个线程占用。如果此时还有其它线程请求该资源,则请求者只能等待,直至占有该资源的线程用毕释放。
(2).请求和保持条件:指线程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其它线程占有,或者已经拥有了该资源却又再次请求,此时请求线程阻塞,但又对自己已获得的资源保持不放。
(3).不剥夺条件:指线程在已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
(4).环路等待条件:指在发生死锁时,必然存在一个线程--资源的环形链,即线程集合{P0,P1,P2,P3,...Pn}中的P0正在等待P1占用的资源;P1正在等待P2占用的资源,......,Pn正在等待已被P0占用的资源。
4.处理死锁的基本方法
为保证系统中诸线程的正常运行,应事先采取必要的措施,来预防发生死锁。在系统中已经出现死锁后,则应及时检测到思索的发生,并应采取适当的措施来解除死锁。目前,处理死锁的方法可归结为以下四种:
(1).预防死锁。这是一种较简单和直观的方法。该方法是通过设置某些限制条件,去破坏产生死锁的四个必要条件中的一个或几个,来预防发生死锁。但由于所施加的限制条件往往太严格,因而会导致系统资源利用率和系统吞吐量低。
(2).避免死锁。该方法同样是属于事先预防的策略,但它并不须事先采取各种限制措施去破坏产生死锁的四个必要条件,而是在资源的动态分配过程中,用某种方法去防止系统进入不安全状态,从而避免发生死锁。这种方法只需事先施加较弱的限制条件,便可获得较高的资源利用率及系统吞吐量,但在事实上有一定的难度。目前在较完善的系统中常用此方法来避免发生死锁。
(3).检测死锁。这种方法并不须事先采取任何限制性措施,也不必检查系统是否已经进入不安全区,而是允许系统在运行过程中发生死锁。但可通过系统所设置的检测机构,及时的检测出死锁的发生,并精确地确定与死锁有关的线程和资源;然后采取适当措施,从系统中将已发生的死锁清除掉。
(4).解除死锁。这是与检测死锁相配套的一种措施。当检测到系统中已发生死锁时,须将线程从死锁状态中解脱出来。常用的实施方法是撤销或挂起一些线程,以便回收一些资源,再将这些资源分配给已处于阻塞状态的线程,是之转为就绪状态,以继续运行。死锁的检测和解除措施有可能使系统获得较好的资源利用率和吞吐量,但在实现上难度也最大。
02
预防死锁的方法
预防死锁的方法是使四个必要条件中的第2、3、4个条件之一不能成立,来避免发生死锁。至于必要条件1,因为它是由设备的固有特性所决定的,不仅不能改变,还应加以保证。
1.摒弃“请求和保持条件”
在采用这种方法时,系统规定所有线程在开始运行之前,都必须一次性的申请其在整个运行过程中所需的全部资源。
优点:简单,易于实现且安全
缺点:资源被严重浪费,线程运行被延迟
2.摒弃“不剥夺”条件
在采用这种方法时系统规定,线程是逐个提出对资源的要求的。当一个已经保持了某些资源的线程,在提出新的资源请求而不能立即得到满足时,必须释放它已经保持了的所有资源,待以后需要时再重新申请。
这种方法实现起来比较复杂且要付出很大的代价。并且有可能使进程的执行被无限的推迟,而且也增加了系统开销,降低了系统吞吐量。
3.摒弃”环路等待“条件
这种方法规定,系统将所有的资源按类型进行线性排队,并赋予不同的序号。
优点:资源利用率和系统吞吐量都有明显的改善
缺点:(1)资源的序号必须相对稳定,限制了新类型设备的增加
(2).限制用户简单自主的编程。
(3).会对资源造成浪费
补充:银行家算法
避免死锁算法中最有代表性的算法是Dijkstra E.W 于1968年提出的银行家算法:
银行家算法是避免死锁的一种重要方法,防止死锁的机构只能确保上述四个条件之一不出现,则系统就不会发生死锁。通过这个算法可以用来解决生活中的实际问题,如银行贷款等。
为啥这个算法叫银行家算法?因为这个算法可以用于银行的贷款业务。考虑下面的情况。
一个银行家共有20亿财产
第一个开发商:已贷款15亿,资金紧张还需3亿。
第二个开发商:已贷款5亿,运转良好能收回。
第三个开发商:欲贷款18亿
在这种情况下,如果你是银行家,你怎么处理这种情况?一个常规的想法就是先等着第二个开发商把钱收回来,然后手里有了5个亿,再把3个亿贷款给第一个开发商,等第一个开发商收回来18个亿,然后再把钱贷款给第三个开发商。
这里面什么值得学习呢?最重要的就是眼光放长一点,不要只看着手里有多少钱,同时要注意到别人欠自己的钱怎么能收回来。
在开头的时候我们说过的第一个例子:醋和酱油是资源,这俩吃饺子的是进程;而这个例子中:银行家是资源,开发商是进程。在操作系统中,有内存,硬盘等等资源被众多进程渴求着,那么这些资源怎么分配给他们才能避免“银行家破产”的风险?
银行家算法
程序实现思路银行家算法顾名思义是来源于银行的借贷业务,一定数量的本金要应多个客户的借贷周转,为了防止银行家资金无法周转而倒闭,对每一笔贷款,必须考察其是否能限期归还。在操作系统中研究资源分配策略时也有类似问题,系统中有限资源要供多个进程使用,必须保证得到的资源的进程能在有限的时间内归还资源,以供其他进程使用资源。如果资源分配不得到就会发生进程循环等待资源,则进程都无法继续执行下去的死锁现象。
把一个进程需要和已占有资源的情况记录在进程控制中,假定进程控制块PCB其中“状态”有就绪态、等待态和完成态。当进程在处于等待态时,表示系统不能满足该进程当前的资源申请。“资源需求总量”表示进程在整个执行过程中总共要申请的资源量。显然,每个进程的资源需求总量不能超过系统拥有的资源总数, 银行家算法进行资源分配可以避免死锁。
后面会分享更多devops和DBA方面的内容,感兴趣的朋友可以关注一下!