操作系统 银行家算法

背景介绍

银行家算法是由荷兰学者Dijkstra(提出goto有害论的大佬Orz)为银行系统设计的,以确保银行发放贷款时不会出现无法满足所有顾客的情况;该算法后来被运用于操作系统以预防死锁。

死锁是指在并发环境中,各进程因竞争资源造成的互相都在等待对方手中的资源,从而导致每个进程都被阻塞而无法推进的现象。

核心思想

在进程提出资源申请时,操作系统会通过该算法来试图找到一个安全序列,若可以找到,则表示满足该进程后的系统不会处于不安全的状态,即不会发生死锁;但是若找不到至少一个安全序列,则现在不能满足该进程的资源申请,该进程必须等待。

系统处于不安全状态时不一定会发生死锁,但是发生死锁时,系统一定处于不安全的状态。

例题

我们通过一道例题来进一步理解银行家算法是如何避免死锁的:

假定系统中有五个进程{P0, P1, P2, P3, P4}和三类资源{A, B, C},各种资源的数量分别为10、5、7,在T0时刻的资源分配情况如图所示:

图1 T0时刻的资源分配表

请求序列:

(1)P1发出请求向量Request1(1,0,2)

(2)P4发出请求向量Request4(3,3,0)

(3)P0发出请求向量Request0(0,2,0)

具体分析

图1各数据结构的说明

二维数组Max:用于存储各个进程对各资源的最大需求量;更进一步地,第i行第j列表示进程i对资源j的最大需求量;

二维数组Allocation:用于存储各个进程对各资源的占有量;第i行第j列表示进程i对资源j的占有总量;

二维数组Need:用存储各个进程还需要的各资源的数量;第i行第j列表示进程i对资源j还需要多少数量;

一维数组Available:用于存储系统中各个资源的数量;第i个位置表示资源i在系统中的数量。系统中原本各个资源的数量为10,5,7;减去各个进程的Allocation后更新为3,3,2。

安全序列的推导

我们首先选择 “P1发出请求向量Request1(1,0,2)” 这一问来进行分析:

操作系统首先尝试着满足该请求(实际还没有正式分配,要先进行模拟,试着找出安全序列),那么此时各个数据结构更新为: 

进程 \ 资源情况

Max

A   B   C

Allocation

A   B   C

Need

A   B   C

Available

A   B   C

P07   5   30   1   07   4   32   3   0
P13   2   23   0   20   2   0
P29   0   23   0   26   0   0
P32   2   22   1   10   1   1
P44   3   30   0   24   3   1

表1 为P1分配更新后的资源分配表

满足P1进程请求{1,0,2}后,Available数组、P1进程的Need数组和Allocation数组都进行了更新,接着从上表的状态开始,开始寻找一个包含所有进程的安全序列:
 

依次检查剩余资源{2,3,0}能否满足各进程的需求(每次检查不包括上一次已经进入安全序列的进程):

发现可以满足P1进程,将P1加入安全序列,并且更新可用资源为{5,5,2}

继续检查剩余资源{5,5,2}能否满足各进程的需求:

发现可以满足P3进程,将P3加入安全序列,并且更新可用资源为{7,7,4}

...

重复上述步骤,直到将所有进程都加入到安全序列中,该算法称为安全性算法;若在某一步时,出现了系统可用资源无法满足任何一个进程的需求,则说明此时系统处于不安全的状态,有可能会发生死锁。

代码实现

代码是我自己写的,上述三个样例也是正确的,但是不知道是否有纰漏,所以还是批判性地使用代码,若发现任何错误,恳请指正Orz;以下是我使用C++实现的银行家算法:

/*
7 5 3 0 1 0
3 2 2 2 0 0
9 0 2 3 0 2
2 2 2 2 1 1
4 3 3 0 0 2
*/

#include<iostream>
#include<vector>
using namespace std;

vector<vector<int> > Max(100, vector<int>(100, 0));//最大需求矩阵
vector<vector<int> > Allocation(100, vector<int>(100, 0));//已分配矩阵
vector<vector<int> > Need(100, vector<int>(100, 0));//需求矩阵
vector<int>Available(100, 0);//资源可用情况
vector<int>Request(100, 0);//进程本次申请的资源量
vector<bool>IsFinish(100, false);//用于标记进程是否已经完成工作,若完成则回收资源
vector<int>SafeQueue;//若存在安全序列,记录并打印
int n, m;//进程数量以及资源数量

void init() {
	cout << "输入进程数量以及资源种数:";
	cin >> n >> m;
	cout << "输入各资源的总量:";
	for (int i = 0; i < m; i++) {
		cin >> Available[i];
	}
	cout << "输入各进程最大所需资源数量以及已分配的资源数量:\n";
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++)
			cin >> Max[i][j];
		for (int j = 0; j < m; j++) {
			cin >> Allocation[i][j];
			Need[i][j] = Max[i][j] - Allocation[i][j];
			Available[j] -= Allocation[i][j];
		}
	}
}

bool IsSafe(int Thread) {
	//使用副本进行模拟
	vector<bool>vis(100, false);
	vector<int>available = Available;
	vector<vector<int> >need = Need;
	//检验当前请求是已经超过系统所剩下的资源数量
	for (int i = 0; i < m; i++) {
		if (Request[i] > available[i]) {
			return false;
		}
		//尝试分配
		available[i] -= Request[i];
		need[Thread][i] -= Request[i];
	}
	//特判首次分配出现恰好分配完该进程所需的最大资源数量的情况
	int flag = 1;
	for (int i = 0; i < m; i++) {
		flag = 0;
		if (need[Thread][i] != 0) {
			flag = 1;
			break;
		}
	}
	//若符合上述条件,回收该进程所占有的所有资源
	if (!flag) {
		vis[Thread] = true;
		for (int i = 0; i < m; i++) {
			available[i] += Max[Thread][i];
		}
		SafeQueue.push_back(Thread);
	}
	//尝试寻找安全序列
	for (int i = 0; i < n; i++) {
		//寻找当前系统资源满足其全部所需的资源的进程
		int pos = -1;
		for (int j = 0; j < n; j++) {
			int IsEnough = 1;
			if (!vis[j]) {
				for (int k = 0; k < m; k++) {
					IsEnough = 0;
					if (available[k] < need[j][k]) {
						IsEnough = 1;
						break;
					}
				}
				if (!IsEnough) {
					pos = j;
					break;
				}
			}
		}
		//pos等于-1表示对于当前的所有进程,系统资源都无法满足其所需的最大资源需求数量,所以可以直接判定找不到安全序列
		if (pos == -1)return false;
		//加入安全序列,回收资源
		SafeQueue.push_back(pos);
		vis[pos] = true;
		for (int j = 0; j < m; j++) {
			available[j] += Max[pos][j];
		}
	}
	//程序能够走到这里说明找到了至少一条安全序列,那么正式进行分配
	for (int i = 0; i < m; i++) {
		Available[i] -= Request[i];
		Need[Thread][i] -= Request[i];
		Allocation[Thread][i] += Request[i];
	}
	return true;
}



int main() {
	init();
	while (1) {
		int choice;
		cout << "输入1或0,1表示分配,0表示退出:\n";
		cin >> choice;
		if (!choice)break;
		int Thread; 
		cin >> Thread;
		for (int i = 0; i < m; i++)
			cin >> Request[i];
		SafeQueue.clear();
		if (IsSafe(Thread)) {
			cout << "分配后系统处于安全状态,找到的安全序列为:{";
			for (int i = 0; i < n; i++)
				cout<<"P" << SafeQueue[i] << " ";
			cout << "}\n";
			//正式分配后,若当前进程的资源已经全部满足,则回收占有的全部资源
			int flag = 1;
			for (int i = 0; i < m; i++) {
				flag = 0;
				if (Need[Thread][i] != 0) {
					flag = 1;
					break;
				}
			}
			if (!flag) {
				IsFinish[Thread] = true;
				for (int i = 0; i < m; i++) {
					Available[i] += Max[Thread][i];
				}
			}
		}
		else {
			cout << "分配后系统将处于不安全状态,当前进程进入等待\n";
		}
	}
	return 0;
}

运行结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值