1. 程式人生 > >計算機作業系統_銀行家演算法

計算機作業系統_銀行家演算法

銀行家演算法

什麼是銀行家演算法?
  銀行家演算法(Banker’s Algorithm)是一個避免死鎖(Deadlock)的著名演算法,是由艾茲格·迪傑斯特拉在1965年為T.H.E系統設計的一種避免死鎖產生的演算法。它以銀行借貸系統的分配策略為基礎,判斷並保證系統的安全執行。
  在銀行中,客戶申請貸款的數量是有限的,每個客戶在第一次申請貸款時要宣告完成該專案所需的最大資金量,在滿足所有貸款要求時,客戶應及時歸還。銀行家在客戶申請的貸款數量不超過自己擁有的最大值時,都應儘量滿足客戶的需要。在這樣的描述中,銀行家就好比作業系統,資金就是資源,客戶就相當於要申請資源的程序。
  銀行家演算法是一種最有代表性的避免死鎖的演算法。在避免死鎖方法中允許程序動態地申請資源,但系統在進行資源分配之前,應先計算此次分配資源的安全性,若分配不會導致系統進入不安全狀態,則分配,否則等待。為實現銀行家演算法,系統必須設定若干資料結構。
  
銀行家演算法中的資料結構


  為了實現銀行家演算法,在系統中必須設定這樣四個資料結構,分別用來描述系統中可利用的資源、所有程序對資源的最大需求、系統中的資源分配,以及所有程序還需要多少資源的情況。
  (1) 可利用資源向量 Available。這是一個含有 m 個元素的陣列,其中的每一個元素代表一類可利用的資源數目,其初始值是系統中所配置的該類全部可用資源的數目,其數值隨該類資源的分配和回收而動態地改變。如果 Available[j] = K,則表示系統中現Rj類資源K個。
  (2) 最大需求矩陣Max。這是一個n x m的矩陣,它定義了系統中n個程序中的每個程序對m類資源的最大需求。如果Max[i,j] = K,則表示程序i需要Rj 類資源的最大數目為K。
  (3) 分配矩陣 Allocation。這也是一個n x m的矩陣,它定義了系統中每一類資源當前已分配給每一程序的資源數。如果 Allocation[i,jl = K,則表示程序i當前己分得Rj類資源的數目為K。
  (4) 需求矩陣Need.這也是一個n×m的矩陣,用以表示每一個程序尚需的各類資源數。如果Need[i,j] = K,則表示程序i還需要Rj類資源K個方能完成其任務。
上述三個矩陣間存在下述關係:
              Need[i,j] = Max[i,j] - allocation[i, j]
              
銀行家演算法詳述:

  設 Request;是程序Pi的請求向量,如果 Requesti[j] = K,表示程序Pi需要K個Rj型別的資源。當Pi發出資源請求後,系統按下述步驟進行檢査:
  (1) 如果 Requesti[j] ≤ Need[i,j]便轉向步驟(2);否則認為出錯,因為它所需要的資源數已超過它所宣佈的最大值。
  (2) 如果 Requesti[j] ≤ Available[j],便轉向步驟(3);否則,表示尚無足夠資源,Pi須等待。
  (3) 系統試探著把資源分配給程序Pi,並修改下面資料結構中的數值
    Available[i] = Available[j] - Requesti[j];
    Allocation[i,j] = Allocation[i,j] + Requesti[j];
    Need[i,j] = Need[i,j] - Requesti[j];
  (4) 系統執行安全性演算法,檢查此次資源分配後系統是否處於安全狀態。若安全,才正式將資源分配給程序Pi,以完成本次分配;否則,將本次的試探分配作廢,恢復原來的資源分配狀態,讓程序Pi等待。
  
安全性演算法:

系統所執行的安全性演算法可描述如下:
  (1) 設定兩個向量:①工作向量Work,它表示系統可提供給程序繼續執行所需的各類資源數目,它含有m個元素,在執行安全演算法開始時,Work = Available;② Finish:它表示系統是否有足夠的資源分配給程序,使之執行完成。開始時先做 Finish[i] = false;當有足夠資源分配給程序時,再令Finish[i] = true。
  (2) 從程序集合中找到一個能滿足下述條件的程序
    ① Finish[i] = false;
    ② Need[i,j] ≤ Work[j];
若找到,執行步驟(3),否則,執行步驟(4)。
  (3)當程序Pi獲得資源後,可順利執行,直至完成,並釋放出分配給它的資源,故應執行:
    Work[i] = Work[j] + Allocation[i,j];
    Finish[i] = true;
    go to step 2;(goto語句不推薦使用 _ )
  (4)如果所有程序的 Finish[i] =true都滿足,則表示系統處於安全狀態;否則,系統處於不安全狀態。
  
難點透析:
  本程式的難點在於安全性演算法,對於一個安全的系統來說,此步驟較為容易,難在於判斷不安全的系統。為什麼這麼說呢?由於本程式再設計尋找安全序列的部分使用while迴圈,就需要找到分別處理安全系統與不安全系統的終止迴圈條件,對於安全的系統,滿足條件 Finish[i] = false 和 Need[i,j] ≤ Work[j] 的,必定也會按照預期的將 Finish[i] 向量全部置為true,那是不是就可以設定一個變數來累加計數,當該變數與程序數量相等的時候,就說明已經全部置為true了,終止迴圈,也就是說系統安全。
  對於不安全的系統,上述方法肯定是不行的,因為不可能將向量 Finish[i] 都置為 true ,必定存在 false。就得尋求一個跳出迴圈的條件,但是由於需要不斷迴圈查詢並嘗試分配,尋求一個安全序列,到底該怎麼算是已經找不到安全路徑了呢?下面說本程式的解決辦法,首先需要想到的是,當我們尋找一輪都沒有找到一個可以安全執行的程序,是不是就說明往後也找不到了呢?沒錯,就是這樣的!所以我們每次在記錄 Finish[i] = true 的次數的時候,不妨把這個次數再使用另一個變數存放起來,然後在判斷語句當中判斷當尋找一輪下來,該值未發生改變,說明已經找不到安全的程序了,即可跳出迴圈,該系統不安全!
圖示:
在這裡插入圖片描述

部分效果圖:
在這裡插入圖片描述
在這裡插入圖片描述
完整程式碼:

#include<stdio.h>
#define resourceNum 3
#define processNum  5

//系統可用(剩餘)資源
int available[resourceNum]={3,3,2};
//程序的最大需求
int maxRequest[processNum][resourceNum]={{7,5,3},{3,2,2},{9,0,2},{2,2,2},{4,3,3}};
//程序已經佔有(分配)資源
int allocation[processNum][resourceNum]={{0,1,0},{2,0,0},{3,0,2},{2,1,1},{0,0,2}};
//程序還需要資源
int need[processNum][resourceNum]={{7,4,3},{1,2,2},{6,0,0},{0,1,1},{4,3,1}};
//是否安全
bool Finish[processNum];
//安全序列號
int safeSeries[processNum]={0,0,0,0,0};
//程序請求資源量
int request[resourceNum];
//資源數量計數
int num;

//列印輸出系統資訊
void showInfo()
{
	printf("\n------------------------------------------------------------------------------------\n");  
	printf("當前系統各類資源剩餘:");
    for(int j = 0; j < resourceNum; j++)
	{
        printf("%d ",available[j]);
    }
    printf("\n\n當前系統資源情況:\n");
    printf(" PID\t Max\t\tAllocation\t Need\n");
    for(int i = 0; i < processNum; i++)
	{
        printf(" P%d\t",i);
        for(int j = 0; j < resourceNum; j++)
		{
            printf("%2d",maxRequest[i][j]);
        }
        printf("\t\t");
        for(j = 0; j < resourceNum; j++)
		{
            printf("%2d",allocation[i][j]);
        }
        printf("\t\t");
        for(j = 0; j < resourceNum; j++)
		{
            printf("%2d",need[i][j]);
        }
        printf("\n");
    }
}

//列印安全檢查資訊
void SafeInfo(int *work, int i)
{
    int j;
    printf(" P%d\t",i);
    for(j = 0; j < resourceNum; j++)
	{
        printf("%2d",work[j]);
    }   
    printf("\t\t");
    for(j = 0; j < resourceNum; j++)
	{
        printf("%2d",allocation[i][j]);
    }
	printf("\t\t");
    for(j = 0; j < resourceNum; j++)
	{
        printf("%2d",need[i][j]);
    }
    printf("\t\t");
    for(j = 0; j < resourceNum; j++)
	{
        printf("%2d",allocation[i][j]+work[j]);
    }
    printf("\n");
}

//判斷一個程序的資源是否全為零
bool isAllZero(int kang)
{
	num = 0;
	for(int i = 0; i < resourceNum; i++ )
	{
		if(need[kang][i] == 0)
		{
			num ++;
		}
	}
	if(num == resourceNum)
	{
		return true;
	}
	else
	{
		return false;
	}   
}

//安全檢查
bool isSafe()
{
	//int resourceNumFinish = 0;
	int safeIndex = 0;
	int allFinish = 0;
    int work[resourceNum] = {0};
	int r = 0;
	int temp = 0;
	int pNum = 0;
	//預分配為了保護available[]
    for(int i = 0; i < resourceNum; i++)
	{		
        work[i] = available[i];	
    }
	//把未完成程序置為false
    for(i = 0; i < processNum; i++)
	{
		bool result = isAllZero(i);
		if(result == true)
		{
			Finish[i] = true;
			allFinish++;
		}
		else
		{
			Finish[i] = false;
		}

    }
	//預分配開始
    while(allFinish != processNum)
	{
		num = 0;	
        for(i = 0; i < resourceNum; i++)
		{
			if(need[r][i] <= work[i] && Finish[r] == false)
			{
				num ++;
			}			
		}
		if(num == resourceNum)
		{		
			for(i = 0; i < resourceNum; i++ )
			{
				work[i] = work[i] + allocation[r][i];
			}
			allFinish ++;
			SafeInfo(work,r);
			safeSeries[safeIndex] = r;
			safeIndex ++;
			Finish[r] = true;
		}
		r ++;//該式必須在此處	
		if(r >= processNum)
		{
			r = r % processNum;
			if(temp == allFinish)
			{
				break;	
			}
			temp = allFinish;
		}		
		pNum = allFinish;
    }	
	//判斷系統是否安全
	for(i = 0; i < processNum; i++)
	{
		if(Finish[i] == false)
		{
			printf("\n當前系統不安全!\n\n");
			return false;	
		}
	}
	//列印安全序列
	printf("\n當前系統安全!\n\n安全序列為:");
	for(i = 0; i < processNum; i++)
	{	
		bool result = isAllZero(i);
		if(result == true)
		{		
			pNum --;
		}	
    }
	for(i = 0; i < pNum; i++)
	{
		printf("%d ",safeSeries[i]);
	}
    return true;
}

//主函式
void main()
{
    int curProcess = 0;
	int a = -1;
       showInfo(); 
	printf("\n系統安全情況分析\n");
	printf(" PID\t Work\t\tAllocation\t Need\t\tWork+Allocation\n");
	bool isStart = isSafe();
	//使用者輸入或者預設系統資源分配合理才能繼續進行程序分配工作
    while(isStart)
	{
		//限制使用者輸入,以防使用者輸入大於程序數量的數字,以及輸入其他字元(亂輸是不允許的)
      	do
		{ 
			if(curProcess >= processNum || a == 0)
			{
				printf("\n請不要輸入超出程序數量的值或者其他字元:\n");
				while(getchar() != '\n'){};//清空緩衝區	
				a = -1;
			}
			printf("\n------------------------------------------------------------------------------------\n");
			printf("\n輸入要分配的程序:");
			a = scanf("%d",&curProcess);
			printf("\n");

		}while(curProcess >= processNum || a == 0);
		
		//限制使用者輸入,此處只接受數字,以防使用者輸入其他字元(亂輸是不允許的)
		for(int i = 0; i < resourceNum; i++)
		{
			do
			{
				if(a == 0)
				{
					printf("\n請不要輸入除數字以外的其他字元,請重新輸入:\n");
					while(getchar() != '\n'){};//清空緩衝區	
					a = -1;
				}
				printf("請輸入要分配給程序 P%d 的第 %d 類資源:",curProcess,i+1);
				a = scanf("%d", &request[i]);
			}while( a == 0);
		}

		//判斷使用者輸入的分配是否合理,如果合理,開始進行預分配
		num  = 0;
        for(i = 0; i < resourceNum; i++)
		{
            if(request[i] <= need[curProcess][i] && request[i] <= available[i])
			{
				num ++;
			}
            else
			{
				printf("\n發生錯誤!可能原因如下:\n(1)您請求分配的資源可能大於該程序的某些資源的最大需要!\n(2)系統所剩的資源已經不足了!\n");
				break;
			}
        }
        if(num == resourceNum)
		{	
			num = 0;	
            for(int j = 0; j < resourceNum; j++)
			{
				//分配資源
                available[j] = available[j] - request[j];
                allocation[curProcess][j] = allocation[curProcess][j] + request[j];
                need[curProcess][j] = need[curProcess][j] - request[j];
				//記錄分配以後,是否該程序需要值為0了
				if(need[curProcess][j] == 0)
				{
					num ++;
				}
            }
			//如果分配以後出現該程序對所有資源的需求為0了,即刻釋放該程序佔用資源(視為完成)
			if(num == resourceNum)
			{
				//釋放已完成資源
				for(int i = 0; i < resourceNum; i++ )
				{
					available[i] = available[i] + allocation[curProcess][i];
				}
				printf("\n\n本次分配程序 P%d 完成,該程序佔用資源全部釋放完畢!\n",curProcess);
			}
			else
			{
				//資源分配可以不用一次性滿足程序需求
				printf("\n\n本次分配程序 P%d 未完成!\n",curProcess);
			}

			showInfo();
           	printf("\n系統安全情況分析\n");
			printf(" PID\t Work\t\tAllocation\t Need\t\tWork+Allocation\n");

			//預分配完成以後,判斷該系統是否安全,若安全,則可繼續進行分配,若不安全,將已經分配的資源換回來
            if(!isSafe())
			{ 	        
				for(int j = 0; j < resourceNum; j++)
				{
					available[j] = available[j] + request[j];
					allocation[curProcess][j] = allocation[curProcess][j] - request[j];
					need[curProcess][j] = need[curProcess][j] +request[j];
				}
				printf("資源不足,等待中...\n\n分配失敗!\n");				
            }
        }
    }
}

參考文獻:計算機作業系統/湯小丹等編著.-4版.-西安:西安電子科技大學出版社,2014.5(2016.4重印)

如有錯誤,歡迎指正!