银行家算法实现
一、实验目的
- 了解什么是操作系统安全状态和不安全状态;
- 了解如何避免系统死锁;
- 理解银行家算法是一种最有代表性的避免死锁的算法,掌握其实现原理及实现过程。
二、实验内容
根据银行家算法的基本思想,编写和调试一个实现动态资源分配的模拟程序,并能够有效避免死锁的发生。
三、实验原理
进程申请资源时,系统通过一定的算法判断本次申请是否不可能产生死锁(处于安全状态)。若可能产生死锁(处于不安全状态),则暂不进行本次资源分配,以避免死锁。算法有著名的银行家算法。
1. 什么是系统的安全状态和不安全状态?
所谓安全状态,是指如果系统中存在某种进程序列<P1,P2,…,Pn>,系统按该序列为每个进程分配其所需要的资源,直至最大需求,则最终能使每个进程都可顺利完成,称该进程序列<P1,P2,…,Pn,>为安全序列。
如果不存在这样的安全序列,则称系统处于不安全状态。
2. 银行家算法
把操作系统看作是银行家,操作系统管理的资源相当于银行家管理的资金,进程向操作系统请求分配资源相当于用户向银行家贷款。
为保证资金的安全,银行家规定:
- 当一个顾客对资金的最大需求量不超过银行家现有的资金时就可接纳该顾客;
- 顾客可以分期贷款,但贷款的总数不能超过最大需求量;
- 当银行家现有的资金不能满足顾客尚需的贷款数额时,对顾客的贷款可推迟支付,但总能使顾客在有限的时间里得到贷款;
- 当顾客得到所需的全部资金后,一定能在有限的时间里归还所有的资金。
操作系统按照银行家制定的规则设计的银行家算法为:
- 进程首次申请资源的分配:如果系统现存资源可以满足该进程的最大需求量,则按当前的申请量分配资源,否则推迟分配。
- 进程在执行中继续申请资源的分配:若该进程已占用的资源与本次申请的资源之和不超过对资源的最大需求量,且现存资源能满足该进程尚需的最大资源量,则按当前申请量分配资源,否则推迟分配。
- 至少一个进程能完成:在任何时刻保证至少有一个进程能得到所需的全部资源而执行到结束。
银行家算法通过动态地检测系统中资源分配情况和进程对资源的需求情况来决定如何分配资源,并能在确保系统处于安全状态时才把资源分配给申请者,从而避免系统发生死锁。
四、实验中用到的系统调用函数
因为是模拟程序,可以不使用系统调用函数。
五、程序流程图
-
总流程
-
安全性算法
文字补充:
- 流程图是这几种方法的思路,具体实现还有很多细节,细节在代码注释;
- 个人感觉最纠结的地方是首次分配
代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//c语言没有bool类型,用int的1和0替代
#define bool int
#define true 1
#define false 0
//生成begin~end随机数
int myrandom(int begin, int end) {
if (begin == 0) return rand() % (end + 1);
else return rand() % (end - begin + 1) + begin;
}
//判断某个进程的某个二维数组是否全为0
bool isAllZeroTwo(int** array, int randomP, int m) {
for (int j = 0; j < m; j++) {
if (array[randomP - 1][j]) {
return false;
}
}
return true;
}
//判断某个进程的某个一维数组是否全为0
bool isAllZeroOne(int* array, int m) {
for (int j = 0; j < m; j++) {
if (array[j]) {
return false;
}
}
return true;
}
//判断某个进程的某个二维数组是否只有一个1
bool isOnlyOneTwo(int** array, int randomP, int m) {
int count = 0;
for (int i = 0; i < m; i++) {
if(array[randomP - 1][i] == 1) count++;
}
if (count > 1) return false;
return true;
}
//判断某个进程的某个一维数组是否只有一个1
bool isOnlyOneOne(int* array, int m) {
int count = 0;
for (int i = 0; i < m; i++) {
if (array[i] == 1) count++;
}
if (count > 1) return false;
return true;
}
//打印资源分配表
void show(int** allocation, int* available, int** need, int** max, int n, int m) {
for (int i = 1; i <= 4; i++) {
printf("\t|");
for (int j = 0; j < m; j++) printf("%3s%d", "r", j + 1);
}
for (int i = 0; i < n; i++) {
printf("\nP%d\t|", i + 1);
for (int j = 0; j < m; j++) {
printf("%4d", allocation[i][j]);
}
printf("\t|");
for (int j = 0; j < m; j++) {
printf("%4d", max[i][j]);
}
printf("\t|");
for (int j = 0; j < m; j++) {
printf("%4d", need[i][j]);
}
printf("\t|");
for (int j = 0; j < m && i == 0; j++) {
printf("%4d", available[j]);
}
}
printf("\n\n");
}
//生成随机的每个资源总数,1~10
//不能只有一个1,因为不希望后面生成的每个进程最大需求数,只有一个1,不利于后续操作
void makeResourceTotal(int* resource, int m) {
do {
for (int i = 0; i < m; i++) resource[i] = myrandom(1, 10);
} while (isOnlyOneOne(resource, m));
}
//生成随机的每个进程的最大资源需求数,0~该资源总数
//不能全为0,也不能只有一个1
//全为0没意义。
//只有一个1,初次分配资源,该进程要么申请0资源,要么初次申请就直接完成任务
void makeMax(int** max, int* resource, int n, int m) {
for (int i = 0; i < n; i++) {
do {
for (int j = 0; j < m; j++) {
max[i][j] = myrandom(0, resource[j]);
}
} while (isAllZeroTwo(max, i + 1, m) || isOnlyOneTwo(max, i + 1, m));
}
}
//判断available是否大于等于该进程的max
bool availableBiggerThanMax(int** max, int* available, int Process, int m) {
for (int i = 0; i < m; i++) {
if (available[i] < max[Process - 1][i]) return false;
}
return true;
}
//判断是否有available大于等于某个进程的max
bool haveAvailableBiggerThanMax(bool* isAllocated, int** max, int* available, int n, int m) {
for (int i = 0; i < n; i++) {
//已经进行初次分配的进程无需判断
if (isAllocated[i]) continue;
if (availableBiggerThanMax(max, available, i + 1, m)) return true;
}
return false;
}
//判断是否全部进程都已进行初次资源的分配
bool allIsAllocated(bool* isAllocated, int n) {
for (int i = 0; i < n; i++) {
if (!isAllocated[i]) return false;
}
return true;
}
//初始化allocation,available,need数组
void makeAllocationAndAvailableAndNeed(int** allocation, int* available, int** need, int* resource, int** max, int n, int m) {
for(int i = 0; i < m; i++) available[i] = resource[i];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
allocation[i][j] = 0;
need[i][j] = max[i][j];
}
}
}
//首次分配
void firstAllocated(int** allocation, int* available, int** need, int** max, int n, int m) {
//记录进程是否已经分配资源
bool* isAllocated = (bool*)malloc(sizeof(bool) * n);
//初始化为false
for (int i = 0; i < n; i++) isAllocated[i] = false;
//为每个进程进行初次资源的分配
//结束条件为全部进程已分配或者available不大于等于任何一个未分配资源进程的max
while (!allIsAllocated(isAllocated, n) && haveAvailableBiggerThanMax(isAllocated, max, available, n, m)) {
//随机选择一个进程进行初次资源的分配
//该进程要未被分配资源,available要大于该进程的max
int randomP;
do {
randomP = myrandom(1, n);
} while (isAllocated[randomP - 1] || (!isAllocated[randomP - 1] && !availableBiggerThanMax(max, available, randomP, m)));
//暂存available,该进程的allocation、need
//在后续分配资源过程中,当随机生成的资源数不合适,则进行恢复并重新分配
int* saveAvailable = (int*)malloc(sizeof(int) * m);
int* saveAllocation = (int*)malloc(sizeof(int) * m);
int* saveNeed = (int*)malloc(sizeof(int) * m);
for (int i = 0; i < m; i++) {
saveAvailable[i] = available[i];
saveAllocation[i] = allocation[randomP - 1][i];
saveNeed[i] = need[randomP - 1][i];
}
//分配的资源不能全为零,也不能第一次分配资源就让该进程完成任务
do {
for (int i = 0; i < m; i++) {
allocation[randomP - 1][i] = saveAllocation[i];
need[randomP - 1][i] = saveNeed[i];
available[i] = saveAvailable[i];
}
for (int i = 0; i < m; i++) {
allocation[randomP - 1][i] = myrandom(0, max[randomP - 1][i]);
need[randomP - 1][i] = max[randomP - 1][i] - allocation[randomP - 1][i];
available[i] -= allocation[randomP - 1][i];
}
} while (isAllZeroTwo(allocation, randomP, m) || isAllZeroTwo(need, randomP, m));
//标记该进程完成初次资源的分配
isAllocated[randomP - 1] = true;
//释放资源
free(saveNeed);
free(saveAllocation);
free(saveAvailable);
saveNeed = NULL;
saveAllocation = NULL;
saveAvailable = NULL;
}
//释放资源
free(isAllocated);
isAllocated = NULL;
}
//安全性算法
bool isSafe(int** allocation, int* available, int** need, int** max, int n, int m) {
//初始化finish
//已结束的进程为false(包括假设分配资源的进程)
//未结束的为true
bool* finish = (bool*)malloc(sizeof(bool) * n);
for (int i = 0; i < n; i++) finish[i] = false;
//work暂存系统可用资源数available
//因为安全性算法的预测过程中要改变系统可用资源数,而预测不应该改变实际的东西
int* work = (int*)malloc(sizeof(int) * m);
for (int i = 0; i < m; i++) work[i] = available[i];
//simulationNeed暂存每个进程的缺少资源数need,理由同上
int** simulationNeed = (int**)malloc(sizeof(int*) * n);
for (int i = 0; i < n; ++i) simulationNeed[i] = (int*)malloc(sizeof(int) * m);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
simulationNeed[i][j] = need[i][j];
}
}
//安全序列securitySequence
//初始化为0,便于后续打印
int* securitySequence = (int*)malloc(sizeof(int) * n);
for (int i = 0; i < n; i++) securitySequence[i] = 0;
int securitySequenceIndex = 0;
//开始预测
//遍历每个进程
for (int i = 0; i < n; i++) {
//finish为true,说明该进程实际已结束或者在预测中已结束
if (finish[i]) continue;
//finish为false,判断系统是否为该进程分配资源
else {
//判断该进程的need是否<=work
int flag = 1;
for (int j = 0; j < m; j++) {
if (simulationNeed[i][j] > work[j]) {
flag = 0;
break;
}
}
//need>work
if (!flag) {
//不是最后一个进程在进行判断,继续下一个进程的判断
if (i != n - 1) continue;
//最后一个进行在进行判断,并且need>work
//说明为进行安全性检测的进程分配资源后的系统处于不安全状态
//将预测过程中改变的finish变回false,释放资源,返回false
else {
free(securitySequence);
for (int i = 0; i < n; i++) free(simulationNeed[i]);
free(simulationNeed);
free(work);
free(finish);
finish = NULL;
work = NULL;
securitySequence = NULL;
return false;
}
}
//need<=work
else {
//假定为遍历中的进程分配资源,该进程完成任务后释放资源
//更新系统可用资源数的暂存数组work,获取该进程释放的所有资源
for (int j = 0; j < m; j++) work[j] += max[i][j];
//该遍历中的进程已完成
finish[i] = true;
//将该进程的缺少资源数的暂存数组simulationNeed改为0
for (int j = 0; j < m; j++) simulationNeed[i][j] = 0;
//更新安全序列
securitySequence[securitySequenceIndex++] = i + 1;
//从头开始遍历进程数组
i = -1;
}
}
}
//打印安全序列
printf("存在一个安全序列:");
for (int i = 0; i < n && securitySequence[i]; i++) {
printf("%d", securitySequence[i]);
if (i != n - 1 && securitySequence[i + 1]) printf("->");
}
printf("\n");
//释放资源
free(securitySequence);
for (int i = 0; i < n; i++) free(simulationNeed[i]);
free(simulationNeed);
free(work);
free(finish);
finish = NULL;
work = NULL;
simulationNeed = NULL;
securitySequence = NULL;
//返回true,表示系统分配完资源的新状态为安全状态
return true;
}
//资源分配算法
void resourceAllocation(int** allocation, int* available, int** need, int** max, int n, int m, int randomP) {
//某个进程的申请向量
//不能全为0
int* request = (int*)malloc(sizeof(int) * m);
do {
for (int i = 0; i < m; i++) request[i] = myrandom(0, need[randomP - 1][i]);
} while (isAllZeroOne(request, m));
//打印申请的资源数
printf("进程%d发出请求request(", randomP);
for (int i = 0; i < m; i++) printf(" %d ", request[i]);
printf(")\n\n");
//判断进程申请资源数request和系统可用资源数available的大小关系
int flag = 1;
for (int i = 0; i < m; i++) {
if (request[i] > available[i]) {
flag = 0;
break;
}
}
//request<=available,假设系统把申请资源分配给该进程,随后执行安全性算法
if (flag) {
//暂存系统假设分配资源前的系统当前资源数available、该进程已分配资源数allocation和该进程缺少资源数need
//因为假设过程要将资源分配给该进程,系统新状态是不安全的时候要恢复原本的资源数
int* saveAvailable = (int*)malloc(sizeof(int) * m);
for (int i = 0; i < m; i++) saveAvailable[i] = available[i];
int* saveAllocation = (int*)malloc(sizeof(int) * m);
for (int i = 0; i < m; i++) saveAllocation[i] = allocation[randomP - 1][i];
int* saveNeed = (int*)malloc(sizeof(int) * m);
for (int i = 0; i < m; i++) saveNeed[i] = need[randomP - 1][i];
//假设分配
for (int i = 0; i < m; i++) {
available[i] -= request[i];
allocation[randomP - 1][i] += request[i];
need[randomP - 1][i] -= request[i];
}
//系统状态不安全,不为该进程分配资源,该进程等待下一次申请
//这里是假设分配资源。因为不安全,所以恢复原本的资源数
if (!isSafe(allocation, available, need, max, n, m)) {
for (int i = 0; i < m; i++) {
available[i] = saveAvailable[i];
allocation[randomP - 1][i] = saveAllocation[i];
need[randomP - 1][i] = saveNeed[i];
}
printf("系统新状态为不安全状态,进程%d等待\n", randomP);
}
//系统新状态是安全的,就实际分配资源
//这里是假设分配资源,安全就直接完成了
else {
printf("系统新状态为安全状态,为进程%d分配资源\n", randomP);
show(allocation, available, need, max, n, m);
}
//释放资源
free(request);
free(saveNeed);
free(saveAllocation);
free(saveAvailable);
saveNeed = NULL;
saveAllocation = NULL;
saveAvailable = NULL;
request = NULL;
}
//request>available,不为该进程分配资源,该进程等待下一次申请
else printf("系统当前资源数不满足进程%d的申请要求,进程%d等待\n", randomP, randomP);
}
int main() {
printf("初始数据\n\n");
//系统中进程总数
int n = 20;
printf("进程总数为%d\t", n);
//资源类总数
srand((unsigned)time(NULL));
int m = myrandom(2, 10);
printf("资源类总数为%d\n\n", m);
//每个资源总数
srand((unsigned)time(NULL));
int* resource = (int*)malloc(sizeof(int) * m);
makeResourceTotal(resource, m);
printf("每个资源总数\n");
for (int i = 0; i < m; i++) printf("r%d\t", i + 1);
printf("\n");
for (int i = 0; i < m; i++) printf("%d\t", resource[i]);
printf("\n\n");
//每个进程的最大资源需求数
int** max = (int**)malloc(sizeof(int*) * n);
for (int i = 0; i < n; ++i) max[i] = (int*)malloc(sizeof(int) * m);
makeMax(max, resource, n, m);
//每个进程的已分配资源数、缺少资源数、系统当前资源数
int** allocation = (int**)malloc(sizeof(int*) * n);
for (int i = 0; i < n; i++) allocation[i] = (int*)malloc(sizeof(int) * m);
int* available = (int*)malloc(sizeof(int) * m);
int** need = (int**)malloc(sizeof(int*) * n);
for (int i = 0; i < n; i++) need[i] = (int*)malloc(sizeof(int) * m);
makeAllocationAndAvailableAndNeed(allocation, available, need, resource, max, n, m);
printf("资源分配表从左到右顺序是Alloceation, Max, Need, Available。\n\n初始资源分配表\n");
show(allocation, available, need, max, n, m);
//初次分配
firstAllocated(allocation, available, need, max, n, m);
//打印初次分配后的表格
printf("T0时刻资源分配表\n");
show(allocation, available, need, max, n, m);
printf("在T0时刻");
isSafe(allocation, available, need, max, n, m);
printf("\n\n");
//用随机数模拟某个进程发出申请
int randomP = myrandom(1, n);
//统计执行时间
clock_t start, finish;
start = clock();
printf("随后");
resourceAllocation(allocation, available, need, max, n, m, randomP);
finish = clock();
printf("\n\nRunning Time:%dms\n", finish - start);
//释放资源
for (int i = 0; i < n; i++) {
free(need[i]);
need[i] = NULL;
}
free(need);
free(available);
for (int i = 0; i < n; i++) {
free(allocation[i]);
allocation[i] = NULL;
}
free(allocation);
for (int i = 0; i < n; i++) {
free(max[i]);
max[i] = NULL;
}
free(max);
free(resource);
need = NULL;
available = NULL;
allocation = NULL;
max = NULL;
resource = NULL;
return 0;
}
运行结果
测试过程中要更改进程数进行多次测试的,下面只放两图,减少篇幅
安全状态
不安全状态
分析
-
资源分配算法和安全性算法只要注意好细节就好了,难度不高;
-
主要是按照题目要求,不能手动输入的情况下,如何进行资源的初始化和进程资源的首次分配;
-
下面是我自己设计的输入模块。为了后续程序的运行和代码不变得更加繁琐,该输入模块设置随机数范围从而忽略几种特殊情况。
初始化的输入模块
① 系统中进程总数n在代码中更改,根据题目要求分别改为10、20、30、50、100;
② 资源类总数m用随机数设置为1~10;
③ 每个资源总数resource[i]用随机数设置为1~10。
- 避免情况:只有一个1其他全为0。
- 理由:避免资源类总数m只有1,而资源总数resource也为1,导致初次分配资源为0或者直接完成任务的情况;
④ 每个进程的最大资源需求数max用随机数设置为0~resource[i]。
- 避免情况: 全为0和只有一个1;
- 理由:全为0没意义,只有一个1的情况理由同上;
⑤ 系统可用资源available=resource,每个进程的allocation为0,need=max初次分配资源的输入模块
⑥ 在循环中选择一个进程用随机数设置为1~10,该进程要未被初次分配资源和available大于等于该进程的max
⑦ 为这个进程随机分配初次资源,随机数设置为0~max。
- 避免情况:全为0和直接满足need完成任务。