操作系统死锁避免核心:银行家算法超详细图解+实战案例
在操作系统并发编程中,死锁是最经典且棘手的问题:多个进程互相持有对方需要的资源、互相等待释放资源,最终所有进程永久阻塞,系统彻底卡死。
为了解决死锁问题,业界衍生出死锁预防、死锁避免、死锁检测与解除三大方案。其中,银行家算法(Banker's Algorithm)是最经典、应用最广泛的死锁避免算法,由荷兰计算机科学家迪杰斯特拉在1965年提出,核心是通过「预判资源分配风险」,让系统始终处于安全状态,从根源规避死锁。
今天这篇博客,我从零拆解银行家算法,包含核心原理、四大数据结构、完整执行流程、手把手实战案例、代码实现、优缺点分析,零基础也能彻底看懂!
一、银行家算法核心思想:通俗易懂理解
1.1 算法由来:银行借贷模型
算法名字源自银行放贷逻辑,我们可以把操作系统类比为银行,系统资源(CPU、内存、设备等)类比为资金,运行的进程类比为贷款客户:
-
每个客户(进程)会提前告知银行,自己最多需要多少资金(资源);
-
客户会分批申请贷款,不会一次性借满;
-
银行不会盲目放贷,每次放款前都会预判:放款后,剩余资金是否足够支撑所有客户完成全部业务、正常还款;
-
如果放款后系统可能卡死(客户无法还款、互相等待),就拒绝本次申请,等待后续资源释放。
简言之:银行家算法不保证进程立刻拿到资源,只保证系统永远不会进入死锁状态。
1.2 核心核心概念:安全状态 & 不安全状态
这是算法的核心判定依据,直接决定资源是否分配:
-
安全状态:系统存在一个安全进程序列,按照这个序列依次为每个进程分配所需资源,所有进程都能顺利执行完毕、释放占用资源,系统无死锁风险。
-
不安全状态:不存在任何安全序列,资源分配后可能出现进程互相等待、无法执行的情况,大概率触发死锁。
💡 关键结论:银行家算法的所有操作,本质都是拒绝一切会让系统进入不安全状态的资源申请,始终维持系统安全。
二、四大核心数据结构(算法基石)
银行家算法基于固定的4组数据结构实现,假设系统中有 n 个进程,m 类资源(如内存、磁盘、打印机),四组结构定义如下:
2.1 可利用资源向量 Available
一维数组,长度为 m,Available[j] 表示:当前系统中第 j 类资源的剩余可用数量。
示例:Available = [3,2,2] 表示系统剩余3个A资源、2个B资源、2个C资源。
2.2 最大需求矩阵 Max
n×m 二维矩阵,Max[i][j] 表示:第 i 个进程,最多需要第 j 类资源的数量(进程预先声明的最大资源需求)。
2.3 已分配矩阵 Allocation
n×m 二维矩阵,Allocation[i][j] 表示:当前已经分配给第 i 个进程的第 j 类资源数量。
2.4 剩余需求矩阵 Need
n×m 二维矩阵,Need[i][j] 表示:第 i 个进程还需要的第 j 类资源数量。
核心计算公式(必考):Need[i][j] = Max[i][j] - Allocation[i][j]
三、银行家算法完整执行流程
当进程 Pi 发起资源申请Request[i] 时,算法会执行三步校验+安全检测,全程无漏洞规避死锁。
步骤1:合法性校验(初步筛选)
判断进程申请的资源是否合理,不合理直接拒绝:
-
若
Request[i][j] > Need[i][j]:进程申请资源超过自身剩余需求,属于非法请求(进程违规申请超额资源),直接报错拒绝; -
若
Request[i][j] > Available[j]:系统剩余资源不足,无法满足本次申请,进程阻塞等待。
步骤2:试探性资源分配
若步骤1校验通过,系统模拟分配资源(不真正分配,仅数据更新),更新三组数据:
-
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:安全性检测(核心步骤)
模拟分配后,检测系统是否仍处于安全状态,流程如下:
-
定义工作向量
Work = Available(记录当前可用资源),定义完成标记数组Finish[n] = false(标记进程是否执行完毕); -
遍历所有未完成的进程,找到满足
Need[i] ≤ Work的进程Pi(当前剩余资源可满足该进程全部需求); -
假设该进程执行完毕,释放所有占用资源:
Work = Work + Allocation[i],标记Finish[i] = true; -
重复遍历,直到所有进程标记完成,或找不到可执行进程;
-
若 所有 Finish[i] = true:系统安全,正式分配资源;否则系统不安全,回滚所有数据,拒绝本次申请。
四、手把手实战案例(看懂即掌握)
为了方便理解,我们用一个极简的真实场景演示完整流程:
场景参数
系统:3类资源(A、B、C),4个进程(P0、P1、P2、P3)
初始数据:
-
Available = [3,3,2] -
Max 最大需求: P0: [7,5,3] P1: [3,2,2] P2: [9,0,2] P3: [2,2,2]
-
Allocation 已分配: P0: [0,1,0] P1: [2,0,0] P2: [3,0,2] P3: [2,1,1]
步骤1:计算初始 Need 矩阵
Need = Max - Allocation
-
P0: [7,4,3] P1: [1,2,2] P2: [6,0,0] P3: [0,1,1]
步骤2:模拟资源申请
假设 P1 发起申请 Request1 = [1,0,2]
合法性校验:
-
Request1 [1,0,2] ≤ Need1 [1,2,2] ✅
-
Request1 [1,0,2] ≤ Available [3,3,2] ✅
步骤3:试探性分配
-
Available = [3-1, 3-0, 2-2] = [2,3,0]
-
Allocation1 = [2+1,0+0,0+2] = [3,0,2]
-
Need1 = [1-1,2-0,2-2] = [0,2,0]
步骤4:安全性检测
初始化:Work = [2,3,0],Finish = [false,false,false,false]
-
遍历进程:P3 的 Need [0,1,1] > Work [2,3,0],不满足;P1 的 Need [0,2,0] ≤ Work,满足条件。执行P1,释放资源:Work = [2+3,3+0,0+2] = [5,3,2],Finish[1]=true;
-
继续遍历:P3 的 Need [0,1,1] ≤ [5,3,2],执行P3,释放资源:Work = [5+2,3+1,2+1] = [7,4,3],Finish[3]=true;
-
继续遍历:P0 的 Need [7,4,3] ≤ [7,4,3],执行P0,释放资源:Work = [7+0,4+1,3+0] = [7,5,3],Finish[0]=true;
-
最后执行P2,Finish[2]=true;
所有进程执行完成,系统安全! 本次资源申请通过,正式分配资源。
✅ 安全序列:P1 → P3 → P0 → P2(安全序列不唯一)
五、Python 极简代码实现
以下是可直接运行的银行家算法核心代码,包含资源申请、安全检测全逻辑:
import numpy as np
# 安全性检测函数
def is_safe(Available, Allocation, Need, n, m):
Work = Available.copy()
Finish = [False] * n
safe_seq = [] # 存储安全序列
for _ in range(n):
find = False
for i in range(n):
# 找到未完成且需求可被满足的进程
if not Finish[i] and all(Need[i][j] <= Work[j] for j in range(m)):
# 模拟进程执行完毕,释放资源
for j in range(m):
Work[j] += Allocation[i][j]
Finish[i] = True
safe_seq.append(f"P{i}")
find = True
break
if not find:
break
if all(Finish):
return True, safe_seq
else:
return False, []
# 银行家算法资源申请函数
def bank_request(pid, Request, Available, Allocation, Need):
n = len(Allocation)
m = len(Available)
# 1. 合法性校验
if any(Request[j] > Need[pid][j] for j in range(m)):
print(f"进程P{pid}申请资源超过剩余需求,申请非法!")
return False
if any(Request[j] > Available[j] for j in range(m)):
print(f"进程P{pid}申请资源,系统资源不足,进程阻塞!")
return False
# 2. 试探性分配
for j in range(m):
Available[j] -= Request[j]
Allocation[pid][j] += Request[j]
Need[pid][j] -= Request[j]
# 3. 安全性检测
safe, seq = is_safe(Available, Allocation, Need, n, m)
if safe:
print(f"资源分配成功!安全序列:{' → '.join(seq)}")
return True
else:
# 不安全则回滚
for j in range(m):
Available[j] += Request[j]
Allocation[pid][j] -= Request[j]
Need[pid][j] += Request[j]
print("分配后系统不安全,拒绝本次资源申请!")
return False
# 测试案例(对应上文实战场景)
if __name__ == "__main__":
Available = np.array([3,3,2])
Max = np.array([[7,5,3],[3,2,2],[9,0,2],[2,2,2]])
Allocation = np.array([[0,1,0],[2,0,0],[3,0,2],[2,1,1]])
Need = Max - Allocation
# P1申请资源 [1,0,2]
print("===== P1 申请资源 [1,0,2] =====")
bank_request(1, [1,0,2], Available, Allocation, Need)
六、银行家算法优缺点总结
6.1 核心优点
-
精准规避死锁:通过事前预判,从源头避免系统进入不安全状态,死锁规避成功率100%;
-
资源利用率高:不同于死锁预防的严格限制,允许资源动态分配,不会过度浪费系统资源;
-
逻辑清晰、易落地:数据结构固定、流程标准化,广泛应用于操作系统、数据库、分布式资源调度场景。
6.2 固有缺点(局限性)
-
前置条件严苛:要求所有进程必须提前声明最大资源需求,实际业务中很多进程无法提前确定最大需求;
-
进程静态化假设:假设系统进程数量、资源总量固定,不支持动态新增进程和资源,适配性有限;
-
存在性能开销:每次资源申请都需要执行安全遍历检测,高并发场景下会增加系统调度开销;
-
无法规避饥饿问题:部分进程可能持续被拒绝资源申请,长期阻塞无法执行。
七、核心知识点复盘(面试必背)
整理面试高频考点,快速巩固核心:
-
银行家算法是死锁避免算法,而非死锁预防、死锁检测;
-
核心逻辑:合法校验 → 试探分配 → 安全检测 → 正式分配/回滚;
-
安全状态一定无死锁,不安全状态可能死锁(不是绝对死锁);
-
四大矩阵核心公式:
Need = Max - Allocation; -
安全序列不唯一,只要存在任意一个安全序列,系统即为安全。
八、写在最后
银行家算法是操作系统课程的重难点,也是面试高频考点。它的核心价值不在于复杂的代码逻辑,而在于「事前预判、风险规避」的设计思想——不盲目分配资源,通过模拟推演保证系统稳态。
虽然受限于前置假设,无法完全落地于复杂的现代操作系统,但它的调度思维被广泛沿用在各类资源调度场景中,是理解并发安全、死锁管控的基础。
本次博客灵感源于:期末周复习计算机操作系统到崩溃,希望小小见解有利于大家学习!
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)