操作系统实验---银行家算法

1、银行家算法的工作原理

银行家算法(Banker’s Algorithm)是一个避免死锁(Deadlock)的著名算法。它以银行借贷系统的分配策略为基础,判断并保证系统的安全运行。我们可以把操作系统看作是银行家,操作系统管理的资源相当于银行家管理的资金,进程向操作系统请求分配资源相当于用户向银行家贷款。
为保证资金的安全,银行家规定:
(1) 当一个顾客对资金的最大需求量不超过银行家现有的资金时就可接纳该顾客;
(2) 顾客可以分期贷款,但贷款的总数不能超过最大需求量;
(3) 当银行家现有的资金不能满足顾客尚需的贷款数额时,对顾客的贷款可推迟支付,但总能使顾客在有限的时间里得到贷款;
(4) 当顾客得到所需的全部资金后,一定能在有限的时间里归还所有的资金。
操作系统按照银行家制定的规则为进程分配资源,当进程首次申请资源时,要测试该进程对资源的最大需求量,如果系统现存的资源可以满足它的最大需求量则按当前的申请量分配资源,否则就推迟分配。当进程在执行中继续申请资源时,先测试该进程本次申请的资源数是否超过了该资源所剩余的总量。若超过则拒绝分配资源,若能满足则按当前的申请量分配资源,否则也要推迟分配。

2、银行家算法流程图

步骤:进程i发出请求资源申请,  
(1)如果Request [j]<=need[i,j],转向步骤(2),否则认为出错,因为他所需要的资源数已 经超过它所宣布的最大值。
(2)如果:Request i[j]<=available[i,j],转向步骤(3),否则表示尚无足够资源,进程i需等待。
(3)若以上两个条件都满足,则系统试探着将资源分配给申请的进程,并修改下面数据结构中的数值:Available[i,j]= Available[i,j]- Request [j];
Allocation[i][j]= Allocation[i][j]+ Request [j];
need[i][j]= need[i][j]- Request [j];
(4)试分配后,执行安全性检查,调用check()函数检查此次资源分配后系统是否处于安全状态。若安全,才正式将资源分配给进程;否则本次试探分配作废,恢复原来的资源分配状态,让该进程等待。
(5)用do{…}while循环语句实现输入字符y/n判断是否继续进行资源申请。
3、安全性检查流程图:
步骤: (1).设置两个工作向量 Work=Available; FINISH
(2).从进程集合中找到一个满足下述条件的进程,FINISH ==false; Need <= Work;
如找到,执行(3);否则,执行(4)
(3).设进程获得资源,可顺利执行,直至完成,从而释放资源。
Work += Allocation; Finish=true;go to 2
(4).如所有的进程Finish= true,则表示安全;否则系统不安全。

3、代码

Banker.h

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#define M 10
#define N 10
#pragma once

typedef int Bdatetype;
typedef struct Node
{
	Bdatetype _request[M][N];//请求资源
	Bdatetype _available[M][N];//可获得的
	Bdatetype _need[M][N];//需要
	Bdatetype _max[M][N];//最大
	Bdatetype _allocation[M][N];	//已分配
	Bdatetype _finish[M][N];//是否完成
	Bdatetype _work[M][N];
	Bdatetype _count[N];//计数
	Bdatetype _sum[M][N];//可以提供的资源数
	size_t size;//进程数量
	size_t resource;//资源数
}Node;

void Bankerinit(Node* psl);  //初始化
void Print();//
void Display(Node* psl, Bdatetype size, Bdatetype resource);
void Nodepush(Node* psl, Bdatetype size, Bdatetype resource);
void request(Node* psl, Bdatetype size, Bdatetype resource, Bdatetype input);
int safetytest(Node psl, Bdatetype size, Bdatetype resource);

Banker.c


#include"banker.h"

void Bankerinit(Node* psl){
	size_t i = 0, j = 0;
	for (i = 0; i < M; ++i) 
		for (j = 0; j < N; ++j) {
			psl->_allocation[i][j] = psl->_available[0][j] = psl->_need[i][j] = psl->_max[i][j]=psl->_work[0][j] =psl->_finish[i][0]=psl->_sum[i][j]=psl->_request[i][j]= 0;
		}

	psl->size=psl->resource= 0;
}
void Print() {
	printf("*************************************\n");
	printf("***********1.输入    **************\n");
	printf("***********2.请求资源    ************\n"); 
	printf("***********3.展示  **************\n");
	printf("***********4.安全性检验 **************\n");
	printf("***********0.退出      **************\n");
	printf("*************************************\n");
}
void request(Node* psl, Bdatetype size, Bdatetype resource,Bdatetype input) {  //执行请求资源
	size_t j = 0, i = 0;
	int tmp = 0;
	int input1= input - 1; 
	int need[M][N] = { 0 };
	int available[M][N] = { 0 };
	int allocation[M][N] = { 0 };
	printf("请输入进程的请求资源数:\n");

	for (j = 0; j < resource; ++j) {
		scanf("%d", &psl->_request[input1][j]);
	}	
	for (j = 0; j < resource; ++j) {		//存在一个请求大于需求就返回
		if (psl->_request[input1][j] > psl->_need[input1][j])
		{
			printf("请求大于需求\n");
			return;
		}
	}
	for (j = 0; j < resource; ++j) {		//存在一个请求大于可用就返回
		if (psl->_request[input1][j] > psl->_available[0][j]) {
			printf("请求大于可用\n");
			return;
		}
	}
										//本次所需资源请求全部符合条件
		for (j = 0; j < resource; ++j)
		{
			need[input1][j] = psl->_need[input1][j];
			allocation[input1][j] = psl->_allocation[input1][j];
			available[0][j] = psl->_available[0][j];		//存储本次进程的情况
			psl->_available[0][j] -= psl->_request[input1][j];
			psl->_allocation[input1][j] = psl->_allocation[input1][j] + psl->_request[input1][j];//更新本次请求进程的情况
			psl->_need[input1][j] = psl->_need[input1][j] - psl->_request[input1][j];
			tmp += psl->_need[input1][j];
		}
	if (tmp==0) {
		for (j = 0; j < resource; ++j) {
			psl->_available[0][j] += psl->_allocation[input1][j];
			psl->_allocation[input1][j] = 0;
		}	
	}
	
	if (safetytest(*psl, size, resource) == 0) {				//分配失败要将刚刚的进程情况还原
		for (j = 0; j < resource; ++j)
		{
			psl->_need[input1][j] = need[input1][j];
			psl->_allocation[input1][j] = allocation[input1][j];
			psl->_available[0][j] = available[0][j];
		}
	}
	else
	{
		printf("分配成功\n");
	}
}

int safetytest(Node psl, Bdatetype size, Bdatetype resource) {  //安全性检验
	size_t j = 0, i = 0, k = 0;
	int flag = 1;
	int count = 0, num = 0;
	for (j = 0; j < resource; ++j)					//work =ava
	{
		psl._work[0][j] = psl._available[0][j];
	}
	for (i = 0; i < size; ++i)						//finish =0
	{
		psl._finish[i][0] = 0;
	}

	while (num < size) {
		for (i = 0; i < size; ++i) {
			flag = 1;
			if (psl._finish[i][0] == 0){
				for (j = 0; j < resource; ++j)
				{
					if (psl._work[0][j] < psl._need[i][j]) {	//存在一组不符合条件的就跳出循环
						flag = 0;
						break;
					}
				}

				if (flag)
				{		//当flag为1时,表示所有可用资源的数量一定小于need
					for (j = 0; j < resource; ++j) {
						psl._work[0][j] += psl._allocation[i][j];
					}
					psl._finish[i][0] = 1;  //将此进程标注为true
					psl._count[count] = i;		//统计安全进程序列
					++num; //计数
					++count;//统计安全序列
					k = 1;
					break;
				}

			}

		}

		if (k == 0)
		{				//循环比较
			printf("不安全\n");
			return 0;
		}
		k = 0;
	}

	printf("安全序列\n");		//输出安全序列
	for (i = 0; i < size; ++i) {
		printf("p%d ", psl._count[i]+1);
	
	}
	printf("\n");
	return 1;
}
void Nodepush(Node* psl, Bdatetype size, Bdatetype resource) {		//输入资源情况
	Bdatetype i ,j= 0;
	
	for (j = 0; j < resource;++j) {
			printf("请输入第 %d 资源的sum\n", j + 1);
			scanf("%d", &psl->_sum[0][j]);
		}
	for (i = 0; i < size; ++i) {
		printf("请输入第%d个进程的资源分配情况\n", i + 1);
		for (j = 0; j < resource; ++j) {
			printf("请输入第 %d 个资源max的值\n", j + 1);
			scanf("%d", &psl->_max[i][j]);
		}
		for (j = 0; j < resource; ++j) {
			printf("请输入第 %d 个资源allocation的值\n", j + 1);
			scanf("%d", &psl->_allocation[i][j]);
			psl->_need[i][j] = psl->_max[i][j] - psl->_allocation[i][j];
		}
		for (j = 0; j < resource; ++j) {
			psl->_available[0][j] = psl->_sum[0][j] -= psl->_allocation[i][j];
			
		}
	}

}

void Display(Node* psl, Bdatetype size, Bdatetype resource) {//展示各个资源情况
	size_t i, j = 0;
	printf("| pid |work|max|allocation|need|available|finish\n");
	for (i = 0; i < size; ++i) {
		printf("|");
		printf("P%d ", i + 1);
		printf("|");
		for (j = 0; j < resource; ++j) {
			printf("%d  ", psl->_work[0][j]);
		}
		printf("|");
		for (j = 0; j < resource; ++j) {
			printf("%d  ", psl->_max[i][j]);
		}
		printf("|");
		for (j = 0; j < resource; ++j) {
			printf("%d  ", psl->_allocation[i][j]);
		}
		printf("|");
		for (j = 0; j < resource; ++j) {
			printf("%d  ", psl->_need[i][j]);
		}
		printf("|");
		for (j = 0; j < resource; ++j) {
			printf("%d  ", psl->_available[0][j]);
		}
		printf("|");

		for (j = 0; j < resource; ++j) {
			printf("%d  ", psl->_finish[i][0]);
		}
		printf("|");
		printf("\n");
	}
}
	

void test() {
	Node psl;
	Bdatetype input=0,size=0,resourse=0;
	do
	{
		Print();
		scanf("%d",&input);
		switch (input) {
		case 0:
			printf("程序退出\n");
			break;
		case 1:
			Bankerinit(&psl);
			printf("请输入进程数和资源数\n");
			scanf("%d %d", &size, &resourse);
			Nodepush(&psl, size, resourse);
			break;
		case 2:			
			printf("请输入进程号\n");
			int input1 = 0;
			scanf("%d", &input1);
			request(&psl, size, resourse, input1);
			break;

		case 3:
			Display(&psl, size, resourse);
			break;
		
		case 4:
			safetytest(psl, size, resourse);
			break;

		default:
			printf("输入错误,请重新输入:\n");
				break;
		
		}

	} while (input);
	
	Bankerinit(&psl);

}

Test.c

#include"banker.h"
int main() {

	test();
	system("pause");
	return 0;
}

4、实验结果展示

1.初始化输入
在这里插入图片描述
在这里插入图片描述
2.安全性检测
(1).给1号进程请求1 1

在这里插入图片描述

(2).不安全后展示原来资源情况
在这里插入图片描述
(3)给3号进程请求 1 1
在这里插入图片描述
更新后的结果
在这里插入图片描述
(4)情况综合展示
在这里插入图片描述
在这里插入图片描述

5、实验心得

最开始定义了一个结构体指针,但是我在输入数据的时候,没有&导致无法写入,在实现finish和available和request的时候最开始错误成定义成二维数组,双层循环的时候,无法使得值良好对应。最后还是改成一个“一维数组”。
在安全性算法的时候,最开始传值的时候用的是地址,导致最后如果不安全,还得恢复,所以这里直接建立临时拷贝,如果不安全,也不会影响结构体指针里的本来的值,所以就不用预存数据了。但是这样做的时候,在请求分配的时候,预分配之后,如果不安全还是的改回去,所以建立一个临时的数组,储存初值,在不安全的时候,直接拷贝恢复,谨记要考虑到回收的问题。
算法的实现还是比较简单的,但是要注意实现的过程中,将各个数组的资源情况熟悉,我在写的时候,由于开始思路不好,导致多次修改,所以写代码的时候,一定要有一个清晰的思路,再动手写,避免后续出现问题。
我建议先写一个完整的流程图,这样对后续代码的实现非常有好处。目前代码能够完整的实现老师的要求,如果后续出现问题,欢迎各位留言告诉我。

5 银行家算法实现 5.1 实验类型 设计型(4学时)。 5.2 实验目的 1) 理解死锁避免相关内容; 2) 掌握银行家算法主要流程; 3) 掌握安全性检查流程。 5.3 实验描述 本实验主要对操作系统中的死锁预防部分的理论进行实验。要求实验者设计一个程序,该程序可对每一次资源申请采用银行家算法进行分配。 5.4 实验内容 1) 设计多个资源(≥3); 2) 设计多个进程(≥3); 3) 设计银行家算法相关的数据结构; 4) 动态进行资源申请、分配、安全性检测并给出分配结果。 5.5 实验要求 1) 编写程序完成实验内容; 2) 画出安全性检测函数流程图; 3) 撰写实验报告。 5.6 测试要求 1) 进行Request请求,输入参数为进程号、资源号和资源数; 2) 进行3次以上的Request请求; 3) 至少进行1次资源数目少于可用资源数,但不安全的请求。 5.7 相关知识 5.7.1 银行家算法的数据结构 1) 可利用资源向量Available。其中每个元素代表每类资源的数目。 2) 最大需求矩阵Max。其中每个元素代表每个进程对于每类资源的最大需求量。Max[i,j]=K表示i进程对于j类资源的最大需求量为K。 3) 分配矩阵Allocation。其中每个元素代表每个进程已得到的每类资源的数目。 4) 需求矩阵Need。其中每个元素代表每个进程还需要的每类资源的数目。 5.7.2 银行家算法 Request i [j]=K表示进程Pi需要K个j类资源。 1) 如果Request i [j]≤Need[i , j],便转向步骤2,否则认为出错。 2) 如果Request i [j]≤Available[j],便转向步骤3,否则表示无足够资源,Pi需等待; 3) 系统尝试分配资源给Pi; 4) 系统进行安全性检查,检查此次资源分配后,系统是否安全。如果安全,则正式分配资源,否则撤销此次分配。 5.7.3 安全性算法 1) 设置两个向量:工作向量Work和Finish。算法开始时Work=Available;Finish表示系统是否有足够的资源分配给进程,使之运行完成,开始时,令Finish[i]=False;如果有足够的资源分配给进程,则令Finish[i]=True。 2) 从进程集合中找到一个能满足下列条件的进程:Finish[i]=False;Need[i,j] ≤ Work[j],若找到,执行步骤3),否则,执行步骤4); 3) Pi获得所需资源后,可顺利执行指导完成,并释放它占有的资源。并执行: Work[j]=Work[j]+Allocation[i , j]; Finish[i] = True; 到第2)步。 4) 直到所有Finish[i]=True,表示系统处于安全状态;否则系统处于不安全状态。 5.8 实验设备 PC机1台,要求安装DOS7.1、Turbo C3.0、Windows2000。 5.9 实验成绩评定 实验成绩评定方式包含实验报告成绩、实验过程成绩两个部分,其中实验过程成绩占60%、实验报告成绩占40%,如果其中任何一个部分成绩不及格,则总成绩按不及格处理。 5.10 实验报告 按照实验目的、实验内容、实验要求、实验设备、测试等部分进行组织。 5.11 实验思考 1) 针对死锁有哪些可行方案? 2) 死锁解除的难点是什么?
【实验目的】 1. 理解死锁的概念; 2. 用高级语言编写和调试一个银行家算法程序,以加深对死锁的理解。 【实验准备】 1. 产生死锁的原因  竞争资源引起的死锁  进程推进顺序不当引起死锁 2.产生死锁的必要条件  互斥条件  请求和保持条件  不剥夺条件  环路等待条件 3.处理死锁的基本方法  预防死锁  避免死锁  检测死锁  解除死锁 【实验内容】 1. 实验原理 银行家算法是从当前状态出发,逐个按安全序列检查各客户中谁能完成其工作,然后假定其完成工作且归还全部贷款,再进而检查下一个能完成工作的客户。如果所有客户都能完成工作,则找到一个安全序列,银行家才是安全的。与预防死锁的几种方法相比较,限制条件少,资源利用程度提高了。缺点:该算法要求客户数保持固定不变,这在多道程序系统中是难以做到的;该算法保证所有客户在有限的时间内得到满足,但实时客户要求快速响应,所以要考虑这个因素;由于要寻找一个安全序列,实际上增加了系统的开销.Banker algorithm 最重要的一点是:保证操作系统的安全状态!这也是操作系统判断是否分配给一个进程资源的标准!那什么是安全状态?举个小例子,进程P 需要申请8个资源(假设都是一样的),已经申请了5个资源,还差3个资源。若这个时候操作系统还剩下2个资源。很显然,这个时候操作系统无论如何都不能再分配资源给进程P了,因为即使全部给了他也不够,还很可能会造成死锁。若这个时候操作系统还有3个资源,无论P这一次申请几个资源,操作系统都可以满足他,因为操作系统可以保证P不死锁,只要他不把剩余的资源分配给别人,进程P就一定能顺利完成任务。 2.实验题目 设计五个进程{P0,P1,P2,P3,P4}共享三类资源{A,B,C}的系统,{A,B,C}的资源数量分别为10,5,7。进程可动态地申请资源和释放资源,系统按各进程的申请动态地分配资源。要求程序具有显示和打印各进程的某一时刻的资源分配表和安全序列;显示和打印各进程依次要求申请的资源号以及为某进程分配资源后的有关资源数据。 3.算法描述 我们引入了两个向量:Resourse(资源总量)、Available(剩余资源量) 以及两个矩阵:Claim(每个进程的最大需求量)、Allocation(已为每个进程分配的数量)。它们共同构成了任一时刻系统对资源的分配状态。 向量模型: R1 R2 R3 矩阵模型: R1 R2 P1 P2 P3 这里,我们设置另外一个矩阵:各个进程尚需资源量(Need),可以看出 Need = Claim – Allocation(每个进程的最大需求量-剩余资源量) 因此,我们可以这样描述银行家算法: 设Request[i]是进程Pi的请求向量。如果Request[i , j]=k,表示Pi需k个Rj类资源。当Pi发出资源请求后,系统按下述步骤进行检查: (1) if (Request[i]<=Need[i]) goto (2); else error(“over request”); (2) if (Request[i]<=Available[i]) goto (3); else wait(); (3) 系统试探性把要求资源分给Pi(类似回溯算法)。并根据分配修改下面数据结构中的值。 剩余资源量:Available[i] = Available[i] – Request[i] ; 已为每个进程分配的数量: Allocation[i] = Allocation[i] + Request[i]; 各个进程尚需资源量:Need[i] = Need[i]-Request[i]; (4) 系统执行安全性检查,检查此次资源分配后,系统是否处于安全状态。若安全,才正式将资源分配给进程以完成此次分配;若不安全,试探方案作废,恢复原资源分配表,让进程Pi等待。 系统所执行的安全性检查算法可描述如下: 设置两个向量:Free、Finish 工作向量Free是一个横向量,表示系统可提供给进程继续运行所需要的各类资源数目,它含有的元素个数等于资源数。执行安全算法开始时,Free = Available .标记向量Finish是一个纵向量,表示进程在此次检查中中是否被满足,使之运行完成,开始时对当前未满足的进程做Finish[i] = false;当有足够资源分配给进程(Need[i]<=Free)时,Finish[i]=true,Pi完成,并释放资源。 (1)从进程集中找一个能满足下述条件的进程Pi ① Finish[i] == false(未定) ② Need[i] D->B->A A 1 6 B 1 5 C 2 4 D 4 7 Available = (2) ; Resourse = (10) ; 测试结果如下 process number:5 resource number:4 resource series:6 3 4 2 assined matrix:p0:3 0 1 1 p1:0 1 0 0 p2:1 1 1 0 p3:1 1 0 1 p4:0 0 0 0 needed matrix: p0:1 1 0 0 p1:0 1 1 2 p2:3 1 0 0 p3:0 0 1 0 p4:2 1 1 0 p3-->p4-->p0-->p2-->p1 p3-->p4-->p0-->p1-->p2 p3-->p0-->p4-->p2-->p1 p3-->p0-->p4-->p1-->p2 p3-->p0-->p2-->p4-->p1 p3-->p0-->p2-->p1-->p4 p3-->p0-->p1-->p4-->p2 p3-->p0-->p1-->p2-->p4 it is safe,and it has 8 solutions
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值