【c/c++】銀行家演算法
1 需求分析
1.1 銀行家演算法的實現思想
允許程序動態地申請資源,系統在每次實施資源分配之前,先計算資源分配的安全性,若此次資源分配安全(即資源分配後,系統能按某種順序來為每個程序分配其所需的資源,直至最大需求,使每個程序都可以順利地完成),便將資源分配給程序,否則不分配資源,讓程序等待。
1.2 死鎖的概念
死鎖是指兩個或兩個以上的程序在執行過程中,由於競爭資源或者由於彼此通訊而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的程序稱為死鎖程序。
銀行家演算法是避免死鎖的一種重要方法。 作業系統按照銀行家制定的規則為程序分配資源,當程序首次申請資源時,要測試該程序對資源的最大需求量,如果系統現存的資源可以滿足它的最大需求量則按當前的申請量分配資源,否則就推遲分配。當程序在執行中繼續申請資源時,先測試該程序已佔用的資源數與本次申請的資源數之和是否超過了該程序對資源的最大需求量。若超過則拒絕分配資源,若沒有超過則再測試系統現存的資源能否滿足該程序尚需的最大資源量,若能滿足則按當前的申請量分配資源,否則也要推遲分配。
1.3 產生死鎖的必要條件
① 互斥條件:指程序對所分配到的資源進行排它性使用,即在一段時間內某資源只由一個程序佔用。如果此時還有其它程序請求資源,則請求者只能等待,直至佔有資源的程序用畢釋放。
② 請求和保持條件:指程序已經保持至少一個資源,但又提出了新的資源請求,而該資源已被其它程序佔有,此時請求程序阻塞,但又對自己已獲得的其它資源保持不放。
③ 不剝奪條件:指程序已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時由自己釋放。
④ 環路等待條件:指在發生死鎖時,必然存在一個程序——資源的環形鏈,即程序集合{P0,P1,P2,···,Pn}中的P0正在等待一個P1佔用的資源;P1正在等待P2佔用的資源,……,Pn正在等待已被P0佔用的資源。
1.4功能實現
理解了死鎖的原因,尤其是產生死鎖的四個必要條件,就可以最大可能地避免、預防和解除死鎖。所以,在系統設計、程序排程等方面注意如何能夠不讓這四個必要條件成立,如何確定資源的合理分配演算法,避免程序永久佔據系統資源。此外,也要防止程序在處於等待狀態的情況下佔用資源,在系統執行過程中,對程序發出的每一個系統能夠滿足的資源申請進行動態檢查,並根據檢查結果決定是否分配資源,若分配後系統可能發生死鎖,則不予分配,否則予以分配 。因此,對資源的分配要給予合理的規劃。
2 概要設計
2.1資料結構
1) 可利用資源向量Available。這是一個含有m個元素的陣列,其中的而每一個元素代表一類可利用資源數目,其初始值是系統中所配置的該類全部可用資源的數目,其數值隨該類資源的分配和回收而動態的改變。如果Available[j]=K,則表示系統中現有Rj類資源K個。
2) 最大需求矩陣Max。這是一個n*m的矩陣,它定義了系統中n個程序中的每一個程序對m類資源的最大需求。如果Max[i,j]=K;則表示程序i需要Rj類資源的最大數目為K。
3) 分配矩陣Allocation。這也是一個n*m的矩陣,它定義了系統中每一類資源當前已分配給每一程序的資源數。如果Allocation[i,j]=K,則表示程序i當前已分得Rj類資源的數目為K。
4) 需求矩陣Need。這也是一個n*m的矩陣,用以表示每一個程序尚需的各類資源數。如果Need[i,j]=K,則表示程序i還需要Rj類資源K個,方能完成任務。
上述三個矩陣間存在下述關係:Need[i,j]=Max[i,j]-Allocation[i,j]
2.2 設計思路
第一部分:銀行家演算法模組
1.如果Request<=Need,則轉向2;否則,出錯
2.如果Request<=Available,則轉向3,否則等待
3.系統試探分配請求的資源給程序
4.系統執行安全性演算法
第二部分:安全性演算法模組
1. 設定兩個向量
① 工作向量:Work=Available(表示系統可提供給程序繼續執行所需要的各類資源數目)
② Finish:表示系統是否有足夠資源分配給程序(True:有;False:沒有).初始化為False
2. 若Finish[i]=False&&Need<=Work,則執行3;否則執行4(i為資源類別)
3. 程序P獲得第i類資源,則順利執行直至完成,並釋放資源: Work=Work+Allocation; Finish[i]=true;轉2
4. 若所有程序的Finish[i]=true,則表示系統安全;否則,不安全!
3 詳細設計
在系統執行過程中,對程序發出的每一個系統能夠滿足的資源申請進行動態檢查,並根據檢查結果決定是否分配資源,若分配後系統可能發生死鎖,則不予分配,否則予以分配 。因此,對資源的分配要給予合理的規劃。
3.1銀行家演算法
設Request i是程序Pi的申請向量,如果Request i[j]=K,則表示程序Pi需要K個Rj型別的資源。當Pi發出資源請求後,系統按下述步驟進行檢查:
1) 如果Request i[j]<=Need[i,j],便轉向步驟2);否則認為出錯,因為它所需要的資源數已經超過它所宣佈的最大值。
2) 如果Request i[j]<=Available[i,j],便轉向步驟3);否則,表示尚無足夠資源,Pi需等待。
3) 系統試探著把資源分配給程序Pi,並修改下面資料結構中的數值:
Available[j]:=Available[j]-Request i[j];
Allocation[i,j]:=Allocation[i,j]+Request i[j];
Need[i,j]:=Need[i,j]-Request i[j];
4) 系統執行安全性演算法,檢查此次資源分配後系統是否處於安全狀態。若安全,才正式將資源分配給程序Pi,以完成本次分配;否則,將本次的試探分配作廢,恢復原來的資源分配狀態,讓程序Pi等待。
3.2安全性演算法
系統所執行的安全性演算法可描述如下:
1) 設定兩個向量
① 工作向量Work,它表示系統可提供給程序繼續執行所需的各類資源數目,它含有m個元素,在執行安全演算法開始時,Work:=Available。
② Finish,它表示系統是否有足夠的資源分配給程序,使之執行完成。開始時先做Finish[i]:=false;當有足夠資源分配給程序時,再令Finish[i]:=ture.
2) 從程序集合中找到一個滿足下述條件的程序:
① Finish[i]=false;
② Need[i,j]<=Work[j];若找不到,執行步驟3),否則,執行步驟4)。
3) 當程序Pi獲得資源後,可順利執行,直至完成,並釋放出分配給它的資源,故應執行:
Work[j]:=Work[j]+Allocation[i,j];
Finish[i]:=true;
Go to step 2;
4) 如果所有程序的Finish[i]=true都滿足,則表示系統處於安全狀態;否則,系統處於不安全狀態。
3.3流程圖
4 測試
4.1結果截圖
首先進行初始化:
T0時刻進行安全性檢測,得到安全序列2->4->5->1->3
T0時刻的資源情況分配圖:
P2請求資源,並得到安全序列2->4->5->1->3
P5請求資源:
P1請求資源:
P2請求資源
4.2結果分析
1. T0時刻,利用安全性演算法對T0時刻的資源分配情況進行分析,可知,在T0時刻存在著一個安全序列{P[2],P[4],P[5],P[1],P[3]},故系統是安全的。
2. P2請求資源:P2發出請求向量Request2(1,0,2),系統按銀行家演算法進行檢查:
① Request 2(1,0,2)<=Need 2(1,2,2,)
② Request 2(1,0,2)<=Available 2(3,3,2)
系統先假定可為P2分配資源,並修改Available,Allocation2和Need2向量,再利用安全性演算法檢查此時系統是否安全;找到一個安全序列{P[2],P[4],P[5],P[1],P[3]},因此,系統是安全的,可立即將P2所申請的資源分配給它。
3. P5請求資源:P5發出請求向量Request5(3,3,0),系統按照銀行家演算法進行檢查:
① Request 5(3,3,0)<=Need 5(4,3,1)
② Request 5(3,3,0)<=Available 5(2,3,0),讓P5等待
4. P1請求資源:P1發出請求向量Request1(0,2,0),系統按照銀行家演算法進行檢查:
① Request 1(0,2,0)<=Need 2(7,4,3)
② Request 1(0,2,0)<=Available 2(2,3,0)
系統先假定可為P0分配資源,並修改資料,進行安全性檢查:
可用資源Available(2,1,0)已不能滿足任何程序需求,故系統進入不安全狀態,此時系統不分配資源。
5. P2請求資源:P2發出請求向量Request2(0,2,0),系統按銀行家演算法進行檢查,可以檢測到安全序列。此時程序P2執行完畢,回收P2的已分配資源。
#include<stdio.h>
#include<stdlib.h>
int Available[10]; //可以申請資源數量
int Max[10][10]; //最大需求矩陣
int Allocation[10][10] = { 0 }; //分配矩陣
int Need[10][10] = { 0 }; //需求矩陣
int Work[10]; //工作數量
int Finish[10]; //是否有足夠的資源分配,狀態標誌
int Request[10][10]; //程序申請資源向量
int Pause[10]; //Pause[i]只是一個暫時寄存的中間變數,為防止在下面安全性檢查時修改到Available[i]而代替的一維陣列
int arr[] = { 0 }; //各類資源總數
int List[10];
int i, j;
int n; //系統資源總數
int m; //總的程序數
int a; //當前申請的程序號
int l, e; //計數器
int b = 0, c = 0, f = 0, g; //計數器
int z = 0;
int securitycheck() //安全性檢測
{
printf("\n\n");
printf("\t\t\t※ 安全性檢測 ※\n\n");
if (n == 3)
{
printf(" 工作向量 尚需求量 已分配 工作向量+已分配\n程序 ");
for (c = 1; c <= 4; c++)
{
for (j = 1; j <= n; j++)
{
printf(" %d類", j);
}
}
}
if (n == 2)
{
printf(" 工作向量 尚需求量 已分配 工作向量+已分配\n程序 ");
for (c = 1; c <= 4; c++)
{
for (j = 1; j <= n; j++)
{
printf(" %d類", j);
}
}
}
for (i = 1; i <= m; i++)
{
Pause[i] = Available[i]; //Pause[i]只是一個暫時寄存的中間變數,為防止在下面安全性檢查時修改到Available[i]而代替的一維陣列
Finish[i] = false;
}
//如果剛好是1 2 3 4 5的狀態迴圈一次就夠了 也就不需要g這個迴圈 最差的情況就是5 4 3 2 1 每次只能確定一個
for (g = 1; g <= m; g++)
{
for (i = 1; i <= m; i++)
{
b = 0; //計數器初始化
Finish[i] == false;
for (j = 1; j <= n; j++)
{
if (Need[i][j] <= Pause[j])
{
b = b + 1;
}
if (Finish[i] == false && b == n)
{
Finish[i] = true;
printf("\nP[%d] ", i); //依次輸出程序安全序列
for (l = 1; l <= n; l++)
{
printf(" %2d ", Pause[l]);
}
for (j = 1; j <= n; j++)
{
printf(" %2d ", Need[i][j]);
}
for (j = 1; j <= n; j++)
{
//Allocation[i][j]=Pause[j]-Need[i][j];
printf(" %2d ", Allocation[i][j]);
}
for (j = 1; j <= n; j++)
{
printf(" %2d ", Pause[j] + Allocation[i][j]);
}
for (l = 1; l <= n; l++)
{
Pause[l] = Pause[l] + Allocation[i][l];
}
}
}
}
}
printf("\n\n");
for (i = 1; i <= m; i++)
{
if (Finish[i] == true)
f = f + 1; //統計Finish[i]==true的個數
}
if (f == m)
{
printf("safe state");
printf("\n\n系統剩餘資源量: ");
for (i = 1; i <= n; i++)
{
printf(" %d ", Available[i]);
}
f = 0; //將計數器f重新初始化,為下一次提出新的程序申請做準備
return 1;
}
else
{
printf("unsafe state ");//不安全序列需要退回之前的狀態
for (i = 1; i <= n; i++)
{
Available[i] = Available[i] + Request[a][i];
Allocation[a][i] = Allocation[a][i] - Request[a][i];
Need[a][i] = Need[a][i] + Request[a][i];
}
return 0;
}
}
//初始化
void initialize()
{
printf("請輸入系統的資源種類數:");
scanf("%d", &n);
for (i = 1; i <= n; i++)
{
printf("第%d類資源總數:", i);
scanf("%d", &arr[i]);
}
printf("請輸入程序總數:");
scanf("%d", &m);
for (i = 1; i <= m; i++)
{
for (j = 1; j <= n; j++)
{
printf("程序P[%d]對第%d類資源的最大需求量:", i, j);
scanf("%d", &Max[i][j]);
}
}
for (i = 1; i <= m; i++)
{
for (j = 1; j <= n; j++)
{
printf("程序P[%d]對第%d類資源已分配數:", i, j);
scanf("%d", &Allocation[i][j]);
Need[i][j] = Max[i][j] - Allocation[i][j];
}
}
//計算每一類資源剩下的資源數目 某一類資源原有的數量減去每一個程序對該類資源的請求數量之和
for (i = 1; i <= n; i++)
{
for (j = 1; j <= m; j++)
{
arr[i] -= Allocation[j][i];
}
}
//更新每類資源現有的數目
for (i = 1; i <= n; i++)
Available[i] = arr[i];
//安全性檢查
securitycheck();
}
//程序申請資源
void mainrequest()
{
printf("請輸入申請資源的程序:");
scanf("%d", &a);
for (i = 1; i <= n; i++)
{
printf("請輸入程序P[%d]對%d類資源的申請量:", a, i);
scanf("%d", &Request[a][i]);
if (Request[a][i] > Need[a][i])
{
printf("\n出錯!程序申請的資源數多於它自己申報的最大需求量\n");
return;
}
if (Request[a][i] > Available[i])
{
printf("\nP[%d]請求的資源數大於可用資源數,必須等待\n", a);
return;
}
}
//以下是試探性分配
for (i = 1; i <= n; i++)
{
Available[i] = Available[i] - Request[a][i]; //剩下資源數目
Allocation[a][i] = Allocation[a][i] + Request[a][i]; //已經申請數目
Need[a][i] = Need[a][i] - Request[a][i];//還需要資源數目
}
int ret=securitycheck();
//如果是安全性序列 就進行分配
if (ret == 1)
{
int key = 0;
for (j = 1; j <= n; j++)
{
if (Need[a][j] == 0)
{
key++;
}
}
if (key == n) //如果程序所有需要資源為0 也就說明其已經完成了工作 可以將資源全部回收
{
for (j = 1; j <= n; j++)
{
Available[j] += Allocation[a][j]; //將程序a佔有的資源全部還給系統
Allocation[a][j] = 0;
}
}
}
}
void mainshow()
{
printf("\n\n");
if (n == 3)
{
printf(" 已分配 最大需求量 尚需要量 \n程序");
}
if (n == 2)
{
printf(" 已分配 最大需求 尚需要量 \n程序");
}
for (i = 1; i <= 3; i++)
{
for (j = 1; j <= n; j++)
{
printf(" %d類", j);
}
}
for (i = 1; i <= m; i++)
{
printf("\nP[%d]", i);
for (j = 1; j <= n; j++)
{
printf(" %2d ", Allocation[i][j]);
}
for (j = 1; j <= n; j++)
{
printf(" %2d ", Max[i][j]);
}
for (j = 1; j <= n; j++)
{
printf(" %2d ", Need[i][j]);
}
}
printf("\n\n系統剩餘資源量: ");
for (i = 1; i <= n; i++)
{
printf(" %d ", Available[i]);
}
printf("\n");
}
void menu()
{
printf("\n\n\t\t卐卍※§ 銀行家演算法 §※卐卍\n");
printf("\n\n\t\t\t1:初始化");
printf("\n \t\t\t2:程序進行資源申請");
printf("\n \t\t\t3:資源分配狀態");
printf("\n \t\t\t4:退出程式");
printf("\n\n\t\t\t\t\t 請輸入你的選擇: ");
}
int main()
{
int key = 0;
printf("\n\n");
while (1)
{
menu();
scanf("%d", &key);
printf("\n\n");
switch (key)
{
case 1:
initialize();
break;
case 2:
mainrequest();
break;
case 3:
mainshow();
break;
case 4:
printf("\n\n\t\t\t謝謝使用 \n");
printf("\n\t\t\tSee you next time !\n\n\n");
system("pause");
return 0;
}
}
system("pause");
return 0;
}