允许多个进程并发执行共享系统资源时,系统必须提供同步机制和进程通信机制。然而,对这种机制使用不当的话,可能会出现进程永远被阻塞的现象。例如,两个进程分别等待对方占有的一个资源,于是两者都不能执行而处于永远等待,这种现象称为死锁。
死锁产生的四个必要条件:
- 互斥条件: 进程应互斥使用资源,任一时刻一个资源仅为一个进程独占
- 占有和等待条件: 一个进程请求资源得不到满足而等待时,不释放已占有的资源
- 不剥夺条件: 任一进程不能从另一进程那里抢夺资源
- 循环等待条件: 存在一个循环等待链,每一个进程分别等待它前一个进程所持有的资源
破坏第一个条件,把独占型资源改造成共享性资源,使资源可同时访问而不是互斥使用。这是一个简单的办法,但对许多资源往往是不能做到的
采用静态分配可以破坏第二个条件。所谓静态分配是指一个进程必须在执行前就申请它所要的全部资源,并且直到它所要的资源都得到满足之后才开始执行,所有并发执行的进程要求的资源总和不超过系统拥有的资源数。采用静态分配后,进程在执行中不再申请资源,因而不会出现占有了某些资源再等待另一些资源的情况。但这么做资源使用率不高。
采用剥夺式调度方法可以破坏第三个条件,但剥夺式调度方法目前只适用于对主存资源和处理器资源的分配,而不适用于所有资源。
层次分配策略将阻止第四个条件的出现,资源被分成多个层次,一个进程得到某一层的一个资源后,它只能再申请更高层的资源;当一个进程要释放某层的一个资源时,必须先释放所占用的更高层的资源; 当一个进程获得了某一层的一个资源后,它想再申请该层中的另一个资源,那么,必须先释放该层中的已占资源。
当不能防止死锁的产生时,如果能掌握并发进程中与每个进程有关的资源申请情况,仍然可以避免死锁的发生。只需在为申请者分配资源前先测试系统状态,若把资源分配给申请者会产生死锁的话,则拒绝分配,否则接收申请,为它分配资源。
银行家算法(资源分配拒绝法):每个进程pk必须预先说明所要的最大资源量Clain[k , *],每次提出部分资源的申请Request[k , *]并获得分配;如果操作系统所拥有的最大资源量满足进程对资源的最大需求量Need[k , *],那么操作系统一定会接纳进程并满足其资源需求,进程在运行后应在有限时间内将资源全部归还操作系统;操作系统在收到一个进程的资源申请时,可能会因资源不足而让进程等待,但保证在有限时间内让进程获得资源。
算法考虑的最坏的情况:所有进程同时要使用它们所声明的资源最大需求数。此时操作系统中所有进程加上新启动进程对资源Ri的需求量,不能超过系统拥有的最大资源数。
即系统若要启动一个新进程工作,其对资源Ri的需求必须满足Ri ≥ Clain[1 , i] + …+ Clain[n , i] + Clain[n+1 , i]
系统安全性:存在一个进程序列,对进程pk满足Need[k , i] ≤ Available[i] + Allocation[1 , i] + … + Allocation[n , i]
银行家算法分配步骤:
(1)若Request[i , *] ≤ Need[i , *],则进程的资源申请量超过了自身的最大需求量,出错处理
(2)若Request[i , *] ≤ Available[i , *],则进程的资源申请量超过了操作系统当前拥有的最大资源量,让其等待
(3)系统对进程pi请求的资源进行试探性分配
Allocation[i , *] = Allocation[i , *] + Request[i , *]
Available[*] = Available[*] - Request[i , *]
Need[i , *] = Need[i , *] - Request[i , *]
(4)转向安全测试算法,如果返回安全状态则承认试分配,否则抛弃试分配,进程等待,还原试分配的操作
(5)执行初始化操作,定义工作向量集合Work[i] = Available[*]。从进程集合中找到满足need[k , *] ≤ Work[*]的进程pk,找到后释放其占有的资源,Work[*] = Work[*] + Allocation[k , *],把pk从进程集合中去掉,再继续找下一个进程,直到找到安全的、绝对不会产生死锁的进程运行序列。
解决死锁问题的另一条途径是死锁检测方法,这种方法对资源的分配不加限制,但系统定时运行一个死锁检测程序,判断系统内是否已出现死锁,若检测到死锁则设法加以解除。
检测的一种方法:可设置两张表格来记录进程使用资源的情况
等待资源表记录每个被阻塞进程等待的资源,占用资源表记录每个进程占有的资源
进程申请资源时,先查该资源是否为其它进程所占用;若资源空闲,则把该资源分配给申请者且登入占用资源表;否则,则登入等待资源表
死锁检测程序定时检测这两张表,若有进程Pi等待资源R,且资源R被进程Pj占用,则说Pi和Pj具有“等待占用关系”,记为W(Pi, Pj)
死锁检测程序反复检测这两张表,可以列出所有的“等待占用关系”
如果出现W(Pi, Pj)、W(Pj, Pt)、……、W(Pm, Pn)、W(Pn, Pi)时,系统中存在一组循环等待资源的进程,也就是说出现了死锁
把两张表格中记录的进程使用和等待资源的情况用一个矩阵A来表示
死锁检测程序可用Warshall的传递闭包算法检测是否有死锁发生,算法详见离散数学:https://www.cnblogs.com/yangyuliufeng/p/10680560.html
检测出死锁后,解除系统死锁的方法主要有:
(1)结束所有进程的执行并重新启动操作系统
(2)撤销陷入死锁的所有进程,解除死锁,继续运行
(3)逐个撤销陷入死锁的进程,回收其资源并重新分派,直至死锁解除。先撤销的进程可以选择CPU消耗时间最少者、产生的输出量最少者、预计剩余执行时间最长者、分得的资源数量最少者、优先级最低者
(4)剥夺陷入死锁的进程所占用的资源,但不撤销进程,直到死锁解除
(5)根据系统保存的检查点让所有进程回退,直到足以解除死锁(要求系统建立保存检查点、回退、重启机制)