[Linux]死鎖

死鎖是指多個進程在運行過程中因爭奪資源而造成的一種僵局,當進程處于這種僵持狀態時,若無外力作用,它們都將無法再向前推進。之前信號量的時候我們知道,如果多個進程等待,主要體現在占有鎖的問題上。死鎖也可以被定義為:一組競爭系統資源或互相通信的進程間相互的永久阻塞。當一組進程中的所有進程都在等待一個事件,而只有在進程集合中的其他阻塞的進程才可以觸發該事件,這時就稱一組進程死鎖。因為沒有事件觸發,因此死鎖是永久性的。

產生死鎖的原因有

(1)競爭資源。當系統中供多個進程共享的資源如打印機、公用隊列等,其數目不足以滿足諸進程的需要時,會引起諸進程對資源的競爭而產生死鎖。

(2)進程間推進順序非法。進程在運行過程中,請求和釋放資源的順序不當,也同樣會導致產生進程死鎖。

詳細分析一下產生死鎖的原因哦。

(1)可剝奪和非剝奪性資源

可剝奪性資源指某進程在獲得這類資源后,該資源可以再被其他進程或系統剝奪。如優先級高的可以剝奪優先級低的進程的處理機。非剝奪性資源是當這類資源分配給某進程后,再不能強行收回,只能在進程用完后自行釋放。

(2)競爭非剝奪性資源

由于它們的數量不能滿足諸進程運行的需要,會使進程在運行過程中,因剝奪這些資源而陷入僵局。

比如,系統中有一臺打印機R1和一臺磁帶機R2,供進程P1,P2共享。如果P1占用了打印機,P2占用了磁帶機。此時如果P1想使用磁帶機,而P2想使用打印機,這就使P1和P2都在等待對方的資源被釋放,而陷入僵局。從而也使P1和P2得不到自己的資源而不能釋放現有的資源,最后進入死鎖狀態。

(3)競爭臨時性資源

上述的打印機資源屬于順序性重復使用的資源,屬于永久性資源。還有一種是臨時性資源,是一個進程產生,由另一進程使用短暫時間后無用的資源。也可能產生死鎖。

(4)推進順序不當。P1—>Request(R2),p2—>Request(R1)時產生死鎖。

產生死鎖的必要條件

如果發生死鎖,則有四個產生死鎖的必要條件:

(1)互斥條件。一次只有一個進程使用資源,其他進程不能訪問已分配的資源。

(2)請求和保持條件(也稱占有且等待)。指進程已經保持了至少一個資源,但又提出了新的資源請求,而該資源又已被其他進程占有,此時請求進程阻塞,但又對自己已獲得的其他資源保持不放。

(3)不剝奪條件(非搶占)。指進程已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完自己釋放。

(4)環路等待(循環等待)。指存在一個封閉的進程鏈,使得每個資源至少占有此鏈中下一個進程所需要的一個資源。在發生死鎖時,必然存在一個進程,資源的環形鏈,即P0,P1,P2….Pn,則P0在等待P1占有的資源,P1占有P2的,….Pn占有P0的。

說完死鎖之后,我們應該想想如何處理這種死鎖呢。處理死鎖的基本方法有:預防死鎖,避免死鎖,檢測死鎖,解除死鎖。

預防死鎖是使四個必要條件中的第2,3,4個中的一個不成立來避免不發生死鎖。為預防死鎖,可以使請求和保持條件中,可以要求進程一次性地請求所有需要的資源,并且阻塞這個進程直到所有請求都同時滿足。對于非搶占式的預防死鎖的方法,如果占有某些資源的一個進程進行進一步資源請求被拒絕,則該進程必須釋放它最初占用的資源,如果有必要,可以再次請求此類資源。還有一種情況是,如果一個進程請求當前被另一個進程占有的一個資源,則操作系統可以搶占另一個進程,要求它釋放資源。(這類情況適合具有不同優先級的進程)

死鎖避免

解決死鎖問題的另一種方法是死鎖避免。與死鎖預防的差距也不大。這里有倆種死鎖避免的方法,

1.如果一個進程的請求會導致死鎖,則不啟動此進程
2.如果一個進程增加資源的請求導致死鎖,則不允許此分配

這里解決死鎖問題的方法有:銀行家算法

銀行家算法通過已分配的資源和可用資源數等來獲取安全序列,如果存在安全序列,就可以分配請求資源,不存在則說明不可以請求資源,這樣就可以有效的避免死鎖。

(1)請求資源,發出請求向量
(2)假定可為P1分配資源,修改Available,Allocation和Need向量
(3)利用安全性算法檢查此時系統是否安全

銀行家算法需求分析:

允許進程動態地申請資源,系統在每次實施資源分配之前,先計算資源分配的安全性,若此次資源分配安全(即資源分配后,系統能按某種順序來為每個進程分配其所需的資源,直至最大需求,使每個進程都可以順利地完成),便將資源分配給進程,否則不分配資源,讓進程等待。
功能實現:
理解了死鎖的原因,尤其是產生死鎖的四個必要條件,就可以最大可能地避免、預防和解除死鎖。所以,在系統設計、進程調度等方面注意如何能夠不讓這四個必要條件成立,如何確定資源的合理分配算法,避免進程永久占據系統資源。此外,也要防止進程在處于等待狀態的情況下占用資源,在系統運行過程中,對進程發出的每一個系統能夠滿足的資源申請進行動態檢查,并根據檢查結果決定是否分配資源,若分配后系統可能發生死鎖,則不予分配,否則予以分配 。因此,對資源的分配要給予合理的規劃。
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]
這里寫圖片描述

#define _CRT_SECURE_NO_WARNINGS 1
extern "C"
#include<stdio.h>
#include<stdlib.h>
#define W 10
int Available[W];           //可使用資源
int Max[W][W];             //最大需求資源數
int Allocation[W][W];      //已分配資源
int Need[W][W];            //需求資源
int Work[W];              //工作向量
int Finish[W];           //是否有足夠的資源分配,狀態標志
int Request[W][W];        //進程申請資源向量
int Temp[W];             //暫存可用資源數
int AllReSourceNum[W];        //各類資源總數
int i, j;
int ResourceNum;              //系統資源總數
int ProcessNum;               //總的進程數
int a;                       //當前申請的進程號
int l, e;                     
int b = 0, c = 0, f = 0, g;  //c: 統計對于每一個進程,成功分配的資源類別數
int  SecurityCheck()     //安全性檢測
{printf("\n\n");printf("\t\t\t 安全性檢測 \n\n");printf("          工作向量       尚需求量       已分配      工作向量+已分配\n進程 ");for (c = 1; c <= 4; c++){for (j = 1; j <=ResourceNum; j++){printf("  %d類", j);}}for (i = 1; i <=ProcessNum; i++){Temp[i] = Available[i];    //Temp[i]只是一個暫時寄存的中間變量,為防止在下面安全性檢查時修改到Available[i]而代替的一維數組Finish[i] = false;}for (g = 1; g <=ProcessNum; g++){for (i = 1; i <=ProcessNum; i++){b = 0;                 //計數器初始化Finish[i] == false;for (j = 1; j <=ResourceNum; j++){if (Need[i][j] <= Temp[j]){b = b + 1;}if (Finish[i] == false && b ==ResourceNum){Finish[i] = true;printf("\nP[%d] ", i);        //依次輸出進程安全序列  for (l = 1; l <=ResourceNum; l++){printf("  %2d ", Temp[l]);}for (j = 1; j <=ResourceNum; j++){printf("  %2d ",Need[i][j]);}for (j = 1; j <=ResourceNum; j++){//Allocation[i][j]=Temp[j]-Need[i][j];printf("  %2d ", Allocation[i][j]);}for (j = 1; j <=ResourceNum; j++){printf("  %2d ", Temp[j] + Allocation[i][j]);}for (l = 1; l <=ResourceNum; l++){Temp[l] = Temp[l] + Allocation[i][l];        }}}}}printf("\n\n");for (i = 1; i <=ProcessNum; i++){if (Finish[i] == true) f = f + 1;  //統計Finish[i]==true的個數}if (f ==ProcessNum){printf("安全序列");printf("\n\n系統剩余資源量:");for (i = 1; i <=ResourceNum; i++){printf("   %d ", Available[i]);}f = 0;       //將計數器f重新初始化,為下一次提出新的進程申請做準備return 1;}else{printf("不安全序列");return 0;}
}
void Initialize()    //初始化
{printf("請輸入系統的資源種類數:");scanf("%d", &ResourceNum);for (i = 1; i <=ResourceNum; i++){printf("第%d類資源總數:", i);scanf("%d", &AllReSourceNum[i]);}printf("請輸入進程總數:");scanf("%d", &ProcessNum);for (i = 1; i <=ProcessNum; i++){for (j = 1; j <=ResourceNum; j++){printf("進程P[%d]對第%d類資源的最大需求量:", i, j);scanf("%d", &Max[i][j]);}}for (i = 1; i <=ProcessNum; i++){for (j = 1; j <=ResourceNum; 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 <=ResourceNum; i++){for (j = 1; j <=ProcessNum; j++){AllReSourceNum[i] -= Allocation[j][i];}}for (i = 1; i <=ResourceNum; i++)Available[i] = AllReSourceNum[i];SecurityCheck();
}
void RequestResource()  //進程申請資源
{printf("請輸入申請資源的進程:");scanf("%d", &a);for (i = 1; i <=ResourceNum; 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 <=ResourceNum; 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 <=ResourceNum; j++){if (Need[a][j] == 0){key++;}}if (key ==ResourceNum){for (j = 1; j <=ResourceNum; j++){Available[j] += Allocation[a][j];Allocation[a][j] = 0;}}}
}void ResourceState()
{printf("\n\n");printf("          已分配       最大需求量       尚需要量 \n進程");for (i = 1; i <= 3; i++){for (j = 1; j <=ResourceNum; j++){printf("  %d類", j);}}for (i = 1; i <=ProcessNum; i++){printf("\nP[%d]", i);for (j = 1; j <=ResourceNum; j++){printf("  %2d ", Allocation[i][j]);}for (j = 1; j <=ResourceNum; j++){printf("  %2d ", Max[i][j]);}for (j = 1; j <=ResourceNum; j++){printf("  %2d ",Need[i][j]);}}printf("\n\n系統剩余資源量:   ");for (i = 1; i <=ResourceNum; i++){printf("   %d ", Available[i]);}printf("\n");
}void menu()
{printf("\n\t-------------銀行家算法---------------\n");printf("             1、初始化                \n");printf("             2、進程請求資源          \n");printf("             3、資源分配狀態          \n");printf("             4、退出                  \n");printf("\t--------------------------------------\n");printf("\n請輸入你的選擇: ");
}
//BankerAlth.cpp
#include"BankerAlth.h"
int main()
{int select = 0;printf("\n\n");while (1){menu();scanf("%d", &select);printf("\n\n");switch (select){case 1:Initialize();break;case 2:RequestResource();break;case 3:ResourceState();break;case 4:printf("\nSee you Next time !\n\n");system("pause");return 0;}}system("pause");return 0;
}

運行結果:

這里寫圖片描述

這里寫圖片描述

這里寫圖片描述

這里寫圖片描述

這里寫圖片描述

這里寫圖片描述

這里寫圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/382703.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/382703.shtml
英文地址,請注明出處:http://en.pswp.cn/news/382703.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python安裝第三方模塊總結 轉載的

轉自 https://www.jellythink.com/archives/541

[C++]vector創建二維數組

c.resize(n);將c重置為大小為n個元素向量&#xff0c;如果n比原來的元素多&#xff0c;則多出的元素常被初始化為0//節選《面向對象的程序設計》杜茂青 int N5, M6; vector<vector<int> > Matrix(N); for(int i 0; i< Matrix.size(); i){ Matrix[i].resize(M…

[Linux]線程安全和可重入函數

線程安全&#xff1a;一個函數被稱為線程安全的&#xff0c;當且僅當被多個并發進程反復調用時&#xff0c;它會一直產生正確的結果。如果一個函數不是線程安全的&#xff0c;我們就說它是線程不安全的。 重入&#xff1a;函數被不同的控制流程調用,有可能在第一次調用還沒返回…

[Linux]信號量

信號量是一個計數器&#xff0c;用于為多個進程提供對共享數據對象的訪問。 在信號量上只有三種操作可以進行&#xff0c;初始化、遞增和增加&#xff0c;這三種操作都是原子操作。遞減操作可以用于阻塞一個進程&#xff0c;增加操作用于解除阻塞一個進程。 為了獲得共享資源…

Linux VIM 程序中有游離的‘\357’ ‘\274’錯誤

gcc date.cpp -o date -lstdc date.cpp:18:20: 錯誤&#xff1a;程序中有游離的‘\357’date.Showdata()&#xfffd;&#xfffd;&#xfffd;^ date.cpp:18:21: 錯誤&#xff1a;程序中有游離的‘\274’date.Showdata()&#xfffd;&#xfffd;&#xfffd;^ date.cpp:18:22…

[Linux]關于SIGCHLD

之前我們就學過&#xff0c;關于wait和waitpid來處理僵尸進程&#xff0c;父進程等待子進程結束后自己才退出&#xff0c;這樣的方法有倆種方式&#xff0c;一種是父進程死死的等子進程退出&#xff0c;也就是使用阻塞的方式等待子進程退出&#xff0c;另一種方式是通過非阻塞的…

C語言思維導圖

本人能力有限&#xff0c;知識點難免概括不全&#xff0c;如有錯誤歡迎指正

轉載一篇關于curl的文章

轉載一篇關于curl的文章 http://www.360doc.com/content/16/0107/15/18578054_526158476.shtml

[Linux]vi/vim下添加多行注釋和取消注釋

添加注釋&#xff08;Centos&#xff09;&#xff1a; 在命令行模式下按ctrlV進入 visual block模式&#xff08;可視化模式&#xff09; 選中你需要注釋的行&#xff0c;再按大寫的I&#xff0c;輸入//&#xff0c;最后按倆下esc即可。 如果想讓前進tab個位&#xff0c;則可在…

pthread和互斥量條件變量函數意義速查表

數據類型 pthread_t 線程 互斥量和條件變量

[Linux]共享內存

共享內存是UNIX提供的進程間通信手段中速度最快的一種&#xff0c;也是最快的IPC形式。為什么是最快的呢&#xff0c;因為數據不需要在客戶進程和服務器進程之間復制&#xff0c;所以是最快的一種IPC。這是虛存中由多個進程共享的一個公共內存塊。 兩個不同進程A、B共享內存的…

僵尸進程的產生,危害和解決方案

概念 僵死狀態&#xff08;Zombies&#xff09;是一個比較特殊的狀態。 當進程退出并且父進程沒有讀取到子進程退出的返回代碼時就會產生僵尸進程。僵尸進程會以終止狀態保持在進程表中&#xff0c;并且會一直在等待父進程讀取退出狀態代碼。所以&#xff0c;只要子進程退出&…

CString string 轉換

https://www.cnblogs.com/HappyEDay/p/7016162.html

[Linux]gdb調試多進程多線程例程

gdb相信學linux的同學已經比較熟悉了吧&#xff0c;它是linux下代碼調試工具。我們在寫c語言&#xff0c;c的代碼時經常會用到&#xff0c;它有一些常用的調試命令: run&#xff08;r&#xff09;&#xff1a;運行程序&#xff0c;如果有斷點在下一個斷點處停止 start&#xf…

gdb調試常用命令速查(段錯誤調試)

編譯程序時需要加上-g&#xff0c;之后才能用gdb進行調試&#xff1a;gcc -g main.c -o main gdb中命令&#xff1a; 回車鍵&#xff1a;重復上一命令 &#xff08;gdb&#xff09;help&#xff1a;查看命令幫助&#xff0c;具體命令查詢在gdb中輸入help 命令,簡寫h &…

C語言字符串 小記

#include "stdafx.h" #include <iostream> #include <string.h> using namespace std;int _tmain(int argc, _TCHAR* argv[]) {char str1[] "12345"; // ""括起來的字符串 會在末尾增加 \0 cout << sizeof(str1) << en…

[Linux]守護進程(精靈進程)

一、守護進程是什么 守護進程是生存期很長的一種進程&#xff0c;可以說它是7*24小時工作的。&#xff08;什么是7*24&#xff0c;一周7天&#xff0c;每天24小時&#xff0c;這不就是一年365天一直在工作嘛&#xff0c;還搞的這么詼諧&#xff0c;哈哈&#xff09;。它們常常…

linux命令行界面下ctrl 常用組合鍵速查表

Ctrlz 暫停正在運行的程序 Ctrll 清屏 Ctrld 結束輸入或退出shell Ctrla 切換到命令行開始 Ctrle 切換到命令行末尾 Ctrlu 刪除光標前內容 Ctrlk 刪除光標后內容 Ctrlxu 撤銷操作

[Linux]運輸層的端口

既然提到端口&#xff0c;我們就來分析一下為什么要使用端口的緣由吧。我們首先要知道的是&#xff0c;運輸層有復用和分用的功能。應用層所有的應用進程都可以通過運輸層再傳送到IP層&#xff0c;這就是復用。運輸層從IP層收到數據后必須交付到指明的應用進程&#xff0c;這就…

淺談shell中的clear命令實現

NAME(名稱) clear - 清除終端屏幕 SYNOPSIS(總覽) clear DESCRIPTION(描述) clear可以在允許的情況下清屏. 它會在環境變量中查找終端的類型, 然后到terminfo數據庫中找出清屏的方法. 《man手冊》 #include <stdio.h>int clear_main(int argc, char **argv) {/* Th…