广州大学2020操作系统实验二:银行家算法

在这里插入图片描述

相关资料

广州大学2020操作系统实验一:进程管理与进程通信
广州大学2020操作系统实验二:银行家算法
广州大学2020操作系统实验三:内存管理
广州大学2020操作系统实验四:文件系统
广州大学2020操作系统实验五:磁盘管理实验
五份实验报告下载链接🔗

课程设计下载链接🔗

一、实验目的

1、了解什么是操作系统安全状态和不安全状态;
2、了解如何避免系统死锁;
3、理解银行家算法是一种最有代表性的避免死锁的算法,掌握其实现原理及实现过程。

二、实验内容

根据银行家算法的基本思想,编写和调试一个实现动态资源分配的模拟程序,并能够有效避免死锁的发生。

三、实验原理

四、实验中用到的系统调用函数(包括实验原理中介绍的和自己采用的),自己采用的系统调用函数要按照指导书中的格式说明进行介绍。

因为是模拟程序,可以不使用系统调用函数。

五、实验步骤
1、画出银行家算法流程图;

在这里插入图片描述

2、对算法所用的数据结构进行说明;

//全局指针变量,方便调用
int *Available = NULL; //每类资源可用的数量
int **Max = NULL; //每个进程对资源的最大需求
int **Allocation= NULL; //当前分给每个进程的资源数目
int **Need= NULL; //每个进程还缺多少资源,在该实验里等价于申请的资源,即等价于Request。

typedef struct{
	int no;		//记录进程申请资源的序号	
	int finish;	//是否完成
}WorkNode;   		//工作节点
WorkNode stack[N];
六、实验数据及源代码(学生必须提交自己设计的程序源代码,并有注释,源代码电子版也一并提交),包括思考题的程序。

bank.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define N 5 //系统进程数
int M; //系统资源类

//全局指针变量,方便调用
int *Available = NULL; //每类资源可用的数量
int **Max = NULL; //每个进程对资源的最大需求
int **Allocation= NULL; //当前分给每个进程的资源数目
int **Need= NULL; //每个进程还缺多少资源,在该实验里等价于申请的资源。
clock_t start=0,end=0;

typedef struct{
int no; //记录申请的序号 
int finish; //是否完成
}WorkNode; //工作节点
WorkNode stack[N];

void init(); //随机初始化测试数据
void release(); //进程完成,释放资源
void banker(); //银行家算法,主要为资源分配算法(包含模拟随机进程发起申请资源)
int security(); //安全性算法部分
int satisfy1(int n); //已有的资源是否能满足该进程的请求
int satisfy2(int n,int Work[]);
int MIN3(int a,int b,int c);
int MAX(int a,int b);
int MIN(int a,int b);

int main()
{
srand((unsigned long)time(0)); //产生随机数种子 
init(); //随机初始化测试数据
start=clock(); //开始
banker(); //银行家算法
end=clock(); //结束
double time = end-start;
printf("time=%lf\n",time); //输出运行时间
release(); //释放资源
}

int security(int num) //安全性算法
{
int Work[M],flag,i,j,No=1;

for(i=0;i<M;i++)
Work[i]=Available[i];
for(i=0;i<N;i++){ //初始化
stack[i].no=0;
stack[i].finish=0;}
stack[num].no = 1; //PIDnum申请执行完归还资源
stack[num].finish=1;
for(i=0;i<M;i++)
Work[i]+=Allocation[num][i];

while(1){
flag =1;
for(i=0;i<N;i++)
if(stack[i].finish==0 && satisfy2(i,Work)==1){
for(j=0;j<M;j++)
Work[j]=Work[j]+Allocation[i][j];
No +=1;
stack[i].no = No;
stack[i].finish = 1;
flag = 0;
break;}
if (flag ==1)break; //说明没有满足以上条件的进程了
}

flag = 1;
for(i=0;i<N;i++)
if(stack[i].finish == 0)flag = 0;
return flag;
}

void banker() //银行家算法,主要为资源分配算法部分(包含模拟随机进程发起申请资源)
{
int num,i,j,Request[M]; //记录申请的是PID几
while(1)
{
num = rand()%N;
if(satisfy1(num)==1)break;//随机选取进程发起申请,现有的资源是否能满足该进程的请求,不满足则推迟分配
}
printf("\nPID%d申请资源,假设系统把资源分配给它",num+1);
for(i=0;i<M;i++){
Request[i] = Need[num][i];
Available[i] -=Request[i];
Allocation[num][i] +=Request[i];
Need[num][i] -= Request[i];
}
int flag = security(num);
if (flag ==0)
{
printf("\n分配资源后系统状态是不安全的,因此不分配资源。");
for(i=0;i<M;i++){ //不分配,应当还原此前状态
Available[i] +=Request[i];
Allocation[num][i] -=Request[i];
Need[num][i] += Request[i];}
}
else
{
printf("\n分配资源后系统状态是安全的,该安全序列为:");
for(i=0;i<N;i++)
for(j=0;j<N;j++)
if(stack[j].no-1==i){
printf("->%d",j+1);break;} 
}
printf("\n");

}

int satisfy2(int n,int Work[]) //可分配的资源是否能满足该进程的请求
{
int flag = 1;
for(int i=0;i<M;i++)
if(Need[n][i]>Work[i])
{flag=0;break;}
return flag;
}

int satisfy1(int n) //可分配的资源是否能满足该进程的请求
{
int flag = 1;
for(int i=0;i<M;i++)
if(Need[n][i]>Available[i])
{flag=0;break;}
return flag;
}

void release() //进程完成,释放资源
{
free(Available);
for(int i=0;i<N;i++)
{
free(Max[i]);
free(Allocation[i]);
free(Need[i]);
}
free(Max);
free(Allocation);
free(Need);
}

void init() //随机初始化测试数据
{
int i,j,flag=1,flag2;
M = (rand()%10)+1; //初始化资源类的总数,1~10,方便观察
M = 10; //固定资源一共10类
int Resource[M],Remain[M];//系统中每类资源的总数量(Resource = Allocation+Available)

while(flag==1){
for(i=0;i<M;i++) //初始化系统中每类资源的总数量(Resource = Allocation+Available)
{Resource[i]=rand()%30+10;
Remain[i]=Resource[i];}

Max = (int**)malloc(sizeof(int*) * N); //初始化每个进程对资源的最大需求 Max <= Resource
for (i=0;i<N;i++) Max[i] = (int*)malloc(sizeof(int) * M);
for (i=0;i<N;i++)
for(j=0;j<M;j++)
Max[i][j]=MIN(rand()%4,Resource[j]);

Allocation = (int**)malloc(sizeof(int*) * N);//初始化当前已分给每个进程的资源数目(Allocation=Resource-Available)
for (i=0;i<N;i++) Allocation[i] = (int*)malloc(sizeof(int) * M);
for (i=0;i<N;i++)
for(j=0;j<M;j++) 
{
Allocation[i][j]=MIN3(rand()%3,Max[i][j],Remain[j]);
Remain[j] = Remain[j] - Allocation[i][j];
}

Available = malloc(sizeof(int) * M); //初始化每类资源可用于的数量 (Available=Resource-Allocation)
for(i=0;i<M;i++){
Available[i] =Resource[i];
for (j=0;j<N;j++) 
Available[i] -= Allocation[j][i];
}

Need = (int**)malloc(sizeof(int*) * N); //初始化每个进程还缺少的资源数(Need = Max - Allocation)
for (i=0;i<N;i++) Need[i] = (int*)malloc(sizeof(int) * M);
for (i=0;i<N;i++)
for(j=0;j<M;j++)
Need[i][j] = Max[i][j] - Allocation[i][j];

for(i=0;i<N;i++){ //保证随机初始化的数据,可以让至少一个进程能得到全部资源到结束
flag2 = 1;
for(j=0;j<M;j++)
if(Available[j]<Need[i][j]){flag2 = 0;break;}
if(flag2==1){flag=0;break;}}
}

printf("进程一共有%d个\n",N); //以下为打印输出
printf("资源一共有%d类\n",M);
printf("系统中每类资源的总数量依次为:");
for(i=0;i<M;i++) 
printf("%d ",Resource[i]);
printf("\n");
for (i=0;i<N;i++)
{
printf("\nPID%d对每类资源数的最大需求依次为:",i+1); 
for(j=0;j<M;j++)
printf("%d ",Max[i][j]);
}
printf("\n");
for (i=0;i<N;i++)
{
printf("\nPID%d对每类资源数的当前已获得数目依次为:",i+1); 
for(j=0;j<M;j++)
printf("%d ",Allocation[i][j]);
}
printf("\n");
printf("\n");
printf("每类资源可用于分配的数量依次为:");
for(i=0;i<M;i++)
printf("%d ",Available[i]);
printf("\n");
for (i=0;i<N;i++)
{
printf("\nPID%d对每类资源数的缺少的数目依次为:",i+1);
for(j=0;j<M;j++)
printf("%d ",Need[i][j]);
}
printf("\n");
} 

int MIN3(int a,int b,int c)
{
a = a<b?a:b;
return a<c?a:c;
}

int MIN(int a,int b)
{
return a<b?a:b;
}

int MAX(int a,int b)
{
return a>b?a:b;
}
七、实验结果分析(截屏的实验结果,与实验结果对应的实验分析)
初步展示

初步展示两次输出,规定进程为5个,其余均为随机初始化,具体初始化思路可查看代码和思考题一的回答。
在这里插入图片描述
从上图来看,当PID4申请分配资源结束并释放资源后,其他进程均可申请到资源并执行完毕,因此可以分配资源给PID3,并输出了该安全序列。
在这里插入图片描述
从上图来看,当PID2申请资源结束并释放资源时,资源可用于分配的数量为
0 2 3 5 4 4 2 5 3 1,可满足PID3的申请,结束并释放资源后,资源可用于分配的数量为2 2 4 6 4 5 2 5 3 3,不满足PID1、PID2、PID4的申请,因此会造成死锁,所以这是一个不安全的状态,不能分配资源给PID2.

充分实验

为了进一步观察进程数对于算法复杂度的影响,接下来将对进程10、20、30、50、100个,资源的种类规定为10类,其余均为随机初始化,具体初始化思路可查看代码和思考题一的回答。输出银行家算法所执行的时间,不包括初始化数据的时间。

该实现由于展示的图实在太多,在这里省略掉。。。

具体分析

进程10个:平均花费时间7.2ms
进程20个:平均花费时间8.4ms
进程30个:平均花费时间12ms
进程50个:平均花费时间18ms
进程100个:平均花费时间45ms
在这里插入图片描述
(一)这里假设输入为固定的资源种类数,以及输出均为安全序列下的耗费时间,因为输出不安全序列花费的时间随机性太大,也许在第一处就测出不安全或者在结尾才检测出不安全,仅仅5个反复实验看不出明显的区别,因此该时间复杂度实验是在假设均为安全序列下的耗费时间的前提下进行的。

(二)总的来说,可以看出该算法所耗费的时间与进程个数成正比。

(三)接下来,进一步的分析,影响银行家算法时间复杂度的主要代码就是安全性算法部分的回溯法这一段,模拟了全部进程的输出。
在这里插入图片描述
(四)理想状态下,这段代码在最坏的情况下,就是全部进程都可以申请到资源顺利完成,这样需要遍历n次,每次把满足要求的一个进程提出来,进程的位置处于from 1 to n,所耗费的时间为 ( 1 + n ) × n 2 \frac{(1+n)\times n}{2} 2(1+n)×n,时间复杂度为 O ( n 2 ) O(n^2) O(n2)

(五)从上面的表格来看,或许不能直观体现出算法复杂度,正是因为该银行家算法并不仅仅只有安全性算法,还有随机进程发起申请以及资源分配算法也是耗费一定的时间的。

八、思考题
1、如何设计程序的输入模块才能满足实验要求,请举例说明;

(一)首先,我们先列出需要初始化的数据:(为简化说明,省去复杂的数组结构)
① Resource :每类资源的总数量
② Allocation :当前已分给每个进程的资源数目
③ Available :每类资源可用于分配的数量
④ Max :每个进程对资源的最大需求
⑤ Need :每个进程还缺少的资源数
⑥ Request :每个进程对资源的申请数量

(二)这些数据之间存在一定的联系,因此直接按顺序随机初始化不能满足题目的要求,下面列出它们之间存在的联系:
① Resource = Available + Allocation
② Max = Need + Allocation
③ Need = Request
④ Resource >= Max
⑤ 均 >= 0

(三)从上面的关系可以看到,首先随机初始化Resource :rand()%a+5;
然后随机初始化Max:MIN(rand()%b,Resource); Resource >= Max >= Allocation,且一式和二式都有Allocation,因此第三个随机初始化的就是Allocation:MIN3(rand()%c,Max,Remain),它的初始化比较特别,Remain代表了前面进程对同一资源没用剩下的,保证所有进程对同一资源的加和小于Resource;接下来的Available 、Need 由前面做差可得;Request直接由Need 赋值可得。

(四)以上初始化后的数据,基本满足条件了,但是实验报告要求至少一个进程能完成:在任何时刻保证至少有一个进程能得到所需的全部资源而执行到结束。因为Available 和Need 最后是做差得到的,并没有一定的联系,因此,在初始化所有数据后,会做一个判断,是否满足有一个进程可以获得资源成功结束,是则初始化完毕,否则重新生成。

(五)对a、b、c进行分析,取模的范围决定了初始化数据的范围,a越大,资源越多,越大概率可以得到安全序列;b越大各个进程争夺的资源相应变多,越大概率得到的是不安全,还有可能会死锁;c越大,说明已分配的资源越多,可用于分配的资源就少了,也是会导致不安全序列。因此为了大概率得到安全序列的初始化数据,下面的验证采用了a=5,b=5,c=3。

(六)进程数和资源数均设置为5,方便观察。下面展示了一个安全序列的例子。可以看到第一组为Resource,第二组为Max,第三组为Allocation,第四组为Available,第五组为Need,都符合上述假设,并满足实际应用场景。
在这里插入图片描述
(七)在进行时间复杂度实验中,首先固定了资源种类为10,进程数依次为10、20、30、50、100,为了使得大进程数实现输出安全状态,会调整a=30,b=4,c=3;这样可以满足实验的具体要求。

2、银行家算法在实现过程中必须注意哪些资源分配细节才能避免死锁?

(一)从第一个回答可以看出,显然,每个进程的资源需求总量不能超过系统拥有的资源总数。

(二)银行家算法避免死锁的核心,就是避免不安全序列,虽然不安全序列不一定会死锁,但银行家算法不允许这种情况发生,一定要安全状态下才可以进行资源分配。

  • 10
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
一、课程设计题目及内容 时间片轮转法实现处理机调度的程序设计提示如下: (1)假设系统有n个进程,每个进程用一个进程控制块(PCB)来代表。进程控制块的格式如下表所示,且参数意义也相同。 进程名 链接指针 到达时间 估计运行时间 进程状态 (2)按照进程到达的先后顺序排成一个循环队列,设一个队首指针指向第一个到达进程的首址。另外再设一个当前运行进程指针,指向当前正运行的进程。 (3)执行处理机调度时,首先选择队首的第一个进程运行。 (4)由于本题目是模拟实验,所以对被选中的进程并不实际启动运行,而只是执行如下操作:1)估计运行时间减1; 2)输出当前运行进程的名字。 用这两个操作来模拟进程的一次运行。 (5)进程运行一次后,以后的调度则将当前指针依次下移一个位置,指向下一个进程,即调整当前运行指针指向该进程的链接指针所指进程,以指示应运行进程,同时还应判断该进程的剩余运行时间是否为0,若不为0,则等待下一轮的运行,若该进程的剩余运行时间为0,则将该进程的状态置为完成状态“C”,并退出循环队列。 (6)若就绪队列不为空,则重复上述的步骤(4)和(5)直到所有进程都运行完为止。 (7)在所设计的调度程序中,应包含显示或打印语句,以便显示或打印每次选中进程的名称及运行一次后队列的变化情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wujiekd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值