银行家算法
银行家算法在OS中可以避免死锁。
实现 银行家算法需要四个数据结构用来分别描述:系统中可利用的资源,所有进程对资源的最大需求,系统中资源的分配情况,所有进程还需要多少资源。
- 一维数组Available(可利用资源):每个资源代表一类资源的数目。其中的值会随着资源的Allocation分配和回收动态改变。
- 矩阵Max(最大需求):n*m矩阵,表示n个进程对m类资源的最大需求。
- 矩阵Allocation(当前分配资源):n*m矩阵,表示系统对n个进程关于m类资源的已分配数目。
- 矩阵Need(进程尚需资源):n*m矩阵,表示n个进程对m类资源尚需的数目;其中有关系Need[i,j]=Max[i,j]-Allocation[i,j]。
另外一个变量n*m二维数组Request表示n个进程当前向系统请求的m类资源的数量
当一个进程pi向系统请求资源,Requesti[j]=k,表示对第j类资源请求k个资源,系统按一下步骤进行:
- 如果Requesti[j]<=Needi[j],说明请求资源不超过声明的尚需最大资源,是合法的,可以进入下一步;否则不合法,因为请求资源超过声明的尚需最大请求量。
- 如果Requesti[j]<=Available[j],则表示当前系统空闲的第j类资源Available[j]可以满足进程pi请求的资源,允许进入下一步;否则当前进程pi需要等待。(可用for循环按顺序查找,遍历一边 )
- 当系统尝试满足pi的请求,会有以下的操作
- Available[j]=Available[j]-Requesti[j],因为资源分配出去,相应的资源就会减少。
- Need[i,j]=Need[i,j]-Requesti[j],更新尚需资源数
- Allocation[i,j]=Allocation[i,j]+Requesti[j],更新已分配资源情况。
- 执行玩第三步,还需要进行安全性检查,若安全则允许分配,否则第三步作废,数据恢复到第三步操作前的状态。(对应多个安全序列)
安全性算法如下:
- 设置两个变量,第一个是一个一维数组Work,表示系统可分配给进程的各类资源数目,开始运行安全算法时,Work=Available:第二个是一个一维bool类型数组Finish,表示集合中各个进程是否完成,开始时全部设置为FALSE。若系统有足够资源分给Pi时,再设置Finish[i]=TRUE。
- 从进程集合种寻找能满足下面条件的进程pi: Finish[i]=FALSE(即尚未完成),Need[i,j]<=Work[j](尚需资源可被当前系统满足的)。若找得到就进入第三步,否则进入第四步。
- 执行以下操作:
- Work[j]=Work[j]+Allocation[i,j],即若执行完Pi后,要释放相应的资源。
- Finish[i]=TRUE,标记该进程执行完毕
- 回到第二步继续执行(while循环的条件是第二步中的两个条件)
- 查找全部Finish,若全为TRUE则表示该系统处于安全状态,有一个对应的安全序列(不唯一) 。否则不安全。
实现代码如下:
/*
* @Author: pumpkins
* @Date: 2021-10-23 14:50:27
* @LastEditTime: 2021-10-24 01:09:32
* @FilePath: \Vscode_workspace\OS\银行家算法.cpp
*/
#pragma warning(disable : 4996)
#include <cstdio>
#define proNum 5 //进程数目
#define resNum 3 //资源种类
int Available[resNum] = {3, 3, 2}; //系统当前的各类资源可用数目
int Need[proNum][resNum] = {0}; //当前各个进程对各类资源尚需的数目
int Max[proNum][resNum] = {{7, 5, 3}, {3, 2, 2}, {9, 0, 2}, {2, 2, 2}, {4, 3, 3}}; //各个进程对各类资源的最大需求数目
int Allocation[proNum][resNum] = {{0, 1, 0}, {2, 0, 0}, {3, 0, 2}, {2, 1, 1}, {0, 0, 2}}; //当前系统给各个进程已经分配的各类资源的情况
int n = 1; //请求执行的进程号
int Request[resNum]; //请求资源序列
void print();
void bank();
bool safeTest();
int main(void)
{
for (int i = 0; i < proNum; ++i) //初始化Need
{
for (int j = 0; j < resNum; ++j)
{
Need[i][j] = Max[i][j] - Allocation[i][j];
}
}
while (n + 1) //输入要执行的进程序号,输入负数退出
{
bool flag = true; //标记所有进程是否都已执行完毕,先假设都完成
for (int i = 0; i < proNum; ++i) //如果Need全为0说明执行完毕
{
if (flag == false)
break;
for (int j = 0; j < resNum; ++j)
{
if (Need[i][j] != 0)
{
flag = false;
break;
}
flag = true;
}
}
if (flag == true)
{
printf("所有进程已执行完毕!\n");
break;
}
print(); //输出当前资源分配情况
printf("请输入要请求的进程序号:");
scanf("%d", &n);
if (n < 0)
break;
printf("请输入资源的请求向量:");
for (int i = 0; i < resNum; ++i)
{
scanf("%d", &Request[i]);
}
bank(); //银行家算法
}
printf("结束!\n");
}
void print() //用于输出当前进程和资源分配状态
{
printf(" Max Allocation Need\n");
printf(" A B C A B C A B C\n");
for (int i = 0; i < proNum; ++i)
{
printf("p%-3d", i);
printf("%-3d%-3d%-4d", Max[i][0], Max[i][1], Max[i][2]);
printf("%-3d%-3d%-6d", Allocation[i][0], Allocation[i][1], Allocation[i][2]);
printf("%-3d%-3d%-4d", Need[i][0], Need[i][1], Need[i][2]);
printf("\n");
}
printf("Available: A:%3d B:%3d C:%3d", Available[0], Available[1], Available[2]);
printf("\n");
}
void bank() //银行家算法
{
//第一步,先判断是否满足Request~i~[j]<=Need
for (int i = 0; i < resNum; ++i)
{
if (Request[i] > Need[n][i]) //如果请求的资源大于最大需求量,说明请求不合法
{
printf("请求不合法!请求的资源数量不可大于最大需求量!\n");
return;
}
}
//第二步,判断是否满足Request~i~[j]<=Available[j]
for (int i = 0; i < resNum; ++i)
{
if (Request[i] > Available[i]) //如果请求的资源数目大于当前系统的空闲资源数,则需要等待
{
printf("当前系统资源不足,p%d需要等待。\n");
}
}
//第三步进行安全性测试决定是否分配资源
if (safeTest() == true)
{
printf("分配后系统处于安全状态,系统可满足p%d的资源请求!\n", n);
}
else
{
printf("分配后系统处于不安全状态,因此无法满足p%d的资源请求!\n", n);
}
printf("\n");
}
bool safeTest() //安全性测试
{
//更新为分配后的状态,并暂时记录分配前的状态,若分配后不安全就恢复
int tmpAva[resNum];
int tmpNeed[proNum][resNum];
int tmpAllo[proNum][resNum];
for (int j = 0; j < resNum; ++j)
{
tmpAva[j] = Available[j];
Available[j] -= Request[j];
for (int i = 0; i < proNum; ++i)
{
tmpNeed[i][j] = Need[i][j];
tmpAllo[i][j] = Allocation[i][j];
//进程Pn的状态信息需要改变
if (i == n)
{
Need[i][j] -= Request[j];
Allocation[i][j] += Request[j];
}
}
}
//进行安全测试
int quen[proNum], tmp = 0; //记录安全序列
int Work[resNum];
bool Finish[proNum] = {false}; //标记进程是否完成,开始时全为false
for (int i = 0; i < resNum; ++i) //初始时Work为Available
{
Work[i] = Available[i];
}
bool flag = true;
while (flag)
{
flag = false; //标记是否找到一个可执行进程
for (int i = 0; i < proNum; ++i)
{
if (Finish[i] == false) //先要满足未执行完毕
{
flag = true;
for (int j = 0; j < resNum; ++j) //需要满足所有类资源才可继续执行
{
if (Need[i][j] > Work[j]) //不满足条件
{
flag = false;
break;
}
}
if (flag == true) //找到了满足条件的可以执行的进程
{
for (int j = 0; j < resNum; ++j)
{
Work[j] += Allocation[i][j]; //释放资源
}
Finish[i] = true; //标记该进程完成
quen[tmp] = i; //计入安全序列
tmp++;
//break; //这里不break也可以,只是不同的思路,break是找到一个后又重头找;不break是找到后继续向后找
}
}
}
}
for (int i = 0; i < proNum; ++i) //若有进程无法完成,则不安全
{
if (Finish[i] == false)
{
//恢复
for (int j = 0; j < resNum; ++j)
{
Available[j] = tmpAva[j];
for (int i = 0; i < proNum; ++i)
{
Need[i][j] = tmpNeed[i][j];
Allocation[i][j] = tmpAllo[i][j];
}
}
return false;
}
}
//安全。输出安全序列,并且释放资源
for (int i = 0; i < resNum; ++i)
{
Available[i] += Allocation[n][i];
}
printf("该情况下的一个安全序列为:");
for (int i = 0; i < proNum; ++i)
{
printf("p%d ", quen[i]);
}
printf("\n");
return true;
}
其中安全序列并不唯一,上面有两种思路,见172行。