一、死锁的定义
- 死锁(deadlock):一个进程集合中的每个进程都在等待只能由该集合中的其他进程才能引发的事件(释放已占有的资源或者进行某项操作);
- 死锁是多个进程因竞争资源且推进顺序不合理而造成的一种僵局,若无外力作用,这些进程将永远不能再向前推进。
- 若考虑进程与I/O设备交互,则可能出现单进程死锁的情况:如果一个进程发出一个I/O命令并被挂起等待结果,然后在开始I/O操作之前(那一部分要进行I/O操作的数据)被换出(移到磁盘),那么该进程被阻塞,它等待I/O事件的发生,而此时I/O操作也被阻塞,它等待该进程(要进行I/O操作的那部分)被换入。这就造成了单进程死锁。(为避免这种情况的死锁,操作系统要求进程在发出I/O请求之前,参与I/O操作的用户存储空间必须立即锁定在内存中,即使这个I/O操作正在排队,且在一段时间内不会被执行)
二、联合进程图(joint progress diagram)
一种记录进程共享资源的历史和分析死锁的工具。两个进程的联合进程图是一个二维网格
联合进程图中的死锁区域也称为敏感区域。
如上图,两个进程的联合进程图,展示了六条不同的路径(A、B是两个不同的资源):
- Q进程获得B,然后获得A,再释放B和A;当P进程恢复执行时,它可以获得全部的资源;
- Q进程获得B,然后获得A;P进程开始执行但阻塞在对A的请求上;Q进程释放B和A;当P进程恢复执行时它可以获得全部资源;
- Q进程获得B,然后P进程获得A;Q继续执行但阻塞在对A的请求上;P进行继续执行但阻塞在对B的请求上,构成死锁;
- P进程获得A,然后Q进程获得B;Q继续执行但阻塞在对A的请求上;P进程继续执行但阻塞在对B的请求上,构成死锁;
- P进程获得A,然后获得B;Q进程开始执行但阻塞在对B的请求上;P释放A和B;当Q进程恢复执行时可获得全部资源;
- P进程获得A,然后获得B,再释放A和B;当Q进程恢复执行时可以获得全部资源。
三、可重用资源
资源通常分为两类:可重用资源和可消耗资源。
- 可重用资源是指一次只能供一个进程安全使用,且不会由于使用而耗尽的资源。包括处理器、I/O通道、内存和外存、数据库和信号量之类的数据结构,等等。
- 可消耗资源是指可以被创建和销毁的资源,通常对某种类型可消耗资源的数目没有限制。例如中断、I/O缓冲区中的信息。
四、资源分配图
- 资源分配图是有向图,每个进程(圆圈)和资源(方框)用节点表示,从进程指向资源的边(申请边)表示进程请求资源但是还没有得到授权,从可重用资源指向进程的边(分配边)表示请求已被授权。
- 如果资源分配图中没有环路,则系统中没有死锁,如果资源分配图中存在环路则系统中可能存在死锁。(死锁定理)
- 如果每个资源类中只包含一个资源实例,则资源分配图存在环路是死锁存在的充分必要条件。
五、死锁的条件
处于等待/阻塞状态的进程无法自己改变自己的状态,需要其他进程来改变。
死锁的三个必要条件:
1、互斥条件:
- 指进程对所分配到的资源进行排他性使用,即在一段时间内某资源只能由一个进程占有;
- 如果此时还有其他进程请求该资源,那该请求的进程只能被阻塞直到占有该资源的进程释放资源;
2、占有且等待条件:
- 进程已经占有至少一个资源,但可以请求新的其他资源;
- 若该资源已被其他进程占有,则该请求的进程被阻塞,同时该进程已占有的资源不会释放;
3、不可抢占(preemption)条件:
- 进程已经获得的资源在未使用完之前不能被抢占,只能在使用完的时候由自己释放。
第四个条件,实际上是前三个条件的潜在结果:
4、循环等待(充分条件):
- 存在一个封闭的进程链(环路),使得每个进程至少占有此链中下一个进程所需要的一种资源。
只有三个必要条件都满足,才可能出现死锁
六、死锁预防
1、破坏互斥条件
方法:允许多个进程同时使用资源;
使用条件:资源的固有特性允许多个进程同时使用或借助特殊的技术允许多个进程同时使用(如打印机借助spooling技术)
缺点:不适用于绝大多数资源;
2、破坏占有且等待条件
方法:禁止已拥有资源的进程再申请其他资源,如要求所有进程在开始执行前一次性地申请在整个运行过程中所需要的全部资源;或申请资源时先释放其占有的资源后再一次性申请所需的全部资源。
优点:简单容易实现、安全;
缺点:进程延迟运行,资源浪费严重;
3、破坏不可抢占条件
方法:
- 一个已占有某些资源的进程在申请其他新资源但无法立即得到满足时必须释放它占有的所有资源待以后需要再重新申请;
- 操作系统可以剥夺一个进程占有的资源并分配给其他进程;
适用条件:资源的状态可以容易地保存和恢复。
缺点:实现复杂、代价大、需要反复申请/释放资源、系统开销大。
4、破坏循环等待条件
方法:对所有资源按照类型进行线性排队,进程申请资源必须严格按照资源序号递增的顺序。
缺点:虽然可以避免循环等待但是容易造成资源的浪费,且会使得进程的执行速度变慢。
七、死锁避免
不需要事先采取限制措施破坏产生死锁的必要条件,而是在资源的动态分配过程中采用某种策略,防止系统进入不安全状态,从而避免发生死锁。
两种方案:
- 如果一个进程的请求会导致死锁,则不启动此进程;
- 如果一个进程增加的资源请求会导致死锁,则不允许分配;
1、进程启动拒绝
模型:
- N个进程,M种资源;
- 系统资源总量向量R=(R1,R2, … , Rm),Rj为第j种资源的总数;
- 系统当前可用资源总量向量V=(V1,V2, … , Vm),Vj为第j种资源的剩余数量;
- 进程-资源需求(Claim)矩阵C,Cij表示进程i对资源j的请求数;
- 进程-资源分配(Allocation)矩阵A,Aij表示当前已经分配给进程i的资源j数;
系统正常状态下模型有如下性质:
- Rj=Vj +(A1j+A2j+…+Anj),对所有的j=1,2,…,m(总资源数=可用资源数+已分配资源数);
- Cij ≤ Rj ,对所有的i=1,2,…,n, j=1,2,…,m(要申请的资源数不能超过资源总数);
- Aij ≤ Cij ,对所有的i=1,2,…,n, j=1,2,…,m(已分配的资源数不能超过对这个资源的请求数);
进程启动拒绝的策略:若Rj≥Cn+1, j +(C1j+C2j+…+Cnj), j=1,2,…,m,则允许启动进程Pn+1,否则系统不启动进程Pn+1;(有足够的资源被进程Pn+1请求)。本策略不能保证资源的最优使用率,但可以保证系统现有进程不会发生死锁。
2、资源分配拒绝(银行家算法)
模型:
- 系统状态由资源总量向量R=(R1,R2, … , Rm )、系统可用资源总量向量V=(V1,V2, … , Vm )、进程-资源需求矩阵C和进程-资源分配矩阵A表示;
- 安全状态:至少存在一个执行时序(资源分配序列)使得当前所有进程都能运行到结束状态;
- 不安全状态:不存在一个执行时序可以使得当前所有进程都能运行到结束状态;
只要系统处于安全状态,就必定不会发生死锁;而不安全状态不一定是死锁状态,但不能保证不会进入死锁。
避免策略:
- 如果一个新进程的资源请求会导致不安全状态,则拒绝启动这个进程;
- 如果满足一个进程新提出的资源请求后会导致不安全状态,则拒绝分配资源给这个进程;
算法:
全局数据结构:
资源分配算法:
- 要求该进程已分配的资源数和当前请求的资源数的综合不可超过进程申请的资源总量;
- 如果进程当前请求的资源数超过该资源的可用数量则挂起进程;
- 否则分配资源给进程(但如果判断出分配后系统不存在安全状态则会恢复到未分配时的状态);
测试安全算法(银行家算法):
- 在进程集合中找到一个进程可以满足进程申请的所有资源数减去已经分配的资源数小于当前可用的资源数。
- 如果满足的话则说明可以将该进程还需要的资源都分配给该资源并让其可以执行完成然后释放其占有的所有资源以供其他进程使用。
- 如果不满足则恢复原始状态,不分配资源给该进程。
3、优点和缺点
优点:
- 比死锁预防的限制少;
- 无需死锁预防中的资源剥夺和进程重启;
缺点:
- 必须事先声明每个进程请求的最大资源;
- 考虑的进程必须是无关的,即它们的执行顺序没有任何同步要求的限制;
- 分配的资源数目必须是固定的;
- 在占有资源时进程不能退出;
八、死锁检测
- 没有任何的预先限制措施——死锁预防;
- 资源分配时不检查系统是否会进入不安全状态——死锁避免;
- 不能保证防止死锁,是否死锁取决于将来同意请求的次序;
- 系统只是检测当前是否出现死锁;
- 死锁发生时进行恢复;
检测的时机:资源申请时;周期性检测。
检测方法:
- 单个资源实例:检测资源分配图中是否存在环路;
- 多个资源实例:类似银行家算法的安全检查;
恢复:
- 连续剥夺资源直到不再存在死锁;(剥夺法)
- 把每个死锁进程回滚到前面定义的某些检查点,并且重新启动所有进程(回退法)
- 杀死所有死锁进程,或连续杀死死锁进程直到不再存在死锁;(杀死进程法)
死锁避免、检测和预防之间的区别:预防是通过限制三种死锁的必要条件的至少一个或者直接限制循环等待的发生;避免允许可能出现的必要条件发生,但是采取措施防止系统进入不安全状态,确保不会出现死锁;检测是允许资源的自由分配,采取周期性的措施来发现并处理可能存在的死锁情况。
九、哲学家就餐问题
死锁与饥饿的经典问题——5个哲学家思考+吃意大利面,每人必须用两把叉子吃,但总共只有5把叉子,要求避免死锁和饥饿,
解决方案:每次只允许四个人进餐。这是采用了死锁预防——防止循环等待出现的方案。