背景介绍
银行家算法是由荷兰学者Dijkstra(提出goto有害论的大佬Orz)为银行系统设计的,以确保银行发放贷款时不会出现无法满足所有顾客的情况;该算法后来被运用于操作系统以预防死锁。
死锁是指在并发环境中,各进程因竞争资源造成的互相都在等待对方手中的资源,从而导致每个进程都被阻塞而无法推进的现象。
核心思想
在进程提出资源申请时,操作系统会通过该算法来试图找到一个安全序列,若可以找到,则表示满足该进程后的系统不会处于不安全的状态,即不会发生死锁;但是若找不到至少一个安全序列,则现在不能满足该进程的资源申请,该进程必须等待。
系统处于不安全状态时不一定会发生死锁,但是发生死锁时,系统一定处于不安全的状态。
例题
我们通过一道例题来进一步理解银行家算法是如何避免死锁的:
假定系统中有五个进程{P0, P1, P2, P3, P4}和三类资源{A, B, C},各种资源的数量分别为10、5、7,在T0时刻的资源分配情况如图所示:
图1 T0时刻的资源分配表
请求序列:
(1)P1发出请求向量Request1(1,0,2)
(2)P4发出请求向量Request4(3,3,0)
(3)P0发出请求向量Request0(0,2,0)
具体分析
图1各数据结构的说明
二维数组Max:用于存储各个进程对各资源的最大需求量;更进一步地,第i行第j列表示进程i对资源j的最大需求量;
二维数组Allocation:用于存储各个进程对各资源的占有量;第i行第j列表示进程i对资源j的占有总量;
二维数组Need:用存储各个进程还需要的各资源的数量;第i行第j列表示进程i对资源j还需要多少数量;
一维数组Available:用于存储系统中各个资源的数量;第i个位置表示资源i在系统中的数量。系统中原本各个资源的数量为10,5,7;减去各个进程的Allocation后更新为3,3,2。
安全序列的推导
我们首先选择 “P1发出请求向量Request1(1,0,2)” 这一问来进行分析:
操作系统首先尝试着满足该请求(实际还没有正式分配,要先进行模拟,试着找出安全序列),那么此时各个数据结构更新为:
进程 \ 资源情况 | Max A B C | Allocation A B C | Need A B C | Available A B C |
P0 | 7 5 3 | 0 1 0 | 7 4 3 | 2 3 0 |
P1 | 3 2 2 | 3 0 2 | 0 2 0 | |
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 |
表1 为P1分配更新后的资源分配表
满足P1进程请求{1,0,2}后,Available数组、P1进程的Need数组和Allocation数组都进行了更新,接着从上表的状态开始,开始寻找一个包含所有进程的安全序列:
依次检查剩余资源{2,3,0}能否满足各进程的需求(每次检查不包括上一次已经进入安全序列的进程):
发现可以满足P1进程,将P1加入安全序列,并且更新可用资源为{5,5,2}
继续检查剩余资源{5,5,2}能否满足各进程的需求:
发现可以满足P3进程,将P3加入安全序列,并且更新可用资源为{7,7,4}
...
重复上述步骤,直到将所有进程都加入到安全序列中,该算法称为安全性算法;若在某一步时,出现了系统可用资源无法满足任何一个进程的需求,则说明此时系统处于不安全的状态,有可能会发生死锁。
代码实现
代码是我自己写的,上述三个样例也是正确的,但是不知道是否有纰漏,所以还是批判性地使用代码,若发现任何错误,恳请指正Orz;以下是我使用C++实现的银行家算法:
/*
7 5 3 0 1 0
3 2 2 2 0 0
9 0 2 3 0 2
2 2 2 2 1 1
4 3 3 0 0 2
*/
#include<iostream>
#include<vector>
using namespace std;
vector<vector<int> > Max(100, vector<int>(100, 0));//最大需求矩阵
vector<vector<int> > Allocation(100, vector<int>(100, 0));//已分配矩阵
vector<vector<int> > Need(100, vector<int>(100, 0));//需求矩阵
vector<int>Available(100, 0);//资源可用情况
vector<int>Request(100, 0);//进程本次申请的资源量
vector<bool>IsFinish(100, false);//用于标记进程是否已经完成工作,若完成则回收资源
vector<int>SafeQueue;//若存在安全序列,记录并打印
int n, m;//进程数量以及资源数量
void init() {
cout << "输入进程数量以及资源种数:";
cin >> n >> m;
cout << "输入各资源的总量:";
for (int i = 0; i < m; i++) {
cin >> Available[i];
}
cout << "输入各进程最大所需资源数量以及已分配的资源数量:\n";
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++)
cin >> Max[i][j];
for (int j = 0; j < m; j++) {
cin >> Allocation[i][j];
Need[i][j] = Max[i][j] - Allocation[i][j];
Available[j] -= Allocation[i][j];
}
}
}
bool IsSafe(int Thread) {
//使用副本进行模拟
vector<bool>vis(100, false);
vector<int>available = Available;
vector<vector<int> >need = Need;
//检验当前请求是已经超过系统所剩下的资源数量
for (int i = 0; i < m; i++) {
if (Request[i] > available[i]) {
return false;
}
//尝试分配
available[i] -= Request[i];
need[Thread][i] -= Request[i];
}
//特判首次分配出现恰好分配完该进程所需的最大资源数量的情况
int flag = 1;
for (int i = 0; i < m; i++) {
flag = 0;
if (need[Thread][i] != 0) {
flag = 1;
break;
}
}
//若符合上述条件,回收该进程所占有的所有资源
if (!flag) {
vis[Thread] = true;
for (int i = 0; i < m; i++) {
available[i] += Max[Thread][i];
}
SafeQueue.push_back(Thread);
}
//尝试寻找安全序列
for (int i = 0; i < n; i++) {
//寻找当前系统资源满足其全部所需的资源的进程
int pos = -1;
for (int j = 0; j < n; j++) {
int IsEnough = 1;
if (!vis[j]) {
for (int k = 0; k < m; k++) {
IsEnough = 0;
if (available[k] < need[j][k]) {
IsEnough = 1;
break;
}
}
if (!IsEnough) {
pos = j;
break;
}
}
}
//pos等于-1表示对于当前的所有进程,系统资源都无法满足其所需的最大资源需求数量,所以可以直接判定找不到安全序列
if (pos == -1)return false;
//加入安全序列,回收资源
SafeQueue.push_back(pos);
vis[pos] = true;
for (int j = 0; j < m; j++) {
available[j] += Max[pos][j];
}
}
//程序能够走到这里说明找到了至少一条安全序列,那么正式进行分配
for (int i = 0; i < m; i++) {
Available[i] -= Request[i];
Need[Thread][i] -= Request[i];
Allocation[Thread][i] += Request[i];
}
return true;
}
int main() {
init();
while (1) {
int choice;
cout << "输入1或0,1表示分配,0表示退出:\n";
cin >> choice;
if (!choice)break;
int Thread;
cin >> Thread;
for (int i = 0; i < m; i++)
cin >> Request[i];
SafeQueue.clear();
if (IsSafe(Thread)) {
cout << "分配后系统处于安全状态,找到的安全序列为:{";
for (int i = 0; i < n; i++)
cout<<"P" << SafeQueue[i] << " ";
cout << "}\n";
//正式分配后,若当前进程的资源已经全部满足,则回收占有的全部资源
int flag = 1;
for (int i = 0; i < m; i++) {
flag = 0;
if (Need[Thread][i] != 0) {
flag = 1;
break;
}
}
if (!flag) {
IsFinish[Thread] = true;
for (int i = 0; i < m; i++) {
Available[i] += Max[Thread][i];
}
}
}
else {
cout << "分配后系统将处于不安全状态,当前进程进入等待\n";
}
}
return 0;
}