在计算机系统中很多独占性的资源,在任一时刻它们都只能被一个进程使用。真因为如此,操作系统都授予一个进程(临时)排他地访问某一种资源的能力。
在很多应用中,需要进程排他性地访问若干资源而不是一种,当两个进程为了保护两个不同的共享资源⽽使⽤了两个互斥锁,那么这两个互斥锁应⽤不当的时候,可能会造成两个进程程都在等待对⽅释放锁,在没有外⼒的作⽤下,这些进程会⼀直相互等待,就没办法继续运⾏,这种情况就是发⽣了死锁。
1. 资源
- 可抢占资源:可以从拥有它的进程中抢占而不会产生任何副作用,例如存储器。
- 不抢占资源:是指在不引起相关的计算失败的情况下,无法把进程从占有它的进程处抢占过来。
2. 死锁简介
死锁的规范定义是:如果一个进程集合中的每个进程都在等待只能由该进程集合中的其他进程才能引发的事件,那么,该进程集合就是死锁的。
在大多数情况下,每个进程所等待的事件是释放进程集合中其他进程所占有的资源。这种死锁成为资源死锁(resource deadlock)
2.1 资源死锁的条件
互斥条件
:每个资源要分已经分配给一个进程,要么就是可用的。多个进程不能同时使用同一个资源。占有和等待条件
:已经得到了某个资源的进程可以再请求新的资源。不可抢占条件
:已经分配给一个进程的资源不能强制性地被抢占,只能被占有他的进程显式的释放。环路等待条件
:死锁发生时,系统中一定有由两个或两个以上的进程组成的一条环路,该环路上的每一个进程都在等待下一个进程释放占有的资源。
2.2 处理死锁的策略
- 忽略该问题,也许你忽略它,它也会忽略你。
- 检测死锁并恢复,让死锁发生,检测它们是否发生,一旦发生死锁,采取行动解决问题(Java 可以使用jstack 或者 jconsole)
- 仔细对资源进行分配,动态地避免死锁。
- 通过破坏引起死锁的四个必要条件之一,防止死锁的产生。
3. 鸵鸟算法
把头埋在沙子里,假装根本没发生问题。因为解决死锁问题的代价很高,因此鸵鸟策略这种不采取任务措施的方案会获得更高的性能。当发生死锁时不会对用户造成多大影响,或发生死锁的概率很低,可以采用鸵鸟策略。大多数操作系统,包括 Unix,Linux 和 Windows,处理死锁问题的办法仅仅是忽略它。
4. 死锁的检测和死锁恢复
① 每种类型一个资源的死锁检测
系统构造一张资源分配图,检测有向图是否有环。
② 每种类型多个资源的死锁检测
提供一种基于矩阵的算法来检测死锁
上图中,有三个进程四个资源,每个数据代表的含义如下:
- E 向量:资源总量
- A 向量:资源剩余量
- C 矩阵:每个进程所拥有的资源数量,每一行都代表一个进程拥有资源的数量
- R 矩阵:每个进程请求的资源数量
进程 P1 和 P2 所请求的资源都得不到满足,只有进程 P3 可以,让 P3 执行,之后释放 P3 拥有的资源,此时 A = (2 2 2 0)。P2 可以执行,执行后释放 P2 拥有的资源,A = (4 2 2 1) 。P1 也可以执行。所有进程都可以顺利执行,没有死锁。
算法总结如下:每个进程最开始时都不被标记,执行过程有可能被标记。当算法结束时,任何没有被标记的进程都是死锁进程。
- 寻找一个没有标记的进程 Pi,它所请求的资源小于等于 A。
- 如果找到了这样一个进程,那么将 C 矩阵的第 i 行向量加到 A 中,标记该进程,并转回 1。
- 如果没有这样一个进程,算法终止。
③ 从死锁中恢复
- 利用抢占条件恢复:临时将某个资源从它的当前所有者那离转移给另一个进程。
- 利用回滚恢复:周期性地对进程进行检查点检查,以后可以回滚到检查点,在该进程还没有取到一个资源之前分配给另一个进程。
- 通过杀死进程恢复:杀死一个进程来打破死锁环,如果行不通就继续杀死别的进程。
5. 避免死锁
- 安全状态和不安全状态:如果没有死锁发生,并且即使所有进程突然请求对资源的最大需求,也仍存在某种调度次序能够使得每一个进程运行完毕,则称该状态是安全的。
- 银行家算法:和上面死锁检测的矩阵类似。
- 有一个系统剩余资源的向量和一个各个进程请求资源的矩阵
- 如果有一个进程的请求资源向量小于系统剩余资源的向量,则该进程可以成功运行,并把资源还给系统
- 继续重复上一个操作,直到所有进程都标记为终止,则状态时安全的。如果有一个状态是不安全的则出现死锁。
6. 死锁预防
破坏互斥条件
- 破坏互斥使用条件,将临界资源改造为可共享使用的资源,如利用假脱机技术。
- 缺点:可行性低,很多时候无法破坏互斥条件
破坏占有并等待条件
- 方案一,所有进程在开始运行前请求所有所需的资源,如果全部可用,则一次分配
- 方案二,当一个进程请求资源时,先暂时释放当前占用的所有资源,然后一次性获取全部资源
- 缺点:实现难度大,资源利用率低
破坏不可抢占条件
- 方案一,申请的资源得不到满足时,立即释放拥有的所有资源
- 方案二,申请的资源被其他进程占用时,由操作系统协助剥夺(考虑优先级)
- 缺点︰
- 实现复杂;
- 剥夺资源可能导致部分工作失效;
- 反复申请和释放导致系统开销大;
- 可能导致饥饿
破坏环路等待条件
- 给资源编号,必须按编号从小到大的顺序申请资源
- 缺点:不方便增加新设备;会导致资源浪费;用户编程麻烦
7. 其他问题
两阶段加锁
- 第一阶段尝试对所有所需的资源加锁,第二阶段对资源进行操作然后释放锁
- 如果第一阶段有加锁不成功的资源,则释放所有锁
通信死锁
- 例如进程A给进程B通信,之后进程B给进程A回复,假设消息丢失,进程A在等待进程B回复,进程B又在等待进程A回复,产生死锁。
活锁
- 没有进程被阻塞,但也不会继续往下执行。
- 例如每个进程都需要获取两个资源,如果其中一个获取不到则会放弃已经获取的,两个进程都获取一个资源准备获取下一个,发现下一个已经被其他进程获取,所以放弃,如此反复。