銀行家算法所避免的死鎖問題並不是我們常說的mutex鎖問題,而是爲了讓系統處於安全狀態,因爲安全狀態是一定不會出現死鎖的。
1.系統安全狀態
安全狀態指的是系統能按某種進程推進順序(P1,P2,…,Pn)爲每個進程分配其所需的資源,直至滿足每個進程對資源的最大需求,使每個進程都可順利地完成。此時,序列(P1,P2,…,Pn)爲安全序列。如果系統無法找到這樣一個序列,則稱系統處於不安全狀態。
雖然並非所有的不安全狀態都必然會轉爲死鎖狀態,但當系統進入不安全狀態,就有可能進入死鎖。反之,只要系統處於安全狀態,就不會進入死鎖狀態。因此,避免死鎖的實質在於,系統在進行資源分配時,應使系統不進入安全狀態。
所以,只要存在一種分配序列時,系統就是安全的。
2.銀行家算法描述
2.1算法數據結構
該算法需要維護多個數組,別看下面字多,內容很簡單。因爲一個進程運行需要的資源不止一種(例如內存、CPU等等),這裏有m種,一共有n個進程。
(1)Avaliable
可利用資源向量,是一個含有m個元素的數組,其中每一個元素代表一類可利用的資源數目,初始值是系統中所分配的該類全部可用資源的數目,其數值隨該類資源的分配和回收而改變。
(2)Max
最大需求矩陣,是一個n*m的矩陣,定義了系統中n個進程中的每一個進程對m類資源的最大需求。
(3)Allocation
分配矩陣,是一個n*m的矩陣,定義了系統中每一類資源當前分配給每一進程的資源數。
(4)Need
需求矩陣,是一個n*m的矩陣,表示每一個進程尚需的各類資源數。
有以下關係:
2.2 算法內容
設 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[j] = Available[j] - Requesti[j];
Allocation[i,j] = Allocation[i,j] + Requesti[j];
Need[i,j] = Need[i,j] - Requesti[j];
(4) 系統執行安全性算法,檢查此次資源分配後系統是否處於安全狀態。若安全,才正式將資源分配給進程Pi,以完成本次分配;否則,將本次的試探分配作廢,恢復原來的資源分配狀態,讓進程Pi等待。
《計算機操作系統_銀行家算法》
2.3 安全性算法
本部分爲僞代碼
系統所執行的安全性算法可描述如下:
(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[j] = Work[j] + Allocation[i,j];
Finish[i] = true;
go to step 2;(goto語句不推薦使用 _ )
(4)如果所有進程的 Finish[i] =true都滿足,則表示系統處於安全狀態;否則,系統處於不安全狀態。
《計算機操作系統_銀行家算法》
2.4 實例
因爲不實用,我就不自己寫代碼了,完了看還沒看懂,完蛋玩意。下面這個哥們特屌,看看他的把。我覺得這塊也不用看,如果面試問就是了解過,具體沒寫過,這就是這樣嘛。
本程序的難點在於安全性算法,對於一個安全的系統來說,此步驟較爲容易,難在於判斷不安全的系統。爲什麼這麼說呢?由於本程序再設計尋找安全序列的部分使用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");
}
}
}
}
3.銀行家算法是否實用
首先,我們寫的時候mutex鎖問題肯定是不太實用的,那操作系統實用這種方式來分配資源嗎?我覺得不太合適(不缺的,只是結合網上的資料我覺得)原因有如下幾條:
- 這個算法要求客戶數保持固定不變,這在多道程序系統中是難以做到的。
- 這個算法保證所有客戶在有限的時間內得到滿足,但實時客戶要求快速響應,所以要考慮這個因素。
- 由於要尋找一個安全序列,實際上增加了系統的開銷
除此之外我還看到了一篇網友對此討論的文章,說的是操作系統內核使用的是這種方式嗎?雖然都是一堆沒看過源碼的人說的,但是結論也是不實用《在實際的OS中,資源分配是用"銀行家算法"嗎》
99.參考的文章
https://blog.csdn.net/ZWE7616175/article/details/80201520