OS- -死锁(二)

OS- -死锁(二)

一、死锁

1.死锁避免

单个资源的银行家算法

  • 银行家算法是Dijkstra在1965年提出的一种调度算法,它本身是一种死锁的调度算法
  • 它的模型是基 于一个城镇中的银行家,银行家向城镇中的客户承诺了一定数量的贷款额度。
  • 算法要做的就是判断请求 是否会进入一种不安全的状态
  • 如果是,就拒绝请求,如果请求后系统是安全的,就接受该请求
  • 比如下面的例子,银行家一共为所有城镇居民提供了 15单位个贷款额度,一个单位表示1k美元,如 下所示:
    在这里插入图片描述
  • 城镇居民都喜欢做生意,所以就会涉及到贷款,每个人能贷款的最大额度不一样,在某一时刻, A/B/C/D的贷款金额如下:
    在这里插入图片描述
  • 上面每个人的贷款总额加起来是13,马上接近15,银行家只能给A和C进行放贷,可以拖着B和 D
  • 所以,可以让A和C首先完成,释放贷款额度,以此来满足其他居民的贷款。这是一种安全的状 态。
  • 如果每个人的请求导致总额会超过甚至接近15 ,就会处于一种不安全的状态,如下所示:
    在这里插入图片描述
  • 这样,每个人还能贷款至少2个单位的额度,如果其中有一个人发起最大额度的贷款请求,就会使系统 处于一种死锁状态。
  • 这里注意一点:不安全状态并不一定引起死锁,由于客户不一定需要其最大的贷款额度,但是银 行家不敢抱着这种侥幸心理
  • 银行家算法就是对每个请求进行检查,检查是否请求会引起不安全状态,如果不会引起,那么就接受该 请求;如果会引起,那么就推迟该请求。

类似的,还有多个资源的银行家算法,可以自行了解。

2.破坏死锁

  • 死锁本质上是无法避免的,因为它需要获得未知的资源和请求,但是死锁是满足四个条件后才出现的, 它们分别是
  • •互斥
  • •保持和等待
  • •不可抢占
  • •循环等待

我们分别对这四个条件进行讨论,按理说破坏其中的任意一个条件就能够破坏死锁

破坏互斥条件

  • 我们首先考虑的就是 破坏互斥使用条件。如果资源不被一个进程独占,那么死锁肯定不会产生。
  • 如果两 个打印机同时使用一个资源会造成混乱,打印机的解决方式是使用假脱机打印机(spooling printer),这项技术可以允许多个进程同时产生输出
  • 在这种模型中,实际请求打印机的唯一进程是 打印机守护进程,也称为后台进程。后台进程不会请求其他资源。我们可以消除打印机的死锁
  • 后台进程通常被编写为能够输出完整的文件后才能打印,假如两个进程都占用了假脱机空间的一半,而 这两个进程都没有完成全部的输出,就会导致死锁。

因此,尽量做到尽可能少的进程可以请求资源。

破坏保持等待的条件

  • 第二种方式是如果我们能阻止持有资源的进程请求其他资源,我们就能够消除死锁。
  • 一种实现方式是让 所有的进程开始执行前请求全部的资源。如果所需的资源可用,进程会完成资源的分配并运行到结束。 如果有任何一个资源处于频繁分配的情况,那么没有分配到资源的进程就会等待。
  • 很多进程无法在执行完成前就知道到底需要多少资源,如果知道的话,就可以使用银行家算法;还有一 个问题是这样无法合理有效利用资源
  • 还有一种方式是进程在请求其他资源时,先释放所占用的资源,然后再尝试一次获取全部的资源。

破坏不可抢占条件

  • 破坏不可抢占条件也是可以的。可以通过虚拟化的方式来避免这种情况

破坏循环等待条件

  • 现在就剩最后一个条件了,循环等待条件可以通过多种方法来破坏。
  • 一种方式是制定一个标准,一个进 程在任何时候只能使用一种资源。如果需要另外一种资源,必须释放当前资源。对于需要将大文件从磁 带复制到打印机的过程,此限制是不可接受的。
  • 另一种方式是将所有的资源统一编号,如下图所示
    在这里插入图片描述
  • 进程可以在任何时间提出请求,但是所有的请求都必须按照资源的顺序提出。如果按照此分配规则的 话,那么资源分配之间不会出现环。
    在这里插入图片描述
  • 尽管通过这种方式来消除死锁,但是编号的顺序不可能让每个进程都会接受。

3.其他问题

下面我们来探讨一下其他问题,包括通信死锁、活锁是什么、饥饿问题和两阶段加锁

两阶段加锁

  • 虽然很多情况下死锁的避免和预防都能处理,但是效果并不好。随着时间的推移,提出了很多优秀的算 法用来处理死锁。
  • 例如在数据库系统中,一个经常发生的操作是请求锁住一些记录,然后更新所有锁定 的记录。当同时有多个进程运行时,就会有死锁的风险。
  • 一种解决方式是使用两阶段加锁(two-phase locking) ,顾名思义分为两个阶段:
  • 一阶段是进程尝 试一次锁定它需要的所有记录。如果成功后,才会开始第二阶段
  • 第二阶段是执行更新并释放锁。第一 阶段并不做真正有意义的工作
  • 如果在第一阶段某个进程所需要的记录已经被加锁,那么该进程会释放所有锁定的记录并重新开始第一 阶段
  • 从某种意义上来说,这种方法类似于预先请求所有必需的资源或者是在进行一些不可逆的操作之 前请求所有的资源
  • 不过在一般的应用场景中,两阶段加锁的策略并不通用。如果一个进程缺少资源就会半途中断并重新开 始的方式是不可接受的。

通信死锁

  • 我们上面一直讨论的是资源死锁,资源死锁是一种死锁类型,但并不是唯一类型,还有通信死锁,也就 是两个或多个进程在发送消息时出现的死锁。
  • 进程A给进程B发了一条消息,然后进程A阻塞直到进 程B返回响应。假设请求消息丢失了,那么进程A在一直等着回复,进程B也会阻塞等待请求消息到 来,这时候就产生死锁
  • 尽管会产生死锁,但是这并不是一个资源死锁,因为A并没有占据B的资源。事实上,通信死锁并没 有完全可见的资源。
  • 根据死锁的定义来说:每个进程因为等待其他进程引起的事件而产生阻塞,这就是 一种死锁。相较于最常见的通信死锁,我们把上面这种情况称为通信死锁(communication deadlock)。
  • 通信死锁不能通过调度的方式来避免,但是可以使用通信中一个非常重要的概念来避免:超时 (timeout)
  • 在通信过程中,只要一个信息被发出后,发送者就会启动一个定时器,定时器会记录消 息的超时时间,如果超时时间到了但是消息还没有返回,就会认为消息已经丢失并重新发送,通过这种 方式,可以避免通信死锁。
  • 但是并非所有网络通信发生的死锁都是通信死锁,也存在资源死锁,下面就是一个典型的资源死锁。
  • 当一个数据包从主机进入路由器时,会被放入一个缓冲区,然后再传输到另外一个路由器,再到另一 个,以此类推直到目的地。缓冲区都是资源并且数量有限。
  • 如下图所示,每个路由器都有10个缓冲区 (实际上有很多)。
    在这里插入图片描述
  • 假如路由器A的所有数据需要发送到B , B的所有数据包需要发送到D,然后D的所有数据包需要发 送到A。
  • 没有数据包可以移动,因为在另一端没有缓冲区可用,这就是一个典型的资源死锁。

活锁

  • 你会发现一个很有意思的事情,死锁就跟榆木脑袋一样,不会转弯。
  • 讲个故事👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏:
  • 故事是这样的:春秋时,鲁国曲阜有个年轻人名叫尾生,为人正直,乐于助人,和朋友交往很守信用,受到四乡八邻的普遍赞誉。
  • 后来,尾生迁居梁地(今陕西韩城南)。
  • 他在那里认识了一位年轻漂亮的姑娘。两人一见钟情,君子淑女,私订终身。
  • 但是姑娘的父母嫌弃尾生家境贫寒,坚决反对这门亲事。
  • 为了追求爱情和幸福,姑娘决定背着父母私奔,随尾生回到曲阜老家去。
  • 那一天,两人约定在韩城外的一座木桥边会面,双双远走高飞。
  • 黄昏时分,尾生提前来到桥上等候。
  • 不料,六月的天气说变就变,突然乌云密布,狂风怒吼,雷鸣电闪,滂沱大雨倾盆而下。
  • 不久山洪暴发,滚滚江水裹挟泥沙席卷而来,淹没了桥面,没过了尾生的膝盖。
  • 城外桥面,不见不散,尾生想起了与姑娘的信誓旦旦;四顾茫茫水世界,不见姑娘踪影。但他寸步不离,死死抱着桥柱,终于被活活淹死。
  • 再说姑娘因为私奔念头泄露,被父母禁锢家中,不得脱身。后伺机黄夜逃出家门,冒雨来到城外桥边,此时洪水已渐渐退去。
  • 姑娘看到紧抱桥柱而死的尾生,悲恸欲绝。她抱着尾生的尸体号啕大哭。阴阳相 隔,生死一体,哭罢,便相拥纵身投入滚滚江中……?
  • 如果说死锁很痴情的话,那么活锁用一则成语来表示就是弄巧成拙
  • 某些情况下,当进程意识到它不能获取所需要的下一个锁时,就会尝试礼貌的释放已经获得的锁,然后 等待非常短的时间再次尝试获取
  • 可以想像一下这个场景:当两个人在狭路相逢的时候,都想给对方让 路,相同的步调会导致双方都无法前进。
  • 现在假想有一对并行的进程用到了两个资源。它们分别尝试获取另一个锁失败后,两个进程都会释放自 己持有的锁,再次进行尝试,这个过程会一直进行重复
  • 很明显,这个过程中没有进程阻塞,但是进程 仍然不会向下执行,这种状况我们称之为 活锁(livelock)

饥饿

  • 死锁和活锁的一个非常相似的问题是 饥饿(starvvation)
  • 想象一下你什么时候会饿?一段时间不 吃东西是不是会饿?
  • 对于进程来讲,最重要的就是资源,如果一段时间没有获得资源,那么进程会产生 饥饿,这些进程会永远得不到服务
  • 我们假设打印机的分配方案是每次都会分配给最小文件的进程,那么要打印大文件的进程会永远得不到 服务,导致进程饥饿,进程会无限制的推后,虽然它没有阻塞。

4.总结

  • 死锁是一类通用问题,任何操作系统都会产生死锁当每一组进程中的每个进程都因等待由该组的其他 进程所占有的资源而导致阻塞,死锁就发生了。这种情况会使所有的进程都处于无限等待的状态。
  • 死锁的检测和避免可以通过安全和不安全状态来判断,其中一个检测方式就是银行家算法;当然你也可 以使用鸵鸟算法对死锁置之不理,但是你肯定会遭其反噬。
  • 也可以在设计时通过系统结构的角度来避免死锁,这样能够预防死锁;也可以破坏死锁的四个条件来破 坏死锁
  • 资源死锁并不是唯一性的死锁,还有通信间死锁,可以设置适当的超时时间来完成
  • 活锁和死锁的问题有些相似,它们都是一种进程无法继续向下执行的状态
  • 由于进程调度策略导致尝试 获取进程的一方永远无法获得资源后,进程会导致饥饿的出现
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页