死锁知识点整理

死锁的概念我第一次遇到,是在学习多线程的时候。先前对死锁的理解仅限于了解死锁的大致意思以及了解死锁的四个必要条件,没有怎么深入,确切得说连外表也没看很清楚。现在再来阅读《操作系统—精髓及概念设计》的时候,打算精读一下死锁那一章,然后对死锁的知识点进行整理。


1. 死锁基本概念

1.1 死锁原理

首先是 死锁 的含义,《操作系统—精髓与设计原理》在第五章这样写道:

两个或两个以上进程因每个进程都在等待其他进程做完某些事情而不能继续执行的情形。

这句话很短,又不是圣人的“曰”,很多东西自然是读不出来的。这本书第五章毕竟不是死锁的主场,想要弄明白死锁还得看第六章。书中写道:

死锁定义为一组相互竞争系统资源进行通信的进程间的“永久”阻塞。当一组进程中的每个进程都在等待某个事件(典型情况下是等待释放所请求的资源),而仅有这组进程中被阻塞的其他进程才可以触发该事件时,就称这组进程发生了死锁。

这段话把前一句话没有说明的某些事件具体是什么样的给说明了一下,并且把具体为什么会“锁住”所得很明白。这里只写了进程,其实换成线程也一样。

这里举一个例子,想象有两个进程:进程A、进程B;有两个资源:资源1、资源2。进程A先后请求资源1和资源2,进程B先后请求资源2和资源1。进程A请求资源1以及进程B请求资源2通常不会遇到什么问题,因为这两个资源并未被占用。随后进程A请求资源2时,由于该资源被进程B占用,因此请求失败,同时进程A也不释放自己占有的资源1,进程B请求资源1时,由于该资源被进程A占用,因此请求失败,同时进程B也不释放自己占有的资源2,由此两个进程永久阻塞,产生死锁。示意图如下:
在这里插入图片描述

注:我个人比较喜欢在复习的时候先找到书中的原话,后面再去自己总结,总觉得这样就可以更准确得理解概念。

个人觉得书中的那个表挺好的,这里贴一下。
在这里插入图片描述

1.2 死锁条件

这东西可以说的面试的常规问题了。这里再来见见这位老朋友。

这里先回忆一下必要条件和充分条件的含义。事件的发生肯定能推出必要条件成立,但满足事件的必要条件可不一定发生事件,另外必要条件不满足,则事件必定不发生(逆否命题)。充分条件满足,一定发生对应事件。

先前学到的一种说法是死锁的四个必要条件,《操作系统—精髓与设计原理》把循环等待条件独立出来,先只了提三个必要而非充分条件,即互斥、占有且等待、不可抢占。这三个条件加上循环等待条件,共同构成死锁的充分必要条件

  • 互斥:一次只有一个进程可以使用一个资源。其他进程不能访问已分配给其他进程的资源。
  • 占有且等待:当一个进程在等待其他进程时,继续占有已分配的资源。
  • 不可抢占:不能强行抢占进程已占有的资源。
  • 循环等待:存在一个闭合的进程链,每个进程至少占有链中下一个进程所需的一个资源。

对于循环等待条件可能画个资源分配图更直观一些,这里为了不失一般性,搞了三个进程三种资源,资源中的小圆圈表示该资源实例。(画的有点烂)
在这里插入图片描述
注意前三个条件满足时还不一定导致死锁的出现,只有引起循环等待时,必然出现死锁。

1.3 死锁处理简述

这里先简单总结死锁的处理策略。可以总结为以下三种:预防(prevent)、避免(avoid)和检测(detect)。

  • 预防,就是采取策略不让上述四个条件的某个条件出现,从而得以预防死锁。
  • 避免,就是基于资源分配的当前状态做动态选择来避免死锁。
  • 检测,就是检测死锁的存在并恢复正常运行。

前两种策略可能比较难区分,书上也提到差别很小。结合书上的描述,以及我个人的理解,我认为可能对于多进程(线程)来说,死锁避免可能相比于死锁预防“乐观”、灵活一些。要知道必要条件的满足不一定会发生死锁,因此死锁避免允许必要条件的满足,只是在可能要出现死锁时通过灵活的资源调度来避免死锁。书中也提到:

…,因此死锁避免与死锁预防相比,可允许更多的并发。

在这里插入图片描述


2. 死锁预防

前文已经提及,死锁预防策略是针对死锁发生的条件,不让某个条件发生,这样就能排除死锁发生的可能性。死锁预防方法可以分为间接死锁预防方法和直接死锁预防方法。间接死锁预防方法是防止前三个必要条件(互斥、占有且等待、不可抢占)中的任意一个发生;直接死锁预防方法是防止循环等待条件发生。

下面记录各条件如何预防:

  • 互斥
    通过允许多个进程对资源的同时访问,即禁止互斥即可预防该条件,然而一般来说考虑到同步问题,互斥不好禁止,有时不应该被禁止。

  • 占有且等待
    为预防该条件成立,可令进程一次性请求所有需要的进程,并阻塞这个进程直到所有请求同时满足。

  • 不可抢占
    在某个进程占有某些资源,同时又进一步申请其他资源被拒绝时,让该进程释放原来占有的资源。另外,在一个进程请求被其他进程占有的资源时,允许抢占,即要求其他进程释放资源。

  • 循环等待
    可以通过定义资源类型的线性顺序来预防循环等待条件。以1.1中提到的场景为例。例如资源 1 和资源 2,定义只能先获取资源1 后获取资源2。这样就不可能出现进程A获得资源1并请求资源2,进程B获得资源2并请求资源1的情况。


3. 死锁避免

这里再提一下预防和避免。如果说死锁预防是在一开始就防止了四个条件中的某个条件的出现,那么死锁避免就是在进程运行过程中允许三个必要条件的出现,但通过某种手段防止死锁点的到来。

书中给出死锁避免的两种方法:

  • 若一个进程的请求会导致死锁,则不启动该进程。 (进程启动拒绝)
  • 若一个进程增加的资源请求会导致死锁,则不允许这一资源分配。(资源分配拒绝策略)

在记录具体方法之前先作出如下假设和定义:

  • 共有 n 个进程和 m 种不同类型的资源;
  • 使用向量 R 表示系统中每种资源的总量,Rj 表示第 j 个资源的总量;
  • 使用向量 V 表示中未分配给进程的每种资源的量,Vj 表示未分配给进程的第 j 种资源的量;
  • 使用矩阵 C 表示进程对资源的需求,Cij 表示进程 i 对资源 j 的需求;
  • 使用矩阵 A 表示当前的资源分配情况,Aij 表示当前分配给进程 i 的资源 j 的数量。

根据该定义可以得到如下关系式成立:

  • 第 j 种资源的总量等于未分配的量加上已分配的量,已分配的量等于分配给各个进程的量的和:

R j = V j + ∑ i = 1 n A i j {{R}_{j}}={{V}_{j}}+\sum\limits_{i=1}^{n}{{{A}_{ij}}} Rj=Vj+i=1nAij

  • 任何一个进程对某种资源的请求量都不能超过该资源的总量:
    C i j ≤ R i j {{C}_{ij}}\le {{R}_{ij}} CijRij
  • 分配给任何一个进程对某种资源的数量都不能超过该进程对此种资源的请求量:
    A i j ≤ C i j {{A}_{ij}}\le {{C}_{ij}} AijCij

以上假设定义和关系,可用于后续的各种分析。

进程拒绝启动
若一个新进程的资源请求会导致死锁,则拒绝启动这个新进程。仅当
R j ≥ C ( n + 1 ) j + ∑ i = 1 n C i j {{R}_{j}}\ge {{C}_{\left( n+1 \right)j}}+\sum\limits_{i=1}^{n}{{{C}_{ij}}} RjC(n+1)j+i=1nCij
对于所有 j 成立时,才启动一个新进程 Pn+1。即只有每种资源总量都不少于所有进程对资源的请求量的和加上新的请求时,才启动该进程。

资源分配拒绝
该策略又称银行家算法。资源的分配有点像银行的贷款,每个进程相当于一个客户。客户对每个资源有一定的需求,银行拥有有限的资源可供“借贷”,客户(进程)用完资源后将资源归还给银行。

当前的资源分配状况被称为系统的状态,系统的状态可分为安全状态不安全状态。安全状态指的是至少有一个资源分配序列不会导致死锁,所有进程都能运行至结束,不安全状态指非安全的状态。

银行家算法的主要思想就是避免系统进入不安全状态,确保系统始终处于安全状态。进程在请求资源时,需检查同意此次请求完成资源分配后的结果是否处于安全状态,如果是,则同意该请求,如果不是,则阻塞该进程直到同意该请求后的系统状态是安全的。由此保证系统始终处于安全状态,从而避免死锁现象的发生。

到底什么是不安全的状态?借用书上的例子,这里假设进程 P1 需要 1 份资源 R1 和 1 份资源 R2,然后系统进行资源分配从剩余资源中将该进程所需的资源分配给他,如此则剩余的资源情况为 [0,1,1],在这种情况下,任意一个进程提出最大请求,即要求获取运行完该进程所需的全部资源,系统都无法给出这么多资源,因此该种状态就是不安全状态。
在这里插入图片描述
其实还是换成银行和客户的例子好想一些,银行借给给客户资源的时候要考虑,后面如果有客户提出他的最大需要,还能不能借出资源。在上面的例子中,借给进程P1资源后,进程P1其实还是缺资源,不能运行完,因此也就不能归还资源,银行此时拥有的资源量 [0,1,1] 不能令任意一个客户结束他的任务,即任意一个进程结束需要的资源都比系统还剩下的多。显然这种情况下就有死锁的危险,进程完不成,系统也给不出这么多资源。


4. 死锁检测

使用死锁检测策略处理死锁,可以是周期性地检测死锁的发生并采取策略进行恢复。

检测
这里记录一下书上提到的方法
沿用上一节的假设定义及关系,这里定义一个请求矩阵 Q(注意区分请求和需求)

  • Qij 表示进程 i 请求资源 j 的数量

该算法主要是一个标记未死进程的过程。最初所有进程未标记,然后执行如下步骤:

  1. 标记矩阵 A (资源分配情况)中一行全为0的进程
  2. 初始化一个临时向量 W ,令 W = V (未分配资源数量)
  3. 查找未标记的进程 i,满足 Q 的第 i 行小于等于 W,即对所有的1≤k≤m,Qik≤Wk,若找不到这样的进程则终止算法
  4. 找到就标记进程 i,并把矩阵 A 中相应的行加到 W 中,即对所有的1≤k≤m,令Wk+=Aik,返回步骤3

其实这个过程就是对进程进行搜索,看现有的资源能不能满足某个进程的请求,找到这样的进程,对其进行标记,表示已经满足,然后把该进程所拥有的资源归还,继续搜索。到最后就看是不是每个进程都被标记了,如果存在未被标记的进程,说明发生了死锁。

恢复
检测到死锁后的恢复策略有以下几种:

  • 取消所有死锁进程
  • 回滚到前面的检查点
  • 连续取消死锁进程直到不存在死锁
  • 连续抢占资源直到不存在死锁

后两种策略需要考虑最小代价。


参考资料: [1] 《操作系统—精髓与设计原理》
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当准备408面试时,以下是一些可能的分科知识点整理: 1. 数据结构与算法:包括数组、链表、栈、队列、树、图等常见数据结构的基本概念、特性和操作,以及常见的排序和搜索算法。 2. 计算机网络:涵盖TCP/IP协议族、网络层、传输层、应用层等网络基础知识,包括IP地址、子网划分、路由器、交换机、HTTP协议等。 3. 操作系统:涉及进程管理、内存管理、文件系统、设备管理等操作系统基本概念和原理,如进程调度算法、内存分配算法、死锁处理等。 4. 数据库:包括关系型数据库的基本概念、SQL语言、事务处理、索引和查询优化等。 5. 编程语言:熟悉至少一种主流编程语言,如C/C++、Java、Python等,了解语言的基本语法、面向对象编程、异常处理等。 6. 离散数学:包括集合论、逻辑推理、图论、代数结构等数学基础知识,对算法和数据结构的理解有一定帮助。 7. 系统设计与架构:了解大型系统的设计原则和常见的架构模式,如分层架构、微服务架构、消息队列等。 8. 数据挖掘与机器学习:了解常见的数据挖掘算法和机器学习算法,如聚类、分类、回归等,以及常用的数据处理和特征工程方法。 9. 软件工程与项目管理:熟悉软件开发的基本流程和常用的开发方法论,如敏捷开发、测试驱动开发等,了解项目管理的基本概念。 10. 网络安全与加密:了解常见的网络攻击方式和防御方法,如DDoS攻击、SQL注入、加密算法等。 这些只是一些可能的知识点,具体还需要根据个人情况和面试要求进行调整和准备。同时,还建议多做一些面试题和编程练习,加强自己的实际操作能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值