操作系统实验报告(二)银行家算法

在这里插入图片描述

一、 实验目的

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

二、 实验环境

Windows、g++

三、 实验内容

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

四、 实验要求

1、 画出银行家算法流程图;
2、 对算法所用的数据结构进行说明;
3、 测试数据随机产生。不可手工输入;
4、 编写程序并调试;
5、 多次测试程序,截屏输出实验结果;
6、 根据实验结果与理论课讲述的原理进行实验分析。

五、 实验原理 实验中用到的系统调用函数(包括实验原理中介绍的和自己采用的),实验步骤,

实验原理:

进程申请资源时,系统通过一定的算法判断本次申请是否不可能产生死锁(处于安全状态)。若可能产生死锁(处于不安全状态),则暂不进行本次资源分配,以避免死锁。算法有著名的银行家算法。
1、什么是系统的安全状态和不安全状态?
所谓安全状态,是指如果系统中存在某种进程序列<P1,P2,…,Pn>,系统按该序列为每个进程分配其所需要的资源,直至最大需求,则最终能使每个进程都可顺利完成,称该进程序列<P1,P2,…,Pn,>为安全序列。
如果不存在这样的安全序列,则称系统处于不安全状态。
2、银行家算法
把操作系统看作是银行家,操作系统管理的资源相当于银行家管理的资金,进程向操作系统请求分配资源相当于用户向银行家贷款。
为保证资金的安全,银行家规定:
(1) 当一个顾客对资金的最大需求量不超过银行家现有的资金时就可接纳该顾客;
(2) 顾客可以分期贷款,但贷款的总数不能超过最大需求量;
(3) 当银行家现有的资金不能满足顾客尚需的贷款数额时,对顾客的贷款可推迟支付,但总能使顾客在有限的时间里得到贷款;
(4) 当顾客得到所需的全部资金后,一定能在有限的时间里归还所有的资金。

操作系统按照银行家制定的规则设计的银行家算法为:
(1)进程首次申请资源的分配:如果系统现存资源可以满足该进程的最大需求量,则按当前的申请量分配资源,否则推迟分配。
(2)进程在执行中继续申请资源的分配:若该进程已占用的资源与本次申请的资源之和不超过对资源的最大需求量,且现存资源能满足该进程尚需的最大资源量,则按当前申请量分配资源,否则推迟分配。
(3)至少一个进程能完成:在任何时刻保证至少有一个进程能得到所需的全部资源而执行到结束。
银行家算法通过动态地检测系统中资源分配情况和进程对资源的需求情况来决定如何分配资源,并能在确保系统处于安全状态时才把资源分配给申请者,从而避免系统发生死锁。

实验中用到的系统调用函数:

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

实验步骤:

该程序所用到的数据结构如下:

//系统可用的剩余资源
int available[resourceNum];
//进程的最大需求
int maxRequest[processNum][resourceNum];
//进程已经占有的资源
int allocation[processNum][resourceNum];
//进程还需要的资源(满足关系need + allocation == maxRequest)
int need[processNum][resourceNum];
//复制available数组,复制的目的仅是为了在检查系统是否安全的时候不破坏原来的available数组
int work[resourceNum];
//进程是否已经完成(为true代表已经完成,全部为true就代表安全)
int finish[processNum];
//安全序列号
int safeSeries[processNum];
//申请资源的数量
int request[resourceNum];
//已经完成的进程数量
int cnt = 0;

首先我定义了5类资源和5个进程,并把程序做成了菜单的形式(如图)
在这里插入图片描述

在程序运行之后,第一件事是随机生成当前的系统资源使用情况,因为后面我们还是需要对这个资源进行分配,所以我们生成数据的时候一定要保证当前系统是安全的,所以我们需要生成一个系统安全的数据,实现思路为:循环生成一段数据,直到生成的数据是系统安全的:
在这里插入图片描述
程序流程图如下:
在这里插入图片描述

检查当前系统是否安全,首先需要把available数组拷贝到work数组,拷贝的目的是为了在检查的过程中不破坏原来的available数组。检查当前系统是否安全,执行以下步骤:

  1. 先将finish数组全部置为false,表示目前没有进程已经完成。然后遍历全部进程,判断每个进程的need[i][j]是否为空,如果为空,则表示当前进程已经完成,执行finish[i] = true,finishNum += 1;

  2. 逐一遍历目前的进程,定义变量pid表示目前正在遍历到的进程,如果该pid满足以下条件,则跳到步骤3,否则pid+1后继续执行执行步骤2
    finish[pid] = false; //当前进程未完成
    ②对于每一个j∈[0, resources),都满足need[pid][j] <= work[j];//当前系统剩余资源可以使这个进程完成工作

  3. 把系统所剩的资源分配给进程pid后,就可以释放该进程已经占用的资源了,执行下面的代码:
    work[j] += allocation[pid][j]; //释放该进程占有的资源
    finish[pid] = 1; //标记一下该进程已完成
    safeSeries[safeIndex++] = pid;//安全序列
    然后执行pid+1后继续执行步骤2,直到所有进程都已完成。

注意事项:

  1. pid + 1之后,需要再执行pid %= processNum,因为当遍历完最后一个进程之后需要重新从pid = 0的进程开始。
  2. 在上面的循环中,只有当所有进程都执行完成之后,也就是只有系统处于安全状态的时候才会退出循环,如果系统处于不安全状态,就会一直死循环。为了判断当前系统是不是处于不安全状态,避免出现死循环的情况,所以定义一个变量lastFinishNum,表示上一趟遍历之后,进程的完成数量,在每一趟循环结束之后再判断lastFinishNum和FinishNum是否相等,如果相等,就表示这一趟遍历并没有使完成的进程数量增加,这样子再继续循环下去也不可能再使完成的进程数量增加的,所以就可以判断当前系统处于不安全状态。

程序流程图如下:
在这里插入图片描述

随机给某一进程分配资源,需要执行以下步骤:

  1. 随机生成从0到processNum – 1的数,代表要分配给的进程号(pid)。如果当前进程已完成,需要重新执行第1步。
  2. 随机生成要分配的资源,需要注意分配的资源不能超过当前系统剩余的资源和当前进程需要的资源。
  3. 执行下面操作,分配资源:
    need[pid][j] -= request[j];
    allocation[pid][j] += request[j];
    available[j] -= request[j];
  4. 判断当前进程是否已完成,如是,则收回该进程占用的资源,否则,继续执行第5步。
  5. 判断分配之后系统是否安全,如是,则同意分配,否则,执行以下操作回滚:
    need[pid][j] += request[j];
    allocation[pid][j] -= request[j];
    available[j] += request[j];

程序流程图如下:
在这里插入图片描述

六、 实验结果分析(截屏的实验结果,与实验结果对应的实验分析)

1、实验结果与实验程序、实验步骤、实验原理、操作系统原理的对应分析;
2、不同条件下的实验结果反应的问题及原因;
3、实验结果的算法时间、效率、鲁棒性等性能分析。

菜单界面:
在这里插入图片描述
查看当前资源使用情况:
在这里插入图片描述
分析当前系统安全性:
在这里插入图片描述
分析:依次将剩余资源分配给进程0 2 3 4 1,最后系统都能分配给每个进程所需要的资源,所以每个进程最后都能完成,所以当前系统是安全的!

分配资源:
在这里插入图片描述
分析:对进程2依次分配资源3 1 0 5 1之后,系统所剩的资源再依次分配给进程2 3 4 0 1,最后系统还是都能分配给每个进程所需要的资源,所以每个进程最后都能完成,所以当前系统是安全的!因此同意本次分配!
在这里插入图片描述

分析:对进程1分配资源3 1 1 0 1之后,系统就只剩1 4 5 1 1资源了,这一点资源不能使剩下的任意进程完成工作,每个进程都在等待着其他进程完成,造成现在系统处于死锁状态,所以一旦同意这次分配,那么接下来系统是不安全的!所以需要回滚本次分配!

七、 实验总结

银行家算法是一种最具有代表性的避免死锁的算法,通过这个实验,我也真正理解了操作系统在为进程分配资源的过程,对理论课的学习有了进一步的理解。

八、 实验数据及源代码(学生必须提交自己设计的程序源代码,并有注释,源代码电子版也一并提交),包括思考题的程序。

实验源代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int resourceNum = 5;  //资源数量
const int processNum = 5;   //进程数量
//系统可用的剩余资源
int available[resourceNum];
//进程的最大需求
int maxRequest[processNum][resourceNum];
//进程已经占有的资源
int allocation[processNum][resourceNum];
//进程还需要的资源(满足关系need + allocation == maxRequest)
int need[processNum][resourceNum];
//复制available数组,复制的目的仅是为了在检查系统是否安全的时候不破坏原来的available数组
int work[resourceNum];
//进程是否已经完成(为true代表已经完成,全部为true就代表安全)
int finish[processNum];
//安全序列号
int safeSeries[processNum];
//申请资源的数量
int request[resourceNum];
//已经完成的进程数量
int cnt = 0;

//判断某一进程的need[]是不是全部为0(全部为0就代表该进程已完成)
bool isAllZero(int pid){
    for(int j = 0;j<resourceNum;++j){
        if(need[pid][j]){
            return 0;
        }
    }
    return 1;
}

//目前资源使用情况
void info(){
    printf("当前系统剩余资源:");
    for(int i = 0;i < resourceNum;++i){
        printf("%d ",available[i]);
    }
    printf("\n");
    printf("当前所有进程的资源使用情况如下:\n");
    printf("PID\t\tmaxRequest\t\tallocation\t\tneed\n");
    for(int i = 0;i < processNum;++i){
        printf("%05d\t\t",i);   //输出PID
        //输出maxRequest
        for(int j = 0;j<resourceNum;++j){
            printf("%-2d ",maxRequest[i][j]);
        }
        printf("\t\t");
        //输出allocation
        for(int j = 0;j<resourceNum;++j){
            printf("%-2d ",allocation[i][j]);
        }
        printf("\t\t");
        //输出need
        for(int j = 0;j<resourceNum;++j){
            printf("%-2d ",need[i][j]);
        }
        printf("\n");
    }
}

//判断当前系统是否安全
bool isSafe(bool showinfo = 1/*showinfo:是否显示分配过程,默认显示*/){
    //初始化finish
    memset(finish,0,sizeof(finish));
    int finishNum = 0;  //已经完成的进程数量
    int safeIndex = 0;  //安全序列的下标
    //复制available数组给work
    for(int i = 0;i < resourceNum;++i){
        work[i] = available[i];
    }
    //检查是否有进程已经完成(所需资源全部为0就视为完成)
    for(int i = 0;i < processNum;++i){
        if(isAllZero(i)){
            finish[i] = 1;
            finishNum += 1;
        }
    }
    int lastFinishNum = -1;//上一趟循环之后完成的进程数量
    int pid = 0;    //当前是哪个进程
    bool tag = 1;   //标识系统是否安全
    while(finishNum < processNum){
        int num;
        pid %= processNum;
        if(pid == 0){
            //pid==0,说明这是新的一趟循环
            //如果一趟循环之后,完成的进程数量都还没有增加,那么剩下的资源都不可能有完成的了,所以不安全
            if(finishNum == lastFinishNum){
                tag = 0;
                break; 
            }
            lastFinishNum = finishNum;
        }
        if(finish[pid]){
            ++pid;
            continue;  //当前进程已完成,跳过
        }
        num = 0;
        for(int j = 0;j < resourceNum;++j){
            num += need[pid][j] <= work[j];
        }
        //当前系统资源能满足进程j所需的所有资源,说明该进程能完成
        if(num == resourceNum){
            for(int j = 0;j<resourceNum;++j){
                work[j] += allocation[pid][j]; //释放该进程占有的资源
            }
            finish[pid] = 1;    //标记一下该进程已完成
            safeSeries[safeIndex++] = pid;  //安全序列
            if(showinfo){
                printf("----------------------------------------------------\n");
                printf("%d) 把当前系统资源分配给进程%05d\n",safeIndex,pid);
                printf(">>> 进程%05d目前还需要的资源为", pid);
                for(int j = 0;j < resourceNum;++j){
                    printf("%-2d ", need[pid][j]);
                }
                printf("能满足要求!\n");
                printf(">>> 进程%05d运行完成后,释放掉该进程占有的资源后,当前系统剩余资源为",pid);
                for(int j = 0;j < resourceNum;++j){
                    printf("%-2d ", work[j]);
                }
                printf("\n");
            }
            finishNum++;
        }
        ++pid;
    }
    if(showinfo){
        if(tag){
            printf("\n当前系统安全,安全序列为:");
            for(int i = 0;i < safeIndex;++i){
                printf("%d ",safeSeries[i]);
            }
            printf("\n");
        }else{
            printf("\n当前剩余资源无法使接下来的任一进程完成工作,所以当前系统不安全!\n");
        }
    }
    return tag;
}

//初始化数据
void init(){
    int i,j;
    //随机数种子
    srand((unsigned long)time(0));
    
    //写available数组
    for(i = 0;i < resourceNum;++i){
        available[i] = rand() % 5 + 3;
    }
    //写maxRequest数组
    for(i = 0;i < processNum;++i){
        for(j = 0;j < resourceNum;++j){
            maxRequest[i][j] = rand() % 5 + 5;
        }
    }
    //写allocation数组
    for(i = 0;i < processNum;++i){
        for(j = 0;j < resourceNum;++j){
            allocation[i][j] = rand() % (maxRequest[i][j]);
        }
    }
    //根据maxRequest和allocation写need数组
    for(i = 0;i < processNum;++i){
        for(j = 0;j < resourceNum;++j){
            need[i][j] = maxRequest[i][j] - allocation[i][j];
        }
    }
}

//随机分配资源
void allot(){
    //随机数种子
    srand((unsigned long)time(0));
    int pid = 0;
    //随机一个进程,但是该进程不能是已经完成的进程
    do{
        pid = rand() % processNum;
    }while(isAllZero(pid));
    //随机分配资源
    for(int i = 0;i < resourceNum;++i){
        request[i] = rand() % min(available[i] + 1, need[pid][i] + 1); //随机分配资源
    }
    printf("正在尝试对进程%05d分配资源:",pid);
    for(int j = 0;j < resourceNum;++j){
        printf("%-2d ", request[j]);
    }
    printf("\n");
    //分配资源
    for(int j = 0;j < resourceNum;++j){
        need[pid][j] -= request[j];
        allocation[pid][j] += request[j];
        available[j] -= request[j];
    }
    info();
    //如果本次分配能直接满足该进程的要求,那么直接同意分配!
    if(isAllZero(pid)){
        printf("本次分配之后,已经能满足该进程的要求,同意分配!进程完成之后释放资源!\n");
        for(int j = 0;j < resourceNum;++j){
            available[j] += allocation[pid][j];
        }
        ++cnt;
    }else{
        printf("本次分配之后,仍未能满足该进程的要求\n");
        //检查分配之后是否安全
        if(isSafe()){
            //同意分配
            printf("同意本次分配!\n");
        }else{
            printf("回滚本次分配!\n");
            for(int j = 0;j < resourceNum;++j){
                need[pid][j] += request[j];
                allocation[pid][j] -= request[j];
                available[j] += request[j];
            }
        }
    }
    
    info();
}


//菜单
bool menu(){
    printf("***************************\n");
    printf("1. 查看系统当前资源使用情况\n");
    printf("2. 分析当前系统安全性\n");
    printf("3. 分配资源\n");
    printf("4. 重新生成系统资源数据\n");
    printf("0. 退出程序\n");
    printf("***************************\n");
    printf("请选择:");
    int a = -1; //当前选择
    do{
        scanf("%d",&a);
        if(a < 0 || a > 4){
            printf("请输入正确的数字!");
            a = -1;
        }	
        while(getchar() != '\n'){};//清空缓冲区	
    }while(a == -1);
    if(a == 0){
        return 0; //退出程序
    }else if(a == 1){
        info();
    }else if(a == 2){
        info();
        isSafe();
    }else if(a == 3){
        allot();
    }else if(a == 4){
        //初始化数据(要保证初始化之后系统是安全的!)
        do{
            init();
        }while(!isSafe(0));
        printf("初始化数据完成!\n");
        info();
    }
    cout << endl;
    system("pause");
    return 1;
};

int main() {
	//初始化数据(要保证初始化之后系统是安全的!)
    do{
        init();
    }while(!isSafe(0));
    //检查系统中是否已经有进程完成:
    for(int i = 0;i < processNum;++i){
        if(isAllZero(i)){
            ++cnt;
            //释放资源
            for(int j = 0;j < resourceNum;++j){
                available[j] += allocation[i][j]; 
            }
        }
    }
    while(menu()){ //显示菜单
        if(cnt >= processNum){
            printf("当前所有进程已经完成,程序结束!\n");
            break;
        }
        system("cls");  //清屏操作
    }
	return 0;
}

九、思考题:

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

答: 因为题目要求的是随机生成数据,所以随机生成数据时需要注意:
① 随机生成的allocation[i][j]一定要小于等于maxRequest[i][j],因为一个进程占有的资源不能超过最大需求。
② 因为初始化时一定要保证系统是安全状态的,所以随机生成的available要尽可能大一些,首先要保证剩余的资源至少能使一个进程完成。如果随机生成的available范围过小的话,初始化的时间会比较长。
③ 在分配资源给进程时,一定要保证request[i] <= min(available[i], need[pid][i]),因为分配的资源不能超过进程的当前需求,也不能超过当前系统的剩余资源。

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

答:
①每个进程的对资源的需求总量不能超过系统拥有的资源总数
②在对每个进程分配资源的时候,都要先判断这一次分配之后系统是否是安全的,如果是,则同意分配,否则需要回滚这次请求。

  • 44
    点赞
  • 315
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值