【操作系统】Operation System-第11章-死锁

操作系统—死锁

image-20220602193107385

死锁问题

image-20220602193417182

  • 一组阻塞的进程持有一种资源等待获取另一个进程所占有的一个资源

  • 例子

    ➢ 系统有 2 2 2 个磁带驱动器

    P 1 P_1 P1 P 2 P_2 P2 各有一个,都需要另外一个

为什么出现死锁?由于进程的并发执行抢占资源。

系统模型

  • 资源类型 R 1 R_1 R1 R 2 R_2 R2 . . . ... ... R m R_m Rm

    C P U   c y c l e s CPU\ cycles CPU cycles m e m o r y   s p a c e memory\ space memory space I / O   d e v i c e s I/O\ devices I/O devices

  • 每个资源类型 R i R_i Ri W i W_i Wi 个实例.

  • 每个进程使用资源如下:

    r e q u i r e / g e t < = f r e e   r e s o u r c e require / get <= free\ resource require/get<=free resource

    u s e / h o l d < = r e q u e s t e d / u s e d   r e s o u r c e use / hold <= requested / used\ resource use/hold<=requested/used resource

    r e l e a s e < = f r e e   r e s o u r c e release <= free\ resource release<=free resource

可重复使用的资源

  • 在一个时间只能有一个进程使用且不能被删除
  • 进程获得资源,后来释放由其他进程重用
  • 处理器,I/O通道,主和副存储器,设备和数据结构,如文件,数据库和信号量
  • 如果每个进程拥有一个资源并请求其他资源,死锁可能发生

使用资源

  • 创建和销毁
  • 在I/O缓存区的中断,信号,消息,信息
  • 如果接收消息阻塞可能会发生死锁
  • 可能少见的组合事件会引起死锁

资源分配图

一组顶点 V V V 和边 E E E 的集合

  • V V V 有两种类型:

    P = { P 1 , P 2 , . . . , P n } P=\{P_1,P_2,...,P_n\} P={ P1P2...Pn},集合包括系统中的所有进程。

    R = { R 1 , R 2 , . . . , R m } R=\{R_1,R_2,...,R_m\} R={ R1R2...Rm},集合包括系统中的所有资源类型。

  • r e q u e s t i n g / c l a i m i n g   e d g e − d i r e c t e d   e d g e   P i = > R j requesting/claiming\ edge - directed\ edge\ P_i => R_j requesting/claiming edgedirected edge Pi=>Rj

  • a s s i g n m e n t / h o l d i n g   e d g e − d i r e c t e d   e d g e   R j = > P i assignment/holding\ edge - directed\ edge\ R_j => P_i assignment/holding edgedirected edge Rj=>Pi

资源分配图(续)

image-20220602222757671

资源分配图例子(无死锁)

image-20220602222821999

资源分配图例子(有死锁)

image-20220602223928930

有循环的资源分配图但没有死锁

image-20220602224238828

基本情况

  • 如果图中不包含循环

    ➢ 没有死锁。

  • 如果图中包含循环

    ➢ 如果每个资源类只有一个实例,那么死锁。

    ➢ 如果每个资源类有几个实例,可能死锁。

死锁特征

死锁出现一定会出现以下四个条件,但是出现以下四个条件不一定死锁:

  • 互斥:在一个时间只能有一个进程使用资源
  • 持有并等待:进程保持至少一个资源正在等待获取其他进程持有的额外资源
  • 无抢占:一个资源只能被进程资源释放,进程已经完成了它的任务之后
  • 循环等待:存在等待进程集合 { P 0 , P 1 , . . . , P n } \{P_0,P_1,...,P_n\} { P0P1...Pn} P 0 P_0 P0 正在等待 P 1 P_1 P1 所占用的资源, P 1 P_1 P1 正在等待 P 2 P_2 P2 占用的资源,…, P n − 1 P_{n-1} Pn1 在等待 P n P_n Pn 的资源, P n P_n Pn 正在等待 P 0 P_0 P0 所占用的资源

死锁处理方法

  • 确保系统永远不会进入死锁状态。
  • 运行系统进入死锁状态,然后恢复。
  • 忽略这个问题,假装系统中从来没有发生死锁,用于大多数操作系统,包括UNIX。

Deadlock Prevention(死锁预防)

限制申请方式

  • 互斥 - 共享资源不是必须的,必须占用非共享资源。

  • 占用并等待 - 必须保证当一个进程请求的资源,它不持有任何其他资源。

    ➢ 需要进程请求并分配其所有资源,它开始执行之前或允许进程请求资源仅当进程没有资源

    ➢ 资源利用率低,可能发生饥饿

  • 无抢占

    ➢ 如果进程占有某些资源,并请求其他不能被立即分配的资源,则释放当前正占有的资源

    ➢ 被抢占资源添加到资源列表中

    ➢ 只有当它能够获得旧的资源以及它请求新的资源,进程可以得到执行

  • 循环等待 - 对所有资源类型进行排序,并要求每个进程按照资源的顺序进行申请。

Deadlock Avoidance(死锁避免)

需要系统具有一些额外的先验信息提供

  • 最简单和最有效的模式是要求每个进程声明它可能需要的每个类型资源的最大数目

  • 资源的分配状态是通过限定提供分配的资源数量,和进程的最大需求

  • 死锁避免算法动态检查的资源分配状态,以确保永远不会有一个环形等待状态

  • 当一个进程请求可用资源,系统必须判断立即分配是否能使系统处于安全状态

  • 系统处于安全状态指:针对所有进程,存在安全序列

  • 序列 < P 1 , P 2 , . . . , P n > <P1,P2,...,Pn> <P1P2...Pn> 是安全的:针对每个 P i P_i Pi P i P_i Pi 要求的资源能够由当前可用的资源 + 所有的 P j P_j Pj 持有的资源来满足,其中 j < i j<i j<i

    ➢ 如果 P i P_i Pi 资源的需求不是立即可用,那么 P i P_i Pi 可以等到所有 P j P_j Pj 完成

    ➢ 当 P i P_i Pi 完成后, P i + 1 P_{i+1} Pi+1 可以得到所需要的资源,执行,返回所分配的资源,并终止。

    ➢ 用同样的方法, P i + 2 P_{i+2} Pi+2 P i + 3 P_{i+3} Pi+3 P n P_n Pn 能获得其所需的资源。

  • 如果系统处于安全状态 => 无死锁

  • 如果系统处于不安全状态 => 可能死锁

  • 避免死锁:确保系统永远不会进入不安全状态

image-20220604122944784

如下图,死锁避免机制检验下一时刻的状态是否安全,然后根据情况将 e d g e edge edge 转换(图中实现虚线的转换,即尽管你有请求意图,但是可能并不允许占用)。

image-20220604124522841

银行家算法

Banker’s Algorithm

银行家算法(Banker’s Algorithm)是一个死锁避免的著名算法,是由艾兹格 · 迪杰斯特拉在1965年为T. H. E系统设计的一种避免死锁产生的算法。
它以银行借贷系统的分配策略为基础,判断并保证系统的安全运行。

背景

在银行系统中,客户完成项目需要申请贷款的数量是有限的,每个客户在第一次申请贷款时要声明完成该项目所需的最大资金量,在满足所有贷款要求并完成项目时,客户应及时归还。

银行家在客户申请的贷款数量不超过自己拥有的最大值时,都应尽量满足客户的需要。

在这样的描述中,银行家就好比操作系统,资金就是资源,客户就相当于要申请资源的进程。

前提条件

  • 多个实例
  • 每个进程都必须能最大限度地利用资源
  • 当一个进程请求一个资源,就不得不等待
  • 当一个进程获得所有的资源就必须在一段有限的时间释放它们

基于上述前提条件,银行家算法通过尝试寻找允许每个进程获得的最大资源并结束(把资源返还给系统)的进程请求一个理想执行时序,来决定一个状态是否是安全的。

不存在这满足要求的执行时序的状态都是不安全的。

数据结构

n = 进 程 数 量 , m = 资 源 类 型 数 量 n = 进程数量,m=资源类型数量 n=m=

  • M a x ( 总 需 求 量 ) Max(总需求量) Max n × m n×m n×m 矩阵。如果 M a x [ i , j ] = k Max[i,j]=k Max[i,j]=k,表示进程 P i P_i Pi 最多请求资源类型 R j R_j Rj k k k 个实例。
  • A v a i l a b l e ( 剩 余 空 限 量 ) Available(剩余空限量) Available:长度为 m m m 的向量,如果 A v a i l a b l e [ j ] = k Available[j]=k Available[j]=k,有 k k k 个类型 R j R_j Rj 的资源实例可用。
  • A l l o c a t i o n ( 已 分 配 量 ) Allocation(已分配量) Allocation n × m n×m n×m 矩阵,如果 A l l o c a t i o n [ i , j ] = k Allocation[i,j]=k Allocation[i,j]=k,则 P i P_i Pi 当前分配了 k k k R j R_j Rj 实例。
  • N e e d ( 未 来 需 要 量 ) Need(未来需要量) Need n × m n×m n×m 矩阵,如果 N e e d [ i , j ] = k Need[i,j]=k Need[i,j]=k,则 P i P_i Pi 可能需要至少 k k k R j R_j Rj 实例完成任务。

N e e d [ i , j ] = M a x [ i , j ] − A l l o c a t i o n [ i , j ] Need[i,j]=Max[i,j]-Allocation[i,j] Need[i,j]=Max[i,j]Allocation[i,j]

Safety State Estimating Algorithm(安全状态估计算法)

  1. W o r k Work Work F i n i s h Finish Finish 分别是长度为 m m m n n n 的向量,初始化:

    Work = Available					 // 当前资源剩余空闲量
    Finish[i] = false for i - 1,2,...,n	 // 线程i没结束 如果为true则说明结束
    
  2. 找这样的 i i i:(找出 N e e d Need Need W o r k Work Work 小的进程 i i i

    (a) Finish[i] = false
    (b) Need[i] <= Work
    

    没有找到这样的 i i i,转到 4 4 4,找到,转 3 3 3

  3. 认为进程可以正常结束,并回收资源

    Work = Work + Allocation[i]	// 进程i的资源需求量小于当前的剩余空闲资源量
    Finish[i] = true			// 所以配置给它再回收
    

    转到 2 2 2

  4. 检验是否是处于安全状态

    If Finish[i] == true for all i,
    	then the system is in a safe state.
    // 所有进程的Finish为true,表明系统处于安全状态。
    

Banker’s Algorithm(银行家算法)

基于上述安全状态判断算法,设计银行家算法。

Initial: Request = request vector for process Pi. If Request[i, j] = k then process Pi wants k instances of resource type Rj.

W h i l e : While: While:

  1. 如果 R e q u e s t i ⩽ N e e d i Request_i \leqslant Need_i RequestiNeedi,转到步骤 2 2 2。否则,提出错误条件,因为进程i已经超过了其最大要求

  2. 如果 R e q u e s t i ⩽ A v a i l a b l e Request_i \leqslant Available RequestiAvailable,转到步骤 3 3 3。否则, P i P_i Pi 必须等待,因为资源不可用。

  3. 假装给 P i P_i Pi 分配它需要的资源:(生成一个需要判断状态是否安全的资源分配环境)

    A v a i l a b l e = A v a i l a b l e − R e q u e s t i ; Available = Available - Request_i; Available=AvailableRequesti;
    A l l o c a t i o n i = A l l o c a t i o n i + R e q u e s t i ; Allocation_i = Allocation_i + Request_i; Allocationi=Allocationi+Requesti;
    N e e d i = N e e d i − R e q u e s t i ; Need_i = Need_i - Request_i; Needi=NeediRequesti;

    将上述 3 3 3 个变量作为安全状态判断算法的 i n p u t input input

    CALL Safety State Estimating Algorithm

    ➢ 如果返回 s a f e safe safe,将资源分配给 P i P_i Pi

    ➢ 如果返回 u n s a f e unsafe unsafe P i P_i Pi 必须等待,旧的资源分配状态被恢复

银行家算法的安全状态判断示例

初始状态:

image-20220604140521137

对于上面目前的 A v a l i a b l e Avaliable Avaliable,再参考每个进程的 N e e d Need Need,只有 P 2 P_2 P2 满足,所以 P 2 P_2 P2 执行,执行完将所属的 A l l o c a t i o n Allocation Allocation 释放,如下图:

image-20220604155901112

接下来可使 P 1 P_1 P1 执行:

image-20220604160459240

此时剩余的 P 3 P_3 P3 P 4 P_4 P4 都可以满足。

image-20220604160937602

得到了一个安全序列: < P 2 , P 1 , P 3 , P 4 > <P_2,P_1,P_3,P_4> <P2P1P3P4>,此时银行家算法判断完返回 s a f e safe safe,它就会给发出请求的进程提供所需要的资源。

银行家算法的安全状态判断示例2

image-20220604161222194

此时可以先执行 P 2 P_2 P2,但假如此时进程 P 1 P_1 P1 申请了 1 , 0 , 1 1,0,1 1,0,1 的资源,并分配给它后,如下图:

image-20220604161259333

此时对于任意一个进程 P i P_i Pi 发出的 N e e d Need Need 请求, A v a i l a b l e Available Available 都无法满足,意味着这是一个 u n s a f e unsafe unsafe 状态,此时银行家算法不会满足进程 P 1 P_1 P1 发出的 1 , 0 , 1 1,0,1 1,0,1 请求。

银行家算法的思路就是:假定我分配之后,剩下的状态是否是安全的,如果安全则分配,反之则不分配。

Deadlock Detection(死锁检测)

  • 允许系统进入死锁状态
  • 死锁检测算法
  • 恢复机制

把死锁的检测放到了更靠后的阶段,不是在每次发出请求时判断,而是在系统运行中的某一个特定时间做一下检测判断。

有死锁 没关系,我们把它检测出来并恢复。

每个资源类型单一实例

  • Maintain wait-for graph

    ➢ 节点是进程

    P i → P j P_i→P_j PiPj P i P_i Pi 等待 P j P_j Pj

  • 定期调用检测算法来搜索图中是否存在循环

  • 算法需要 n 2 n^2 n2 次操作, n n n 是图中顶点的数目

image-20220604162532229

数据结构(资源类型的几个实例)

  • A v a i l a b l e Available Available:长度为 M M M 的向量表示每种类型可用资源的数量。
  • A l l o c a t i o n Allocation Allocation:一个 n × m n×m n×m 矩阵定义了当前分配给各个进程每种类型资源的数量,如果 A l o c a t i o n [ i , j ] = k Alocation[i, j] = k Alocation[i,j]=k, 进程 P i P_i Pi 拥有资源 R j R_j Rj k k k 个实例。
  • R e q u e s t Request Request:一个 n × m n×m n×m 矩阵表示各进程的当前请求.如果 R e q u e s t [ i , j ] = k Request[i, j] = k Request[i,j]=k,表示进程 P i P_i Pi 请求 k k k 个资源 P j P_j Pj 的实例。

死锁检验算法

  1. W o r k Work Work F i n i s h Finish Finish 分别是长度为 m m m n n n 的向量,初始化:

    (a) Work = Available	// work为当前空闲资源量
    (b) For i = 1,2,...,n if Allocation[i] > 0
    	then Finish[i] = false
    	otherwise Finish[i] = true
    	// Finish为线程是否结束
    
  2. 找出这样的索引 i i i

    (a) Finsih[i] == false	// 线程没有结束
    (b) Request[i] <= Work	// 且需要资源量小于当前空闲资源量
    

    如果没有找到,转步骤 4 4 4,找到,转步骤 3 3 3

  3. 让该线程正常结束,将该线程持有资源释放回空闲资源中

    Work = Work + Allocation[i]
    Finish[i] = true
    

    转步骤 2 2 2

  4. 检验 F i n i s h Finish Finish 是否有死锁

    if Finish[i] == false, for some i, 1 <= i <= n	// 系统处于死锁状态
    if Finish[i] == false	// Pi死锁
    // 如果有Finish[i]等于false 这表示系统处于死锁状态
    

定期的执行该算法,检测系统中是否有环。

算法需要 O ( m × n 2 ) O(m×n^2) O(m×n2) 操作检测是否系统处于死锁状态。

银行家算法、死锁检测算法都需要先检测每个进程的最大需求量,时间复杂度比较高,因此,死锁检测过程更多是用来调试应用程序、操作系统,真正正常的跑某个系统基本不会用死锁检测算法。

检查算法使用

  • 何时,使用什么样的频率来检测依赖于:

    ➢ 死锁多久可能会发生?

    ➢ 多少进程需要被回滚?

    one for each disjoint cycle

  • 如果检测算法多次被调用,有可能是资源图有多个循环,所以我们无法分辨出多个可能死锁进程中的哪些“造成”死锁。

检测算法在实际的运行环境中很难使用,更多是用在开发阶段来判断系统是否正确。

Recovery from Deadlock(死锁恢复)

  • 终止所有的死锁进程

  • 在一个时间内终止一个进程直到死锁消除

  • 终止进程的顺序应该是

    ➢ 进程的优先级

    ➢ 进程运行了多久以及需要多少时间才能完成

    ➢ 进程占用的资源

    ➢ 进程完成需要的资源

    ➢ 多少进程需要被终止

    ➢ 进程是交互还是批处理

  • 选择一个受害者 - 最小的成本。

  • 回滚 - 返回到一些安全状态,重启进程到安全状态。

  • 饥饿 - 同一进程可能一直被选作受害者,包括回滚的数量。

整理自 【清华大学】 操作系统

猜你喜欢

转载自blog.csdn.net/weixin_53407527/article/details/125182514
今日推荐