设计题目 | 银行家算法的模拟实现 |
设计形式 | 独立完成 |
设计目的 | 1.掌握避免死锁的原理。 2.研究银行家算法的实现方法。 |
设计预备知识 | 1.死锁概念 多个并发进程,每个进程占有部分资源,又都等待其它进程释放所占资源,造成均不能向前推进的现象。 2.死锁的避免 死锁避免原理就是使系统始终处于安全状态。安全状态:所谓安全状态是指能够找到一个安全序列,系统能够按照这个安全序列依次为进程分配资源,使所有进程都能执行完毕,如果找不到这样的安全序列,系统就处于不安全状态。 3.银行家算法 银行家算法的基本思想是:始终保持系统处于安全状态,当进程提出资源请求时,系统先进行预分配,再判断系统分配后是否仍然处于安全状态。如果仍然处于安全状态,就进行实际分配;如果处于不安全状态,则撤销预分配、拒绝该进程的资源请求。 |
设计内容 | 编程实现银行家算法,语言不限。程序中设置资源向量和各个矩阵的元素个数及初始值,由用户通过输入界面输入某进程对各类资源的请求向量,由程序判断是否能为用户请求进行资源分配,并显示结果。 |
银行家算法的分析、设计与实现
- 银行家算法的理论描述
银行家算法(Banker's Algorithm)是一个避免死锁(Deadlock)的著名算法,是由艾兹格·迪杰斯特拉在1965年为T.H.E系统设计的一种避免死锁产生的算法。它以银行借贷系统的分配策略为基础,判断并保证系统的安全运行。
它定义了一个结构体,结构体里面的不同域分别表示不同资源的数量。初始化:由用户输入数据,分别对可利用资源向量矩阵AVAILABLE、最大需求矩阵MAX、分配矩阵ALLOCATION、需求矩阵NEED赋值。
我们可以把操作系统看作是银行家,操作系统管理的资源相当于银行家管理的资金,进程向操作系统请求分配资源相当于用户向银行家贷款。为保证资金的安全,银行家规定:
(1) 当一个顾客对资金的最大需求量不超过银行家现有的资金时就可接纳该顾客;
(2) 顾客可以分期贷款,但贷款的总数不能超过最大需求量;
(3) 当银行家现有的资金不能满足顾客尚需的贷款数额时,对顾客的贷款可推迟支付,但总能使顾客在有限的时间里得到贷款;
(4) 当顾客得到所需的全部资金后,一定能在有限的时间里归还所有的资金.
操作系统按照银行家制定的规则为进程分配资源,当进程首次申请资源时,要测试该进程对资源的最大需求量,如果系统现存的资源可以满足它的最大需求量则按当前的申请量分配资源,否则就推迟分配。当进程在执行中继续申请资源时,先测试该进程本次申请的资源数是否超过了该资源所剩余的总量。若超过则拒绝分配资源,若能满足则按当前的申请量分配资源,否则也要推迟分配。
二、银行家算法的数据结构模型及算法描述
结构模型:
为了实现银行家算法,在系统中设置以下4个数据结构,分别用来描述系统中可以利用的资源,所有进程对资源的最大需求,系统中的资源分配,以及所有进程还需要多少资源的情况。
(1)可利用资源向量Available
这是含有m个元素的数组向量,其中的每一个元素代表一类可利用的资源数目,其初始值是系统中所配置的该类全部可用资源的数目,其数值随着该类资源的分配和回收而动态地改变,如果Available[j]=K,则表示系统中现有Rj资源K个。
(2)最大需求矩阵Max
这是一个n×m的矩阵,它定义了系统中n个进程中的每一个进程对m类资源的最大需求。如果Max[i,j]=K,则表示进程i需要Rj类资源的最大数目为K。
(3)分配矩阵Allocation
这也是一个n×m的矩阵,它定义了系统中每一类资源当前已分配给每一个进程的资源数。如果Allocation[i,j]=K,则表示进程i当前已分得Rj类资源的数目为K。
(4)需求矩阵Need
这也是一个n×m的矩阵,用于表示每一个进程尚需的各类资源数。如果Need[i,j]=K,则表示进程i还需要Rj类资源K个方能完成其任务。
算法描述:
在避免死锁的方法中,所施加的限制条件较弱,有可能获得令人满意的系统性能。在该方法中把系统的状态分为安全状态和不安全状态,只要能使系统始终都处于安全状态,便可以避免发生死锁。
银行家算法的基本思想是分配资源之前,判断系统是否是安全的;若是,才分配。
设进程cusneed提出请求REQUEST [i],则银行家算法按如下规则进行判断:
(1)如果REQUEST [cusneed] [i]<= NEED[cusneed][i],则转(2);否则,出错。
(2)如果REQUEST [cusneed] [i]<= AVAILABLE[i],则转(3);否则,等待。
(3)系统试探分配资源,修改相关数据:
AVAILABLE[i]-=REQUEST[cusneed][i];
ALLOCATION[cusneed][i]+=REQUEST[cusneed][i];
NEED[cusneed][i]-=REQUEST[cusneed][i];
系统执行安全性检查,如安全,则分配成立;否则试探险性分配作废,系统恢复原状,进程等待。
(1)设置两个工作向量Work=AVAILABLE;FINISH
(2)从进程集合中找到一个满足下述条件的进程,
FINISH==false;
NEED<=Work;
如找到,执行(3);否则,执行(4)
(3)设进程获得资源,可顺利执行,直至完成,从而释放资源。
Work=Work+ALLOCATION;
Finish=true;
GOTO 2
(4)如所有的进程Finish= true,则表示安全;否则系统不安全。
流程图:
三、源代码(可以另附C源程序文件)(5分)
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <windows.h>
#define M 3 //资源种类
#define N 5 //作业数量
#define False 0
#define True 1
char name[N];//资源的名称
int Request[N]={0}; //进程的请求资源向量
int Resource[M];//各资源总数
int Max[N][M];//各进程对各类资源的最大需求
int Allocation[N][M];//各进程已分配资源
int Need[N][M];//各进程还需要资源
int Available[M];//系统可用资源向量
int Work[M];//存放系统可提供使用资源,工作向量
int Finish[N];//判断安全性标志
int temp[N];//存放安全序列的下标序列 list
void initial()
//创建初始状态:先输入 Resource、Max和 Allocation,再计算出 Need、Available。
{
int i,j;
printf("Resource--输入M种资源的总数量:\n");
for(i=0;i<M;i++)
{
scanf("%d",&Resource[i]);
Available[i]=Resource[i]; // 此时,可利用资源数=总资源数
}
printf("Max--输入N个进程分别对M种资源的最大需求量:\n");
for(j=0;j<N;j++)
for(i=0;i<M;i++)
scanf("%d",&Max[j][i]); // 最大需求量
printf("Allocation--输入N个进程获得M种资源的数量:\n");
for(j=0;j<N;j++)
for(i=0;i<M;i++)
scanf("%d",&Allocation[j][i]); // 已分配各资源数
for(j=0;j<N;j++)
for(i=0;i<M;i++)
Need[j][i]=Max[j][i]-Allocation[j][i]; // 还需资源数
for(j=0;j<M;j++)
for(i=0;i<N;i++)
Available[j]=Available[j]-Allocation[i][j]; // 还可利用资源数
}
正在上传…取消
void printState()
//输出当前的状态表|Process |Max |Allocation |Need |Available |
{
int i;
printf("状态表:\n|Process |Max |Allocation |Need |Available | \n");
printf("\t | %c %c %c| %c %c %c| %c %c %c| %c %c %c| \n",name[0],name[1],name[2],name[0],name[1],name[2],name[0],name[1],name[2],name[0],name[1],name[2]);
for(i=0;i<N;i++)
{
if(i==0)
printf("|P%-11d|%4d%4d%4d|%4d%4d%4d|%4d%4d%4d|%4d%4d%4d|\n",i,Max[i][0],Max[i][1],Max[i][2],Allocation[i][0],Allocation[i][1],Allocation[i][2],Need[i][0],Need[i][1],Need[i][2],Available[0],Available[1],Available[2]); // 输出11位整型数, 不够11位按左对齐
else
printf("|P%-11d|%4d%4d%4d|%4d%4d%4d|%4d%4d%4d| |\n",i,Max[i][0],Max[i][1],Max[i][2],Allocation[i][0],Allocation[i][1],Allocation[i][2],Need[i][0],Need[i][1],Need[i][2]);
}
}
正在上传…取消
int isfinish()
//返回同时满足两个条件{①Finish[i]=false; ②Need[i][j]≤Work[j]}的进程下标 i(修改Finish[i]=true),否则返回-1。 需求≤可利用
{
int i,j,count;
for(i=0;i<N;i++) // 计算每个进程finish值
{
for(j=0,count=0;j<M;j++)
if(Finish[i]==0 && Need[i][j]<=Work[j]) // 仅暂时未分配成功的进程的资源进行判断并计数
{
count++;
}
if(count==3) // 表示进程中三个资源均可被分配
{
for(j=0;j<M;j++)
Work[j]+=Allocation[i][j]; // 分配
Finish[i]=1; // 进程可结束
return i; // 返回新分配资源的进程号
}
}
return -1;
}
int issafe()
//判定当前状态是否为安全状态 (返回 true 或 false),把安全序列的下标放入 List[N]数组。
{
int i,a,count=0;
for(i=0;i<M;i++)
Work[i]=Available[i]; // 此时,各资源现可利用数=剩余可利用的各资源数
for(i=0;i<N;i++)
Finish[i]=0; // 初始化finish[ ]数组,均设为false
for(i=0;i<N;i++)
{
a=isfinish();
if(a!=-1) // 表示可结束的安全序列下标
{
temp[i]=a;
count++; // 记录可安全结束的进程数量
}
}
if(count==5)
return 1; // 存在安全序列
else
return 0; // 不存在安全序列
}
void printList( )
//输出安全序列表|Process |Work |Need |Allocation |Work+Alloc |Finish |
{
int i,j;
printf("\n安全序列表如下:\n|Process |Work |Need |Allocation |Work+Alloc |Finish |\n");
printf("\t | %c %c %c| %c %c %c| %c %c %c| %c %c %c|\n",name[0],name[1],name[2],name[0],name[1],name[2],name[0],name[1],name[2],name[0],name[1],name[2]);
for(j=0;j<M;j++)
{
Work[j]=Available[j];
}
for(i=0;i<N;i++)
{
printf("|P%-11d|%4d%4d%4d|%4d%4d%4d|%4d%4d%4d|%4d%4d%4d|true\n",temp[i],Work[0],Work[1],Work[2],Need[temp[i]][0],Need[temp[i]][1],Need[temp[i]][2],Allocation[temp[i]][0],Allocation[temp[i]][1],Allocation[temp[i]][2],Work[0]+Allocation[temp[i]][0],Work[1]+Allocation[temp[i]][1],Work[2]+Allocation[temp[i]][2]);
for(j=0;j<M;j++)
Work[j]+=Allocation[temp[i]][j];
}
}
正在上传…取消
void reqresource(int i, int Request[M])
//表示第 i个进程请求 M类资源 request[M]
{
int flag,count1,count2;
int j;
//Step1: 判断条件 Request[j]≤Need[i][j]
for(j=0,count1=0;j<M;j++)
if(Request[j]<=Need[i][j])
count1++;
//Step2: 判断条件 Request[j]≤Available[j]
for(j=0,count2=0;j<M;j++)
if(Request[j]<=Available[j])
count2++;
if(count2!=3)
printf("\n尚无足够的资源,第%d个进程堵塞。\n",i); // 越界
//Step3: 预先分配
if(count2==3 && count1==3) // 未越界
{
for(j=0;j<M;j++)
{
Available[j]=Available[j]-Request[j];
Allocation[i][j]=Allocation[i][j]+Request[j];
Need[i][j]=Need[i][j]-Request[j];
} // 预先更新进程i的Available,Allocation,Need状态表
if(issafe()==0)
{
printf("\n不存在安全序列,不是安全状态。\n");
for(j=0;j<M;j++)
{
Available[j]=Available[j]+Request[j];
Allocation[i][j]=Allocation[i][j]-Request[j];
Need[i][j]=Need[i][j]+Request[j];
} // 恢复进程i的Available,Allocation,Need状态表
}
else
{
printf("\n是安全序列分配成功!\n");
printList();
}
}
//Step4:检测是否为安全状态
}
int main()
{
int reqid=-1,j;
initial(); // 初始化
for(int a=0;a<M;a++){
name[a]=a+65;
} // 定义资源名称A,B,C
printState(); // 输出状态表
if(issafe()==0) // 判断安全状态
{
printf("Initial state is unsafe!\n");
}
else
{
printf("\nInitial state is safe!\n");
printList(); // 输出安全序列状态表
printf("Input the id of request process:");
scanf("%d",&reqid); // 输入请求资源id号
while(reqid>=0 && reqid<N) // 判断输入进程 id是否合法
{
printf("Input request resources:");
for(j=0;j<M;j++)
{
scanf("%d",&Request[j]);
} // 输入请求资源进程的各资源请求数
reqresource(reqid, Request); // 进程reqid请求Request[M]资源
printState();
printf("Input the id of request process:");
scanf("%d",&reqid);
}
}
return 0;
}
四、程序运行结果及分析(45分)
T0 时刻的资源分配表(各种资源的数量分别为:10、5、7)
资源情况 进程 | 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 | 3 3 2 |
P1 | 3 2 2 | 2 0 0 | 1 2 2 | |
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 |
自行设计各进程的请求向量,至少提出3个请求向量,采用各种请求向量来验证银行家算法。例如:非法请求,资源不足的情况或合法且资源足够的情况等。将运行结果截图,并对结果进行分析。
- P1请求资源:
P1发出请求向量Request1(1,0,2),系统按银行家算法进行检查:
① Request1(1, 0, 2)≤Need1(1, 2, 2);
② Request1(1, 0, 2)≤Available1(3, 3, 2);
③ 系统先假定可为P1分配资源,并修改Available, Allocation和Need向量;
④ 利用安全性算法检查此时系统是否安全。
- P4请求资源:
P4发出请求向量Request4(3,3,0),系统按银行家算法进行检查:
① Request4(3, 3, 0)≤Need4(4, 3, 1);
② Request4(3, 3, 0) ≮ Available(2, 3, 0),P4阻塞等待。
- P0请求资源:
P0发出请求向量Requst0(0,2,0),系统按银行家算法进行检查:
① Request0(0, 2, 0)≤Need0(7, 4, 3);
② Request0(0, 2, 0)≤Available(2, 3, 0);
③ 系统暂时先假定可为P0分配资源,并修改有关数据;
④ 利用安全性算法检查此时系统是否安全,可用资源Available(2, 1, 0)不满足任何进程的要求,系统进入不安全状态,恢复Available, Allocation和Need向量。