计算机操作系统——死锁

一、死锁的定义

大部分死锁都和资源相关,资源分为两类:可抢占的和不可抢占的:

  • 可抢占资源(preemptable resource)可以从拥有它的进程中抢
    占而不会产生任何副作用,存储器就是一类可抢占的资源。
  • 不可抢占资源(nonpreemptable resource)是指在不引起相关的计算失败的情况下,无法把它从
    占有它的进程处抢占过来。

总的来说,死锁和不可抢占资源有关,有关可抢占资源的潜在死锁通常可以通过在进程之间重新分配资源而化解。所以,我们的重点放在不可抢占资源上。

死锁的规范定义如下:

如果一个进程集合中的每个进程都在等待只能由该进程集合中的其他进程才能引发的事件,那么,该进
程集合就是死锁的。

二、资源死锁发生的必要条件:

死锁的四个必要条件:
  • 互斥条件。每个资源要么已经分配给了一个进程,要么就是可用的。
  • 占有和等待条件。已经得到了某个资源的进程可以再请求新的资源。
  • 不可抢占条件。已经分配给一个进程的资源不能强制性地被抢占,它只能被占有它的进程显式地释
    放。
  • 环路等待条件。死锁发生时,系统中一定有由两个或两个以上的进程组成的一条环路,该环路中的每
    个进程都在等待着下一个进程所占有的资源。

死锁发生时,以上四个条件一定是同时满足的。如果其中任何一个条件不成立,死锁就不会发生。因此,我们可以通过破坏上述条件来预防死锁。

三、处理死锁的策略:

有四种处理死锁的策略:
1. 忽略该问题。

忽略死锁,也许如果你忽略它,它也会忽略你。
1) 鸵鸟算法
把头埋在沙子里,假装根本没发生问题。
因为解决死锁问题的代价很高,因此鸵鸟策略这种不采取任务措施的方案会获得更高的性能。
当发生死锁时不会对用户造成多大影响,或发生死锁的概率很低,可以采用鸵鸟策略。
大多数操作系统,包括 Unix,Linux 和 Windows,处理死锁问题的办法仅仅是忽略它。

2. 检测死锁并恢复。

在使用这种技术时,系统并不试图阻止死锁的产生,而是允许死锁发生,当检测到死锁发生后,采取措施进行恢复。
死锁检测方法:
1)每种类型一个资源的死锁检测方法:
通过检测有向图是否存在环来实现,从一个节点出发进行深度优先搜索,对访问过的节点进行标记,如果访问了已经标记的节点,就表示有向图存在环,也就是检测到死锁的发生。

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的数量加起来再和所有可供使用的资源数相加,结果就是该类资源的资源总数。
  • 死锁检测算法就是基于向量的比较。我们定义向量A和向量B之间的关系为A≤B以表明A的每一个分量要么等于要么小于和B向量相对应的分量。从数学上来说,A≤B当且仅当且Ai ≤Bi (0≤i≤m)。
  • 每个进程起初都是没有标记过的。算法开始会对进程做标记,进程被标记后就表明它们能够被执行,不会进入死锁。当算法结束时,任何没有标记的进程都是死锁进程。该算法假定了一个最坏情形:所有的进程在退出以前都会不停地获取资源。

算法执行过程如下:

  1. 寻找一个没有标记的进程Pi ,对于它而言R矩阵的第i行向量小于或等于A。
  2. 如果找到了这样一个进程,那么将C矩阵的第i行向量加到A中,标记该进程,并转到第1步。
  3. 如果没有这样的进程,那么算法终止。
    算法结束时,所有没有标记过的进程(如果存在的话)都是死锁进程。

算法示例:
在这里插入图片描述
在图6-7中展示了用该算法检测死锁的工作过程。这里我们有3个进程、4种资源(可以任意地将它们标记为磁带机、绘图仪、扫描仪和CD-ROM驱动器)。进程1有一台扫描仪。进程2有2台磁带机和1个CD-ROM驱动器。进程3有1个绘图仪和2台扫描仪。每一个进程都需要额外的资源,如矩阵R所示。

要运行死锁检测算法,首先找出哪一个进程的资源请求可被满足。第1个不能被满足,因为没有CDROM
驱动器可供使用。第2个也不能被满足,由于没有打印机空闲。幸运的是,第3个可被满足,所以进程3
运行并最终释放它所拥有的资源,给出
A=(2 2 2 0)
接下来,进程2也可运行并释放它所拥有的资源,给出
A=(4 2 2 1)
现在剩下的进程都能够运行,所以这个系统中不存在死锁。
死锁恢复方法:
1)利用抢占恢复
在某些情况下,可能会临时将某个资源从它的当前所有者那里转移到另一个进程。许多情况下,尤其是
对运行在大型主机上的批处理操作系统来说,需要人工进行干预。

在不通知原进程的情况下,将某一资源从一个进程强行取走给另一个进程使用,接着又送回,这种做法
是否可行主要取决于该资源本身的特性。用这种方法恢复通常比较困难或者说不太可能。若选择挂起某个进程,则在很大程度上取决于哪一个进程拥有比较容易收回的资源。
2)利用回滚恢复
如果系统设计人员以及主机操作员了解到死锁有可能发生,他们就可以周期性地对进程进行检查点检查
(checkpointed)。进程检查点检查就是将进程的状态写入一个文件以备以后重启。该检查点中不仅包括存储映像,还包括了资源状态,即哪些资源分配给了该进程。为了使这一过程更有效,新的检查点不应覆盖原有的文件,而应写到新文件中。这样,当进程执行时,将会有一系列的检查点文件被累积起来。

一旦检测到死锁,就很容易发现需要哪些资源。为了进行恢复,要从一个较早的检查点上开始,这样拥
有所需要资源的进程会回滚到一个时间点,在此时间点之前该进程获得了一些其他的资源。在该检查点后所做的所有工作都丢失。(例如,检查点之后的输出必须丢弃,因为它们还会被重新输出。)实际上,是将该进程复位到一个更早的状态,那时它还没有取得所需的资源,接着就把这个资源分配给一个死锁进程。如果复位后的进程试图重新获得对该资源的控制,它就必须一直等到该资源可用时为止。

总的来说,是把占用死锁进程所需资源的进程回滚到较早的时间点(该时间点时,该进程还未占用死锁进程所需的资源),然后把该所需资源先分配给死锁进程使用,而该被回滚的进程要想重新获得对该所需资源的控制,它就必须一直等到该所需资源可用时为止。
3)通过杀死进程恢复
一种方法是杀掉环中的一个进程。如果走运的话,其他进程将可以继续。如果这样做行不通的话,就需要继续杀死别的进程直到打破死锁环。

另一种方法是选一个环外的进程作为牺牲品以释放该进程的资源。在使用这种方法时,选择一个要被杀
死的进程要特别小心,它应该正好持有环(死锁进程集)中某些进程所需的资源,从而通过杀死环外进程将某些资源释放,来打破环中进程的死锁。

3. 仔细对资源进行分配,动态地避免死锁。

死锁避免即是系统能够判断分配资源是否安全,并且保证在安全的条件下才分配资源。
我们将要研究的死锁避免算法使用了图6-6中的有关信息。
安全状态和不安全状态:
在任何时刻,当前状态包括了E、A、C和R。如果没有死锁发生,并且即使所有进程突然请求对资源的最大需求,也仍然存在某种调度次序能够使得每一个进程运行完毕,则称该状态是安全的。
在这里插入图片描述
图6-9a的状态是安全的,这是由于存在一个分配序列使得所有的进程都能完成。也就是说,这个方案可
以单独地运行B,直到它请求并获得另外两个资源实例,从而到达图6-9b的状态。当B完成后,就到达了图6-9c的状态。然后调度程序可以运行C,再到达图6-9d的状态。当C完成后,到达了图6-9e的状态。现在A可以获得它所需要的6个资源实例,并且完成。这样系统通过仔细的调度,就能够避免死锁,所以图6-9a的状态是安全的。
在这里插入图片描述

现在假设初始状态如图6-10a所示。但这次A请求并得到另一个资源,如图6-10b所示。我们还能找到一
个序列来完成所有工作吗?我们来试一试。调度程序可以运行B,直到B获得所需资源,如图6-10c所示。

最终,进程B完成,状态如图6-10d所示,此时进入困境了。只有4个资源实例空闲,并且所有活动进程
都需要5个资源实例。任何分配资源实例的序列都无法保证工作的完成。于是,从图6-10a到图6-10b的分配方案,从安全状态进入到了不安全状态。从图6-10c的状态出发运行进程A或C也都不行。回过头来再看,A的请求不应该满足。

值得注意的是,不安全状态并不是死锁。从图6-10b出发,系统能运行一段时间。实际上,甚至有一个
进程能够完成。而且,在A请求其他资源实例前,A可能先释放一个资源实例,这就可以让C先完成,从而避免了死锁。因而,安全状态和不安全状态的区别是:在进程不主动释放所占进程直到进程结束的情况下,从安全状态出发,系统能够保证所有进程都能完成;而从不安全状态出发,就没有这样的保证。
1) 单个资源的银行家算法:
一个小城镇的银行家,他向一群客户分别承诺了一定的贷款额度,算法要做的是判断对请求的满足是否会进入不安全状态,如果是,就拒绝请求;否则予以分配。
在这里插入图片描述
客户们各自做自己的生意,在某些时刻需要贷款(相当于请求资源)。在某一时刻,具体情况如图6-
11b所示。这个状态是安全的,由于保留着2个单位,银行家能够拖延除了C以外的其他请求。因而可以让C先完成,然后释放C所占的4个单位资源。有了这4个单位资源,银行家就可以给D或B分配所需的贷款单位,以此类推。

考虑假如向B提供了另一个他所请求的贷款单位,如图6-11b所示,那么我们就有如图6-11c所示的状
态,该状态是不安全的。如果忽然所有的客户都请求最大的限额,而银行家无法满足其中任何一个的要求,那么就会产生死锁。不安全状态并不一定引起死锁,由于客户不一定需要其最大贷款额度,但银行家不敢抱这种侥幸心理。

银行家算法就是对每一个请求进行检查,检查如果满足这一请求是否会达到安全状态。若是,那么就满
足该请求;若否,那么就推迟对这一请求的满足。为了看状态是否安全,银行家看他是否有足够的资源满足某一个客户。如果可以,那么这笔投资认为是能够收回的,并且接着检查最接近最大限额的一个客户,以此类推。如果所有投资最终都被收回,那么该状态是安全的,最初的请求可以批准。

2) 多个资源的银行家算法
可以把银行家算法进行推广以处理多个资源。图6-12说明了多个资源的银行家算法如何工作。
在这里插入图片描述
在图6-12中我们看到两个矩阵。左边的矩阵显示出为5个进程分别已分配的各种资源数,右边的矩阵显
示了使各进程完成运行所需的各种资源数。这些矩阵就是图6-6中的C和R。和一个资源的情况一样,各进程在执行前给出其所需的全部资源量,所以在系统的每一步中都可以计算出右边的矩阵。

图6-12最右边的三个向量分别表示现有资源E、已分配资源P和可用资源A。由E可知系统中共有6台磁带
机、3台绘图仪、4台打印机和2台CD-ROM驱动器。由P可知当前已分配了5台磁带机、3台绘图仪、2台打印机和2台CD-ROM驱动器。该向量可通过将左边矩阵的各列相加获得,可用资源向量可通过从现有资源中减去已分配资源获得。

检查一个状态是否安全的算法如下:
1)查找右边矩阵中是否有一行,其没有被满足的资源数均小于或等于A。如果不存在这样的行,那么系
统将会死锁,因为任何进程都无法运行结束(假定进程会一直占有资源直到它们终止为止)。
2)假若找到这样一行,那么可以假设它获得所需的资源并运行结束,将该进程标记为终止,并将其资源
加到向量A上。
3)重复以上两步,或者直到所有的进程都标记为终止,其初始状态是安全的;或者所有进程的资源需求
都得不到满足,此时就是发生了死锁。
如果在第1步中同时有若干进程均符合条件,那么不管挑选哪一个运行都没有关系,因为可用资源或者
会增多,或者至少保持不变。

我觉得后面的“单个资源银行家算法”和“多个资源银行家算法”的原理都跟前面《第2点——死锁检测方法》的“基于矩阵的死锁检测算法”的原理一样,如果分配贷款之后检测到死锁,那么就是不安全状态,银行家应该拒绝这种分配贷款的请求。

银行家算法最早由Dijkstra于1965年发表。从那之后几乎每本操作系统的专著都详细地描述它,很多论
文的内容也围绕该算法讨论了它的不同方面。但很少有作者指出该算法虽然很有意义但缺乏实用价值,因为很少有进程能够在运行前就知道其所需资源的最大值。而且进程数也不是固定的,往往在不断地变化(如新用户的登录或退出),况且原本可用的资源也可能突然间变成不可用(如磁带机可能会坏掉)。因此,在实际中,如果有,也只有极少的系统使用银行家算法来避免死锁。

4. 通过破坏引起死锁的四个必要条件之一,防止死锁的产生。

通过前面的学习我们知道,死锁避免从本质上来说是不可能的,因为它需要获知未来的请求,而这些请 求是不可知的。因此,在实际系统中,我们是通过保证死锁发生的四个必要条件中至少有一个不成立,那么死锁将不会产生。
1) 破坏互斥条件
先考虑破坏互斥使用条件。如果资源不被一个进程所独占,那么死锁肯定不会产生。当然,允许两个进
程同时使用打印机会造成混乱,通过采用假脱机打印机(spooling printer)技术可以允许若干个进程同时产生输出。该模型中惟一真正请求使用物理打印机的进程是打印机守护进程,由于守护进程决不会请求别的资源,所以不会因打印机而产生死锁。
2) 破坏占有和等待条件
只要禁止已持有资源的进程再等待其他资源便可以消除死锁。一种实现方法是规定所有进程在开始执行前请求所需的全部资源。如果所需的全部资源可用,那么就将它们分配给这个进程,于是该进程肯定能够运行结束。如果有一个或多个资源正被使用,那么就不进行分配,进程等待。

这种方法的一个直接问题是很多进程直到运行时才知道它需要多少资源。实际上,如果进程能够知道它 需要多少资源,就可以使用银行家算法。另一个问题是这种方法的资源利用率不是最优的。例如,有一个进程先从输入磁带上读取数据,进行一小时的分析,最后会写到输出磁带上,同时会在绘图仪上绘出。如果所有资源都必须提前请求,这个进程就会把输出磁带机和绘图仪控制住一小时。

另一种破坏占有和等待条件的略有不同的方案是,要求当一个进程请求资源时,先暂时释放其当前占用
的所有资源,然后再尝试一次获得所需的全部资源。

3) 破坏不可抢占条件
破坏第三个条件(不可抢占)也是可能的。假若一个进程已分配到一台打印机,且正在进行打印输出,
如果由于它需要的绘图仪无法获得而强制性地把它占有的打印机抢占掉。
4) 破坏环路等待条件
将所有资源统一编号,,如图6-13a所示。’现在的规则是:进程可以在 任何时刻提出资源请求,但是所有请求必须按照资源编号的顺序(升序)提出。进程可以先请求打印机后请求磁带机,但不可以先请求绘图仪后请求打印机。
在这里插入图片描述
该算法的一个变种是摈弃必须按升序请求资源的限制,而仅仅要求不允许进程请求比当前所占有资源编
号低的资源。所以,若一个进程起初请求9号和10号资源,而随后释放两者,那么它实际上相当于从头开
始,所以没有必要阻止它现在请求1号资源。

总的来说,死锁预防的各种方法如图6-14所示:
在这里插入图片描述

参考文献:
《CS-Notes/notes/计算机操作系统 - 死锁》
《现代操作系统(第3版)》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值