O07-银行家算法

银行家算法 🏦

本文档系统介绍银行家算法的核心原理、数据结构、安全性检查机制和完整的 Python 实现。通过理论与实践相结合的方式,帮助读者深入理解操作系统中死锁避免的经典算法 🔐

章节阅读路线图 🗺️

  1. 算法概述 → 理解银行家算法的核心思想和应用场景
  2. 核心数据结构 → 掌握四大关键数据结构及其关系
  3. 安全性检查算法 → 学习如何判断系统是否处于安全状态
  4. 资源请求算法 → 理解进程资源请求的处理流程
  5. 总结 → 回顾核心要点

1. 算法概述 📖

本章介绍银行家算法的核心思想、历史背景和应用场景

1.1 什么是银行家算法?

银行家算法(Banker's Algorithm)是一个避免死锁的著名算法,由荷兰计算机科学家艾兹赫尔·戴克斯特拉(Edsger W. Dijkstra)在 1965 年为 T.H.E 操作系统设计。该算法以银行借贷系统的分配策略为基础,通过预防性措施来确保系统的安全性,判断并保证系统始终处于安全状态。

核心思想:操作系统在分配资源前,先模拟资源分配的过程,判断分配后系统是否仍处于安全状态。如果安全则分配,否则让进程等待。

类比理解

想象一个银行场景:

  • 银行(操作系统)管理着有限的资金(资源)
  • 客户(进程)申请贷款(资源请求)
  • 银行家在批准贷款前会思考:"如果我把这笔钱贷出去,金库里剩余的钱是否还能满足至少一个客户的全部需求?"
  • 如果可以,说明系统是安全的,可以批准贷款
  • 如果不行,说明可能导致"挤兑"(死锁),必须拒绝或让客户等待

这就是银行家算法"谨慎的慷慨"设计哲学------既想最大限度地把资源分配出去提高系统利用率,又必须确保在任何时候都能避免死锁。

1.2 为什么需要银行家算法?

在操作系统中,多个进程并发执行时需要共享系统资源。如果资源分配不当,可能导致死锁(Deadlock)------多个进程互相等待对方释放资源,导致所有进程都无法继续执行。

死锁产生的四个必要条件

  1. 互斥条件:资源一次只能被一个进程使用
  2. 占有并等待:进程已占有部分资源,同时等待其他资源
  3. 不可抢占:资源不能被强制从进程中夺走
  4. 循环等待:存在一个进程等待环路

银行家算法通过破坏循环等待条件来避免死锁,它在资源分配前进行安全性检查,确保系统始终处于安全状态。

1.3 安全状态与安全序列

安全状态:如果系统存在至少一个进程执行序列,使得按照该序列依次为每个进程分配其所需资源,所有进程都能顺利完成,则称系统处于安全状态。

安全序列 :一个进程序列 ⟨P1,P2,...,Pn⟩ \langle P_1, P_2, \ldots, P_n \rangle ⟨P1,P2,...,Pn⟩,如果对于每个进程 Pi P_i Pi,其后续所需的资源都能被系统当前可用资源(加上之前已完成进程释放的资源)所满足,则该序列称为安全序列。

关键结论

  • 系统处于安全状态 ⇒\Rightarrow ⇒ 不会发生死锁
  • 系统处于不安全状态 ⇒\Rightarrow ⇒ 可能发生死锁(不一定会发生)
  • 银行家算法的目标:确保系统始终处于安全状态

参考资料:


2. 核心数据结构 📊

本章详解银行家算法的四个关键数据结构及其数学关系

为了实现银行家算法,系统中必须设置四个核心数据结构,分别描述系统中可利用的资源、所有进程对资源的最大需求、系统中的资源分配情况,以及所有进程还需要多少资源。

假设系统中有:

  • nn n 个进程: P0,P1,..., Pn−1 P_0, P_1, \ldots, P_{n-1} P0,P1,...,Pn−1
  • mm m 类资源: R1,R2,...,Rm R_1, R_2, \ldots, R_m R1,R2,...,Rm

2.1 可用资源向量(Available)

定义 :一个长度为 mm m 的一维数组,表示系统中每类资源当前的可用数量。
Available=v1,v2,...,vm \text{Available} = v_1, v_2, \\ldots, v_m Available=v1,v2,...,vm

其中 vj v_j vj 表示第 jj j 类资源的可用数量。

示例

python 复制代码
Available = [10, 5, 7]  # 系统有 3 类资源:A 类 10 个,B 类 5 个,C 类 7 个

动态变化

  • 当分配资源给进程时: Availablej=Availablej−分配数量\text{Available}j = \text{Available}j - \text{分配数量} Availablej=Availablej−分配数量
  • 当进程释放资源时: Availablej=Availablej+释放数量\text{Available}j = \text{Available}j + \text{释放数量} Availablej=Availablej+释放数量

2.2 最大需求矩阵(Max)

定义 :一个 n×mn \times m n×m 的二维矩阵,表示每个进程对每类资源的最大需求量。
Max= Max0,1 Max0,2 ⋯ Max0,m Max1,1 Max1,2 ⋯ Max1,m ⋮ ⋮ ⋱ ⋮ Maxn−1,1 Maxn−1,2 ⋯ Maxn−1,m \text{Max} = \begin{bmatrix} \text{Max}{0,1} & \text{Max}{0,2} & \cdots & \text{Max}{0,m} \\ \text{Max}{1,1} & \text{Max}{1,2} & \cdots & \text{Max}{1,m} \\ \vdots & \vdots & \ddots & \vdots \\ \text{Max}{n-1,1} & \text{Max}{n-1,2} & \cdots & \text{Max}_{n-1,m} \end{bmatrix} Max= Max0,1Max1,1⋮Maxn−1,1Max0,2Max1,2⋮Maxn−1,2⋯⋯⋱⋯Max0,mMax1,m⋮Maxn−1,m

其中 Maxi,j \text{Max}_{i,j} Maxi,j 表示进程 Pi P_i Pi 对资源 Rj R_j Rj 的最大需求量。

示例

python 复制代码
Max = [
    [7, 5, 3],  # P0 对 A、B、C 的最大需求分别是 7、5、3
    [3, 2, 2],  # P1 对 A、B、C 的最大需求分别是 3、2、2
    [9, 0, 2],  # P2 对 A、B、C 的最大需求分别是 9、0、2
    [2, 2, 2],  # P3 对 A、B、C 的最大需求分别是 2、2、2
    [4, 3, 3]   # P4 对 A、B、C 的最大需求分别是 4、3、3
]

重要假设:进程必须在开始执行前声明其对每类资源的最大需求量,这是银行家算法的前提条件。


2.3 已分配矩阵(Allocation)

定义 :一个 n×mn \times m n×m 的二维矩阵,表示当前已分配给每个进程的每类资源数量。
Allocation= Alloc0,1 Alloc0,2 ⋯ Alloc0,m Alloc1,1 Alloc1,2 ⋯ Alloc1,m ⋮ ⋮ ⋱ ⋮ Allocn−1,1 Allocn−1,2 ⋯ Allocn−1,m \text{Allocation} = \begin{bmatrix} \text{Alloc}{0,1} & \text{Alloc}{0,2} & \cdots & \text{Alloc}{0,m} \\ \text{Alloc}{1,1} & \text{Alloc}{1,2} & \cdots & \text{Alloc}{1,m} \\ \vdots & \vdots & \ddots & \vdots \\ \text{Alloc}{n-1,1} & \text{Alloc}{n-1,2} & \cdots & \text{Alloc}_{n-1,m} \end{bmatrix} Allocation= Alloc0,1Alloc1,1⋮Allocn−1,1Alloc0,2Alloc1,2⋮Allocn−1,2⋯⋯⋱⋯Alloc0,mAlloc1,m⋮Allocn−1,m

其中 Alloci,j \text{Alloc}_{i,j} Alloci,j 表示当前分配给进程 Pi P_i Pi 的资源 Rj R_j Rj 的数量。

示例

python 复制代码
Allocation = [
    [0, 1, 0],  # P0 已分配 A:0, B:1, C:0
    [2, 0, 0],  # P1 已分配 A:2, B:0, C:0
    [3, 0, 2],  # P2 已分配 A:3, B:0, C:2
    [2, 1, 1],  # P3 已分配 A:2, B:1, C:1
    [0, 0, 2]   # P4 已分配 A:0, B:0, C:2
]

2.4 需求矩阵(Need)

定义 :一个 n×mn \times m n×m 的二维矩阵,表示每个进程还需要的每类资源数量。
Need= Need0,1 Need0,2 ⋯ Need0,m Need1,1 Need1,2 ⋯ Need1,m ⋮ ⋮ ⋱ ⋮ Needn−1,1 Needn−1,2 ⋯ Needn−1,m \text{Need} = \begin{bmatrix} \text{Need}{0,1} & \text{Need}{0,2} & \cdots & \text{Need}{0,m} \\ \text{Need}{1,1} & \text{Need}{1,2} & \cdots & \text{Need}{1,m} \\ \vdots & \vdots & \ddots & \vdots \\ \text{Need}{n-1,1} & \text{Need}{n-1,2} & \cdots & \text{Need}_{n-1,m} \end{bmatrix} Need= Need0,1Need1,1⋮Needn−1,1Need0,2Need1,2⋮Needn−1,2⋯⋯⋱⋯Need0,mNeed1,m⋮Needn−1,m

其中 Needi,j \text{Need}_{i,j} Needi,j 表示进程 Pi P_i Pi 还需要资源 Rj R_j Rj 的数量。

核心公式
Needi,j = Maxi,j − Allocationi,j \text{Need}{i,j} = \text{Max}{i,j} - \text{Allocation}_{i,j} Needi,j=Maxi,j−Allocationi,j

即:还需资源 = 最大需求 - 已分配资源

示例计算

python 复制代码
# Need = Max - Allocation
Need = [
    [7-0, 5-1, 3-0],   # P0: [7, 4, 3]
    [3-2, 2-0, 2-0],   # P1: [1, 2, 2]
    [9-3, 0-0, 2-2],   # P2: [6, 0, 0]
    [2-2, 2-1, 2-1],   # P3: [0, 1, 1]
    [4-0, 3-0, 3-2]    # P4: [4, 3, 1]
]

2.5 数据结构关系总结

四个数据结构之间的关系可以用下图表示:

复制代码
Max(最大需求)
  ↓
  ↓ 减去
  ↓
Allocation(已分配)
  ↓
  ↓ 等于
  ↓
Need(还需资源)

Available(可用资源)独立维护,随分配和回收动态变化

数学关系
∑i=0n−1 Allocationi,j +Availablej=系统资源总量j \sum_{i=0}^{n-1} \text{Allocation}_{i,j} + \text{Available}_j = \text{系统资源总量}_j i=0∑n−1Allocationi,j+Availablej=系统资源总量j

即:某类资源的已分配总量 + 可用量 = 系统该类资源的总量。


参考资料:


3. 安全性检查算法 🔍

本章详解如何判断系统是否处于安全状态

安全性检查算法是银行家算法的核心,用于判断当前系统状态是否安全。如果安全,算法还会找出一个安全序列。

3.1 算法流程

安全性检查算法的执行步骤如下:

步骤 1:初始化

设置两个辅助向量:

  • Work\text{Work} Work:长度为 mm m 的工作向量,表示系统当前可提供的资源数量,初始时 Work=Available\text{Work} = \text{Available} Work=Available
  • Finish\text{Finish} Finish:长度为 nn n 的布尔向量,表示每个进程是否能完成,初始时所有 Finishi=False\text{Finish}i = \text{False} Finishi=False

步骤 2:寻找可执行进程

从进程集合中寻找一个满足以下条件的进程 Pi P_i Pi:
Finishi=False且Needi≤Work\text{Finish}i = \text{False} \quad \text{且} \quad \text{Need}_i \leq \text{Work} Finishi=False且Needi≤Work

即:该进程尚未完成,且其所需资源不超过系统当前可用资源。

如果找到这样的进程,转到步骤 3;如果找不到,转到步骤 4。

步骤 3:模拟进程执行完成

假设找到进程 Pi P_i Pi,模拟其执行并完成:
Work =Work+Allocationi Finishi =True \begin{align*} \text{Work} &= \text{Work} + \text{Allocation}_i \\ \text{Finish}i &= \text{True} \end{align*} WorkFinishi=Work+Allocationi=True

即将该进程已分配的资源释放回系统,然后返回步骤 2 继续寻找下一个可执行进程。

步骤 4:判断安全性

检查是否所有进程都能完成:
如果所有 Finishi=True,则系统处于安全状态\text{如果所有 } \text{Finish}i = \text{True} \text{,则系统处于安全状态} 如果所有 Finishi=True,则系统处于安全状态

否则,系统处于不安全状态。

3.2 算法伪代码

ini 复制代码
function is_safe():
    Work = Available.copy()                     # 工作向量初始化为可用资源
    Finish = [False] * n                        # 所有进程标记为未完成
    safe_sequence = []                          # 安全序列
    
    while True:
        found = False                           # 标记本轮是否找到可执行进程
        
        for i in range(n):                      # 遍历所有进程
            if not Finish[i] and Need[i] <= Work:  # 进程未完成且资源足够
                Work = Work + Allocation[i]     # 模拟进程释放资源
                Finish[i] = True                # 标记进程已完成
                safe_sequence.append(i)         # 加入安全序列
                found = True                    # 标记找到进程
        
        if not found:                           # 本轮没找到进程,退出循环
            break
    
    if all(Finish):                             # 所有进程都能完成
        return True, safe_sequence              # 系统安全,返回安全序列
    else:
        return False, []                        # 系统不安全

3.3 算法复杂度分析

时间复杂度 O(n2×m)O(n^2 \times m) O(n2×m)

  • 外层循环最多执行 nn n 次(每次找到一个进程)
  • 内层循环遍历 nn n 个进程
  • 向量比较和加法操作需要 O(m)O(m) O(m) 时间

空间复杂度 O(n+m)O(n + m) O(n+m)

  • Work\text{Work} Work 向量: O(m)O(m) O(m)
  • Finish\text{Finish} Finish 向量: O(n)O(n) O(n)
  • 安全序列: O(n)O(n) O(n)

3.4 安全性检查示例

假设系统状态如下:

python 复制代码
Available = [10, 5, 7]

Max = [
    [7, 5, 3],  # P0
    [3, 2, 2],  # P1
    [9, 0, 2],  # P2
    [2, 2, 2],  # P3
    [4, 3, 3]   # P4
]

Allocation = [
    [0, 1, 0],  # P0
    [2, 0, 0],  # P1
    [3, 0, 2],  # P2
    [2, 1, 1],  # P3
    [0, 0, 2]   # P4
]

Need = [
    [7, 4, 3],  # P0
    [1, 2, 2],  # P1
    [6, 0, 0],  # P2
    [0, 1, 1],  # P3
    [4, 3, 1]   # P4
]

安全性检查执行过程

步骤 Work 找到进程 Need ≤ Work? 新 Work = Work + Allocation Finish
初始 10, 5, 7 - - - F,F,F,F,F
1 10, 5, 7 P1 1,2,210,5,7 10,5,7 + 2,0,0 = 12,5,7 F,T,F,F,F
2 12, 5, 7 P3 0,1,112,5,7 12,5,7 + 2,1,1 = 14,6,8 F,T,F,T,F
3 14, 6, 8 P4 4,3,114,6,8 14,6,8 + 0,0,2 = 14,6,10 F,T,F,T,T
4 14, 6, 10 P0 7,4,314,6,10 14,6,10 + 0,1,0 = 14,7,10 T,T,F,T,T
5 14, 7, 10 P2 6,0,014,7,10 14,7,10 + 3,0,2 = 17,7,12 T,T,T,T,T

结果 :所有 Finishi=True\text{Finish}i = \text{True} Finishi=True,系统处于安全状态。

安全序列 ⟨P1,P3,P4,P0,P2⟩ \langle P_1, P_3, P_4, P_0, P_2 \rangle ⟨P1,P3,P4,P0,P2⟩

(注意:安全序列不唯一,其他安全序列如 ⟨P3,P4,P1,P0,P2⟩ \langle P_3, P_4, P_1, P_0, P_2 \rangle ⟨P3,P4,P1,P0,P2⟩ 也可能存在)


参考资料:


4. 资源请求算法 📥

本章详解进程请求资源时的处理流程

当进程 Pi P_i Pi 发出资源请求 Requesti \text{Request}_i Requesti 时,银行家算法按以下步骤处理:

4.1 算法流程

步骤 1:检查请求合法性

检查进程 Pi P_i Pi 的请求是否合法:
Requesti≤Needi \text{Request}_i \leq \text{Need}_i Requesti≤Needi

如果请求超过其声明的最大需求,则拒绝请求(进程行为异常)。

步骤 2:检查资源可用性

检查系统是否有足够的可用资源:
Requesti≤Available \text{Request}_i \leq \text{Available} Requesti≤Available

如果可用资源不足,则让进程 Pi P_i Pi 等待。

步骤 3:试探性分配

假设系统满足该请求,修改数据结构:
Available =Available−Requesti Allocationi =Allocationi+Requesti Needi =Needi−Requesti \begin{align*} \text{Available} &= \text{Available} - \text{Request}_i \\ \text{Allocation}_i &= \text{Allocation}_i + \text{Request}_i \\ \text{Need}_i &= \text{Need}_i - \text{Request}_i \end{align*} AvailableAllocationiNeedi=Available−Requesti=Allocationi+Requesti=Needi−Requesti

步骤 4:执行安全性检查

调用安全性检查算法,判断新的系统状态是否安全:

  • 如果安全 :正式分配资源给进程 Pi P_i Pi
  • 如果不安全 :恢复原状态,让进程 Pi P_i Pi 等待

4.2 算法伪代码

ini 复制代码
function request_resources(i, Request_i):
    # 步骤1:检查请求是否超过需求
    if Request_i > Need[i]:
        return "错误:请求超过最大需求"
    
    # 步骤2:检查可用资源是否足够
    if Request_i > Available:
        return "等待:可用资源不足"
    
    # 步骤3:试探性分配
    Available = Available - Request_i
    Allocation[i] = Allocation[i] + Request_i
    Need[i] = Need[i] - Request_i
    
    # 步骤4:安全性检查
    safe, sequence = is_safe()
    
    if safe:
        return f"分配成功,安全序列:{sequence}"
    else:
        # 恢复原状态
        Available = Available + Request_i
        Allocation[i] = Allocation[i] - Request_i
        Need[i] = Need[i] + Request_i
        return "拒绝:分配后系统不安全"

4.3 资源请求示例

继续使用前面的系统状态,假设进程 P1 P_1 P1 发出请求 Request1=1,0,2 \text{Request}_1 = 1, 0, 2 Request1=1,0,2

步骤 1:检查请求合法性
Request1=1,0,2≤Need1=1,2,2✓ \text{Request}_1 = 1, 0, 2 \leq \text{Need}_1 = 1, 2, 2 \quad \checkmark Request1=1,0,2≤Need1=1,2,2

请求合法。

步骤 2:检查资源可用性
Request1=1,0,2≤Available=10,5,7✓ \text{Request}_1 = 1, 0, 2 \leq \text{Available} = 10, 5, 7 \quad \checkmark Request1=1,0,2≤Available=10,5,7

资源充足。

步骤 3:试探性分配

python 复制代码
Available = [10, 5, 7] - [1, 0, 2] = [9, 5, 5]
Allocation[1] = [2, 0, 0] + [1, 0, 2] = [3, 0, 2]
Need[1] = [1, 2, 2] - [1, 0, 2] = [0, 2, 0]

步骤 4:安全性检查

使用新的系统状态执行安全性检查,如果能找到安全序列,则正式分配;否则拒绝请求并恢复原状态。


参考资料:


5. 总结 📝

银行家算法是操作系统中避免死锁的经典算法,核心要点回顾:

组件 作用 关键公式/操作
Available 记录可用资源 动态变化:分配时减,回收时加
Max 记录最大需求 进程启动时声明
Allocation 记录已分配资源 分配时增加,回收时清零
Need 记录还需资源 Need=Max−Allocation\text{Need} = \text{Max} - \text{Allocation} Need=Max−Allocation
安全性检查 判断系统是否安全 寻找安全序列 ⟨P1,...,Pn⟩ \langle P_1, \ldots, P_n \rangle ⟨P1,...,Pn⟩
资源请求 处理进程请求 检查 → 试探分配 → 安全检查 → 决定

🔴 关键理解

  • 银行家算法的核心:在分配资源前进行安全性检查,确保系统始终处于安全状态
  • 安全状态 ≠ 不会死锁:安全状态保证不会死锁,不安全状态可能导致死锁(不一定会发生)
  • 算法的前提:进程必须预先声明最大需求,这在实际系统中可能难以满足
  • 算法的代价 :每次资源请求都需要执行安全性检查,时间复杂度 O(n2×m)O(n^2 \times m) O(n2×m)
  • 实际应用:虽然完全照搬银行家算法的场景不多,但其"分配前检查"的思想渗透在现代系统的资源管理中

最后更新时间:2026-06-02

相关推荐
装不满的克莱因瓶1 小时前
图像尺寸调整:缩放矩阵如何改变像素坐标?
人工智能·线性代数·数学·算法·机器学习·矩阵
Lumbrologist1 小时前
【C++】零基础入门 · 第 13 节:类与对象基础
java·c++·算法
LONGZETECH2 小时前
软硬协同+故障注入:无人机仿真维修与操控仿真底层算法逻辑拆解
大数据·c语言·算法·3d·unity·无人机
Lsk_Smion2 小时前
力扣实训 _ [543].二叉树的直径 _ [23].合并K个升序列表
数据结构·算法·leetcode
凯瑟琳.奥古斯特2 小时前
力扣1235:加权区间调度最优解
java·python·算法·leetcode·职场和发展
耶叶3 小时前
餐厅出入最少人数问题:贪心算法
算法·贪心算法
gihigo19983 小时前
基于小波框架与稀疏表示的SAR图像目标识别系统(MATLAB实现)
算法
吴可可1233 小时前
CAD2004自定义实体开发环境配置
c++·算法