银行家算法 🏦
本文档系统介绍银行家算法的核心原理、数据结构、安全性检查机制和完整的 Python 实现。通过理论与实践相结合的方式,帮助读者深入理解操作系统中死锁避免的经典算法 🔐
章节阅读路线图 🗺️
- 算法概述 → 理解银行家算法的核心思想和应用场景
- 核心数据结构 → 掌握四大关键数据结构及其关系
- 安全性检查算法 → 学习如何判断系统是否处于安全状态
- 资源请求算法 → 理解进程资源请求的处理流程
- 总结 → 回顾核心要点
1. 算法概述 📖
本章介绍银行家算法的核心思想、历史背景和应用场景
1.1 什么是银行家算法?
银行家算法(Banker's Algorithm)是一个避免死锁的著名算法,由荷兰计算机科学家艾兹赫尔·戴克斯特拉(Edsger W. Dijkstra)在 1965 年为 T.H.E 操作系统设计。该算法以银行借贷系统的分配策略为基础,通过预防性措施来确保系统的安全性,判断并保证系统始终处于安全状态。
核心思想:操作系统在分配资源前,先模拟资源分配的过程,判断分配后系统是否仍处于安全状态。如果安全则分配,否则让进程等待。
类比理解:
想象一个银行场景:
- 银行(操作系统)管理着有限的资金(资源)
- 客户(进程)申请贷款(资源请求)
- 银行家在批准贷款前会思考:"如果我把这笔钱贷出去,金库里剩余的钱是否还能满足至少一个客户的全部需求?"
- 如果可以,说明系统是安全的,可以批准贷款
- 如果不行,说明可能导致"挤兑"(死锁),必须拒绝或让客户等待
这就是银行家算法"谨慎的慷慨"设计哲学------既想最大限度地把资源分配出去提高系统利用率,又必须确保在任何时候都能避免死锁。
1.2 为什么需要银行家算法?
在操作系统中,多个进程并发执行时需要共享系统资源。如果资源分配不当,可能导致死锁(Deadlock)------多个进程互相等待对方释放资源,导致所有进程都无法继续执行。
死锁产生的四个必要条件:
- 互斥条件:资源一次只能被一个进程使用
- 占有并等待:进程已占有部分资源,同时等待其他资源
- 不可抢占:资源不能被强制从进程中夺走
- 循环等待:存在一个进程等待环路
银行家算法通过破坏循环等待条件来避免死锁,它在资源分配前进行安全性检查,确保系统始终处于安全状态。
1.3 安全状态与安全序列
安全状态:如果系统存在至少一个进程执行序列,使得按照该序列依次为每个进程分配其所需资源,所有进程都能顺利完成,则称系统处于安全状态。
安全序列 :一个进程序列 ⟨P1,P2,...,Pn⟩,如果对于每个进程 Pi,其后续所需的资源都能被系统当前可用资源(加上之前已完成进程释放的资源)所满足,则该序列称为安全序列。
关键结论:
- 系统处于安全状态 ⇒ 不会发生死锁
- 系统处于不安全状态 ⇒ 可能发生死锁(不一定会发生)
- 银行家算法的目标:确保系统始终处于安全状态
参考资料:
2. 核心数据结构 📊
本章详解银行家算法的四个关键数据结构及其数学关系
为了实现银行家算法,系统中必须设置四个核心数据结构,分别描述系统中可利用的资源、所有进程对资源的最大需求、系统中的资源分配情况,以及所有进程还需要多少资源。
假设系统中有:
- n 个进程: P0,P1,...,Pn−1
- m 类资源: R1,R2,...,Rm
2.1 可用资源向量(Available)
定义 :一个长度为 m 的一维数组,表示系统中每类资源当前的可用数量。
Available=v1,v2,...,vm
其中 vj 表示第 j 类资源的可用数量。
示例:
python
Available = [10, 5, 7] # 系统有 3 类资源:A 类 10 个,B 类 5 个,C 类 7 个
动态变化:
- 当分配资源给进程时: Availablej=Availablej−分配数量
- 当进程释放资源时: Availablej=Availablej+释放数量
2.2 最大需求矩阵(Max)
定义 :一个 n×m 的二维矩阵,表示每个进程对每类资源的最大需求量。
Max= Max0,1Max1,1⋮Maxn−1,1Max0,2Max1,2⋮Maxn−1,2⋯⋯⋱⋯Max0,mMax1,m⋮Maxn−1,m
其中 Maxi,j 表示进程 Pi 对资源 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×m 的二维矩阵,表示当前已分配给每个进程的每类资源数量。
Allocation= Alloc0,1Alloc1,1⋮Allocn−1,1Alloc0,2Alloc1,2⋮Allocn−1,2⋯⋯⋱⋯Alloc0,mAlloc1,m⋮Allocn−1,m
其中 Alloci,j 表示当前分配给进程 Pi 的资源 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×m 的二维矩阵,表示每个进程还需要的每类资源数量。
Need= Need0,1Need1,1⋮Needn−1,1Need0,2Need1,2⋮Needn−1,2⋯⋯⋱⋯Need0,mNeed1,m⋮Needn−1,m
其中 Needi,j 表示进程 Pi 还需要资源 Rj 的数量。
核心公式:
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=0∑n−1Allocationi,j+Availablej=系统资源总量j
即:某类资源的已分配总量 + 可用量 = 系统该类资源的总量。
参考资料:
3. 安全性检查算法 🔍
本章详解如何判断系统是否处于安全状态
安全性检查算法是银行家算法的核心,用于判断当前系统状态是否安全。如果安全,算法还会找出一个安全序列。
3.1 算法流程
安全性检查算法的执行步骤如下:
步骤 1:初始化
设置两个辅助向量:
- Work:长度为 m 的工作向量,表示系统当前可提供的资源数量,初始时 Work=Available
- Finish:长度为 n 的布尔向量,表示每个进程是否能完成,初始时所有 Finishi=False
步骤 2:寻找可执行进程
从进程集合中寻找一个满足以下条件的进程 Pi:
Finishi=False且Needi≤Work
即:该进程尚未完成,且其所需资源不超过系统当前可用资源。
如果找到这样的进程,转到步骤 3;如果找不到,转到步骤 4。
步骤 3:模拟进程执行完成
假设找到进程 Pi,模拟其执行并完成:
WorkFinishi=Work+Allocationi=True
即将该进程已分配的资源释放回系统,然后返回步骤 2 继续寻找下一个可执行进程。
步骤 4:判断安全性
检查是否所有进程都能完成:
如果所有 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)
- 外层循环最多执行 n 次(每次找到一个进程)
- 内层循环遍历 n 个进程
- 向量比较和加法操作需要 O(m) 时间
空间复杂度 : O(n+m)
- Work 向量: O(m)
- Finish 向量: 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,2 ≤ 10,5,7 ✓ | 10,5,7 + 2,0,0 = 12,5,7 | F,T,F,F,F |
| 2 | 12, 5, 7 | P3 | 0,1,1 ≤ 12,5,7 ✓ | 12,5,7 + 2,1,1 = 14,6,8 | F,T,F,T,F |
| 3 | 14, 6, 8 | P4 | 4,3,1 ≤ 14,6,8 ✓ | 14,6,8 + 0,0,2 = 14,6,10 | F,T,F,T,T |
| 4 | 14, 6, 10 | P0 | 7,4,3 ≤ 14,6,10 ✓ | 14,6,10 + 0,1,0 = 14,7,10 | T,T,F,T,T |
| 5 | 14, 7, 10 | P2 | 6,0,0 ≤ 14,7,10 ✓ | 14,7,10 + 3,0,2 = 17,7,12 | T,T,T,T,T |
结果 :所有 Finishi=True,系统处于安全状态。
安全序列 : ⟨P1,P3,P4,P0,P2⟩
(注意:安全序列不唯一,其他安全序列如 ⟨P3,P4,P1,P0,P2⟩ 也可能存在)
参考资料:
4. 资源请求算法 📥
本章详解进程请求资源时的处理流程
当进程 Pi 发出资源请求 Requesti 时,银行家算法按以下步骤处理:
4.1 算法流程
步骤 1:检查请求合法性
检查进程 Pi 的请求是否合法:
Requesti≤Needi
如果请求超过其声明的最大需求,则拒绝请求(进程行为异常)。
步骤 2:检查资源可用性
检查系统是否有足够的可用资源:
Requesti≤Available
如果可用资源不足,则让进程 Pi 等待。
步骤 3:试探性分配
假设系统满足该请求,修改数据结构:
AvailableAllocationiNeedi=Available−Requesti=Allocationi+Requesti=Needi−Requesti
步骤 4:执行安全性检查
调用安全性检查算法,判断新的系统状态是否安全:
- 如果安全 :正式分配资源给进程 Pi
- 如果不安全 :恢复原状态,让进程 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 发出请求 Request1=1,0,2。
步骤 1:检查请求合法性
Request1=1,0,2≤Need1=1,2,2✓
请求合法。
步骤 2:检查资源可用性
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 |
| 安全性检查 | 判断系统是否安全 | 寻找安全序列 ⟨P1,...,Pn⟩ |
| 资源请求 | 处理进程请求 | 检查 → 试探分配 → 安全检查 → 决定 |
🔴 关键理解:
- 银行家算法的核心:在分配资源前进行安全性检查,确保系统始终处于安全状态
- 安全状态 ≠ 不会死锁:安全状态保证不会死锁,不安全状态可能导致死锁(不一定会发生)
- 算法的前提:进程必须预先声明最大需求,这在实际系统中可能难以满足
- 算法的代价 :每次资源请求都需要执行安全性检查,时间复杂度 O(n2×m)
- 实际应用:虽然完全照搬银行家算法的场景不多,但其"分配前检查"的思想渗透在现代系统的资源管理中
最后更新时间:2026-06-02