目录
一、实验目的
用高级语言编写和调试一个简单的银行家算法程序。加深了解有关资源申请、避免死锁等概念,并体会和了解死锁和避免死锁的具体实施方法。
二、实验内容
设计一个P(例如P=3)个并发进程共享J(例如J=4)类不同资源的系统,进程可动态地申请资源和释放资源,系统按各进程的申请动态地分配资源。
设计用银行家算法 ,实现资源分配,应具有显示或打印各进程依次要求申请的资源数以及依次分配资源的情况。
确定一组各进程依次申请资源数的序列,输出运行结果。
把每个进程需要和已占有资源的情况记录在进程控制中,假定进程控制块PCB其中“状态”有就绪态、等待态和完成态。当进程在处于等待态时,表示系统不能满足该进程当前的资源申请。“资源需求总量”表示进程在整个执行过程中总共要申请的资源量。显然,每个进程的资源需求总量不能超过系统拥有的资源总数,银行算法进行资源分配可以避免死锁。
三、程序代码及运行情况
Ⅰ:函数实现:
安全性检查:int Safe(Status *S)
拷贝系统资源状态结构:void Copy(Status *S1,Status *S2)
银行家算法:Status* Bank(Status *S,int num,int re[])
初始化系统资源状态:Status* Init(Status *S)
输出系统资源状态矩阵:void Disp(Status *S)
Ⅱ:主要功能的实现过程:
1.构建系统资源分配状态数据结构:
实验指导中给出了系统资源分配状态的数据结构,增加了工作向量Work,完成标志Finish和进程的状态state,其中M为进程总数,N为系统资源的种类数,ALLResource为各资源数量总和,Available为系统可用资源数,MAX为进程对资源需求的最大值,Need为进程还需资源的数量,Allocation为进程已分配到资源的数量,Request为进程请求资源的数量,Work为工作向量,用于安全性检测,Finish为进程完成发资源分配标志,用于安全性检测,state为进程的状态,包括就绪,等待,完成三个状态。具体实现的代码截图如下:
图3-1系统资源分配状态的数据结构
2.初始化进程和系统资源:
给系统资源状态数据结构开辟存储空间,提示用户依次输入进程数量,资源种类数,根据输入的资源种类数,提示用户输入个资源的总数,输入每个进程对各种资源的最大需求数量,输入已给进程分配的系统资源的数量,起初Available的值为系统总资源数,状态全为就绪状态,完成标志为false,本代码中用0表示false,用1表示true,将各个进程已分配的资源数赋值给Allocation,Need=MAX-Allocation,系统可用资源数等于总资源数减每个进程已分配的各个资源数,返回系统资源状态S。具体代码截图如下:
图3-2初始化进程和系统资源
3.银行家算法
首先为S1,S2开辟存储空间将此时系统资源分配状态拷贝到S1,S2,S1用来在系统不安全时返回原来的值,S2用来执行安全性检测,判断输入的进程号是否小于1,小于一则报错,将进程请求传入系统资源分配块中,判断请求向量的值是否都大于等于0,否则报错,判断请求向量的值是否小于等于该进程的尚需Need,判断请求向量的值是否小于等于系统资源数Available,满足上述条件后系统试探的将资源费配给进程,最后判断该进程的尚需Need是否全部为0,是则更改进程状态为完成F,完成标志Finich置为1,释放该进程占用的资源,执行安全性检测算法,系统安全返回S试探分配后状态,即完成分配操作,,不安全返回S1试探分配前的状态,即不执行分配操作。
具体代码截图如下:
图3-3 银行家算法
4.安全性检测算法
首先说明代码中各变量的用途,count用来记录写入安全序列数字的下标,list数组用来保存安全序列,flag1判断Finish向量是否全部为true,flag判断是否存在Finish=false且Need<=Work的进程;
之后进行安全性检测,首先判断进程已经处于运行完成状态的进程将其直接加入到安全序列中,将工作向量Work的初值设为Available,在有进程满足上述两个条件时(flag),每个进程依次判断是否满足两个条件,满足Finish=0的进程继续判断是否满足Need<=Work,满足条件的进程将Work向量累加该进程已占用的资源Allocation,将该进程添加到安全序列数组中保存,继续循环上述操作,直到没有进程满足两个条结束。
判断全部进程的Finish向量是否为1(true),是则代表系统处于安全状态,输出提示并输出安全序列返回1,否则代表系统处于不安全状态,输出提示返回0.
进程调度的代码截图如下:
图3-4 安全性检测算法
Ⅲ运行过程及分析:
输入:
系统中有五个进程,进程号分别为1,2,3,4,5,有三类资源,总数分别为10,5,7,进程所需资源的最大值分别为7 5 3,3 2 2,9 0 2,2 2 2,4 3 3,起始状态进程已分配的各类资源分别为0 1 0,2 0 0,3 0 2,2 1 1,0 0 2。
图3-5输入初始状态
输出:
1.初始状态以及安全性检测:
图3-6-1 初始状态以及安全序列
解释:
进行安全性检测,系统处于安全状态,安全序列为2,4,5,1,3,各进程状态都为就绪状态,系统剩余可分配的资源数为3 3 2。
2. 2号进程发出请求向量Request2(1,0,2),系统按银行家算法进行检测:
图3-6-2 分配后状态及安全序列
解释:
2号进程请求分配第一类资源数1,第二类资源数0,第三类资源数2,系统试探的为其分配后进行安全性检查,系统处于安全状态,安全序列为2,4,5,1,3,此时系统剩余可分配的资源数为2 3 0,没有进程运行完成,所有进程的状态为就绪状态。
3. 5号进程发出请求向量Request5(3,3,0),系统按银行家算法进行检查:
图3-6-3资源不足,分配失败
解释:
5号进程请求第一类资源数3,第二类资源数3,第三类资源数0,由于系统此时剩余可分配的资源数为2 3 0,无法满足5号进程提出的资源分配请求,因此输出资源不足,不能完成资源分配操作,系统资源分配状态不变。
4. 1号进程发出请求向量Request1(0,2,0),系统按银行家算法进行检查:
图3-6-4系统不安全,分配失败
解释:
1号进程申请第一类资源数0,第二类资源数2,第三类资源数0,系统试探的为其分配资源后执行安全性检查,系统处于不安全状态,不进行分配操作,系统资源分配状态维持原来的状态。
5. 2号进程发出请求向量Request2(0,2,0),系统按银行家算法进行检查:
图3-6-5 2号进程完成运行,系统安全,分配成功
解释:2号请求分配后系统处于安全状态,分配资源后2号进程完成执行,状态为完成状态。
四、实验过程中出现的问题及解决方法
1. 安全性检测算法中不安全时返回原来状态,拷贝系统资源状态快的指针时出错,将其地址直接赋值给S1,导致无法正确拷贝,使之没法正确执行安全性检测,通过拷贝值的方法解决了该问题。
2. 安全性检测是使用了原有状态指针导致执行安全检测算法时将各进程的Finish向量全部赋值为1,使后续分配的安全性检测出错,通过拷贝原有指针指向的系统资源状态快,使其进行安全性检查不影响原有的状态解决了该问题。
3随着进程资源的申请,安全队列的总是从头开始,这个问题并没有解决,而是在后续进程申请过程中并不显示安全队列,正确的方式应该是,随着进程资源的申请,安全队列随之变化,不安全的不允许申请。
五、源码
#include <stdio.h>
#include <stdlib.h>
#define W 10 //最大进程数
#define R 20 //最大资源种类
typedef struct{
int M; //总进程数
int N; //资源种类
int ALLResource[R]; //各种资源数目总和
int Available[R]; //系统可用资源数
int MAX[W][R]; //M个进程对N类资源最大需求量
int Need[W][R]; //M个进程还需N类资源的数量
int Allocation[W][R]; //M个进程已经得到N类资源的数量
int Request[R]; //请求资源个数
int Work[R]; //工作向量
int Finish[W]; //初始值为0(false)进程获得资源后为1(true)
char state[W]; //M个进程的状态,R就绪,W等待,F完成
}Status;
int Safe(Status *S){ //安全检测
int count=0; //保存安全序列数组的下标
int list[S->M]; //保存安全序列的进程号
int flag1=0; //判断Finish向量是否全为true标志
int flag=1; //判断是否存在Finish=false且Need<=Work标志
int i,j;
for(i=0;i<S->M;i++){ //安全检测前已运行完进程直接加入安全序列
if(S->Finish[i]==1){
list[count]=i+1;
count++;
}
}
for(i=0;i<S->N;i++){ //Work工作向量初值为Available
S->Work[i]=S->Available[i];
}
while(flag!=0){ //全部进程都不满足两个条件
flag=0;
for(i=0;i<S->M;i++){ //检测进程i的Finsh是否为false
if(S->Finish[i]==0){ //进程i满足条件一
int temp=S->N;
for(j=0;j<S->N;j++){ //检测进程i的Need是否小于等于Work
if(S->Need[i][j]<=S->Work[j]){
temp--;
}
}
if(temp==0){ //进程i满足条件二
flag--;
for(j=0;j<S->N;j++){ //更改工作向量Work
S->Work[j]+=S->Allocation[i][j];
}
S->Finish[i]=1;
list[count]=i+1; //保存安全序列
count++;
}
}
}
}
for(i=0;i<S->M;i++){
if(S->Finish[i]==0){
flag1--;
}
}
if(flag1==0){
printf("系统处于安全状态!\n");
printf("安全序列:");
for(i=0;i<S->M;i++){
printf("%d ",list[i]);
}
printf("\n");
return 1; //安全返回1
}else{
printf("系统处于不安全状态!\n");
return 0; //不安全返回0
}
}
void Copy(Status *S1,Status *S2){ //将S1的数据拷贝到S2
int i,j;
S2->M=S1->M;
S2->N=S1->N;
for(i=0;i<S1->N;i++){
S2->ALLResource[i]=S1->ALLResource[i];
S2->Available[i]=S1->Available[i];
S2->Request[i]=S1->Request[i];
S2->Work[i]=S1->Work[i];
}
for(i=0;i<S1->M;i++){
S2->Finish[i]=S1->Finish[i];
S2->state[i]=S1->state[i];
}
for(i=0;i<S1->M;i++){
for(j=0;j<S1->N;j++){
S2->MAX[i][j]=S1->MAX[i][j];
S2->Allocation[i][j]=S1->Allocation[i][j];
S2->Need[i][j]=S1->Need[i][j];
}
}
}
Status* Bank(Status *S,int num,int re[]){ //银行家算法,第num+1个进程发出请求Request
int i;
int safe;
Status *S1,*S2;
S1=(Status *)malloc(sizeof(Status)); //为S1开辟存储空间
S2=(Status *)malloc(sizeof(Status)); //为S2开辟存储空间
Copy(S,S1); //复制进程信息S
int temp=S->N;
if(num+1>S->M||num<0){ //第num个进程大于总进程数或小于1
printf("无该进程!");
return 0;
}
for(i=0;i<S->N;i++){
S->Request[i]=re[i];
}
for(i=0;i<S->N;i++){
if(S->Request[i]<0){ //非法请求
printf("请求资源错误!");
return 0;
}
}
for(i=0;i<S->N;i++){
if(S->Request[i]>S->Need[num][i]){ //所需要的资源数已经超过它宣布的最大值
printf("错误,请求资源数超过需求!");
return 0;
}
}
for(i=0;i<S->N;i++){
if(S->Request[i]>S->Available[i]){ //没有足够的资源分配给该进程
printf("资源不足!\n");
S->state[num]='W'; //进程状态更改为等待状态
return S;
}
}
for(i=0;i<S->N;i++){ //试探的把资源分配给该进程
S->Available[i]-=S->Request[i];
S->Allocation[num][i]+=S->Request[i];
S->Need[num][i]-=S->Request[i];
}
for(i=0;i<S->N;i++){
if(S->Need[num][i]==0){
temp--;
}
}
if(temp==0){
S->state[num]='F';
S->Finish[num]=1;
for(i=0;i<S->N;i++){ //试探分配后该进程已经运行完毕则释放该进程占用资源
S->Available[i]+=S->Allocation[num][i];
S->Allocation[num][i]=0;
}
}
Copy(S,S2); //拷贝S到S2
safe=Safe(S2); //使用S2进行安全检测不会破坏S的值
if(safe==1){ //安全返回S,即完成分配
return S;
} else{
return S1; //不安全返回S1,即不进行分配
}
}
Status* Init(Status *S){ //初始化进程和系统资源
int i,j;
S=(Status *)malloc(sizeof(Status)); //为进程状态结构开辟内存空间
printf("请输入进程数:");
scanf("%d",&S->M);
printf("请输入资源种类数:");
scanf("%d",&S->N);
for(i=0;i<S->N;i++){
printf("请输入第%d类资源的总数:",i+1);
scanf("%d",&S->ALLResource[i]);
S->Available[i]=S->ALLResource[i];
}
for(i=0;i<S->M;i++){
for(j=0;j<S->N;j++){
printf("请输入第%d个进程第%d类资源的最大需求数:",i+1,j+1);
scanf("%d",&S->MAX[i][j]);
S->state[i]='R'; //进程状态初始为就绪状态R
S->Finish[i]=0; //进程完成分配标志初始为未完成0,false
}
}
for(i=0;i<S->M;i++){
for(j=0;j<S->N;j++){
printf("请输入已分配给第%d个进程第%d类资源数:",i+1,j+1);
scanf("%d",&S->Allocation[i][j]);
}
}
for(i=0;i<S->M;i++){
for(j=0;j<S->N;j++){
S->Need[i][j]=S->MAX[i][j]-S->Allocation[i][j];
S->Available[j]-=S->Allocation[i][j]; //Available初值为MAX,依次减去个进程已分配的资源数
}
}
return S;
}
void Disp(Status *S){ //输出系统资源使用状态矩阵
int i,j,x,y,m;
printf("此时系统资源使用状态矩阵:\n");
printf("矩阵格式:[PID,MAX,Allocation,Need,state,Finish,Available]\n");
for(i=0;i<S->M;i++){
printf("%d ",i+1);
for(j=0;j<S->N;j++){
if(j<S->N-1){
printf("%d,",S->MAX[i][j]);
} else{
printf("%d ",S->MAX[i][j]);
}
}
for(x=0;x<S->N;x++){
if(x<S->N-1){
printf("%d,",S->Allocation[i][x]);
}else{
printf("%d ",S->Allocation[i][x]);
}
}
for(y=0;y<S->N;y++){
if(y<S->N-1){
printf("%d,",S->Need[i][y]);
} else{
printf("%d ",S->Need[i][y]);
}
}
printf("%c ",S->state[i]);
printf("%d ",S->Finish[i]);
if(i==0){
for(m=0;m<S->N;m++){
printf("%d ",S->Available[m]);
}
}
printf("\n");
}
}
int main() {
int i;
int temp=1;
Status *S,*S1;
S1=(Status *)malloc(sizeof(Status)); //用于初始状态检测安全序列
S=Init(S);
Copy(S,S1);
printf("初始系统资源状态:\n");
Disp(S);
Safe(S1);
do{
int num;
int re[S->N];
printf("请输入请求进程号:");
scanf("%d", &num);
printf("请输入该进程请求各类资源数:");
for (i = 0; i < S->N; i++) {
scanf("%d", &re[i]);
}
S=Bank(S,num-1,re);
Disp(S);
printf("是否继续请求? 0/1");
scanf("%d", &temp);
}while(temp);
return 0;
}