第六章 死锁
6.1 资源
资源就是随着时间的推移, 必须能获得、 使用以及释放的任何东西。
6.1.1 可抢占资源和不可抢占资源
资源分为两类: 可抢占的和不可抢占的。 可抢占资源(preemptable resource) 可以从拥有它的进程中抢占而不会产生任何副作用, 存储器就是一类可抢占的资源。
不可抢占资源(nonpreemptable resource) 是指在不引起相关的计算失败的情况下, 无法把它从占有它的进程处抢占过来。
总的来说, 死锁和不可抢占资源有关, 有关可抢占资源的潜在死锁通常可以通过在进程之间重新分配资源而化解。 所以, 我们的重点放在不可抢占资源上。
使用一个资源所需要的事件顺序可以用抽象的形式表示如下:
- 请求资源。
- 使用资源。
- 释放资源。
6.2 死锁简介
如果一个进程集合中的每个进程都在等待只能由该进程集合中的其他进程才能引发的事件, 那么, 该进程集合就是死锁的。
6.2.1 资源死锁的条件
死锁的四个必要条件:
- 互斥条件。 每个资源要么已经分配给了一个进程, 要么就是可用的。
- 占有和等待条件。 已经得到了某个资源的进程可以再请求新的资源。
- 不可抢占条件。 已经分配给一个进程的资源不能强制性地被抢占, 它只能被占有它的进程显式地释放。
- 环路等待条件。 死锁发生时, 系统中一定有由两个或两个以上的进程组成的一条环路, 该环路中的每个进程都在等待着下一个进程所占有的资源。
6.2.2 死锁建模
有四种处理死锁的策略:
- 忽略该问题。 也许如果你忽略它, 它也会忽略你。
- 检测死锁并恢复。 让死锁发生, 检测它们是否发生, 一旦发生死锁, 采取行动解决问题。
- 仔细对资源进行分配, 动态地避免死锁。
- 通过破坏引起死锁的四个必要条件之一, 防止死锁的产生。
6.3 鸵鸟算法
最简单的解决方法是鸵鸟算法: 把头埋到沙子里, 假装根本没有问题发生 。
6.4 死锁检测和死锁恢复
第二种技术是死锁检测和恢复。 在使用这种技术时, 系统并不试图阻止死锁的产生, 而是允许死锁发生, 当检测到死锁发生后, 采取措施进行恢复。 本节我们将考察检测死锁的几种方法以及恢复死锁的几种方法。
6.4.1 每种类型一个资源的死锁检测
这一算法是依次将每一个节点作为一棵树的根节点, 并进行深度优先搜索。 如果再次碰到已经遇到过的节点, 那么就算找到了一个环。 如果从任何给定的节点出发的弧都被穷举了, 那么就回溯到前面的节点。 如果回溯到根并且不能再深入下去, 那么从当前节点出发的子图中就不包含任何环。 如果所有的节点都是如此, 那么整个图就不存在环, 也就是说系统不存在死锁。
6.4.2 每种类型多个资源的死锁检测
如果有多种相同的资源存在, 就需要采用另一种方法来检测死锁。 现在我们提供一种基于矩阵的算法来检测从P1 到Pn 这n个进程中的死锁。 假设资源的类型数为m, E1 代表资源类型1, E2 代表资源类型2, Ei 代表资源类型i(1≤i≤m)。 E是现有资源向量(existing resource vector) , 代表每种已存在的资源总数。 比如, 如果资源类型1代表磁带机, 那么E1 =2就表示系统有两台磁带机。
在任意时刻, 某些资源已被分配所以不可用。 假设A是可用资源向量(available resource vector) , 那么Ai 表示当前可供使用的资源数(即没有被分配的资源) 。 如果仅有的两台磁带机都已经分配出去了, 那么A1 的值为0。
现在我们需要两个数组: C代表当前分配矩阵(current allocation matrix) , R代表请求矩阵(request matrix) 。 C的第i行代表Pi 当前所持有的每一种类型资源的资源数。 所以, Cij 代表进程i所持有的资源j的数量。 同理, Rij 代表Pi 所需要的资源j的数量。 这四种数据结构如图6-6所示。
这四种数据结构之间有一个重要的恒等式。 具体地说, 某种资源要么已分配要么可用。 这个结论意味着:
换言之, 如果我们将所有已分配的资源j的数量加起来再和所有可供使用的资源数相加, 结果就是该类资源的资源总数。
第1个不能被满足, 因为没有CDROM驱动器可供使用。
第2个也不能被满足, 由于没有打印机空闲。 幸运的是, 第3个可被满足, 所以进程3运行并最终释放它所拥有的资源, 给出
A=(2 2 2 0)
接下来, 进程2也可运行并释放它所拥有的资源, 给出
A=(4 2 2 1)
现在剩下的进程都能够运行, 所以这个系统中不存在死锁。
6.4.3 从死锁中恢复
利用抢占恢复
在某些情况下, 可能会临时将某个资源从它的当前所有者那里转移到另一个进程。 许多情况下, 尤其是对运行在大型主机上的批处理操作系统来说, 需要人工进行干预。
利用回滚恢复
一旦检测到死锁, 就很容易发现需要哪些资源。 为了进行恢复, 要从一个较早的检查点上开始, 这样拥有所需要资源的进程会回滚到一个时间点, 在此时间点之前该进程获得了一些其他的资源。
通过杀死进程恢复
最直接也是最简单的解决死锁的方法是杀死一个或若干个进程。 一种方法是杀掉环中的一个进程。 如果走运的话,
6.5 死锁避免
6.5.1 资源轨迹图
避免死锁的主要算法是基于一个安全状态的概念。
6.5.2 安全状态和不安全状态
如果没有死锁发生, 并且即使所有进程突然请求对资源的最大需求, 也仍然存在某种调度次序能够使得每一个进程运行完毕, 则称该状态是安全的。
安全状态和不安全状态的区别是: 从安全状态出发, 系统能够保证所有进程都能完成;
而从不安全状态出发, 就没有这样的保证。
6.5.3 单个资源的银行家算法
银行家算法就是对每一个请求进行检查, 检查如果满足这一请求是否会达到安全状态。 若是, 那么就满足该请求; 若否, 那么就推迟对这一请求的满足。 为了看状态是否安全, 银行家看他是否有足够的资源满足某一个客户。 如果可以, 那么这笔投资认为是能够收回的, 并且接着检查最接近最大限额的一个客户, 以此类推。 如果所有投资最终都被收回, 那么该状态是安全的, 最初的请求可以批准。
6.5.4 多个资源的银行家算法
可以把银行家算法进行推广以处理多个资源。
6.6 死锁预防
6.6.1 破坏互斥条件
先考虑破坏互斥使用条件。 如果资源不被一个进程所独占, 那么死锁肯定不会产生。
6.6.2 破坏占有并等待条件
只要禁止已持有资源的进程再等待其他资源便可以消除死锁。 一种实现方法是规定所有进程在开始执行前请求所需的全部资源。 如果所需的全部资源可用, 那么就将它们分配给这个进程, 于是该进程肯定能够运行结束。 如果有一个或多个资源正被使用, 那么就不进行分配, 进程等待。
6.6.3 破坏不可抢占条件
破坏第三个条件(不可抢占) 也是可能的。
然而, 并不是所有的资源都可以进行类似的虚拟化。 例如, 数据库中的记录或者操作系统中的表都必须被锁定, 因此存在出现死锁的可能。
6.6.4 破坏环路等待条件
消除环路等待有几种方法。 一种是保证每一个进程在任何时刻只能占用一个资源, 如果要请求另外一个资源, 它必须先释放第一个资源。 但假若进程正在把一个大文件从磁带机上读入并送到打印机打印, 那么这种限制是不可接受的。
另一种避免出现环路等待的方法是将所有资源统一编号, 如图6-13a所示。 现在的规则是: 进程可以在任何时刻提出资源请求, 但是所有请求必须按照资源编号的顺序(升序) 提出。 进程可以先请求打印机后请求磁带机, 但不可以先请求绘图仪后请求打印机。
6.7 其他问题
6.7.1 两阶段加锁
常用的方法是两阶段加锁(two-phase locking) 。 在第一阶段, 进程试图对所有所需的记录进行加锁,一次锁一个记录。 如果第一阶段加锁成功, 就开始第二阶段, 完成更新然后释放锁。 在第一阶段并没有做实际的工作。
6.7.2 通信死锁
源死锁是最普遍的一种类型, 但不是惟一的一种。 另一种死锁发生在通信系统中(比如说网络) , 即两个或两个以上进程利用发送信息来通信时。 一种普遍的情形是进程A向进程B发送请求信息, 然后阻塞直至B回复。 假设请求信息丢失, A将阻塞以等待回复, 而B会阻塞等待一个向其发送命令的请求, 因此发生死锁。
根据标准的定义, 在一系列进程中, 每个进程因为等待另外一个进程引发的事件而产生阻塞, 这就是一种死锁。 相比于更加常见的资源死锁, 我们把上面这种情况叫做通信死锁(communication deadlock) 。
另外一种技术通常可以用来中断通信死锁: 超时。
并非所有在通信系统或者网络发生的死锁都是通信死锁。 资源死锁也会发生, 如图6-15中的网络。
6.7.3 活锁
在某种情形下, 轮询(忙等待) 可用于进入临界区或存取资源。 采用这一策略的主要原因是, 相比所做的工作而言, 互斥的时间很短而挂起等待的时间开销很大。
6.7.4 饥饿
在动态运行的系统中, 在任何时刻都可能请求资源。 这就需要一些策略来决定在什么时候谁获得什么资源。 虽然这个策略表面上很有道理, 但依然有可能使一些进程永远得不到服务, 虽然它们并不是死锁进程。
标签:抢占,请求,进程,死锁,第六章,等待,资源 From: https://blog.csdn.net/wgy17734894660/article/details/140887519