操作系统实验——银行家算法
文章目录一、实验目的二、实验内容和要求三、实验原理算法实现四、实验程序代码如下:五、验证数据和运行结果运行结果截图六、思考与分析附一、实验目的掌握银行家算法思想,并能编程实现。二、实验内容和要求1、在Linux环境下编译运行程序;2、按照教材的算法编写;3、(*)输入数据从文本文件中读出,不从键盘录入,数据文件格式见以下说明;4、主要数据结构的变量名和教材中的一致,包括Available、Max、
一、实验目的
掌握银行家算法思想,并能编程实现。
二、实验内容和要求
1、在Linux环境下编译运行程序;
2、按照教材的算法编写;
3、(*)输入数据从文本文件中读出,不从键盘录入,数据文件格式见以下说明;
4、主要数据结构的变量名和教材中的一致,包括Available、Max、Allocation、Need、Request、Work、Finish。
5、程序可支持不同个数的进程和不同个数的资源;
6、验证教材中的“银行家算法示例”中的例子(包括可成功分配、不可分配)。
三、实验原理
要解释银行家算法,必须先解释操作系统安全状态和不安全状态。
- 安全状态:如果存在一个由系统中所有进程构成的安全序列P1,…,Pn,则系统处于安全状态。安全状态一定是没有死锁发生。
- 不安全状态:不存在一个安全序列。不安全状态不一定导致死锁。
那么什么是安全序列呢?
安全序列:一个进程序列{P1,…,Pn}是安全的,如果对于每一个进程Pi(1≤i≤n),它以后尚需要的资源量不超过系统当前剩余资源量与所有进程Pj (j < i)当前占有资源量之和。
银行家算法:
按银行家制定的规则为进程分配资源,可以使系统保持在安全状态,具体方法如下:
· 进程首次申请资源的分配
如果系统现存资源可以满足该进程的最大需求量,则按当前的申请量分配资源,否则推迟分配。
· 进程在执行中继续申请资源的分配
若该进程已占用的资源与本次申请的资源之和不超过对资源的最大需求量,且现存资源能满足该进程尚需的最大资源量,则按当前申请量分配资源,否则推迟分配。
· 至少一个进程能完成
在任何时刻保证至少有一个进程能得到所需的全部资源而执行到结束。
银行家算法通过动态地检测系统中资源分配情况和进程对资源的需求情况来决定如何分配资源,并能在确保系统处于安全状态时才把资源分配给申请者,从而避免系统发生死锁。
算法实现
初始化:
由data文件中读取数据数据,分别对可利用资源向量矩阵Available、最大需求矩阵Max、分配矩阵Allocation、需求矩阵Need赋值。
银行家算法:
在避免死锁的方法中,所施加的限制条件较弱,有可能获得令人满意的系统性能。在该方法中把系统的状态分为安全状态和不安全状态,只要能使系统始终都处于安全状态,便可以避免发生死锁。
银行家算法的基本思想是分配资源之前,判断系统是否是安全的;若是,才分配。它是最具有代表性的避免死锁的算法。
设进程curProcess提出请求Request [i],则银行家算法按如下规则进行判断:
-
如果Request [curProcess] [i] <= NEED[curProcess][i],则转2;否则,出错。
-
如果Request [curProcess] [i] <= Available [i],则转3;否则,等待。
-
系统试探分配资源,修改相关数据:
Available[i] -= Request[cusneed][i];
Allocation[cusneed][i] += Request[cusneed][i];
Need[cusneed][i] -= Request[cusneed][i]; -
系统执行安全性检查,如安全,则分配成立;否则试探险性分配作废,系统恢复原状,进程等待。
安全性检查算法:
- 设置两个工作向量Work = Available,Finish
- 从进程集合中找到一个满足下述条件的进程:
Finish == false;
Need <= Work;
如找到,执行3;否则,执行4 - 设进程获得资源,可顺利执行,直至完成,从而释放资源。
Work = Work + Allocation;
Finish = true;
GOTO 2 - 如所有的进程Finish = true,则表示安全;否则系统不安全。
四、实验程序
代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
typedef int bool;
#define true 1
#define false 0
int processNum; //进程数
int resourcesNum; //资源数
int **Allocation; //各个进程已分配的各种资源数
int **Max; //各个进程最大需要的各种资源数
int **Need; //各个进程还需要的各种资源数
int *Available; //当前可用的各种资源数
int *Request; //当前进程请求的各资源数
bool *Finish; //记录各进程是否完成
int *safeSequence; //安全序列
//初始化
void init(char* argv[], int *flag1, FILE *fp) {
if(fp == NULL) {
printf("ERROR!\n");
*flag1 = 0;
return ;
}
//从文件中读取进程数和资源数
fscanf(fp, "%d", &processNum);
fscanf(fp, "%d", &resourcesNum);
//分配二维数组(Allocation、Max、Need)的大小
Allocation = (int**)malloc(processNum * sizeof(int*));
Max = (int**)malloc(processNum * sizeof(int*));
Need = (int**)malloc(processNum * sizeof(int*));
for(int i = 0;i < processNum; i++) {
Allocation[i] = (int*)malloc(resourcesNum * sizeof(int));
Max[i] = (int*)malloc(resourcesNum * sizeof(int));
Need[i] = (int*)malloc(resourcesNum * sizeof(int));
}
//分配一维数组大小(Available)
Available = (int*)malloc(resourcesNum * sizeof(int));
Finish = (int*)malloc(resourcesNum * sizeof(int));
safeSequence = (int*)malloc(processNum * sizeof(int));
//初始化系统剩余资源数(刚开始为最大资源数)
for(int i = 0; i < resourcesNum; i++){
fscanf(fp, "%d", &Available[i]);
}
//初始化已分配的资源矩阵和最大需求矩阵
int count = 0;
while(count < processNum) {
int curProcess; //当前进程号
fscanf(fp, "%d", &curProcess);
//从data文件中读取进程curProcessd的Allocation和Max
for(int i = 0; i < resourcesNum; i++) {
fscanf(fp, "%d", &Allocation[curProcess][i]);
}
for(int i = 0; i < resourcesNum; i++) {
fscanf(fp, "%d", &Max[curProcess][i]);
}
count++;
}
//初始化进程还需要的各种资源数(Need数组)
for(int i = 0;i < processNum; i++) {
for(int j = 0; j < resourcesNum; j++) {
Need[i][j] = Max[i][j] - Allocation[i][j];
}
}
//分配给各进程一定资源数后,更新系统剩余的资源数
for(int i = 0; i < resourcesNum; i++) {
for(int j = 0; j < processNum; j++) {
Available[i] = Available[i] - Allocation[j][i];
}
}
}
//打印当前时刻的安全序列表
void safeShow(int *Work, int curProcess) {
printf("P%d\t\t ", curProcess);
for(int j = 0; j < resourcesNum; j++) {
printf("%d ", Work[j]);
}
printf("\t\t ");
for(int j = 0; j < resourcesNum; j++) {
printf("%d ", Need[curProcess][j]);
}
printf("\t\t ");
for(int j = 0; j < resourcesNum; j++) {
printf("%d ", Allocation[curProcess][j]);
}
printf("\t\t\t ");
for(int j = 0; j < resourcesNum; j++) {
printf("%d ", Work[j] + Allocation[curProcess][j]);
}
printf("\n");
}
//判断当前系统是否处于安全状态
bool isSafe() {
int trueFinished = 0; //记录进程分配成功的个数
int *Work = (int*)malloc(resourcesNum * sizeof(int));
//初始化Work数组
for(int i = 0; i < resourcesNum; i++) {
Work[i] = Available[i];
}
//初始化Finish数组,开始时所有进程的资源都未分配成功
for(int i = 0; i < processNum; i++) {
Finish[i] = false;
}
int curProcess = 0; //当前进程号
//下面两个temp的作用是:对进程组的每一轮遍历后,即curProcess到头之后,检查一下trueFinished有没有增加。
int temp = 0; //记录这一次遍历的trueFinished
int temp0 = 0; //记录上一次遍历的trueFinished
int flag = 0; //记录是否是第一次遍历各个进程
while(trueFinished != processNum) {
int count = 0; //进程需要的资源数小于系统剩余的资源数的个数
//当前进程curProcess未完成分配
if(Finish[curProcess] == false) {
//检查是否进程需要的资源数是否都小于系统剩余的资源数
for(int i = 0; i < resourcesNum; i++) {
if(Need[curProcess][i] <= Work[i])
count++;
else break;
}
if(count == resourcesNum) {
Finish[curProcess] = true; //进程curProcess分配完成
safeShow(Work, curProcess);
//将分配给curProcess的资源回收
for(int i = 0; i < resourcesNum; i++) {
Work[i] += Allocation[curProcess][i];
}
//将curProcess例如安全序列
safeSequence[trueFinished] = curProcess;
trueFinished++;
}
}
curProcess++;
if(curProcess >= processNum) {
curProcess = curProcess % processNum;
//第一次遍历各个进程
if(flag == 0) {
temp = trueFinished;
temp0 = trueFinished;
}
//非第一次遍历各个进程
if(flag == 1) {
temp = trueFinished;
//遍历完后的trueFinish和这次遍历前的trueFinish相等
//即这次遍历完分配完成的进程数没有增加,说明系统剩余
//的资源已经不能满足任意未分配的进程了,即系统进入不安全状态
if(temp == temp0) {
break; //退出while循环
}else {
temp0 = temp;
}
}
flag = 1;
}
temp = trueFinished;
}
free(Work);
//完成分配的进程数等于内存进程数,即系统出于安全状态
if(trueFinished == processNum)
return true;
else //系统处于不安全状态
return false;
}
//打印当前资源分配表
void show() {
printf("----------------------------当前资源分配表----------------------------\n");
printf("各资源剩余:");
for(int i = 0; i < resourcesNum; i++) {
printf("%d ", Available[i]);
}
printf("\n");
printf("PID\t\t Max\t\t\t Allocation\t\t Need\n");
for(int i = 0; i < processNum; i++) {
printf("P%d\t\t ", i);
for(int j = 0; j < resourcesNum; j++) {
printf("%d ", Max[i][j]);
}
printf("\t\t\t ");
for(int j = 0; j < resourcesNum; j++) {
printf("%d ", Allocation[i][j]);
}
printf("\t\t\t ");
for(int j = 0; j < resourcesNum; j++) {
printf("%d ", Need[i][j]);
}
printf("\n");
}
printf("----------------------------------------------------------------------\n");
printf("\n");
}
int main(int argc, char* argv[])
{
printf("-------------进行系统资源初始化过程-------------\n\n");
FILE *fp = NULL;
//printf("%s\n", argv[1]);
fp = fopen(argv[1],"r");
printf("---->正在从文件data中读取数据···\n");
int flag1 = 1; //记录文件读取成功与否
init(argv, &flag1, fp);
if(flag1 == 0) {
printf("---->读取数据失败!\n\n");
return 0;
}
printf("---->读取数据完成!\n");
printf("\n-----------------系统初始化完成-----------------\n\n");
printf("\n系统安全情况分析\n");
printf("-----------------------------------当前时刻的安全序列表-----------------------------------\n");
printf("PID\t\t Work\t\t Need\t\t Allocation\t\t Work + Allocation\n");
if(isSafe()) {
printf("------------------------------------------------------------------------------------------\n");
printf("当前系统处于安全状态,其中一个安全序列为:");
for(int i = 0; i < processNum; i++) {
printf("P%d",safeSequence[i]);
if(i != processNum - 1)
printf(" -> ");
}
printf("\n");
}else {
printf("------------------------------------------------------------------------------------------\n");
printf("当前系统已处于不安全状态!\n");
//释放用malloc动态分配的内存,防止内存泄露
free(Allocation);
free(Max);
free(Need);
free(Available);
free(Finish);
free(safeSequence);
return 0;
}
printf("\n");
show(); //打印当前资源分配表
while(true) {
//printf("\n\n是否继续为进程分配资源?(1:是 0:否):");
//int flag;
//scanf("%d", &flag);
int flag;
fpos_t home; //记录当前行首位置
fgetpos(fp, &home); //将文件fp的当前读写位置保存到home中
int temp;
if(fscanf(fp, "%d", &temp) != EOF) { //判断当前文件指针是否指向末尾,注意这里文件指针会前进
flag = 1;
} else {
flag = 0;
}
//回退文件指针,回到行首
fsetpos(fp,&home);
if(flag) { //继续分配资源
printf("\n\n单击回车键继续为进程分配资源");
getchar();
int curProcess; //分配的进程号
Request = (int*)malloc(resourcesNum * sizeof(int)); //分配请求资源数组大小
fscanf(fp, "%d", &curProcess);
printf("从data文件中读取分配资源的进程号:%d\n", curProcess);
printf("从data文件中读取分配给 P%d 的各资源数:", curProcess);
for(int i = 0; i < resourcesNum; i++) {
fscanf(fp, "%d", &Request[i]);
printf("%d ", Request[i]);
}
printf("\n");
int count = 0; //记录curProcess请求的各资源小于其需要(Need)资源的个数
int count2 = 0; //记录curProcess请求的各资源小于系统剩余(Available)资源的个数
for(int i = 0; i < resourcesNum; i++) {
if(Request[i] <= Need[curProcess][i]) {
count++;
} else {
printf("\nERROR!请求的资源数大于需要的资源数!\n\n");
break;
}
}
if(count == resourcesNum) {
for(int i = 0; i < resourcesNum; i++) {
if(Request[i] <= Available[i]) {
count2++;
}
else {
printf("\n资源不足,等待其它进程释放资源中!\n\n");
break;
}
}
}
if(count2 == resourcesNum) {
//尝试为该进程分配其请求的资源
for(int i = 0; i < resourcesNum; i++) {
Available[i] -= Request[i];
Allocation[curProcess][i] += Request[i];
Need[curProcess][i] -= Request[i];
}
printf("\n系统安全情况分析\n");
printf("-----------------------------------当前时刻的安全序列表-----------------------------------\n");
printf("PID\t\t Work\t\t Need\t\t Allocation\t\t Work + Allocation\n");
if(isSafe()) {
printf("------------------------------------------------------------------------------------------\n");
printf("资源分配成功!安全序列为:");
for(int i = 0; i < processNum; i++) {
printf("P%d",safeSequence[i]);
if(i != processNum - 1)
printf(" -> ");
}
printf("\n\n");
show();
}else {
printf("------------------------------------------------------------------------------------------\n");
printf("资源分配失败!若分配会导致系统进入不安全状态!\n\n");
//回收尝试分配的资源
for(int i = 0; i < resourcesNum; i++) {
Available[i] += Request[i];
Allocation[curProcess][i] -= Request[i];
Need[curProcess][i] += Request[i];
}
}
}
}else {
//释放用malloc动态分配的内存,防止内存泄露
free(Allocation);
free(Max);
free(Need);
free(Available);
free(Finish);
free(safeSequence);
return 0;
}
}
//释放用malloc动态分配的内存,防止内存泄露
free(Allocation);
free(Max);
free(Need);
free(Available);
free(Finish);
free(safeSequence);
return 0;
}
五、验证数据和运行结果
运行结果截图
注:实验中所有的数据均从data文件中获取,data文件与banker.c文件放在同一个文件夹中。
测试用例一(实验要求的数据)
测试用例二 (自己另外加的一组数据)
测试用例三(自己另外加的一组数据):测试系统刚开始就已经处于不安全状态的情况
六、思考与分析
本次实验要求输入数据从文本文件中读出,不从键盘录入,且要求通过命令行参数指定文件,如: ./banker ./data运行。因此需要在程序目录下建立data文件,然后在main函数中添加参数:int main(int argc, char* argv[]) ,其中argc记录参数的个数,argv数组记录在命令行中输入的参数,这里我们运行程序时在命令行中输入的是两个参数:./banker和./data,因此数组argv中存放的就是这两个字符串,argv[1]就是data文件的路径,通过文件指针指向data文件:FILE *fp = fopen(argv[1],“r”); 然后根据data文件中给的数据的顺序依次读取赋给给进程数、系统资源种数、资源最大数向量、已分配资源向量、最大资源需求量向量和资源请求向量赋值,进行系统的初始化。
实验还要求程序可支持不同个数的进程和不同个数的资源;因此程序中的各种数据结构采用数组形式,用malloc实现动态分配大小,根据进程数(processNum)和资源数(resourcesNum)动态分配各数组空间大小,具体可见代码中的初始化函数:void init()。
程序实现:首先从data文件中读取数据对各数据结构进行初始化,若读取失败(命令行参数指定的文件在程序目录下不存在),提示读取失败,结束程序;读取成功,则对刚初始化的系统进行安全性检测,打印安全序列表,若系统处于不安全状态,安全序列表打印出来的进程数就会小于总进程数,并提示系统处于不安全状态,然后结束程序;处于安全状态则列出一个安全序列,并列出当前的资源分配表(包括系统各资源剩余、各进程的Max、Allocation和Need),然后根据当前文件是否读到文件尾(即data文件中有无资源请求),若无则结束程序,有则利用getchar()并提示按下回车进行资源分配,若进程请求的资源大于其需要的最大资源,则提示错误信息;若进程请求的资源大于系统剩余的资源,则提示“资源不足,等待其他进程释放资源“;否则系统尝试分配给该进程申请的资源,然后调用安全性检测isSafe()函数,若系统处于不安全状态,则提示“分配失败,若分配系统将处于不安全状态”,并将尝试分配的资源进行回收;若系统处于安全状态,则将分配后的安全序列表(包括一个安全序列)和当前资源分配表打印出来,然后继续判断文件是否读到文件尾,通过getchar()实现每次按下回车进行一个进程的资源分配。
思考:为什么说找到了安全序列,就能够保证这个此次分配是安全的?safe的本质是什么?
这种安全之说,实际上是建立在一种假设之上的。其实,在实际的执行过程中,我们根本无法确定各进程是否会尽力去申请其声明的最大数额的资源,这是其一;其二,这种序列,也只是一种假设,是我们自己找出来的一种可能的不会造成死锁的方案,进程实际的执行顺序是不可能与我们预测的完全一致的。进程何时停止?到底会申请多少资源?我们无从而知。所以,系统假定所有进程将最终试图获取其声明的最大资源并在之后马上终止,并释放资源。这是一个合理的假设,因为系统不会特意关注每个进程运行了多久。此外,如果一个进程终止前没有获取它能获取的最大数额的资源,也无伤大雅,这种情况对系统而言反而更容易处理。 基于这一假设,该算法通过尝试寻找一个理想的进程请求序列集合来决定是否安全,根据这个集合中的序列方案,每个进程能够获得的最大资源,之后结束,并把资源返还给系统。
附
附:银行家算法的验证数据文件data格式说明
验证数据:建立在程序目录下建立data文件,文件内容是:
5 3
10 5 7
0 0 1 0 7 5 3
1 2 0 0 3 2 2
2 3 0 2 9 0 2
3 2 1 1 2 2 2
4 0 0 2 4 3 3
1 1 0 2
4 3 3 0
0 0 2 0
第一行:5个进程,3种资源。
第二行:每种资源系统拥有的最大数量。
3-7行:第一列是进程号(按顺序排),2-4列是Allocation(资源请求)向量,5-7列是Max(最大资源需求量)向量。
8-10行:第一列是进程号,2-4列是Request(资源请求)向量。
运行程序,通过命令行参数指定文件,如: ./banker ./data运行。
更多推荐
所有评论(0)