一、定义与背景
银行家算法(Banker’s Algorithm)是由艾兹格・迪杰斯特拉(Edsger Dijkstra)在 1965 年为解决死锁问题而提出的一种资源分配和死锁避免算法。它的命名源于银行系统的资金借贷模型,银行家需要在满足客户贷款需求的同时,确保自身资金的安全,避免出现无法满足所有客户需求的情况,对应到操作系统中,就是避免进程因资源分配不当而产生死锁。
二、数据结构
- 可利用资源向量 Available:这是一个数组,记录了系统中各类资源的可用数量。例如,系统中有三种资源(A、B、C),Available = [3, 3, 2],表示当前系统中资源 A 有 3 个可用,资源 B 有 3 个可用,资源 C 有 2 个可用。
- 最大需求矩阵 Max:是一个二维数组,Max [i][j] 表示进程 i 对资源 j 的最大需求量。比如,Max [2][1] = 4,表示进程 2 对资源 B 的最大需求量为 4 个。
- 分配矩阵 Allocation:同样是二维数组,Allocation [i][j] 表示系统已经分配给进程 i 的资源 j 的数量。如 Allocation [1][0] = 1,意味着系统已经给进程 1 分配了 1 个资源 A。
- 需求矩阵 Need:二维数组,Need [i][j] = Max [i][j] - Allocation [i][j],表示进程 i 还需要的资源 j 的数量。
三、算法步骤
-
检查请求是否合理:当一个进程 Pi 发出资源请求 Requesti 时,首先检查 Requesti 是否小于等于 Need [i],即检查该进程请求的资源是否超过了它声明的最大需求。如果 Requesti > Need [i],则认为出错,因为该进程的请求超出了其最大需求,可能是程序错误导致的非法请求。
-
检查资源是否足够:若请求合理,接着检查 Requesti 是否小于等于 Available,即当前系统的可用资源是否能满足进程 Pi 的这次请求。如果 Requesti > Available,说明系统当前没有足够的资源来满足该请求,进程 Pi 必须等待,直到有足够的资源可用。
-
尝试分配资源
:当 Requesti <= Available 时,系统尝试将资源分配给进程 Pi。此时,对系统资源状态进行如下调整:
- Available = Available - Requesti,即从可用资源中减去分配给进程 Pi 的资源。
- Allocation [i] = Allocation [i] + Requesti,更新进程 Pi 已分配到的资源数量。
- Need [i] = Need [i] - Requesti,更新进程 Pi 还需要的资源数量。
-
安全性检查
:资源分配后,系统需要执行安全性检查,以确保当前的资源分配状态不会导致死锁。检查过程如下:
- 定义一个 Work 向量,初始值等于 Available,表示当前可用于分配的资源。
- 定义一个 Finish 向量,初始值为 false,表示所有进程都未完成。
- 寻找一个满足以下两个条件的进程 Pj:
- Finish [j] == false,即进程 Pj 尚未完成。
- Need [j] <= Work,即进程 Pj 还需要的资源量小于等于当前可用于分配的资源量。
- 如果找到了这样的进程 Pj,则:
- Work = Work + Allocation [j],将进程 Pj 已分配的资源回收,增加到可用于分配的资源中。
- Finish [j] = true,标记进程 Pj 已完成。
- 回到上一步,继续寻找下一个满足条件的进程。
- 如果所有进程的 Finish 向量都为 true,说明系统处于安全状态,本次资源分配是可行的;否则,说明系统处于不安全状态,需要撤销刚才的资源分配操作,让进程 Pi 继续等待。
四、举例说明
假设系统中有五个进程 P0、P1、P2、P3、P4,三种资源 A、B、C,初始状态如下:
进程 | Max | Allocation | Need | Available |
---|---|---|---|---|
P0 | [7, 5, 3] | [0, 1, 0] | [7, 4, 3] | [3, 3, 2] |
P1 | [3, 2, 2] | [2, 0, 0] | [1, 2, 2] | |
P2 | [9, 0, 2] | [3, 0, 2] | [6, 0, 0] | |
P3 | [2, 2, 2] | [2, 1, 1] | [0, 1, 1] | |
P4 | [4, 3, 3] | [0, 0, 2] | [4, 3, 1] |
现在,假设进程 P1 发出请求 Request1 = [0, 2, 2],按照银行家算法步骤:
- 首先检查 Request1 = [0, 2, 2] 是否小于等于 Need1 = [1, 2, 2],满足条件,请求合理。
- 接着检查 Request1 是否小于等于 Available = [3, 3, 2],满足条件,系统有足够资源。
- 尝试分配资源:
- Available = [3, 3, 2] - [0, 2, 2] = [3, 1, 0]
- Allocation1 = [2, 0, 0] + [0, 2, 2] = [2, 2, 2]
- Need1 = [1, 2, 2] - [0, 2, 2] = [1, 0, 0]
- 进行安全性检查:
- Work = [3, 1, 0],Finish 向量初始值全为 false。
- 找到进程 P3,因为 Need3 = [0, 1, 1] <= Work = [3, 1, 0],满足条件。
- Work = [3, 1, 0] + [2, 1, 1] = [5, 2, 1],Finish3 = true。
- 找到进程 P1,因为 Need1 = [1, 0, 0] <= Work = [5, 2, 1],满足条件。
- Work = [5, 2, 1] + [2, 2, 2] = [7, 4, 3],Finish1 = true。
- 依次类推,可以发现所有进程的 Finish 向量都能变为 true,系统处于安全状态,所以可以将资源分配给进程 P1。