操作系统 第七章 死锁

第七章 死锁

7.1 系统模型

每个系统都有有限数量的资源,需要分配到若干竞争进程。这些资源可以分为多种类型,每种类型有一定数量的实例。资源类型有很多,例如CPU周期、文件、I/O设备(打印机和DVD驱动器)等。互斥锁和信号量这类同步工具也应作为系统资源。进程在使用资源前应申请资源,在使用后应释放资源。在正常操作模式下,进程只能按如下顺序使用资源:

  • 1.申请,若不能立即被允许,那么申请进程应等待,直到能获得该资源为止
  • 2.使用
  • 3.释放

当一组进程内的每个进程都在等待一个事件,而这一事件只能由这一组进程的另一个进程引起,那么这组进程就处于死锁状态,这里所关心的主要事件是资源的获取和释放。

7.2 死锁特征

这里简单说明互斥锁的死锁。即两个线程各持有一个锁,并且在未释放的情况下,请求对方的锁,这样两个线程将会形成死锁。书中是以 Pthreads 为例来说明这一情况。

7.2.1 必要条件

如果一个系统中以下四个条件同时成立,那么就能引起死锁:

  • 互斥(mutual exclusion):至少有一个资源必须处于非共享模式,一次只能供一个进程使用。
  • 占有并等待(hold and wait):一个进程至少占有一个资源,并等待另一个资源,而该资源被其他进程占用
  • 非抢占(no preemption):资源不能被抢占
  • 循环等待(circular wait):有一组进程{1,2,3,…,n},1等待的资源被2占有,2等待的资源被3占有,…,n等待的资源被1占有。

所有四个条件必须同时成立才会出现死锁。

7.2.2 资源分配图

通过称为系统资源分配图(system resource-allocation graph)的有向图可以更精准地描述死锁。改图包括一个节点集 V 和一个边集 E。节点集分为两种类型,一种是所有活动进程的集合 P ,一种是所有资源类型的集合 R 。有向边 Pi->Rj 称为申请边(request edge),表示 Pi 已经申请了资源类型 Rj 的一个实例;有向边 Rj->Pi 称为分配边(assignment edge),表示资源类型 Rj 的一个实例已经分配给了进程 Pi。根据上述定义,如果分配图没有环,那么系统就没有进程死锁;如果有环,那么可能存在死锁(因为当一个可能引起死锁的资源类型有多个实例时,死锁可能不会发生)。

7.3 死锁处理方法

一般来说处理死锁问题有三种方法:

  • 通过协议来预防或避免,确保系统不会进入死锁状态。
  • 可以允许进入死锁状态,然后检测,恢复。
  • 忽视这个问题,认为死锁不可能发生。

大多数操作系统采用第三种,包括 Linux 和 Windows 。需要应用程序开发人员自己编写程序处理死锁。

死锁处理方法:

  • 死锁预防(deadlock prevention):确保至少一个必要条件不成立。
  • 死锁避免(deadlock avoidance):操作系统根据事先得到的有关进程申请和使用资源的额外信息,考虑做出相应选择。
  • 如果不使用前面两种方案,那么死锁就可能发生,需要系统提供算法检测和恢复。

7.4 死锁预防

7.4.1 互斥

非共享资源的存在才可能满足互斥条件,但资源是共享还是非共享由它本身的性质决定,不好通过这个方面进行死锁预防。

7.4.2 持有且等待

为确保持有并等待条件不会出现,应保证:每个进程申请一个资源时,它不能占有其他资源。
一种可在用的协议:每个进程在执行前申请并获得所有资源。实现:要求进程申请资源的系统调用在所有其他系统调用之前进行。
另一种协议:允许进程仅在没有资源时才可申请资源。
以一个进程将数据从DVD驱动器复制到磁盘文件,对其进行排序后打印结果到打印机。第一种方案一开始申请到这三个资源,尽管打印机只在最后阶段才使用到,它也一直占有这个资源。第二种方案,先申请DVD驱动器和磁盘文件,再释放DVD驱动器和磁盘文件,然后申请磁盘文件和打印机。
这两种协议有两个主要缺点:资源利用率可能比较低,许多资源可能已经分配,但很长时间没有被使用;可能发生饥饿(一个进程需要多个常用资源,可能必须永久等待,因为所需的资源中至少一个已分配给其他进程)。

7.4.3 无抢占

为确保无抢占(不能抢占已分配的资源)条件不成立,可以采用以下协议:如果一个进程持有资源并申请另一个不能立即分配的资源(这个进程将等待),那么它现在分配的资源都可以被抢占。
如果一个进程申请一些资源,首先检查它们是否可用,如果可用则分配它们,如果不可用,则检查这些资源是否分配给等待额外资源的其他进程,如果是,那么从等待进程中抢占这些资源,并分配给申请进程。如果资源不可用也不被其他等待进程持有,那么申请进程应等待,该进程的部分资源可以被抢占。这个协议通常用于状态可以保存和恢复的资源,如CPU寄存器和内存,一般不适用于其他资源,如互斥锁和信号量。

7.4.4 循环等待

确保循环等待不成立的一个方法是:对所有资源类型进行完全排序,而且要求每个进程按递增顺序来申请资源。定义这样一个函数,F:R -> N,其中 N 是自然数集合这样每个资源类型对应一个自然数(不同资源类型对应不同自然数),例如资源类型 R 包括磁带驱动器、磁盘驱动器和打印机,那么 F 可定义成:

    F(磁盘驱动器) = 1
    F(磁带驱动器) = 5
    F(打印机) = 12

当一个进程开始申请了一定数量资源类型 Ri , 当且仅当 F(Rj) > max(F(Ri)) 时,该进程才可以申请资源类型 Rj 的实例。当要请求一个函数值较小的资源类型时,必须先释放掉所以函数值比它大的资源类型。可用反证法证明正确性。

7.5 死锁避免

7.5.1 安全状态

只有存在一个安全序列(safe sequence),系统才处于安全状态。进程序列 <P1, P2, … ,Pn> 在当前分配状态下为安全序列是指:对于每个 Pi 即使当前申请的资源不能立即可用,仍然可以通过等待直到所有 Pj(j < i) 释放资源,通过等待,Pi 可以得到需要的所有资源,当 Pi 终止时,Pi+1可得到他需要的资源。
安全状态不是死锁状态,死锁状态是非安全状态。然而不是所有的非安全状态都能导致死锁状态。
通过安全状态的概念,我们可以定义避免算法,以确保系统不会死锁。

7.5.2 资源分配图法

如果一个资源分配系统,它的每种资源类型只有一个实例,那么之前定义的资源分配图的一种变形可用于避免死锁。引入一新类型的边,称为需求边(claim edge)(需求边 Pi -> Rj 表示进程 Pi 可能在未来某时刻申请资源 Rj ,类似申请边,但用虚线表示)。当进程真的申请时,如果存在需求边,则需求边变成申请边,类似的,进程释放资源时,分配边变成了需求边。

系统资源的需求应事先说明。即当进程 Pi 开始执行时,所有需求边应先处于资源分配图内;也可以稍微放松这个条件:只有当与一个进程相关的所有边都是需求边时 ,才允许再添加需求边。

现假设进程 Pi 申请资源 Rj ,只有在申请边 Pi -> Rj 变成分配边 Rj -> Pi 并且不会导致资源分配图形成环时,才能允许申请。如果没有环存在,那么处于安全状态,否则处于非安全状态,这种情况进程 Pi 应等待资源申请。

7.5.3 银行家算法

对于每种资源类型有多个实例的资源分配系统,资源分配图算法就不适用了。银行家(banker’s algorithm)适用于这种系统。

当一个新进程进入系统时,它应声明可能需要的各种类型资源的最大数量,这一数量不能超过系统资源的总和。当用户申请一组资源时,系统应确定这些资源的分配是否仍能使系统处于安全状态,如果会则分配资源,否则,进程应等待,直到其他进程释放足够多资源为止。为了实现这一算法,需要以下数据结构对资源分配系统的状态进行记录( n 为系统进程的数量,m 为资源类型的种类):

  • Available:长度为 m 的向量,记录每种资源的实例数量。 Available[j] = k,则资源类型 Rj 有 k 个可用实例。
  • Max:n x m 矩阵,定义每个进程的最大需求。Max[i][j] = k,那么进程 Pi 最多可(会)申请资源类型 Rj 的 k 个实例。
  • Allocation:n x m 矩阵,定义每个进程现在分配的每种资源类型的实例数量。
  • Need:n x m 矩阵,表示每个进程还需要的剩余资源。Need[i][j] = k,那么进程 Pi 还可能申请 k 个资源类型 Rj 的实例。 Need[i][j] = Max[i][j] - Allocation[i][j]。
    这些数据结构的大小和值会随着时间而改变。可将矩阵 Allocation 和 Need 的每行作为向量,并分别用 Allocation_i 和 Need_i 表示。
7.5.3.1 安全算法

下面这个算法判断系统是否处于安全状态:

  • 1.令 Work 和 Finish 分别为长度 m 和 n 的向量。初始化 Work = Available;Finish[i] = false(i = 0,1,…,n-1)。
  • 2.查找这样的 i 使其满足:a.Finish[i] = false; b.Need_i <= Work; 如果没有这样的 i 存在,则跳到第4步。
  • 3.Work = Work + Allocation_i; Finish[i] = true; 返回第二步。
  • 4.如果对所有的 i,Finish[i] = true,那么系统处于安全状态。

第二步,如果一个进程未完成,且可以完成,则跳转到第三步;第三步分配资源,完成进程,然后回收资源。最后判断是否所有进程都可以完成,是则系统处于安全状态。这个算法可能需要 m x n2 数量级的操作。难点在第2步,需不断找出能够执行的进程,并分配资源,执行,然后回收资源,再寻找符合要求的进程。直至最后要么所有进程都完成,要么存在进程因请求的资源不足而不能完成。

7.5.3.2 资源请求算法

下面描述判断是否安全允许请求的算法。
设 Request_i 为进程 Pi的请求向量,当进程 Pi 做出请求时,采取如下动作:

  • 1.如果 Request_i <= Need_i,转到第2步,否则生成出错条件,因为 Pi 已超过其最大需求。
  • 2.如果 Request_i <= Available,转到第3步,否则 Pi 应等待,因为请求的资源大于可用资源。
  • 3.假定系统可以分配给进程 Pi 请求的资源,并按如下方式修改状态:
    Available = Available - Request_i
    Allocation_i = Allocation_i + Request_i
    Need_i = Need_i - Request_i

如果新的资源分配状态是安全的,那么交易完成,进程 Pi 可分配到需要的资源。否则 Pi 应等待 Request_i 并恢复到原来的资源分配状态。

7.6 死锁检测

如果一个系统既不采用死锁预防算法也不采用死锁避免算法,那么可能出现死锁,系统可以提供:

  • 一个用来检查系统状态从而确定是否出现死锁的算法;
  • 一个用于从死锁状态中恢复的算法。

下面的讨论中,对每种资源类型只有单个实例和可有多个实例的两种情况分别研究。

7.6.1 每种资源类型只有单个实例

从资源分配图中删除所有资源类型节点,合并适当边,得到一个资源分配图的变形:等待(wait-for)图。
等待图的从 Pi 到 Pj 的边表示:进程 Pi 等待进程 Pj 释放一个 Pi 所需要的资源。等待图有一条边 Pi -> Pj ,当且仅当相应的资源分配图包含两条边: Pi -> Rq 和 Rq -> Pj。

当且仅当等待图中有一个环,系统死锁。为了检测死锁,系统需维护等待图,并周期调用用于搜索图中环的算法,从图中检测环的算法需要 n2 数量级的操作,其中 n 为图的节点数。

7.6.2 每种资源类型可有多个实例

等待图方案不适用于这种系统,下面描述的死锁检测算法适用于这样的系统。下面是要用到的数据结构(数据结构和算法类似于银行家算法):

  • Available:长度为m的向量,表示各种资源的可用实例数量。
  • Allocation:n x m 矩阵,定义每个进程现在分配的每种资源类型的实例数量。
  • Request:n x m矩阵,表示当前每个进程的每种资源的当前请求。

这里描述的检测算法为需要完成的所有进程探究各种可能的分配序列:

  • 1.令 Work 和 Finish 分别为长度 m 和 n 的向量。初始化 Work = Available;如果 Allocation_i 不为0,则 Finish[i] = false,否则 Finish[i] = true(i = 0,1,…,n-1)。
  • 2.查找这样的 i 使其满足:a.Finish[i] = false; b.Request_i <= Work; 如果没有这样的 i 存在,则跳到第4步。
  • 3.Work = Work + Allocation_i ; Finish[i] = true。
  • 4.如果对于某个 i ,Finish[i] = false ,则系统死锁,且进程 Pi 死锁。

这里第一步为什么是根据 Allocation 判断,而不是 Request ???

7.6.3 应用检测算法

何时调用检测算法,答案取决于下面两个因素:

  • 死锁可能发生的频率。
  • 死锁发生时,受影响的进程数量。

7.7 死锁恢复

7.7.1 进程终止

通过中止进程消除死锁,有两种方法,这两种方法都允许系统收回终止进程的所有分配资源。

  • 中止所有死锁进程,代价较大,已计算出的就算结果也要放弃。
  • 一次中止一个进程,直到消除死锁循环为止。这种方法开销相当大,因为每次中止一个进程都应调用死锁检测算法,以确定是否仍有进程处于死锁。

如果采用部分终止,应确定哪个或哪些死锁进程应该中止呢?这类似于CPU调度决策,是策略决策。我们应中止造成代价最小的进程,然而最小代价(minimum cost)并不精确。

7.7.2 资源抢占

通过资源抢占来消除死锁,不断抢占一些进程的资源以便给其他进程使用,直到死锁循环被打破为止。这种方法需处理三个问题:

  • 选择牺牲进程:与进程终止一样,应确定抢占顺序,使得代价最小。
  • 回滚:如果从一个进程抢占了资源,应将该进程回滚到某个安全状态,以便从该状态重启进程。但很难确定什么是安全状态。最简单的解决方案是完全回滚:中止进程并重新执行;然而更有效的方法是回滚进程直到足够打破死锁。
  • 饥饿:如何确保不会发生饥饿(如果总是从同一个进程抢占资源,那么该进程永远不能完成指定任务)。最常用的方法是在代价因素中加上回滚次数。
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 计算机操作系统是一本经典的计算机科学教材,涵盖了操作系统的基本概念和实现细节。第二版的作者是庞丽萍老师,她在教授操作系统的多年经验和研究成果的基础上,对第一版进行了细微的修订和扩展。 《计算机操作系统第二版》PDF版本是一份共享的电子文档,对于操作系统学习者和研究者来说是非常有帮助的。本书讲解了操作系统的各个方面,从进程管理、内存管理到文件系统和IO等,涉及面广泛,深入浅出,每个主题都有大量代码和实例贯穿其中,便于读者理解。 此外,本书还介绍了一些经典的操作系统理论,如进程同步、、内存虚拟化等;并对操作系统的优化和安全方面进行了深入探讨。读者可以根据自己的需求,选择性地阅读相关章节,掌握所需要的知识。 总的来说,《计算机操作系统第二版》是一本操作系统方面的必备教材,无论是学生、从业者还是研究者,都可以从中受益匪浅。同时,PDF电子版的共享使得读者能够免费获取到这一宝贵的资源,更为广泛地传播学术知识。 ### 回答2: 《计算机操作系统(第2版)》是由庞丽萍所著的一本关于操作系统的教材,它具有广泛的应用价值和教育意义。 本书主要分为七个章节,涉及了操作系统的概述、进程管理、存储管理、文件系统、输入输出系统、网络操作系统和分布式操作系统等方面。每个章节都对相应的知识点进行了系统全面的阐述,以帮助读者深入理解操作系统的核心概念和实现技术。 本书使用清晰的语言和简洁的篇幅,使得读者可以轻松理解并掌握操作系统相关的内容。此外,本书还配有大量的案例和习题,可以帮助读者加深对操作系统问题的理解,并提高解决问题的能力。 总的来说,《计算机操作系统(第2版)》是一本结构严谨、信息全面、注重实践的优秀教材,它不仅适用于计算机相关的专业教育,也可供广大计算机爱好者借鉴和参考。同时,由于该书已经公开发布了电子版,方便读者随时随地进行阅读和学习。 ### 回答3: 《计算机操作系统第二版庞丽萍PDF》是一本关于操作系统的教材,由庞丽萍编写,主要讲解了操作系统的基础知识、进程管理、内存管理、文件系统、输入输出、安全等内容。这本书适合计算机专业的学生和从事计算机相关工作的人员阅读。 本书首先介绍了操作系统的基本概念和历史背景,对操作系统的功能、类型、结构、进程和线程、存储管理等方面进行了详细的论述。随后,本书重点讲解了进程管理、内存管理和文件系统三个方面。进程管理包括进程的概念、状态、并发、同步与互斥、进程通信等内容;内存管理包括内存的分配、保护、置换以及虚拟内存等知识;文件系统则讨论了文件的概念、组织、存储、保护等方面,并介绍了操作系统如何处理各种输入输出请求以及系统的安全性问题。 本书的编写风格简明易懂,适合在学校或自学时使用。通过本书的学习,读者能够掌握操作系统的基本原理和实现技术,更好地理解和应用计算机。值得一提的是,本书配有大量的案例、实验和习题,让读者能够进一步巩固知识。总之,《计算机操作系统第二版庞丽萍PDF》是一本值得推荐的计算机操作系统教材,具有很高的实用价值和教学参考价值。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值