头歌实验5 银行家算法
任务描述
本关任务:编写银行家算法程序找出进程执行的安全序列。
相关知识
为了完成本关任务,你需要掌握:1.理解银行家算法原理,2.输出安全序列。
安全序列
并发运行的多个进程会竞争计算机资源,进程有多种可能的执行序列,不适宜的推进序列可能造成进程进入死锁状态,死锁严重影响计算机性能发挥。避免发生死锁的典型算法是Dijsktra银行家算法,它尝试找出一个进程的执行序列,使得所有进程都可以正常执行完毕。这个含有所有进程的序列就称为安全序列。
算法介绍
计算机系统允许进程并发执行及动态申请资源,但是并发运行的进程可能因资源分配顺序不合理而造成死锁。预防死锁是通过对运行中进程增加资源申请的限制条件,往往限制条件过多,不利于机器性能发挥。避免死锁通过寻找系统的安全状态能获得较好的系统性能,由 Dijkstra 发明的银行家安全算法是最具代表性的避免死锁的算法。
进程拥有若干数量资源,每个进程的成功执行后,因为系统回收资源使可用资源增加,从而使其它进程有更多可能性满足资源要求而得以执行。在当前资源数不能满足任何进程需求时,所有进程无法继续执行,进入死锁状态。银行家算法是对多种资源进行判断,试图找到一个进程执行序列,使所有进程执行完毕。
考虑只有一种资源的情况,当进程执行完毕,由于资源回收操作,系统总资源数会增加。如下图:
银行家算法的问题条件是系统中存在n个进程和m种资源,m种资源用数量为m的数组表示,每个进程都可申请m种资源,每种资源数量不一。在初始情况时,资源向量 Available 表示每种资源可用总数,随着进程的申请与回收,资源数值产生动态变化,Available代表当前尚未分配出去的该类资源数量,Available[j]=K 表示当前系统中第j类资源剩余数量为K个。 每个进程申请的m种资源有最大数量值限制,当系统能满足其最大数量需求后,该进程能够正常运行完毕。将系统中n个进程的最大需求向量的用矩阵 Max 表示,它实际是n×m的二维数组。Max[i][j]=Q 表示第 i 个进程需要第 j 种资源最大数值为 Q向量。 银行家算法允许进程分多次动态申请资源而不是一次性分配最大值,使用 Allocation 矩阵表示某时刻资源分配情况,它也是 n×m 的二维数组, Allocation[i][j]= P表示第 i 个进程已经分配了第 j 种资源数值为P。由于分配不是一次性分配完成,进程可能还需要若干数量的资源,进程需要的资源数量用矩阵 Need 表示,它是 n×m 的二维数组,Need[i][j]=S 表示第i个进程还需第j种资源数值为S。而三个二维数组存在关系,它表示进程当前已经分配的资源数和最多需要的资源数与还需要的资源数间的关系。 Max[i][j]-Allocation[i][j]=Need[i][j] 银行家算法的目标是得到一个使所有进程依次运行完结的进程序列,当进程按指定顺序执行时,操作系统在每个进程的终结后都将其已分配资源回收,可用资源会增多,再将当前资源分配下去让后续进程正常执行,最终所有进程资源需求得到满足而全部执行完成,不发生死锁现象。由于进程总数为 n,进程的全排列总数为 n 的阶乘,只要在资源分配时验算资源申请是否满足系统要求即可得知当前排列是否可取,据此获得所有安全序列,这非常类似树的遍历,因此算法采用递归方式。
银行家算法设计
安全序列的产生过程资源情况一直在发变化,定义一个包含当前可用资源数和现有进程序列的数据结构来表示算法的推进过程,并在每次选择进程时,申请新的内存空间,并复制现有资源信息,就可以为每个序列配上相应的资源变化情况。因为序列数量未知,当得到一个安全序列时,生成一个链表节点来保存,并将节点加入到链表中。
原始数据量较多,将数据先存入文件中,通过读文件的方式录入数据,并将字符转化为整数。数据在文件中的组织方式如下图所示。 银行家算法是进程执行序列生成过程,由于进程组合序列可能性较多,采用递归的方法可以生成所有可能的进程序列,通过判断是否满足资源需求可确定该序列是否是安全的序列。为了方便进行在线结果检测,仅仅输出安全序列的个数。
主要函数说明:ReadBankData函数实现从文件中读入数据,初始化到相应数据结构中;BankerSecurity是核心的递归算法,最终生成所有的安全序列。递归生成过程中,随着进程序列推进序列不同,资源数据也在发生变化,每个序列的判断都必须有一个完整的当前资源信息,定义一个结构体表示当前资源信息。 每种不同的进程选择都采用malloc方式分配内存空间操作数据,使用完毕的内存空间还要通过free方法释放,直到所有进程都能执行完成即为一个安全序列。由于安全序列数量未知,得到一个安全序列后,创建一个链表节点,使用addSecueQtoLink方法将安全序列加入到链表中,最后getSecurePCount函数遍历链表得到安全序列的总数。
LineToInts是将一行字符串转化为整数组。CheckResource方法检测当前系统资源是否满足进程需要的数值。
编程要求
本实验采用 C 语言实现了银行家安全算法。根据缺失部分逻辑提示,补充合适的代码,使程序可以正常运行,得出正确结果。
代码展示
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
//系统当前可用资源和正在生成中的进程序列
typedef struct _ResAndP {
int iAvailableRa;//系统当前资源A总数量
int iAvailableRb;//系统当前资源B总数量
int iAvailableRc;//系统当前资源C总数量
int iPOrder[5];//当前进程序列,最多5个
int iPCount; //进程队列中进程个数
}ResAndProcess;
//一个进程资源状态结构体
typedef struct _ResOfP {
int iMaxRa;//进程需要A资源的最大数量
int iMaxRb;//进程需要B资源的最大数量
int iMaxRc;//进程需要C资源的最大数量
int iAllocRa;//进程已分配的A资源数量
int iAllocRb;//进程已分配的B资源数量
int iAllocRc;//进程已分配的C资源数量
int iNeedRa;//进程还需要的A资源数量
int iNeedRb;//进程还需要的B资源数量
int iNeedRc;//进程还需要的C资源数量
}ResOfProcess;
ResAndProcess StartResAndP;
ResOfProcess AllResOfP[5];
//链表节点结构体,保存一个安全序列
typedef struct POrder {
int iPOrder[5];//安全的进程序列
struct POrder* next;//下个节点指针
}POrderNode;
POrderNode* HeadOfPOrder;//序列链表的头指针
//当前字符串转换到整数数组[5]
void LineToInts(char* lbuf, int* iArray, int iCount)
{
char StrIntBuf[10];//将字符串转为整数的缓冲区
int i, j, iStrStart, iStrEnd, iStrLen, iProIndex;
//17,5,20
iStrLen = strlen(lbuf);
i = 0;
iStrEnd = 0;
iStrStart = 0;
for (j = 0; j < iCount; j++)
{
memset(StrIntBuf, 0, 10);
for (; (i < iStrLen) && (lbuf[i] != ','); i++);
iStrEnd = i;
memcpy(StrIntBuf, lbuf + iStrStart, iStrEnd - iStrStart);
iArray[j] = atoi(StrIntBuf);
i++;
iStrStart = i;
}
}
//从banker.dat文件读入数据
int ReadBankData()
{
/*
FILE * fp=NULL;
int fd = -1,i;
ssize_t size = -1;
char Linebuf[100]; /*存放数据的缓冲区*/
/*
char *pchar=NULL;
char filename[] = "banker.dat";
int Numbers[6];//用于保存字符串转换的临时整数数组
fp=fopen(filename,"r");
if(fp==NULL)
{//文件打开失败
printf("Open file %s failure,fd:%d\n",filename,fd);
return;
}
memset((void *)Linebuf,0,(size_t)100);//清空数组
pchar=fgets(Linebuf,100,fp);
LineToInts(Linebuf,Numbers,3);
StartResAndP.iAvailableRa=Numbers[0];
StartResAndP.iAvailableRb=Numbers[1];
StartResAndP.iAvailableRc=Numbers[2];
for(i=0;i<5;i++)
{
memset((void *)Linebuf,0,(size_t)100);//清空数组
pchar=fgets(Linebuf,100,fp);
LineToInts(Linebuf,Numbers,6);
AllResOfP[i].iMaxRa=Numbers[0];
AllResOfP[i].iMaxRb=Numbers[1];
AllResOfP[i].iMaxRc=Numbers[2];
AllResOfP[i].iAllocRa=Numbers[3];
AllResOfP[i].iAllocRb=Numbers[4];
AllResOfP[i].iAllocRc=Numbers[5];
AllResOfP[i].iNeedRa=Numbers[0]-Numbers[3];
AllResOfP[i].iNeedRb=Numbers[1]-Numbers[4];
AllResOfP[i].iNeedRc=Numbers[2]-Numbers[5];
}
//关闭文件
fclose(fp);
*/
/* 运行矩阵:
Pi Maxa,Maxb,Maxc,Alloca,Allocb,Allocc,Needa,Needb,Needc,Avaia,Avaib,Avaic
0 5,5,9, 2,1,2, 3,4,7, 1,5,2,
1 5,3,6, 4,0,2, 1,3,4,
2 4,0,11, 4,0,5, 0,0,6
3 4,2,5, 2,0,4, 2,2,1
4 4,2,4, 3,1,4, 1,1,0
*/
/*
1,5,2,
5,5,9,2,1,2,
5,3,6,4,0,2,
4,0,11,4,0,5,
4,2,5,2,0,4,
4,2,4,3,1,4,
*/
StartResAndP.iAvailableRa = 1;
StartResAndP.iAvailableRb = 5;
StartResAndP.iAvailableRc = 2;
int i = 0;
AllResOfP[i].iMaxRa = 5;
AllResOfP[i].iMaxRb = 5;
AllResOfP[i].iMaxRc = 9;
AllResOfP[i].iAllocRa = 2;
AllResOfP[i].iAllocRb = 1;
AllResOfP[i].iAllocRc = 2;
AllResOfP[i].iNeedRa = AllResOfP[i].iMaxRa - AllResOfP[i].iAllocRa;
AllResOfP[i].iNeedRb = AllResOfP[i].iMaxRb - AllResOfP[i].iAllocRb;
AllResOfP[i].iNeedRc = AllResOfP[i].iMaxRc - AllResOfP[i].iAllocRc;
i = 1;
AllResOfP[i].iMaxRa = 5;
AllResOfP[i].iMaxRb = 3;
AllResOfP[i].iMaxRc = 6;
AllResOfP[i].iAllocRa = 4;
AllResOfP[i].iAllocRb = 0;
AllResOfP[i].iAllocRc = 2;
AllResOfP[i].iNeedRa = AllResOfP[i].iMaxRa - AllResOfP[i].iAllocRa;
AllResOfP[i].iNeedRb = AllResOfP[i].iMaxRb - AllResOfP[i].iAllocRb;
AllResOfP[i].iNeedRc = AllResOfP[i].iMaxRc - AllResOfP[i].iAllocRc;
i = 2;
AllResOfP[i].iMaxRa = 4;
AllResOfP[i].iMaxRb = 0;
AllResOfP[i].iMaxRc = 11;
AllResOfP[i].iAllocRa = 4;
AllResOfP[i].iAllocRb = 0;
AllResOfP[i].iAllocRc = 5;
AllResOfP[i].iNeedRa = AllResOfP[i].iMaxRa - AllResOfP[i].iAllocRa;
AllResOfP[i].iNeedRb = AllResOfP[i].iMaxRb - AllResOfP[i].iAllocRb;
AllResOfP[i].iNeedRc = AllResOfP[i].iMaxRc - AllResOfP[i].iAllocRc;
i = 3;
AllResOfP[i].iMaxRa = 4;
AllResOfP[i].iMaxRb = 2;
AllResOfP[i].iMaxRc = 5;
AllResOfP[i].iAllocRa = 2;
AllResOfP[i].iAllocRb = 0;
AllResOfP[i].iAllocRc = 4;
AllResOfP[i].iNeedRa = AllResOfP[i].iMaxRa - AllResOfP[i].iAllocRa;
AllResOfP[i].iNeedRb = AllResOfP[i].iMaxRb - AllResOfP[i].iAllocRb;
AllResOfP[i].iNeedRc = AllResOfP[i].iMaxRc - AllResOfP[i].iAllocRc;
i = 4;
AllResOfP[i].iMaxRa = 4;
AllResOfP[i].iMaxRb = 2;
AllResOfP[i].iMaxRc = 4;
AllResOfP[i].iAllocRa = 3;
AllResOfP[i].iAllocRb = 1;
AllResOfP[i].iAllocRc = 4;
AllResOfP[i].iNeedRa = AllResOfP[i].iMaxRa - AllResOfP[i].iAllocRa;
AllResOfP[i].iNeedRb = AllResOfP[i].iMaxRb - AllResOfP[i].iAllocRb;
AllResOfP[i].iNeedRc = AllResOfP[i].iMaxRc - AllResOfP[i].iAllocRc;
return 1;
}
//检查安全序列集回安全序列个数
int getSecurePCount()
{
int SecuPCount = 0;
int i = 0;
POrderNode* curNode = NULL;
//释放所有节点
while (HeadOfPOrder != NULL)
{
SecuPCount++;//为了方便在线检测,这里仅获得安全序列个数
curNode = HeadOfPOrder;
//根据需要也可以输出安全序列,去掉注释符号即可输出安全序列 //printf("安全序列是:%d,%d,%d,%d,%d.\n",curNode->iPOrder[0],curNode->iPOrder[1],curNode->iPOrder[2],curNode->iPOrder[3],curNode->iPOrder[4]);
HeadOfPOrder = HeadOfPOrder->next;
free((void*)curNode);
}
return SecuPCount;
}
//检查当前资源条件,序号为iPIndex的进程资源需求是否可以分配资源
int CheckResource(ResAndProcess* aRAndP, int iPIndex)
{
int iResult = 0;
//iPIndex为进程序列中的序号
//Available>=Need,则返回值1
//给出iPIndex编号的进程是否可以分配资源的判断逻辑
/* begin ******************************************************** */
if (aRAndP->iAvailableRa >= AllResOfP[iPIndex].iNeedRa &&
aRAndP->iAvailableRb >= AllResOfP[iPIndex].iNeedRb &&
aRAndP->iAvailableRc >= AllResOfP[iPIndex].iNeedRc) {
iResult = 1;
}
/* end ************************************************************** */
//如果不满足要求会返回0值
return iResult;
}
//银行家安全算法递归算法,得到所有安全序列,
void BankerSecurity(ResAndProcess* curRAndP)
{
int i = 0, j = 0;
int iDxDontInQue = 1;
int iNewPOrder = 0;
ResAndProcess* tmpP;
//请在begin end语句间补判断语句
//判断逻辑是当前生成的进程序列已达到满序列,表示全部进程可以执行完成
/* begin *******************程序代码一行******************************* */
if (curRAndP -> iPCount == 5)
/* end ************************************************************** */
{
//将当前安全序列添加到链表中
//创建一个安全序列的节点指针
//将一个安全的进程序列拷贝到添加到链表中添加到链表中
//注意不同平台的int字节数不同,要使用sizeof(int)确定长度
//因curRAndP节点会被回收,所以要将值拷贝到oneNode节点
//将当前安全序列添加到链表头。
//请根据上述提示补上相应功能的代码
/* begin *******************程序代码四行******************************* */
POrderNode* oneNode;
oneNode = (POrderNode*)malloc(sizeof(POrderNode));
for (int i = 0; i < 5; i++) {
oneNode->iPOrder[i] = curRAndP->iPOrder[i];
}
oneNode->next = HeadOfPOrder;
HeadOfPOrder = oneNode;
/* end ************************************************************** */
//递归到此结束,开始回退
return;
}
else {
//取下面可能的进程编号继续下层递归
for (i = 0; i < 5; i++)
{
iDxDontInQue = 1;
//检查当前进程序号是否在队列中
for (j = 0; j < curRAndP->iPCount; j++)
{
if (i == curRAndP->iPOrder[j])
{//找到下标则表示i在当前队列中
iDxDontInQue = 0;
break;
}
}
//请编写逻辑,实现递归生成安全序列的代码,注意内存的申请和回收
/* begin *******************程序代码约十二行******************************* */
if (iDxDontInQue == 1 && CheckResource(curRAndP, i)) {
ResAndProcess* tmpP = (ResAndProcess*)malloc(sizeof(ResAndProcess));
for (int k = 0; k < 5; k++) {
tmpP->iPOrder[k] = curRAndP->iPOrder[k];
}
tmpP->iAvailableRa = curRAndP->iAvailableRa - AllResOfP[i].iNeedRa + AllResOfP[i].iMaxRa;
tmpP->iAvailableRb = curRAndP->iAvailableRb - AllResOfP[i].iNeedRb + AllResOfP[i].iMaxRb;
tmpP->iAvailableRc = curRAndP->iAvailableRc - AllResOfP[i].iNeedRc + AllResOfP[i].iMaxRc;
tmpP->iPCount = curRAndP->iPCount + 1;
tmpP->iPOrder[tmpP->iPCount - 1] = i;
BankerSecurity(tmpP);
free(tmpP);
}
/* end ************************************************************** */
}
}
}
int main(void)
{
int iSqcount = 0;
//初始化数据,安全序列链表初始为空
HeadOfPOrder = NULL;
//读入文件数据
if (ReadBankData())
{
ResAndProcess* initResAndPro = (ResAndProcess*)malloc(sizeof(ResAndProcess));
//请在begin end语句间补全程序语句实现对initResAndPro赋初值操作
/* begin *******************程序代码约四行******************************* */
initResAndPro->iAvailableRa = StartResAndP.iAvailableRa;
initResAndPro->iAvailableRb = StartResAndP.iAvailableRb;
initResAndPro->iAvailableRc = StartResAndP.iAvailableRc;
initResAndPro->iPCount = 0;
/* end ************************************************************** */
//启动银行家算法
BankerSecurity(initResAndPro);
//得到安全序列的个数(或者输出全部安全序列)
iSqcount = getSecurePCount();
if (iSqcount > 0)
{
printf("%d", iSqcount);
}
else {
printf("未找到安全序列。");
}
}
else {
//文件数据读取失败则输出0
printf("banker.dat文件读取失败。");
};
return 0;
}