最近刚刚做了银行家算法的课程设计,记录一下。
**银行家算法分析、设计与实现**
摘要
银行家算法(Banker’s Algorithm)是一个避免死锁(Deadlock)的著名算法,是由艾兹格·迪杰斯特拉在1965年为T.H.E系统设计的一种避免死锁产生的算法。它以银行借贷系统的分配策略为基础,判断并保证系统的安全运行。
银行家算法是一种最有代表性的避免死锁的算法。在避免死锁方法中允许进程动态地申请资源,但系统在进行资源分配之前,应先计算此次分配资源的安全性,若分配不会导致系统进入不安全状态,则分配,否则等待。为实现银行家算法,系统必须设置若干数据结构。
关键词:安全状态;安全序列;银行家算法;安全性算法;安全性序列;流程图。
Abstract
Banker Algorithm is a famous algorithm to avoid Deadlock. It is formed by Ezra pound, dijkstra in 1965 for T.H.E system design of a kind of to avoid deadlock algorithm. It is based on bank lending system allocation strategy, determine and ensure the safe operation of the system.
Banker algorithm is one of the most representative algorithm is proposed to avoid the deadlock.In the method to avoid deadlock allows processes dynamically application resources, but the system before the allocation of resources, should first calculate the security of the allocation of resources, if not bring into the system safety state, assigned, or wait.To realize the banker algorithm, the system must be set a number of data structures.
Key words: Security status; Security sequence; Bankers algorithm; Security algorithm;The flow chart.
1. 算法综述
1.1背景
在多道程序系统中,虽可以借助多个进程的并发执行来改善系统的资源利用率,提高系统吞吐量,但可能发生一种危险——死锁,即多个进程在运行过程中因争夺资源而造成的一种僵局,若无外力作用,将无法再向前推进。因此,我们只需在当前的有限资源下,找到一组合法的执行顺序,便能很好的避免死锁,我们称它为安全序列。而银行家算法起源于银行系统的发放贷款,和计算机操作系统的资源分配完全符合,因此可以借鉴该算法的思想,设计出一种有效的算法程序,解决该问题。
死锁是进程死锁的简称,是由Dijkstra于1965年研究银行家算法时首先提出来的。是指多个进程循环等待它方占有的资源而无限期地僵持下去的局面。很显然,如果没有外力的作用,那麽死锁涉及到的各个进程都将永远处于封锁状态。它是计算机操作系统乃至并发程序设计中最难处理的问题之一。
1.2.1产生死锁的原因
(1)竞争资源引起进程死锁
当系统中提供多个进程共享的资源,其数目不足以满足诸进程的需要时,会引起诸进程对资源的竞争产生死锁。
(2)进程推进顺序不当
进程在运行过程中,请求和释放资源的顺序不当,同样也会导致进程产生死锁。
1.2.2产生死锁的必要条件
(1)互斥条件
进程对所分配的资源进行排它性使用,即在一段时间内某一资源只由一个进程占用。如果还有其它进程请求该资源,则请求者只能等待,直至占有该资源的进程用毕释放。
(2)请求和保持条件
进程已经保持了至少一个资源,但是又提出了新的资源请求,而该资源又被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
(3)不剥夺条件
进程已获得的资源,在未使用完之前,不剥夺,只能在使用完时由自己释放。
(4)环路等待条件
在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{p0,p1,p2….,pn}中的p0在等待一个p1占用的资源;p1在等p2占用的资源……,pn正在等待已被p0占用的资源。
1.2.2处理死锁的方法
(1)预防死锁
破坏产生死锁的必要条件中的一个或几个,但是不能破坏互斥条件。
(2)避免死锁
属于事先预防策略,不必事先采取破坏产生死锁的必要条件,而是在资源的动态分配过程中,用某种方法去防止系统不进入安全状态,从而避免死锁。
(3)检测死锁
不须事先采取任何限制措施,也不必检查系统是否已经进入不安全区。但是可通过系统所设置的检测机制,及时检测除死锁的发生,采取适当的措施,从系统中将已发生的死锁清除。
(4)解除死锁
与检测死锁相配套。当检测到系统中发生死锁时,须将进程从死锁状态解脱出来。
1.3银行家算法的应用
排课系统是高校不可缺少的一部分,它的内容 对于学校的决策者和管理者来说都至关重要,随着高校的扩招和学生课程的增加,而学校的资源又是有限的,用传统的人工管理方式排课就显得比较复杂,所以高校教务处如何使当前所有选课的学生在有限的时间内修完课程,并归还所有申请的资源,这一点非常重要。
2. 算法分析
银行家算法,顾名思义是来源于银行的借贷业务,一定数量的本金要应多个客户的借贷周转,为了防止银行加资金无法周转而倒闭,对每一笔贷款,必须考察其是否能限期归还。在操作系统中研究资源分配策略时也有类似问题,系统中有限的资源要供多个进程使用,必须保证得到的资源的进程能在有限的时间内归还资源,以供其他进程使用资源。如果资源分配不得到就会发生进程循环等待资源,则进程都无法继续执行下去的死锁现象。
把一个进程需要和已占有资源的情况记录在进程控制中,假定进程控制块PCB其中“状态”有就绪态、等待态和完成态。当进程在处于等待态时,表示系统不能满足该进程当前的资源申请。“资源需求总量”表示进程在整个执行过程中总共要申请的资源量。显然,,每个进程的资源需求总量不能超过系统拥有的资源总数, 银行算法进行资源分配可以避免死锁.
3. 算法设计
3.1银行家算法
设进程i提出请求Request[j],则银行家算法按如下规则进行判断。
(1)如果Request[j]>Need[i][j],则报错返回。
(2)如果Request[j] > Avaliable[j],则进程i进入等待资源状态,返回。
(3) 假设进程i的申请已获批准,利用FBstore()函数先将目前系统状态保存,再修改系统状态如下:
Avaliable[j]=Avaliable[j]-Request[j];
Allocation[i][j]=Allocation[i][j]+Request[j];
Need[i][j]=Need[i][j]-Request[j];
(4)系统执行安全性检查,如安全,则分配成立;否则试探险性分配作废,调用FBback()函数恢复系统原状,进程等待。
3.2安全性算法
(1)设置两个工作向量Work=Available , Finish[i]=False
(2)从进程集合中找到一个满足下述条件的进程,
Finish[i]=False
Need<=Work,
如找到,执行(3);否则,执行(4)
(3)设进程获得资源,可顺利执行,直至完成,从而释放资源。
Work=Work+Allocation
Finish=True
GO TO 2
(4)如所有的进程Finish[i]=true,则表示安全;否则系统不安全。
3.3数据结构
#define MAX_RESOURCE 100 //最大资源总数
#define MAX_COURSE 100 //最大进程总数
#define True 1
#define False 0
int Available[MAX_RESOURCE]; //可使用资源向量
int Max[MAX_COURSE ][MAX_RESOURCE]; //最大需求矩阵
int Allocation[MAX_COURSE ][ MAX_RESOURCE]; //分配矩阵
int Need[MAX_COURSE ][MAX_RESOURCE ]; //需求矩阵
int Request[MAX_RESOURCE];
int resource;//系统资源总数m;
int course;//总进程数n;
4. 编码实现
.1 SafetyArithmetic()函数
4.1.1函数功能
检测系统的安全性,系统安全就可以允许进程的申请资源分配,不安全则不予分配。根据进程数,从头开始查找,直至当前的进程数为零时,停止检测
4.1.2函数中数据类型
int Finish[j] //记录当前的安全序列状态
int Work[j] //工作向量
int p[j] //记录当前满足安全状态的进程
int falg //检测进程满足条件与否标识
4.2 BankArithmetic()函数
4.2.1函数功能
实现银行家算法,当Request请求分配之后,最重要是判断安全状态。
4.2.2函数中数据类型
int flag1; //标志位
int flag2;//标志位
//保存请求资源分配前的可利用资源向量Available
int AvailableSave[MAX_RESOURCE];
//保存请求资源分配前的已分配向量Allocation
int AllocationSave[MAX_COURSE ][ MAX_RESOURCE];
//保存请求资源分配前的需求矩阵Need
int NeedSave[MAX_COURSE ][MAX_RESOURCE ];
4.3 InitArr()函数
4.3.1函数功能
初始化银行家算法中需要的各类资源向量,以及资源总类和需要操作的进程总数
4.3.2函数中数据类型
该函数数据使用的是全局变量的数据
int Available[MAX_RESOURCE]; //可使用资源向量
int Max[MAX_COURSE ][MAX_RESOURCE]; //最大需求矩阵
int Allocation[MAX_COURSE ][ MAX_RESOURCE]; //分配矩阵
int Need[MAX_COURSE ][MAX_RESOURCE ]; //需求矩阵
int Request[MAX_RESOURCE];
int resource;//系统资源总数m;
int course;//总进程数n;
4.4 print()函数
4.4.1函数功能
显示当前各个资源向量的,为了让用户能够更好的观察当前系统中资源的分配
4.4.2函数中数据类型
该函数数据使用的是全局变量的数据
int Available[MAX_RESOURCE]; //可使用资源向量
int Max[MAX_COURSE ][MAX_RESOURCE]; //最大需求矩阵
int Allocation[MAX_COURSE ][ MAX_RESOURCE]; //分配矩阵
int Need[MAX_COURSE ][MAX_RESOURCE ]; //需求矩阵
5. 测试及测试过程
5.1 测试一
输入T0 时刻的资源分配表(3种资源的总量分别为:10、5、7),输入后进行安全性算法检查,如下图。
5.2 测试二
选择分配资源功能号,P1进行资源请求,申请A类资源1个单元,申请B
类资源0个单元,申请C类资源2个单元,用于测试请求资源分配,且系统当前资源情况可以满足分配,且找到一组安全序列的情况,测试结果如下图。
5.3 测试三
选择分配资源功能号,P4进行资源申请,申请A类资源3个单元,申请B类资源3个单元,申请C类资源0个单元,用于测试申请的资源大于系统可用资源的情况,测试结果如下图。
5.4测试四
选择分配资源功能号,P0进行资源请求,申请A类资源0个单元,B类资源2个单元,C类资源0个单元,用于测试满足银行家算法,但无安全序列的情况,测试结果如下图。
5.5测试五
选择分配资源功能号,P0进行资源请求,申请A类资源0个单元,B类资源1个单元,C类资源0个单元,用于测试第二次请求资源分配,系统当前资源情况可以满足分配,且找到一组安全序列的情况,测试结果如下图。
6. 总结
银行家算法是避免死锁的一种重要方法。死锁的产生,必须同时满足四个条件,即一个资源每次只能由一个进程;第二个为等待条件,即一个进程请求资源不能满足时,它必须等待,但它仍继续保持已得到的所有其他资源;第三个为非剥夺条件,即在出现死锁的系统中一定有不可剥夺使用的资源;第四个为循环等待条件,系统中存在若干个循环等待的进程,即其中每一个进程分别等待它前一个进程所持有的资源。防止死锁的机构只能确保上述四个条件之一不出现,则系统就不会发生死锁。但是在产生死锁的必要条件中不能破坏互斥条件。在安全算法,可以不在银行算法实现的任何时候进行。系统安全状态不安全,不一定造成死锁。但是系统安全状态安全,一定不会造成死锁。
参考文献
[1] 谭浩强.C程序设计.第四版.清华大学出版社.2010.
[2] 梁红兵, 哲凤屏, 汤子赢等, 计算机操作系统.第四版.西安电子科技大学.2007.
附录
#include
#include
/////////////////////初始化定义/////////////////////////////
#define MAX_RESOURCE 100 //最大资源总数100
#define MAX_COURSE 100 //最大进程总数100
#define True 1
#define False 0
int Available[MAX_RESOURCE]; //可使用资源向量
int Max[MAX_COURSE ][MAX_RESOURCE]; //最大需求矩阵
int Allocation[MAX_COURSE ][ MAX_RESOURCE]; //分配矩阵
int Need[MAX_COURSE ][MAX_RESOURCE ]; //需求矩阵
int Request[MAX_RESOURCE];
int resource; //系统资源总类;
int course; //总进程数;
///////////////////////函数声明///////////////////////////
bool InitArr();
void Print(int *Available, int (*Need)[MAX_RESOURCE],
int (*Allocation)[MAX_RESOURCE],int resource,int course);
bool BankArithmetic(int *Available,int *Request,int (*Need)[MAX_RESOURCE],
int (*Allocation)[MAX_RESOURCE],int resource,int course ,int p);
bool SafetyArithmetic(int * Available,int (*Need)[MAX_RESOURCE],
int (*Allocation)[MAX_RESOURCE ],int resource ,int course);
////////////////////////主函数////////////////////////////
int main()
{
InitArr(); //各资源向量初始化
int i;
int j;
int choice=1;
if(SafetyArithmetic(Available,Need,Allocation,resource,course)) //T0时刻是否处于安全状态
{
printf("T0时刻处于安全状态\n"); //T0时刻安全状态下继续执行操作
}
else
{
printf("T0时刻处于不安全状态\n");//T0时刻不安全状态下直接结束工作
return 0;
}
while(choice)
{
printf("\n*********银行家算法的实现************\n");
printf(" 1: 分配资源 \n");
printf(" 0: 退出 \n");
printf("**************************************\n");
printf("选择功能号:");
scanf("%d",&choice);
switch(choice)
{
case 1:
for(i=0;i//最大进程数为100
{
printf("输入请求资源的进程号:");
scanf("%d",&i);
if(i>=course)
{
printf("输入错误,重新开始!\n");
continue;
}
else
{
break;
}
}
printf("请输入进程p[%d]所请求的资源数Request<%d>:",i,resource);
for(j=0;j"%d",&Request[j]);
}
//调用银行家算法
BankArithmetic(Available,Request,Need,Allocation,resource,course,i); break;
case 0:
choice=0;
break;
default:
printf("选择正确的功能号!\n");
break;
}
}
return 0;
}
/////////////////////////其他函数///////////////////////////
bool SafetyArithmetic(int * Available,int (*Need)[MAX_RESOURCE],
int (*Allocation)[MAX_RESOURCE ],int resource ,int course)
{
int count = course; //进程的总数
int *Finish; //当前进程的标识
Finish = (int *)malloc(sizeof(int)*course);
int *Work; //工作向量
Work = (int *)malloc(sizeof(int)*resource);
int *p; //记录安全序列号数组
p = (int *)malloc(sizeof(int)*course);
int flag; //标志位,表示当前进程被检测是否符合要求
flag = 1;//被检测的符合要求
flag = 0;//被检测不符合要求
int i;
int j;
int k=0;
for (i=0;i//将所有的进程设置为False
{
Finish[i]=False;
}
for(i=0;iwhile (count!=0)
{
for(i=0;iif(Finish[i]==False)
{
flag = 1;
for(j=0;jif (Need[i][j]>Work[j])
{
flag = 0;
}
}
if (flag==1)
{
Finish[i] = True;
p[k++] = i; //将当前的安全序列记录下来
for (j=0;j//未完成检测的进程数-1
}
for (i=0;iif (Finish[i]==False)
{
flag = 0;
break;
}
}
if (flag == 0 )
{
printf("系统处于不安全状态!\n");
return false;
}
else
{
printf("当前的系统安全!\n");
printf("当前时刻存在的安全序列:");
for(i=0;i"P[%d] ",p[i]);
}
}
printf("\n");
free(Work);
free(Finish);
free(p);
return true;
}
//定义打印函数
void Print(int *Available, int (*Need)[MAX_RESOURCE],
int (*Allocation)[MAX_RESOURCE],int resource,int course)
{
int i;
int j;
printf("\n=================资源情况====================\n");
printf("==Available:可用资源 Need:需求矩阵 ==\n");
printf("==Allocation: 分配矩阵 Max:最大需求矩阵==\n");
printf("=============================================\n\n");
printf("Available:");
for(i=0;i"%d ",Available[i]);
}
printf("\n");
printf("Need |Allocation| Max\n");
for(i=0;ifor(j=0;j"%d ",Need[i][j]);
}
printf("\t");
for(j=0;j"%d ",Allocation[i][j]);
}
printf("\t");
for(j=0;j"%d ",Allocation[i][j]+Need[i][j]);
}
printf("\n");
}
printf("=============================================\n");
printf("=============================================\n\n");
}
bool BankArithmetic(int *Available,int *Request,int (*Need)[MAX_RESOURCE],
int (*Allocation)[MAX_RESOURCE],int resource,int course ,int p)
//p表示第p个进程想要分配
{
int i;
int j;
int flag1;
int flag2;
int AvailableSave[MAX_RESOURCE];
//保存请求资源分配前的可利用资源向量Available
int AllocationSave[MAX_COURSE ][ MAX_RESOURCE];
//保存请求资源分配前的已分配向量Allocation
int NeedSave[MAX_COURSE ][MAX_RESOURCE ];
//保存请求资源分配前的需求矩阵Need
for (i=0;ifor (i=0;ifor (j=0;jfor (i=0;ifor (j=0;jfor(i=0;iif(p==i)
{
flag1 = 1;
flag2 = 1;
for(j=0;jif(Request[j]>Need[i][j])
{
flag1=0;
}
}
if (flag1==0)
{
printf("请求资源超过该进程资源需求量,请求失败!\n");
printf("当前的各类资源数向量:\n");
Print(AvailableSave,NeedSave,AllocationSave,resource,course);
return false;
}
else
{
for (j=0;jif(Request[j]>Available[j])
{
flag2=0;
}
}
if (flag2==0)
{
printf("没有足够的资源分配,请求失败!\n");
printf("当前的各类资源数向量:\n");
Print(AvailableSave,NeedSave,AllocationSave,resource,course);
return false;
}
else
{
for(j=0;jif(SafetyArithmetic(Available,Need,Allocation,resource,course))
{
printf("该进程p[%d]获得申请资源资格\n",p);
printf("分配后的各类资源数:\n");
for(i=0;iif(p==i)
{
flag1=1;
for(j=0;jif(Need[i][j] != 0)
{
flag1=0;
break;
}
}
if(flag1==1)
{
for(j=0;j0;
}
break;
}
}
}
Print(Available,Need,Allocation,resource,course);//调用打印函数
}
else
{
for(i=0;ifor(j=0;j"当前的各类资源数向量:\n");
Print(Available,Need,Allocation,resource,course);//调用打印函数
return false;
}
return true;
}
bool InitArr()
{
printf("*****************银行家算法的初始化*****************\n\n");
while(1)
{
int i;
int j;
printf("输入系统资源总类:");
scanf("%d",&resource);
printf("输入进程总数:");
scanf("%d",&course);
if(resource>=MAX_RESOURCE||course>=MAX_COURSE)
{
printf("超出设定的最大的进程数和资源\n");
continue;
}
printf("可利用资源Available向量个元素<%d>:",resource);
for(i=0;i"%d",&Available[i]);
}
printf("进程资源的最大需求Need<%d*%d>向量:\n",resource,course);
for(i=0;ifor(j=0;j"%d",&Need[i][j]);
}
}
printf("进程资源的分配Allocation<%d*%d>向量:\n",resource,course);
for(i=0;ifor(j=0;j"%d",&Allocation[i][j]);
}
}
Print(Available,Need,Allocation,resource,course);//调用打印函数
return true;
}
}