死锁产生的必要条件
- 互斥:每个资源要么已经分配给了一个进程,要么就是可用的。
- 占有和等待:已经得到了某个资源的进程可以在请求新的资源。
- 不可抢占:已经分配给一个进程的资源不能强制性地被抢占,它只能被占有它的进程显式地释放。
- 环路等待:有两个或者两个以上的进程组成一条环路,该环路中的每个进程都在等待下一个进程所占有的资源。
处理方法
- 鸵鸟策略
- 死锁检测与死锁恢复
- 死锁预防
- 死锁避免
鸵鸟策略
鸵鸟策略就是不采取任何措施,因为解决死锁问题的代价很高,因此这种策略可以获得更高的性能。
死锁检测和恢复
死锁检测
不试图阻止死锁,而是当检测到死锁发生时,采取措施进行恢复。
- 每种类型一个资源的死锁检测方式
- 每种类型多个资源的死锁检测方式
从死锁中恢复
通过上面的方式检测出死锁,还要对其进行恢复
通过抢占进行恢复
某些情况下,会临时将某个资源从它的持有者转移到另一个进程,使用完又送回。这种方式比较困难,并不可取。
通过回滚进行恢复
杀死进程恢复
死锁预防
在程序运行之前预防死锁
-
破坏互斥条件
-
破坏占有和等待条件
- 一种实现方式是所有进程在开始执行之前请求所需要的全部资源。
-
破幻不可抢占条件
-
破幻环路条件
-
一种方式进程在任何时候只能使用一种资源,如果需要两外一种资源必须释放当前资源。
-
给进程统一编号,进程只能按编号顺序来请求资源。
-
死锁避免
- 单个资源的银行家算法
- 多个资源的银行家算法
安全状态
如果没有发生死锁,并且即使所有进程突然请求对资源的自大需求,也仍然存在某种调度次序能够使得每个进程运行完毕,则称该状态是安全的。
银行家算法就是对每个请求进行检查,检查是否请求会引起不安全状态,如果不会引起,那么久接收该请求,如果会引起,就推迟该请求。
两阶段加锁
很多情况系死锁的避免和预防都能处理,但是效果并不好,随着时间的推移,提出了很多的算法来处理死锁
在数据库系统中,一个经常发生的操作是请求锁住一些记录,然后更新所有锁定的记录。当同时有多个进程运行的时候就会有死锁的风险。
一种解决方法就是 两阶段提交 。一个阶段是进程尝试一次锁定它需要的所有记录,如果成功才会开始第二阶段,第二阶段是执行更新并释放锁。
如果第一个阶段某个进程所需要的记录已经被加锁,那么该进程会释放所有锁定的记录并重新开始第一阶段。这种方式类似于预先请求所有需要的资源或者在进行一个不可逆操作之前请求所有资源。
通信死锁
还有一种死锁类型是通信死锁,就是两个进程在发送消息时出现的死锁。进程A给进程B发送了一条消息,然后进程A阻塞等待进程B返回响应,如果消息在网络中丢失,那么就成A会一直等着,进程B也会阻塞等待消息的到来。这时候就产生了死锁。
这个过程并不是一个资源死锁,因为进程A并没有占据B的资源。因此称为通信死锁。
通信死锁不能通过调度的方式来避免,但是可以使用网络中的一个概念来解决超时
。在通信过程中,只要一个消息发出后,发送者就会启动一个计时器,如果时间超过了规定的时间还没有收到返回的消息,就会认为消息已经丢失并重新发送。
在网络中也会产生资源死锁,我们都知道在网络中传输数据,就是一个数据包从主机进入到路由器,被放入到缓冲区中,再从路由器传输到另一个路由器最终到达目的地。假设一个路由器有10个缓冲区,路由器a的所有数据需要发送给路由器b,路由器b的所有数据需要发送给路由器c,路由器c的所有数据需要发送给路由器a,这个过程中没有数据可以移动,因为在另一端没有缓冲区可用,这就是一个典型的资源死锁。
活锁
有时候,当进程意识到无法获取到所需要的下一个锁时,就会尝试礼貌的释放已经获得的锁,然后等待非常短的时间再次尝试获取。
有一对并行的进程用到了两个资源,分别获取另一个锁失败后,两个进程就会释放自己持有的锁,再次进行尝试,这个过程一直重复。这个过程中没有进程阻塞,但是进程仍然不会向下执行。这种状况就叫做活锁。
饥饿
如果一段时间没有获得资源那么进程就会产生饥饿这些进程会永远得不到服务。
例如进程调度算法中的短作业优先调度算法,有可能长作业会一直等待出现饥饿现象。
参考文章
https://www.cyc2018.xyz/计算机基础/操作系统基础/计算机操作系统 - 死锁.html