软考(软件设计师)进程管理—死锁,RAG,银行家算法


第一部分:死锁(Deadlock)

1. 死锁的定义

死锁是指两个或多个进程(或线程) 在执行过程中,因争夺资源而造成的一种相互等待的现象。若无外力干涉,这些进程都将无法向前推进。

2. 死锁产生的四个必要条件

死锁的发生必须同时满足以下四个条件:

  1. 互斥条件: 资源一次只能被一个进程独占使用(如打印机、临界区代码)。
  2. 持有并等待(请求保持条件): 一个进程在持有至少一个资源的同时,又在等待获取其他进程持有的资源。
  3. 非抢占(不可剥夺条件): 资源不能被强制从持有它的进程那里剥夺,只能由持有者主动释放。
  4. 循环等待(环路条件): 存在一个进程等待链:P0 等待 P1 持有的资源,P1 等待 P2 持有的资源,……,Pn 等待 P0 持有的资源。

3. 处理死锁的策略

  1. 预防(Prevention): 在设计系统时,确保至少破坏死锁四个必要条件中的一个
    • 破坏互斥: 让某些资源可共享(如只读文件)。但很多资源本质必须互斥(如打印机)。
    • 破坏持有并等待:
      • 方案1:一次性申请所有资源。 进程在运行前申请所需的所有资源,否则阻塞直到所有资源都可用。缺点:资源利用率低,可能饥饿。
      • 方案2:申请新资源前先释放所有已持有资源。 缺点:代价大,可能导致状态丢失。
    • 破坏非抢占:
      • 如果进程请求资源失败,强制释放其持有的某些资源(可能导致该进程回退)。
      • 如果进程请求的资源被其他等待进程持有,可以强制剥夺该资源(实现复杂,代价高)。
    • 破坏循环等待:
      • 强制顺序申请资源。 给所有资源类型编号,进程只能按递增顺序申请资源(如必须先申请编号小的资源,才能申请编号大的资源)。这是最实用的预防方法。
  2. 避免(Avoidance): 在资源分配时动态检查,确保系统始终处于安全状态,从而避免进入可能死锁的状态。核心算法就是银行家算法(第二部分详解)。
  3. 检测(Detection)与恢复(Recovery): 允许死锁发生,但系统能检测到死锁并采取措施恢复
    • 检测: 周期性地运行死锁检测算法(如基于资源分配图RAG的简化方法)。
    • 恢复:
      • 进程终止:
        • 终止所有死锁进程: 简单粗暴,代价高。
        • 终止部分进程(按优先级、代价最小原则): 逐步终止进程直到死锁解除。
      • 资源抢占:
        • 选择一个牺牲者进程,强制剥夺其资源给其他进程。
        • 需解决回滚(Rollback) 问题(进程状态恢复)和饥饿(Starvation) 问题(同一进程反复被选为牺牲者)。

4.资源分配图(RAG)化简步骤详解(带图例)

以下是资源分配图化简的核心步骤,结合具体例子和图解说明:

找到进程P只有分配边
找不到符合条件的P
开始化简
寻找非孤立结点
移除P的所有分配边
释放P的资源
有进程等待该资源?
将等待进程的请求边变为分配边
资源标记为空闲
所有结点都孤立?
无死锁
存在死锁

案例演示(系统资源:R1(2个实例), R2(1个实例)

进程状态:

  • P1:持有R1×1,请求R2
  • P2:持有R1×1,请求R2
  • P3:持有R2×1,请求R1
初始资源分配图
分配
分配
分配
请求
请求
请求
R1:2个资源
P1
P2
R2:1个资源
P3

化简步骤演示
步骤1:找非孤立且只有分配边的进程

✅ 符合条件:无(P1/P2有请求边,P3有请求边)

步骤2:找非孤立且只有请求边的资源

🔍 发现资源R2:

  • 有2个请求边(P1→R2, P2→R2)
  • 但无空闲实例(已被P3占用)
  • 无法化简

死锁验证
请求
分配
请求
分配
分配
请求
P1
R2
P3
R1
P2
  • 环路1:P1→R2→P3→R1→P1
  • 环路2:P2→R2→P3→R1→P2
  • 结论:存在死锁!

无死锁案例对比

修改后的状态

  • P1:持有R1×1,请求R2
  • P3:持有R2×1(无请求)
初始图
分配
空闲
分配
请求
R1:2个资源
P1
null
R2:1个资源
P3
化简过程
  1. 找到P3:只有分配边(无请求边)
移除分配边
R2
P3
  1. 释放R2资源
R2:1个资源
  1. 转换P1的请求边
请求边转分配边
R2
P1
  1. 找到P1:只有分配边(R1→P1和R2→P1)
移除分配边
移除分配边
R1
P1
R2
  1. 所有结点变孤立无死锁

关键总结表
步骤操作目标成功标志死锁判断
1. 找只有分配边的进程释放其所有资源进程变孤立若找不到则进入步骤2
2. 转换等待进程请求边请求边→分配边资源重新分配循环直到无法操作
最终状态检查所有结点全孤立=无死锁有非孤立结点=存在死锁

第二部分:银行家算法(Banker’s Algorithm)

银行家算法是 Dijkstra 提出的一种死锁避免算法。它模拟银行家放贷的策略:在分配资源前,先检查此次分配是否会导致系统进入不安全状态。只有能确保系统始终处于安全状态的分配请求才会被批准。

1. 核心概念与数据结构

假设系统有 n 个进程 (P0, P1, ..., Pn-1) 和 m 种资源类型 (R0, R1, ..., Rm-1)。

  • 可用资源向量(Available Vector) - Available[1..m]
    • 长度为 m 的数组。
    • Available[j] = k 表示资源类型 Rj 当前有 k 个实例可用。
  • 最大需求矩阵(Max Matrix) - Max[n][m]
    • n x m 矩阵。
    • Max[i][j] = k 表示进程 Pi 最多需要 k 个资源类型 Rj 的实例。
  • 已分配矩阵(Allocation Matrix) - Allocation[n][m]
    • n x m 矩阵。
    • Allocation[i][j] = k 表示进程 Pi 当前已持有 k 个资源类型 Rj 的实例。
  • 需求矩阵(Need Matrix) - Need[n][m]
    • n x m 矩阵。
    • Need[i][j] = k 表示进程 Pi 未来还可能需要 k 个资源类型 Rj 的实例。
    • 计算: Need[i][j] = Max[i][j] - Allocation[i][j]

2. 安全性算法(Safety Algorithm)

用于判断当前系统状态是否安全。安全状态意味着存在一个安全序列(Safe Sequence):一个进程执行顺序 <P1, P2, ..., Pn>,使得按此顺序执行,每个进程 Pi 都能顺利完成(即 Pi 需要的资源 <= 当前可用资源 + 所有排在它前面的进程 Pj (j < i) 释放的资源)。

算法步骤:

  1. 初始化:
    • Work[1..m] = Available[1..m] // 工作向量,初始化为当前可用资源
    • Finish[1..n] = false // 标记每个进程是否完成
  2. 寻找一个进程 Pi 满足:
    • Finish[i] == false // 进程未完成
    • Need[i][1..m] <= Work[1..m] // 该进程所需的所有资源 <= 当前可用资源 (Work)
    • 如果找到,执行步骤 3;否则(一个都找不到),执行步骤 4。
  3. 假设进程 Pi 运行完成并释放其持有的资源:
    • Work[1..m] = Work[1..m] + Allocation[i][1..m] // 回收 Pi 的资源
    • Finish[i] = true // 标记 Pi 完成
    • 返回步骤 2。
  4. 检查所有进程是否完成:
    • 如果 Finish[i] == true 对所有 i 都成立,则系统处于安全状态
    • 否则,系统处于不安全状态

3. 资源请求算法(Resource-Request Algorithm)

当进程 Pi 发出资源请求向量 Request_i[1..m] 时,银行家算法执行以下步骤决定是否批准请求:

  1. 基本检查:
    • 如果 Request_i[j] <= Need[i][j] 对所有 j (1 ≤ j ≤ m) 成立,则转步骤 2。否则,请求非法(进程申请超过其声明的最大需求),报错拒绝。
    • 如果 Request_i[j] <= Available[j] 对所有 j (1 ≤ j ≤ m) 成立,则转步骤 3。否则,Pi 必须等待(资源不足)。
  2. 试探性分配(Pretend to Allocate): 假设系统批准了请求:
    • Available[j] = Available[j] - Request_i[j] // 更新可用资源
    • Allocation[i][j] = Allocation[i][j] + Request_i[j] // 更新已分配资源
    • Need[i][j] = Need[i][j] - Request_i[j] // 更新需求资源
  3. 检查安全性: 运行安全性算法检查试探性分配后的新状态是否安全。
    • 如果新状态是安全的:则实际执行分配操作(即确认步骤 2 的试探性分配)。
    • 如果新状态是不安全的:则拒绝请求!进程 Pi 必须等待。撤销步骤 2 的试探性分配,恢复原状态(Available, Allocation, Need 回滚)。

4. 银行家算法示例 (简化)

一、核心数据结构(矩阵表示)

初始状态矩阵(5个进程,3类资源)

进程Allocation (已分配)Max (最大需求)Need (还需资源)Available (可用资源)
A B CA B CA B CA B C
P00 1 07 5 37 4 33 3 2
P12 0 03 2 21 2 2
P23 0 29 0 26 0 0
P32 1 12 2 20 1 1
P40 0 24 3 34 3 1

关键公式:Need = Max - Allocation

二、安全性检查算法流程

步骤1:初始化工作向量

Work = Available = [3, 3, 2]
Finish = [false, false, false, false, false]

步骤2:寻找可执行进程

7,4,3 > 3,3,2
1,2,2 < 3,3,2
检查P0
跳过
检查P1
选中P1

步骤3:执行P1并更新矩阵

Work = Work + Allocation[P1] = [3,3,2] + [2,0,0] = [5,3,2]
Finish[P1] = true
进程AllocationMaxNeedWork更新
P12 0 03 2 21 2 2[5,3,2] ✅

步骤4:继续寻找可执行进程

7,4,3 > 5,3,2
6,0,0 > 5,3,2
0,1,1 < 5,3,2
检查P0
跳过
检查P2
跳过
检查P3
选中P3

步骤5:执行P3并更新矩阵

Work = [5,3,2] + [2,1,1] = [7,4,3]
Finish[P3] = true
进程AllocationMaxNeedWork更新
P32 1 12 2 20 1 1[7,4,3] ✅

最终安全序列

开始
P1
P3
P0
P2
P4
安全状态

完整安全序列:P1 → P3 → P0 → P2 → P4

三、资源请求算法示例

场景:P1请求资源 Request = [1, 0, 2]

步骤1:检查请求合法性

1,0,2 < 1,2,2
1,0,2 < 3,3,2
请求 < Need?
请求 < Available?

步骤2:模拟分配(更新矩阵)

矩阵更新前更新后
Available[3,3,2][3-1,3-0,2-2]=[2,3,0]
Allocation[P1][2,0,0][2+1,0+0,0+2]=[3,0,2]
Need[P1][1,2,2][1-1,2-0,2-2]=[0,2,0]

步骤3:安全性检查(新状态)

进程AllocationNeedWorkFinish
A B CA B CA B C
P00 1 07 4 32 3 0false
P13 0 20 2 0false
P23 0 26 0 0false
P32 1 10 1 1false
P40 0 24 3 1false

安全序列查找过程

  1. P1: Need[0,2,0] ≤ Work[2,3,0] → 执行

    Work = [2,3,0] + [3,0,2] = [5,3,2]
    
  2. P3: Need[0,1,1] ≤ [5,3,2] → 执行

    Work = [5,3,2] + [2,1,1] = [7,4,3]
    
  3. P0: Need[7,4,3] ≤ [7,4,3] → 执行

    Work = [7,4,3] + [0,1,0] = [7,5,3]
    
  4. P2: Need[6,0,0] ≤ [7,5,3] → 执行

    Work = [7,5,3] + [3,0,2] = [10,5,5]
    
  5. P4: Need[4,3,1] ≤ [10,5,5] → 执行

✅ 存在安全序列:P1 → P3 → P0 → P2 → P4
✅ 请求被批准

五、死锁场景(无安全序列)

场景:P2请求资源 Request = [1, 0, 1]

模拟分配后状态:

矩阵
Available[2,3,1]
Allocation[P2][4,0,3]
Need[P2][5,0,-1] ❌(无效)

⚠️ 请求被拒绝:请求超过最大需求(Need矩阵出现负值)

六、算法总结流程图

No
Yes
No
Yes
安全
不安全
接收请求Req
Req < or = Need?
错误:超出声明
Req < or = Available?
进程等待
模拟分配
执行安全性算法
分配资源
撤销分配
进程等待

银行家算法矩阵操作要点

  1. 资源请求时需双验证:Req ≤ NeedReq ≤ Available
  2. 安全性检查核心:寻找安全序列
  3. 矩阵更新需保持一致性:
    • Available = Available - Request
    • Allocation = Allocation + Request
    • Need = Need - Request
  4. 安全状态判断标准:存在完整安全序列
  5. 多资源类型需逐元素比较

💡 实际应用:该算法需要预知进程最大资源需求,适用于批处理系统或资源分配固定的场景,在交互式系统中应用有限但原理重要。

5. 银行家算法的优缺点

  • 优点:
    • 理论上能完全避免死锁
    • 比死锁预防策略(如强制顺序申请)更灵活,资源利用率可能更高。
  • 缺点(限制):
    • 需要预知最大需求 (Max 矩阵): 进程必须事先声明其整个生命周期所需的最大资源量。这在动态变化的系统中很难做到。
    • 进程数量固定: 算法假设进程数量 n 是固定的。在动态创建/销毁进程的环境中,维护 Max, Allocation, Need 矩阵很困难。
    • 开销大: 每次资源请求都需要运行安全性算法(时间复杂度约为 O(m * n^2)),在进程和资源数量多时开销显著。
    • 资源类型固定: 资源类型 m 需要固定。
    • 可能导致不必要的拒绝: 即使系统有足够资源且分配后实际不会死锁,但安全性算法认为不安全也会拒绝请求,降低了资源利用率。
  • 实际应用: 由于上述限制,银行家算法很少在通用操作系统的核心资源分配中直接使用。但它作为死锁避免理论的典范被广泛研究和教学。其思想(安全状态检查)可能被用于特定场景(如某些数据库管理系统、嵌入式系统)。

线程

一、线程基础概念

进程
线程
轻量级执行单元
共享进程资源
独立执行流

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值