1、绪论
1.1 概述
银行家算法(Banker’s Algorithm)是一个避免死锁(Deadlock)的著名算法,是由艾兹格•迪杰斯特拉在1965年为T.H.E系统设计的一种避免死锁产生的算法。它以银行借贷系统的分配策略为基础,判断并保证系统的安全运行。
在资源分配的过程中,能有效地对系统的安全性进行检测,整个算法就是对输入的数据进行试分配,并对系统安全性进行检测,当系统安全时,根据安全序列执行进程;若系统不安全,则进入阻塞状态。
1.2 主要思想
1.当一个用户对资金的最大的需求量(即信用额度)不超过银行家现有的资金时就可以接纳该用户。
2.用户可以分期贷款,但贷款的总数不能超过最大需求量
3.当银行家现有的资金不能满足用户的尚需贷款时,对用户的贷款可推迟支付,但总能使用户在有限的时间里得到贷款。
4.当用户得到所需的全部资金后,一定能在有限的时间里归还所有资金。 (客户信用良好,都能安期还款)
1.3 实现意义
在多道程序系统中,多个进程的并发执行来改善系统的资源利用率,提高系统的吞吐量,但可能会产生死锁。
死锁(Deadlock)是指多个进程在运行过程中因争夺资源而造成的一种僵局(DeadlyErbrace),当进程处于这种状态时,若无外力作用,它们都无法在向前推进。
整个算法的计算步骤为:对输入的数据进行试分配,并对安全性进行检测,当系统安全时,依照安全序列执行程序;如果不安全则进入阻塞状态。
1.4 内容及环境
通过编写一个系统动态分配资源的模拟程序,观察死锁产生的条件,利用银行家算法,有效的避免死锁的产生。
采用的系统是Windows10,主要使用的是C语言,使用了部分C++输出格式,开发环境是Microsoft Visual Studio 2017。
2、需求分析
2.1 题目描述
运用银行家算法,避免死锁的发生。在确保当前系统安全的前提下推进的,对进程的请求先进行安全性检测,来决定资源是否分配,从而保证系统的安全,有效的避免了死锁的发生。银行家算法的关键在于安全性算法,即安全性序列的查找。
2.2 产生死锁的原因
1).竞争非剥夺性资源。
2).进程间推进顺序非法。
2.3 产生死锁要条件
1).互斥条件
2).请求和保持条件
3).不剥夺条件
4). 环路和等待条件
2.4 处理死锁的基本方法
1).预防死锁:通过破坏产生死锁的四个必要条件之一
2).避免死锁:不破坏死锁产生的四个必要条件,在资源的动态分配中,防止进程进入可能发生死锁的不安全状态。
3).检测死锁
4).解除死锁:允许系统出现死锁,但系统设置了检测机制,及时检测出死锁;检测出死锁后,系统将采取措施解除死锁。
2.5 预防死锁
避免死锁的实质在于系统在进行资源分配时,如何使系统不进入不安全状态。所谓安全状态,是指系统能按某种进程顺序(P1, P2… Pn) (称<P1,P2, …Pn>序列为安全序列),来为每个进程P分配其所需资源,直至满足每个进程对资源的最大需求,使每个进程都可顺利的完成。如果系统无法找到这样一个安全序列 则称系统处于不安全状态。
1). 破坏“请求和保持条件” ------静态资源分配法
a.思想:资源调度时,若资源全满足,则调度,否则等待。
b.缺点:
- 资源浪费;
- 进程延迟运行,降低进程的并发性;
2). 破坏“不剥夺条件”
a.思想:进程新申请资源得不到满足时,必须释放已占有资源;
b.缺点:
- 保存放弃资源时的现场及以后的现场恢复;
- 可能出现反复申请、释放资源而被无限延迟执行的现象;
3). 破坏“环路等待条件” ------有序资源分配法
a.思想:
- 资源编号;
- 进程以递增顺序申请资源;
b.缺点
- 资源编号难以确定;
- 后用的资源先申请,资源利用率低;
2.6 结论
在预防死锁中,通过破坏产生死锁的必要条件,排除发生死锁的可能性,但都会导致低效的资源使用率和低效的进程执行。
3、设计概要
3.1 设计思路
先对用户提出的请求进行合法性检查,即检查请求的资源数是否大于需要的资源数,是否大于可利用的资源数。若请求合法,则进行试分配,对分配后的状态调用安全性算法检测,若安全,则分配资源给请求的进程;否则进入阻塞状态,并将资源还给系统。
3.2 银行家算法
1)如果Request <= Need,转向2);否则出错;
2)如果Request <= Available,转向3);否则表示系统中没有足够的资源可以满足提出请求的进程,此时令进程等待;
3)系统试着将资源分配给提出请求的进程,并修改相应的数据:Available = Available - Request;Allocation = Allocation + Request;Need = Need - Request;
4)系统执行安全性算法,检查此次资源分配后,系统是否处于安全状态。
3.3 安全性算法
1)设置两个向量
a.工作向量Work表示系统当前可提供给进程继续执行所需要的各类资源的数目,执行安全性算法时,Work = Allocation;
b.布尔向量Finish表示进程当前的状态,初始值为false,当有资源分配给进程完成执行时,令Finish 为true。
2)从进程集合中找到一个可以满足下列条件的进程:
a.Finish[i] = false;
b.Need <= Work.
若找到,执行步骤3),否则执行4);
3)当进程获得资源后,进程开始执行,完成后归还系统分配的资源,数据修改如下:Work = Work + Allocation; Finish[i] = true;转2);
4)当所有的进程的Finish[i] = true时,表示系统处于安全状态,安全序列为顺序满足的进程的顺序;否则处于不安全状态。
3.4 数据结构
1)可利用资源向量Available。这是一个含有m个元素的数组,其中的每一个元素代表一类可利用的资源数目,其初始值是系统中所配置的该类全部可用资源的数目,其数值随该类资源的分配和回收而动态地改变。Available[i]则表示系统中现有R类资源i个;
2)最大需求矩阵Max。这是个n * m的矩阵,它定义了系统中n个进程中的每个进程对m类资源的最大需求。如果Max[i,j]=K,则表示进程i需要R类资源的最大数目为K;
3)分配矩阵Allocation。这也是个n * m的矩阵 它定义了系统中每一类资源当前已分配给每一进程的资源数。如果Allocation[i,j]=K,则表示进程i当前已分得R类资源的数目为K;
4)需求矩阵Need。这也是个n * m的矩阵 用以表示每个进程尚需的各类资源数。 如果Need[i,j]=K,则表示进程还需要R类资源K个方能完成其任务。
上述三个矩阵的关系:Need[i,j]= Max[i,j] - Allocation[i,j]
4、详细设计
4.1 主要函数
1).void get_res(); // 获取资源相关信息
2).void get_pro(); // 获取进程相关信息
3).void show(); // 显示进程信息
4).void display(); // 显示资源分配信息
5).bool safe_check(); // 安全性检测
6).void request(); // 资源请求
7).void Logic(); // 逻辑函数
4.2 数据判断
首先将所有的进程状态全置为false,然后判断进程请求的资源数是否小于Need矩阵,满足则将该进程加入安全序列,否则进入阻塞状态,进程等待。
4.3 流程图
- 主流程图

- 安全性检测算法流程图

- 银行家算法流程图

附上代码:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <stdio.h>
#include <string>
#include <iomanip>
#include <stdbool.h>
using namespace std;
const int N = 1e3 + 10;
// 变量
int pro_num; // 进程数
int res_num; // 资源数
int Available[N]; // 可利用资源数
int Allocation[N][N]; // 已分配矩阵
int Max[N][N]; // 最大矩阵---------------------------------------------------------------------
int Need[N][N]; // 仍需求资源矩阵
// Need[i][j] = Max[i][j] - Allocation[i][j]
int Work[N][N]; // 工作向量(系统可提供给进程继续运行所需的各类资源数目)
// 为了存储整个过程的 Work 变化
int Work_add_All[N][N]; // 用来存储安全性检查过程中的Work+Allocation数据
int safe_list[N]; // 安全队列
int safe_count; // 安全数列中元素个数
bool Finish[N]; // 标记系统是否有足够资源分配给进程并使之运行完成
int re_pro_num; // 请求进程对应编号
int requset_res[N]; // 请求资源数
//函数
void get_res(); // 获取资源相关信息
void get_pro(); // 获取进程相关信息
void request(); // 请求资源
bool safe_check(); // 安全性检测
void menu(); // 菜单栏
void display(); // 展示资源分配状况
void get_res() // 获取资源相关信息
{
printf("请输入资源的类数: ");
scanf("%d", &res_num);
printf("请输入各类资源的总数:");
for (int i = 1; i <= res_num; i++)
{
scanf("%d", &Available[i]);
}
printf("-------------------------------------------------\n");
}
void get_pro() // 获取进程相关信息
{
printf("请输入进程的个数: ");
scanf("%d", &pro_num);
for (int i = 1; i <= pro_num; i++)
{
printf("请输入第%d个进程需要资源的个数: ", i);
for (int j = 1; j <= res_num; j++)
{
scanf("%d", &Max[i][j]);
}
printf("请输入第%d个进程已经分配的资源的个数: ", i);
for (int j = 1; j <= res_num; j++)
{
scanf("%d", &Allocation[i][j]);
Need[i][j] = Max[i][j] - Allocation[i][j];
Available[j] -= Allocation[i][j];
}
printf("\n");
}
printf("------------------------------------------------\n\n");
}
void show() //显示进程信息
{
string str[15] = { "Work|","Need|","All|","Work+All|","Finish|","R","P()","Pro|","Res","","|" };
cout << setw(10) << str[7] << setw(5) << str[9];
for (int i = 1; i<safe_count; i++)
cout << setw(7) << "P(" + to_string(safe_list[i]) + ")";
cout << endl;
cout << setw(10) << str[10] << setw(5) << str[8] << endl;
for (int i = 0; i<5; i++)
{
printf("------------------------------------------------\n");
if (i == 4)
{
cout << setw(10) << str[i] << setw(5) << str[9];
for (int j = 1; j<safe_count; j++)
{
int now = safe_list[j] + 1;
cout << setw(6) << ((Finish[now] == true) ? "T" : "F") << " ";
}
printf("\n");
break;
}
for (int j = 1; j <= res_num; j++)
{
cout << setw(10) << ((j == 1) ? str[i] : str[10]) << setw(5) << str[5] + to_string(j);
if (i == 0)
for (int k = 1; k<safe_count; k++)
printf("%6d ", Work[k][j]);
else if (i == 1)
for (int k = 1; k<safe_count; k++)
printf("%6d ", Need[safe_list[k] + 1][j]);
else if (i == 2)
for (int k = 1; k<safe_count; k++)
printf("%6d ", Allocation[safe_list[k] + 1][j]);
else
for (int k = 1; k<safe_count; k++)
printf("%6d ", Work_add_All[k][j]);
printf("\n");
}
}
}
void display() //显示资源分配信息
{
printf("\n\n----------------资源分配情况----------------\n");
string str[15] = { "Max|","All|","Need|","Avl|","R","P()","Pro|","Res","","|" };
cout << setw(5) << str[6] << setw(5) << str[8];
for (int i = 0; i<pro_num; i++)
cout << setw(6) << "P(" + to_string(i) + ")";
cout << endl << setw(5) << str[9] << setw(5) << str[7] << endl;
for (int i = 0; i<4; i++)
{
printf("----------------------------------------\n");
for (int j = 1; j <= res_num; j++)
{
cout << setw(5) << ((j == 1) ? str[i] : str[10]) << setw(5) << str[4] + to_string(j);
if (i == 0)
{
for (int k = 1; k <= pro_num; k++)
printf("%5d ", Max[k][j]);
}
else if (i == 1)
{
for (int k = 1; k <= pro_num; k++)
{
printf("%5d ", Allocation[k][j]);
}
}
else if (i == 2)
{
for (int k = 1; k <= pro_num; k++)
{
printf("%5d ", Need[k][j]);
}
}
else
{
printf("%5d ", Available[j]);
}
printf("\n\n");
}
}
}
bool safe_check() //安全性检测
{
safe_count = 1;
printf("-------------------安全性检测-------------------\n");
memset(Finish, false, sizeof Finish);
for (int i = 1; i <= res_num; i++)
{
Work[1][i] = Available[i];
}
for (int i = 1; i <= pro_num; i++)
{
// 标记是否还存在 Finish[i] = false && Need[i][j]<=Work[j]
bool flag = false;
for (int j = 1; j <= pro_num; j++)
{
if (Finish[j])
continue;
// 标记该进程是否满足 Finish[i] = false && Need[i][j]<=Work[j]
bool flag_satisfy = true;
for (int k = 1; k <= res_num; k++)
{
if (Need[j][k] > Work[safe_count][k])
flag_satisfy = false;
}
if (flag_satisfy)
{
//若该进程满足条件,则将其加入安全序列,并更新Work向量
flag = Finish[j] = true;
safe_list[i] = j - 1;
printf("P(%d)", j - 1);
for (int k = 1; k <= res_num; k++)
{
Work[safe_count + 1][k] = Work[safe_count][k] + Allocation[j][k];
Work_add_All[safe_count][k] = Work[safe_count + 1][k];
}
safe_count++;
printf("\t\tTRUE\n");
break;
}
}
if (flag == false && i <= pro_num)
{
show();
return false;
}
}
show();
printf("\n系统安全,存在安全序列:");
string str[15] = { "Work|","Need|","All|","Work+All|","Finish|","R","P()","Pro|","Res","","|" };
for (int i = 1; i < safe_count - 1; i++)
cout << setw(7) << "P(" + to_string(safe_list[i]) + ")-->";
cout << "P(" + to_string(safe_list[safe_count - 1]) + ")";
printf("\n");
return true;
}
void request() //资源请求
{
printf("请输入请求资源的进程:");
scanf("%d", &re_pro_num);
printf("请输入进程%d对资源的请求: ", re_pro_num); //(两种资源之间用空格或换行符隔开), res_num
bool flag1 = false; // 标记请求的资源数量是否超过了所需要的资源数量
bool flag2 = false; // 标记请求的资源数量是否超过了当前所能利用的资源数量
for (int i = 1; i <= res_num; i++)
{
scanf("%d", &requset_res[i]);
if (requset_res[i] > Need[re_pro_num][i])
{
flag1 = true;
}
if (requset_res[i] > Available[i])
{
flag2 = true;
}
}
if (flag1 || flag2)
{
if (flag1 && flag2)
{
printf("\n请求的资源数量超过了所需要的资源数量和当前所能利用的资源数量!\n");
}
else if (flag1)
{
printf("\n请求的资源数量超过了所需要的资源数量,无法满足!\n");
}
else
{
printf("\n请求的资源数量超过了当前所能利用的资源数量,让其等待!\n");
}
}
else
{
//试分配
for (int i = 1; i <= res_num; i++)
{
Available[i] -= requset_res[i];
//更新进程资源分配状况,即更新 All 和 Need
Allocation[re_pro_num][i] += requset_res[i];
Need[re_pro_num][i] -= requset_res[i];
}
display(); //展示当前资源分配状况
//检查系统安全性
if (safe_check())
{
//系统安全,分配资源给进程,展示分配状况
display();
}
else
{
//系统不安全,恢复分配资源前可利用资源分配状态
printf("此时系统不安全,不为进程 P(%d)分配资源,让其等待!\n", re_pro_num - 1);
for (int i = 1; i <= res_num; i++)
{
//将资源重新还给系统
Available[i] += requset_res[i];
Allocation[re_pro_num][i] -= requset_res[i];
Need[re_pro_num][i] += requset_res[i];
}
}
}
printf("------------------------------------------------\n");
}
void menu() //菜单
{
printf("\n\n");
printf("---------------------------------------\n");
printf("---- 1.请求资源 ------\n");
printf("---- 2.显示当前资源分配情况 ------\n");
printf("---- 3.退出 ------\n");
printf("---------------------------------------\n");
}
void Logic() //逻辑函数
{
while (1)
{
printf("--------------------银行家算法--------------------\n");
get_res(); // 获取资源信息
get_pro(); // 获取进程信息
display(); // 显示进程信息
printf("\n检查T0时刻的系统安全性:\n\n");
bool safe = safe_check(); // 检查T0时刻的系统安全性
bool over = false; // 结束的标志
while (!over)
{
menu();
int ope = 0; // 选择操作
printf("请输入要执行的操作:");
scanf("%d", &ope);
if (!safe)
printf("系统处于不安全状态!\n");
switch (ope)
{
case 1:
request(); // 请求资源
break;
case 2:
display(); // 展示当前资源分配状况
break;
case 3:
over = true; // 结束此次算法
exit(0);
break;
default:
printf("操作无效!\n");
break;
}
}
}
}
int main()
{
Logic();
return 0;
}
银行家算法是一种避免死锁的策略,由艾兹格•迪杰斯特拉于1965年提出。它通过在资源分配前进行安全性检测,确保系统能够按安全序列执行进程。算法主要包括对资源需求的分析、资源分配的条件判断、安全性算法的实现等。通过模拟程序,银行家算法可以在Windows10环境下,使用C/C++进行实现,以防止系统进入不安全状态。

2507

被折叠的 条评论
为什么被折叠?



