1.死锁的条件
死锁有三个必要条件:
1) 互斥——一次只有一个进程可以使用一个资源。其他进程不可访问已分配给其他进程的资源。
2) 占有且等待——当一个进程等待其他进程时,继续占有已经分配给自己的资源不释放。
3) 不可抢占——进程不能强行抢占别的进程所占有的资源。
这三个条件都是死锁存在的必要条件,但不是充分条件,对死锁的产生还需要第四个条件。
4) 循环等待——每个进程只是占有部分资源,而不能继续执行下去。等待其他进程释放资源。
2.死锁预防
因为死锁的四个条件要全部满足才会发生死锁,所以只要避免其中的一个就可以预防死锁的发生。
1) 互斥
一般来说互斥不可能禁止,禁止互斥很容易发生错误。
2) 占有且等待
可以让进程一次性请求所有需要的资源,并且阻塞这个进程直到所有请求都同时得到满足。这个方法很低效,其一,要满足所有请求的资源,进程可能被阻塞很长的时间;其二,分配给进程的资源可能相当长时间都不会使用。(这两点主要的问题在于进程只需要部分资源就可以执行。)另外还有一个问题,进程事先未必知道它所需要的所有资源。
3) 不可抢占
有两种方法可以预防这个条件。首先,如果占有资源的进程进行进一步资源请求被拒绝,则该进程释放它所占有的资源,当然可以重新请求这些以及另外的资源。另一种方法是,如果进程请求的资源被另一个进程所占有,那么操作系统可以抢占另一个进程,要求其释放资源。只有任意两个进程的优先级都不同的条件下,后一种方案才能实现。
只有在资源状态可以很容易地保存和恢复(回滚机制)的情况下,这两种方法才是现实的。
4) 循环等待
可以定义资源类型的线性顺序来预防。如果一个进程已经分配了R类型的资源那么该进程只能申请R类型之后的资源。即这种请求方式只能向下请求。
和占有且等待的预防方法一样,这种方法低效。这会使得进程执行速度变慢,且可能在没有必要的情况下拒绝资源访问。
3.死锁避免
与死锁预防不同的是,死锁避免允许三个必要条件,但通过某种选择,确保永远不会到达死锁点。在死锁避免中,是否允许当前资源分配请求是通过判定是否可能导致死锁来决定的。因此,死锁避免需要知道将来进程资源请求的情况。
死锁避免有两种方法:
1) 进程启动拒绝
如果一个进程的请求会导致死锁,则不启动此进程。这个方法有一些条件的规划方程,也就是说只有已启动进程请求的最大资源数加上新进程的最大请求资源数总和小于系统拥有资源数才启动进程。这个方式很难有效率,因为它假设最坏的情况。太保守了!
2) 资源分配拒绝
如果一个进程增加的资源请求会导致死锁,则不允许此分配。资源分配拒绝策略使用的是银行家算法。
死锁避免的优点是它不需要死锁预防中的抢占与回滚机制,并且比死锁预防的限制少。但是它在使用中也有许多限制:
1) 必须事先声明每个进程请求的最大资源。(最坏情况下考虑,低效。)
2) 考虑得进程必须是无关的,也就是说,它们执行的顺序必须没有任何同步要求的限制。(这样各进程不能协作,很少情况才能出现。)
3) 分配资源的数目必须是固定的。(进程对资源的需求可能会变化。)
4) 在占有资源时,进程不能退出。
4.死锁检测
对于死锁检测来说,只要有可能,被请求的资源就被授权给进程。操作系统周期性地执行一个算法检测前面的条件4(循环等待)。这种方法是最开放的,所以效率较高。
死锁检测运用的也是银行家算法。
4.1恢复
一旦检测到死锁,就需要某种策略以恢复死锁。下面按复杂度递增的顺序列出可能的方法:
1) 取消每个死锁的进程。不管怎么样,这是操作胸中最常采用的方法。
2) 把每个死锁的进程回滚到前面定义的某些检测点。
3) 连续取消死锁进程直到不再存在死锁。
4) 连续抢占资源直到不再存在死锁。
3与4都需要一种基于代价的选择方法。
5.综合的死锁策略
死锁策略各有优缺点,所以面对不同的情况应当采用不同的策略。有一种方法的思想是:
l 把资源分成几种不同的资源类。
l 为预防在资源类之间犹豫循环等待产生死锁,可使用线性排序策略。
l 在各个资源类中,使用该类资源最合适的算法。
例子,按以下次序划分资源类,这个次序考虑进程的生命周期,分配是最合理的。
l 可交换空间:外存中的存储块。
l 进程资源:可分配的设备,如磁带设备和文件。
l 内存:可以按页或段分配给进程。
l 内部资源:I/O通道
在各类资源中采用不同的策略:
l 可交换空间:通过一次性分配所有资源来预防死锁。如果知道最大存储需求,这个策略就是合理的。
l 进程资源:对于这类资源,死锁避免是邮箱的,这是因为进程可以事先声明它们需要的这类资源。采用资源排序的预防策略也是可能的。
l 内存:对于内存,基于抢占的预防是最合适的。因为内存被抢占仅仅是一个“交换”技术。
l 内部资源:可以基于资源排序的策略。因为这些资源较少。
最后说明一下,死锁的经典问题就是哲学家就餐问题。