预防死锁
不允许死锁的发生
静态策略:预防思索
破坏互斥条件
如果能把互斥使用的资源改造为允许共享使用,则系统不会进入死锁状态,比如SPOOLing技术,操作系统可以采用SPOOLing技术把独占设备在逻辑上改造为共享设备。
使用了SPOOLing技术后,在各进程看来,自己对打印机资源的使用请求立即被接受处理了,不需要再阻塞等待.
缺点
并不是所有的资源都可以改造成可共享的资源,并且为了系统安全,很多地方还必须要保护这种互斥性,因此,很多时候都无法破坏互斥条件。
破坏不可剥夺条件
方案一
当某个进程请求新的资源得不到满足时,它必须主动释放保持的所有资源,待以后需要时再申请
方案二
当某个进程需要的资源被其他进程所占有的时候,可以由操作系统协助。这种方式一般要考虑操作系统的优先级(比如:[[002-2-2-调度#剥夺调度方式 抢占式 |剥夺调度方式]])
缺点
- 实现起来复杂
- 释放已获得的资源可能会造成前一阶段的工作的失效,因此这种方法一般只适用于易保存和恢复的状态
- 反复地申请和释放资源意味着增加系统开销,降低系统吞吐量
- 若采用方法一,意味着只要暂时得不到某个资源,之前获得的那些资源都要放弃,如果一直这样,将导致进程饥饿
破坏请求和保持条件
静态分配
静态分配方法:即进程在运行前一次申请完它所需要的全部资源,在它的资源未满足前,不让它投入运行,一旦投入运行后,这些资源就一直归他所有,该进程不会再请求别的任何资源
缺点:有的资源可能使用很短的时间,因此如果整个运行期间都一直保持着所有资源,就会造成严重的资源浪费,资源利用率低.另外,该策略也有可能导致某些进程饥饿
破坏循环等待条件
可以采用顺序资源分配法,首先给系统中的资源编号,规定每个进程必须要按编号递增的顺序请求资源,同类资源(即编号相同的资源)一次申请完。
一个进程只有已占用小编号的资源的时候,才能有资格申请更大编号的资源,按此规则,已持有大编号资源的进程不可能逆向的回来申请小编号资源,从而不会产生循环等待的现象。
缺点:
- 不方便增加新的设备,因此可能需要重新分配所有的编号
- 进程实际使用资源的顺序可能和编号递增顺序不一致,可能导致资源的浪费
- 必须按规定次序申请资源,用户编程麻烦
动态策略:避免死锁
安全序列
所谓安全序列,就是如果系统按照这种顺序分配资源,则每个进程都能顺序完成,只要能找到一个安全序列,系统就是安全状态,当然,安全序列可能有多个。
不安全状态
不安全状态:如果分配了资源之后,系统中找不出任何一个安全序列,系统就进入了不安全状态。这就意味着之后可能所有进程都无法顺利执行下去。当然,如果有进程提前归还了一些资源,那系统也有可能重新回到安全状态,不过我们再分配资源之前总是要考虑到最坏的情况。
如果系统处于安全状态,就一定不会发生死锁。如果系统进入不安全状态,就可能发生死锁(处于不安全状态未必就是发生了死锁,但发生死锁时一定是在不安全状态)。
银行家算法
银行家算法的核心思想:在资源分配之前预先判断这次分配是否会导致系统进入不安全状态,以此决定是否答应资源的分配请求。如果会进入不安全状态,就暂时不答应这种请求,让该进程先阻塞等待。
银行家算法是Dijsktra为银行系统设置的,后来为了避免死锁。
此时系统是否处于安全状态?—>尝试找到一个安全序列
每一轮检查都从编号较小的进程开始检查,实际做题时可以更快速的得到安全序列。
银行家代码实现
假设系统中有n个进程,m种资源,每个进程在运行前先声明对各种资源的最大需求数,则可用一个nxm的矩阵(可用二维数组实现)表示所有进程对各种资源的最大需求数,不妨设为最大需求矩阵Max,Max[i,j]=K表示进程Pi最多需要K个资源Rj。同理,系统可以用一个nxm的分配矩阵Allocation表示对所有进程的资源分配情况,Max-Allocation=Need矩阵,表示各进程最多还需要多少各类资源
另外还要用一个长度为m的一维数组Available表示当前系统中还有多少可用资源。
当进程Pi向系统申请资源,可用一个长度为m的一维数组Request,表示本次申请的各种资源量。
可用银行家算法预判本次分配是否会导致各种系统进入不安全状态。
① 如果request[j]<=Need[i,j]便会转向②,否则认为出错
② 如果request[j]<=Available[j]便转向③,否则表示尚无足够资源,Pi必须等待
③ 系统试探着把资源分配给Pi,并修改相应的数据(并非真的分配,修改数值只是为了做预判).
Available=Available-Request;
Allocation[i,j]=Allocation[i,j]+Request[j];
Need[i,j]=Need[i,j]-Request[j];
④ 操作系统执行安全性算法,检查此次资源分配后,系统是否处于安全状态。若安全则正是分配,否则恢复相应数据,让进程阻塞等待。
银行家算法的步骤:
- 进程此次请求是否超过了之前声明的最大需求数
- 检查此时系统剩余的可用资源是否还能满足这次请求
- 试探着分配,更改各数据结构
- 用安全性算法检查此次分配是否会导致进入不安全状态
安全性算法步骤:
检查当前的剩余可用资源是否能满足某个进程的最大需求,如果可以,就把该进程加入安全序列,并把该进程持有的资源全部回收。
不断重复上述过程,看最重是否能让所有的进程都加入安全序列## 允许死锁的发生
死锁的检测
死锁检测算法:用于检测系统状态,以确定系统中是否发生了死锁
为了能对系统是否已发生了死锁进行检测,必须:
- 用某种数据结构来保存资源的请求和分配信息
- 提供一种算法,利用上述信息来检测系统是否进入死锁状态
如果系统中剩余的可用资源数足够满足进程的需求,那么这个进程暂时是不会阻塞的,可以顺利地执行下去。
如果这个进程执行结束了把资源归还系统,就可能使某些正在等待资源的进程被激活,并顺利的执行下去。
如果能够消除所有边,就称这个图是可完全简化,此时一定没有发生死锁。
如果不能消除所有边,那么此时就是发生了死锁。最终还连着边的进程就是死锁检测进程。
死锁的解除
死锁解除算法:当认定系统中已经发生了死锁,利用该算法可将系统从死锁状态中解除出来。
一旦检测出死锁,就应该立即解除死锁。
资源剥夺法
挂起(暂时放到外存上)某些死锁进程,并抢占它的资源,将这些资源分配给其他死锁进程,但是应方式被挂起的进程长时间得不到资源而饥饿。
撤销进程法
强制撤销部分、甚至全部死锁进程,并剥夺这些进程的资源。这种方式的优点是实现简单,但所付出的代价可能会很大,因为有些进程可能已经运行了很长时间,已经接近结束了,一旦终止就功亏一篑
进程回退法
让一个或多个死锁进程回退到足以避免死锁的地步。这就要求系统要记录进程的历史信息,设置还原点。
考虑方面
- 进程优先级
- 已执行多长时间
- 还要多久能完成
- 进程已经使用了多少资源
- 进程是交互式的还是批处理式的