银行家算法详解
一、银行家算法详解
-
银行家算法是一种避免死锁的方法
1.背景简介
在银行中,
客户申请贷款的数量是有限
的,每个客户
在第一次申请贷款时要声明
完成该项目所需的最大资金量
,在满足所有贷款
要求时,客户应及时归还
。银行家在客户申请的贷款数量不超过自己拥有的最大值时,都应尽量满足客户的需要
。在这样的描述中,银行家就好比操作系统,资金就是资源,客户就相当于要申请资源的进程。
银行家算法是一种最有代表性的
避免死锁的算法
。在避免死锁方法中允许进程动态地申请资源,但系统在进行资源分配之前,应先计算此次分配资源的安全性,若分配不会导致系统进入不安全状态,则分配,否则等待
。
2.安全序列
-
安全序列是指某个进程序列{P1,…,Pn}是安全的
,即对于每一个进程Pi(1≤i≤n),它以后尚需要的资源量不超过系统当前剩余资源量与所有进程Pj(j < i)当前占有资源量之和
。(即在分配过程中,不会出现某一进程后续需要的资源量比其他所有进程及当前剩余资源量总和还大的情况)
注:存在安全序列则系统是安全的,如果不存在则系统不安全,但不安全状态不一定引起死锁。
3.实现方法:
为保证资金的安全,银行家规定:
- (1) 当一个
顾客对资金的最大需求量不超过银行家现有的资金时就可接纳该顾客
;
(即当资源池中剩余的可利用资源 >=线程还需要的资源时,就可以将可利用资源分配给此线程) - (2) 顾客可以分期贷款,但贷款的总数不能超过最大需求量;
(线程可以请求分配资源,但是请求的资源总数不能超过资源池中剩余的可利用资源) - (3) 当银行家现有的资金不能满足顾客尚需的贷款数额时,对顾客的贷款可推迟支付,但总能使顾客在有限的时间里得到贷款;
(当线程池中的资源暂时不满足当前的线程所需时,将此线程先暂时搁置,先将资源分配给能够满足的需求的其他线程,等到线程池中的资源足够满足先前搁置的线程时,在将资源分配给搁置的线程) - (4) 当
顾客得到所需的全部资金
后,一定能在有限的时间里归还所有的资金
。
(当线程拿到所需要的所有资源,运行结束后,将自身所有的资源放回资源池中)
当一个进程申请使用资源的时候,银行家算法通过先 试探 分配给该进程资源,然后通过安全性算法判断给该进程分配资源后的系统是否处于安全状态,若系统处于不安全状态,则试探分配作废,让该进程继续等待
;
系统给当前进程分配资源时,先检查是否安全
:
在满足当前的进程X资源申请后
,是否还能有足够的资源去满足下一个距最大资源需求最近的进程
(如某进程最大需要5个单位资源,已拥有1个,还尚需4个),若可以满足,则继续检查下一个距最大资源需求最近的进程,若均能满足所有进程,则表示为安全
,可以允许给当前进程X分配其所需的资源申请,否则让该进程X进入等待
。
- Available[ ]矩阵数组表示某类资源的可用量
- Claim[ i ][ j ]表示进程Pi最大需要Rj类资源的数量
- Allocation[ i ][ j ]表示Pi已占有的Rj类资源数量
- Need[ i ][ j ]表示Pi尚需Rj类资源的数量:Need[ i ][ j ]=Claim[ i ][ j ]—Allocation[ i ][ j ]
- Request[ i ]表示进程Pi进程的申请向量,如 Request[ i ][ j ]=m 表示Pi申请m个Rj类资源
- 对于当前进程Pi X
- (1) 检查if(
Request[ i ][ j ]<=Need[ i ][ j ]
) goto (2)
else error(“进程 i 对资源的申请量大于其说明的最大值 ”); - (2) 检查 if (
Request[ i ][ j ]<=Available[ j ]
) goto (3)
else wait() ; /注意是等待!即在对后续进程的需求资源判断中,若出现不符合的则安全检查结束,当前进程进入等待/ - (3) 系统
试探地把资源分给Pi
并修改各项属性值 (具体是否成立,则根据安全检查的结果)
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]
- (4) 安全检查,若检查结果为安全,则(3)中执行有效,否则分配作废,使该Pi进程进入等待
4.检查算法描述
- 向量Free[ j ]表示系统可分配给各进程的Rj类资源数目,初始与当前Available等值
- 向量Finish[ i ]表示进程Pi在此次检查中是否被满足,初始均为false 当有足有资源可分配给进程时,
Finish[i] = true, Pi完成并释放资源(Free[j] += Allocation[i][j])
- 1) 从进程队列中找一个能满足下述条件的进程Pi
①、Finish[i] == false,表示资源未分配给Pi进程
②、Need[i][j] < Free[j],表示资源足够分配给Pi进程
- 2) 当Pi获得资源后,认为Pi完成,释放资源
Free[j] += Allocation[i][j];
Finish[i] = true;
goto Step 1);
例:
int trueSum=0, i=0 ;
boolean Flag=true;
while( trueSum < P.length - 1 && Flag == true )
{
i = i % P.length;
if( Finish[i] == false)
{
if(Need[i][j] < Free[j])
{
Free[j] += Allocation[i][j];
Finish[i] = true;
trueSum++;
i++;
}
else
{
Flag = false;
}
}
}
if( Flag==false)
检查不通过,拒绝当前进程X的资源申请
else
检查通过,允许为当前进程X分配资源
即若可达到Finish[ 0,1,2,……n ] ==true 成立则表示系统处于安全状态
(银行家算法在避免死锁角度上非常有效,但是需要在进程运行前就知道其所需资源的最大值,且进程数也通常不是固定的,因此使用有限,但从思想上可以提供了解,可以转换地应用在其他地方)
假设
资源P1申请资源
,银行家算法先试探的分配给它
(当然先要看看当前资源池中的资源数量够不够),若申请的资源数量小于等于Available,然后接着判断分配给P1后剩余的资源,能不能使进程队列的某个进程执行完毕,若没有进程可执行完毕,则系统处于不安全状态
(即此时没有一个进程能够完成并释放资源,随时间推移,系统终将处于死锁状态)。
若有进程可执行完毕,则假设回收已分配给它的资源(剩余资源数量增加),把这个进程标记为可完成,并继续判断队列中的其它进程,若所有进程都可执行完毕,则系统处于安全状态,并根据可完成进程的分配顺序生成安全序列
如此就可避免系统存在潜在死锁的风险。
5.案例
有5个进程{P1,P2,P3,P4,P5} 。4类资源{R1,R2,R3,R4} 各自数量为6、3、4、2
T0时刻各进程分配资源情况如下
T0时刻为安全状态,存在安全序列{P4,P1,P2,P3,P5} 如下:
二、简单实现
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxpro = 100; //最大进程数
const int maxres = 100; //最大资源数
int pro; //进程数
int res; //资源数
int request[maxres];//进程请求资源数目
//int R[maxres]; //总资源
int V[maxres]; //可提供
int C[maxpro][maxres]; //总需求
int A[maxpro][maxres]; //已分配
int vis[maxpro]; //表示第i个进程是否已分配资源,1表示已分配
int path[maxpro]; //路径
//安全状态判断
bool safe()
{
int curV[maxres]; //目前可提供资源
for(int i = 0; i < res; i++)
curV[i] = V[i];
memset(vis, 0, sizeof(vis));
int flag = 1;
for(int i1 = 0; i1 < pro; i1++)
{
int i;
for(i = 0; i < pro; i++)
{
if(vis[i] == 1)
continue;
int flagpro = 1; //0表示未找到合适的进程
for(int j = 0; j < res; j++)
{
if(C[i][j] - A[i][j] > curV[j])
{
flagpro = 0; break;
}
}
if(flagpro)
{
path[i1] = i;
vis[i] = 1;
for(int k = 0; k < res; k++)
curV[k] += A[i][k];
break;
}
}
if(i == pro)
{
flag = 0;
}
}
return flag == 1;
}
void print()
{
cout << endl << "显示当前状态" << endl;
cout << "总需求矩阵C" << endl;
for(int i = 0; i < pro; i++)
{
for(int j = 0; j < res; j++)
{
printf("%2d ", C[i][j]);
}
cout << endl;
}
cout << "已分配矩阵A" << endl;
for(int i = 0; i < pro; i++)
{
for(int j = 0; j < res; j++)
{
printf("%2d ", A[i][j]);
}
cout << endl;
}
cout << "需求矩阵N (C-A)" << endl;
for(int i = 0; i < pro; i++)
{
for(int j = 0; j < res; j++)
{
printf("%2d ", C[i][j] - A[i][j]);
}
cout << endl;
}
/* cout << "总资源向量R" << endl;
for(int i = 0; i < res; i++)
cout << R[i] << ' ';
cout << endl;*/
cout << "可提供资源向量V" << endl;
for(int i = 0; i < res; i++)
cout << V[i] << ' ';
cout << endl << endl;
}
void bank()
{
while(true)
{
cout << endl << "请求资源输入1,显示当前状态输入2, 结束输入3" << endl;
int k;
cin >> k;
if(k == 3)
break;
else if(k == 2)
{
print();
continue;
}
cout << "请输入请求资源的进程编号, 进程号为0 - " << pro - 1 << endl;
int proindex;
cin >> proindex;
cout << "请输入此进程每个资源需求数目" << endl;
for(int i = 0; i < res; i++)
cin >> request[i];
//检查该进程所需要的资源是否已超过它所宣布的最大值
int flag = 1; //flag为1表示没超过,为0表示超过
for(int i = 0; i < res; i++)
{
if(request[i] + A[proindex][i] > C[proindex][i])
flag = 0;
}
if(flag == 0)
{
cout << "资源请求失败,该进程所需要的资源已超过总资源的最大值" << endl;
continue;
}
//检查系统当前是否有足够资源满足该进程的请求
flag = 1; //flag为1有足够资源,为0表示没有
for(int i = 0; i < res; i++)
{
if(request[i] > V[i])
flag = 0;
}
if(flag == 0)
{
cout << "资源请求失败,系统当前没有有足够资源满足该进程的请求" << endl;
continue;
}
//尝试分配资源给该进程,得到新的状态
for(int i = 0; i < res; i++)
{
A[proindex][i] += request[i]; //已分配资源矩阵A更新
V[i] -= request[i]; //可提供资源向量V更新
}
//执行安全性算法,若该新状态是安全的,则分配完成;若新状态是不安全的,则恢复原状态,阻塞该进程
if(safe())
{
cout << "资源分配成功" << endl;
cout << "安全路径是:";
for(int i = 0; i < pro; i++)
{
cout << path[i] << " ";
}
cout << endl;
for(int i = 0; i < pro; i++)
{
int j;
for(j = 0; j < res; j++)
{
if(A[i][j] != C[i][j])
break;
}
if(j == res)
{
for(j = 0; j < res; j++)
{
V[j] += A[i][j];
A[i][j] = 0;
}
}
}
}
else
{
cout << "该状态不安全,资源分配失败" << endl;
for(int i = 0; i < res; i++)
{
A[proindex][i] -= request[i]; //已分配资源矩阵A更新
V[i] += request[i]; //可提供资源向量V更新
}
}
}
}
int main()
{
cout << "请输入总资源数: " << endl;
cin >> res;
cout << "请输入总进程数: " << endl;
cin >> pro;
/*
cout << "请分别输入每个资源的数目(R向量),目前有" << res << "个资源" << endl;
for(int i = 0; i < res; i++)
cin >> R[i];*/
cout << "请分别输入每个资源的已分配数目(V向量),目前有" << res << "个资源" << endl;
for(int i = 0; i< res; i++)
cin >> V[i];
cout << "请输入总需求矩阵C,共有" << res << "个资源," << pro << "个进程" << endl;
cout << "格式: 每行输入单个进程的总需求资源数目, 输入" << pro << "行" << endl;
for(int i = 0; i < pro; i++)
for(int j = 0 ; j < res; j++)
cin >> C[i][j];
cout << "请输入已分配矩阵A,共有" << res << "个资源," << pro << "个进程" << endl;
cout << "格式: 每行输入单个进程的已分配资源数目, 输入" << pro << "行" << endl;
for(int i = 0; i < pro; i++)
for(int j = 0 ; j < res; j++)
cin >> A[i][j];
bank();
return 0;
}