進程和程序:
進程:是個動態的概念,指的是一個靜態的程序對某個數據集的一次運行活動,而程序是靜態的概念,是由代碼和數據組成的程序塊而已。
進程5大特點:動態性,並發性,獨立運行性,異步性,和結構化的特性。
在多道程序環境下,程序不能獨立運行,操作系統所有的特征都是基於進程而體現的,只有進程可以在系統中運行,程序運行必須有進程才行。進程是操作系統里資源分配的基本單位,也是獨立運行的基本單位,具有動態的特點,暫時出現的特點,而且一個進程產生之后,可以再次生成多個進程出來。也可以多個進程並發執行,也可以同步或者異步執行。
進程的結構化特性:指的是說,進程一般由 進程控制塊(PCB)+程序段+數據段 組成,其中 PCB 是唯一標識進程存在的標志,當系統或父進程創建一個進程時,實際上就是為其建立一個進程控制塊PCB,進程控制塊既能標識進程的存在,又能刻畫出進程的動態特征,它是一個進程僅有的被操作系統真正感知的部分,對操作系統而言,所有進程控制塊將構成並發執行控制和維護系統工作的依據。
進程的三個最基本的狀態:(至少是這3個)就緒態,執行態,阻塞態
就緒態:進程已經獲得除處理器之外的所有資源
執行態:進程獲得了必要的資源,且在 cpu 中執行
阻塞態:進程暫時無法執行(分配了處理器也不行)
運行態—》就緒態:時間片用完 or 優先級調度問題。除了執行態-》阻塞態是主動轉換,其他的都是被動轉換。
狀態的轉換具有唯一性
並發和並行
並發與並行是兩個既相似而又不相同的概念:並發性,又稱共行性,是指能處理多個同時性活動的能力;
並行是指同時發生的兩個並發事件,具有並發的含義,而並發則不一定並行,也亦是說並發事件之間不一定要同一時刻發生。
並發的實質:一個物理CPU(也可以多個物理CPU) 在若干道程序之間,在同一時間段里,多路復用,並發性是對有限物理資源強制行使多用戶共享以提高效率。
並行性實質:指兩個或兩個以上事件或活動在同一時刻發生。在多道程序環境下,並行性使多個程序同一時刻可在不同CPU上同時執行。
並發,是在同一個cpu上同時(不是真正的同時,而是看來是同時,因為cpu要在多個程序間切換)運行多個程序。
並行,是每個cpu運行一個程序。是真正的同時發生的。
打個比方:並發,就像一個人(cpu)喂2個孩子(程序),輪換着每人喂一口,表面上兩個孩子都在吃飯。並行,就是2個人喂2個孩子,兩個孩子同時同刻的在吃飯。
有兩種並發關系分別是同步和互斥
同步是進程間直接的制約關系,互斥是進程間間接的制約關系。
互斥:進程(同類的)間相互排斥的使用臨界資源的現象,就叫進程互斥。
同步:進程(不同類的)之間的關系不是相互排斥臨界資源的關系,而是相互依賴的關系。進一步的說明:就是前一個進程的輸出作為后一個進程的輸入,當第一個進程沒有輸出時第二個進程必須等待。具有同步關系的一組並發進程相互發送的信息稱為消息或事件。簡單的說就是不同類的進程相互合作使用臨界資源。
線程的概念:引入線程是為了減少程序並發執行時付出的時間和空間的開銷,提高並發度!在引入線程的 OS中,線程是獨立調度的基本單位(進程是獨立運行和擁有資源的基本單位),線程只需要一點兒必需的資源即可。一個線程可以創建和撤銷另一個線程,同一個進程中的多個線程之間可以並發執行。
多線程:多線程是程序設計的邏輯層概念,它是進程里的並發運行的一段代碼。多線程可以實現線程間的切換執行。
異步和同步是相對的概念
同步是順序執行,執行完一個再執行下一個,需要等待、協調運行。異步就是彼此獨立,在等待某事件的過程中繼續做自己的事,不需要等待這一事件完成后再工作。
線程就是實現異步的一個方式。異步是讓調用方法的主線程不需要同步等待另一線程的完成,從而可以讓主線程干其它的事情。
異步和多線程並不是一個同等關系,異步是最終目的,多線程只是實現異步的一種手段。異步是當一個調用請求發送給被調用者,而調用者不用等待其結果的返回而可以做其它的事情。實現異步可以采用多線程技術或則交給另外的進程來處理。異步和同步的區別, 在io等待的時候,同步不會切走,浪費了時間。
進程調度的目的
記錄系統中所有進程的有關情況,確定分配處理機的原則,分配處理機給進程,從進程收回處理機。
進程調度的三個層次:
高級調度(作業調度):頻率較低,幾分鍾一次、中級調度(交換技術)、低級調度(進程調度):頻率很高,幾十 ms 一次
進程調度算法
1.先來先服務(類似隊列FIFO)
按照進程進入就緒隊列的先后順序來調度進程,到達得越早,其優先數越高。獲得處理機的進程,未遇到其他情況時,一直運行下去,系統只需具備一個先進先出的隊列,在管理優先數的就緒隊列時,這種方法是一種最常見策略,並且在沒有其他信息時,也是一種最合理的策略。
2.輪轉調度(先來先服務的一個重要變形,就是輪轉規則)
系統把所有就緒進程按先后次序排隊,處理機總是優先分配給就緒隊列中的第一個就緒進程,並分配它一個固定的時間片(如100毫秒)。當該運行進程用完規定的時間片時,被迫釋放處理機給下一個處於就緒隊列中的第一個進程,分給這個進程相同的時間片,每個運行完時間片的進程,當未遇到任何阻塞時,就回到就緒隊列的尾部,並等待下次轉到它時再投入運行。於是,只要是處於就緒隊列中的進程,按此種算法遲早總可以分得處理機投入運行。
3.分級輪轉法
將先前的一個就緒隊列,根據進程的優先數不同划分兩個或兩個以上的就緒隊列,並賦給每個隊列不同的優先數。以兩個就緒隊列為例,一個具有較高優先數,另一個具有較低優先數,前者稱為前台隊列,后者稱為后台隊列。
4.優先數法
根據已占有處理機的進程是否可被剝奪而分為優先占有法和優先剝奪法兩種 。
優先占有法的原理是:一旦某個最高優先數的就緒進程分得處理機之后,只要不是其自身的原因被阻塞(如要求I/O操作)而不能繼續運行時,就一直運行下去,直至運行結束。
優先剝奪法的原理是:當一個正在運行的進程即使其時間片未用完,無論什么時候,只要就緒隊列中有一個比它的優先數高的進程,優先數高的進程就可以取代以前正在運行的進程,投入運行 。
調度用的進程狀態切換圖
原語的概念:在操作系統中,某些被進程調用的操作,例如隊列操作、對信號燈的操作、檢查啟動外設操作等,一旦開始執行就不能被中斷,否則就會出現操作錯誤,造成系統混亂。原語就是為實現這些操作而設置的。本質是若干條機器指令構成的一段程序,用來完成特定的功能(執行不可分割,具有原子性)。
信號量:一個同步機構,設置為 S,當s信號量>0表示可用資源數目,當信號量s<0,絕對值為在該信號量上等待的進程個數
P、V 原語
p 申請資源,v 釋放資源,可以不再一個進程中,但是必須成對的出現,信號量 s 除去初始化之外,只有 pv 操作可以改變它。
single s;//設置信號量,s>=0 //p 操作(wait 操作) s = s - 1; if (s >= 0) { //繼續運行下去; }else{ //阻塞該進程,把它插入到信號量等待隊列中; } //v 操作(single 操作) s = s + 1; if (s > 0) { //繼續運行下去; }else{ //從信號量等待隊列里,移出第一個進程,進入就緒隊列中,再返回原進程執行; }
進程之間通信
低級通信方式:PV 原語操作,進程互斥和同步。(信息量少,效率低)
高級通信方式:共享存儲器系統,消息傳遞系統,管道通信系統
臨界資源、臨界區、進入區、退出區、剩余區
臨界資源:計算機中有許多資源只允許一個進程使用,如果有多個進程同時去使用這類資源(也叫臨界資源)就會產生嚴重的錯誤。幾個進程若共享同一臨界資源,它們必須以互斥的方式使用這個臨界資源,即當一個進程正在使用臨界資源且尚未使用完畢時,則其他進程必須推遲對該資源的進一步操作,在當前進程的使用完成之前,不能從中插進去使用這個臨界資源,否則將會造成信息混亂和操作出錯。在臨界區之前設立的進入區。之后設立的退出區,最后余下的為剩余區。
臨界區:進程中用於訪問臨界資源的代碼段
訪問臨界區的規則
空閑讓進,忙則等待,有限等待,讓權等待
信號量分類:
整型信號量,整型 s:在 p 操作時,如果沒有可用資源,則進程持續對 s 測試,出現了『忙等』現象,不遵循『讓權等待』原則。
記錄型信號量(資源信號量):為解決整型信號量的問題,用鏈表鏈接所有的等待資源的進程,當進程對 s 進行 p 操作,如果沒有剩余資源,則進程自我阻塞,並插入等待鏈表中,v 操作時,如果鏈表里仍然有等待資源的進程,那么就喚醒第一個等待的進程。有效的解除了忙等現象,無限等待的現象。
經典同步問題
1.生產者與消費者問題
Dijkstra把廣義同步問題抽象成一種“生產者與消費者問題”(Producer-consumer-relationship)的抽象模型。
事實上,計算機系統中的許多問題都可歸結為生產者與消費者問題,生產者與消費者可以通過一個環形緩沖池聯系起來,環形緩沖池由幾個大小相等的緩沖塊組成,每個緩沖塊容納一個產品。每個生產者可不斷地每次往緩沖池中送一個生產產品,而每個消費者則可不斷地每次從緩沖池中取出一個產品。
下面給出基於環形緩沖區的生產者與消費者關系的形式描述,設:
1、生產者和消費者為同步關系(不同類的進程,順序的訪問資源),緩沖區為資源,需要兩個同步信號量:生產者私用信號量empty:初值為n,指示空緩沖塊數目,也就是緩沖區大小。消費者私用信號量full:初值為0,指示滿緩沖塊數目,也就是當前產品的數量。
2、因為生產者 或者 消費者是需要使用同一個記錄型信號量的進程,故需要一個互斥信號量 mutex,初值為1,用於實現臨界區互斥。如果只有一個消費者和一個生產者,那么不需要使用 mutex 信號量。
3、pv 操作里,有多個信號量同時存在,p 操作順序有講究,必須先p 操作去申請資源,再占有信號量訪問權,防止發生『死鎖』!
模塊 設計如下:
1 const int N = 10;//空緩沖池的空塊的數量是10 2 3 int mutex = 1;//公有信號量 mutex,初值為1,實現臨界區互斥訪問 4 int empty = N;//生產者私有信號量empty,初值為 n=10,代表空緩沖池的空快數量 5 int full = 0;//消費者私有信號量full,初值為0,代表滿的緩沖區的滿快數量。 6 //生產者進程 7 producer(){ 8 while (true) { 9 //生成一個產品放入臨時的緩沖區; 10 creat(); 11 //先申請資源,防止死鎖發生 12 p(empty);//先占下一個空位置,來防止 creat 的產品 13 //再占據信號量的訪問權,進行互斥訪問過程 14 p(mutex); 15 //執行產品放入緩沖區的操作 16 add(); 17 //釋放訪問權 18 v(mutex); 19 //產品+1,放入空位置 20 v(full); 21 } 22 } 23 //消費者進程 24 consumer(){ 25 while (true) { 26 //必須是先申請資源,然后再占有互斥信號量,實現互斥,不能逆序! 27 p(full); 28 p(mutex); 29 //執行某個產品取出的操作 30 sub(); 31 v(mutex); 32 //多了一個空位 33 v(empty); 34 //把取出的產品消費掉 35 eat(); 36 } 37 }
注意:勿忘循環 while,且先生產了,才能去消費,注意實際的順序!
2.讀者與寫者問題(三類情況討論)
有一塊共享的數據區域,然后有讀者和寫者前來分別讀取數據,或者寫入數據,要求如下:
1、讀者只讀出數據,寫者只寫入數據,不能混淆
2、任意多個讀者可以同時讀取數據(類似於內存的存取)
3、每次只能有一個寫者去寫入數據
4、一個寫者正在寫入數據的同時,其他的任意讀者或者寫者不能讀取或者寫入數據
三類情況:讀者優先,公平情況,寫者優先
一:讀者優先算法;
讀者先來讀取數據(其他的讀者也可以來讀取數據),此時寫者等待,也就是說讀者可以插寫者的隊,這是讀者優先的關鍵點,只有當讀者為0,寫者才能來寫入數據。
1、寫者要有一個互斥信號量 writeMutex=1,因為寫者一次只能一個來寫數據
2、對讀者要有一個記錄數目的 int 變量,readcount=0,一個互斥信號量readMutex = 1,保證多個讀者來的時候,能似的 readcount 互斥的變化,也就是不被混亂的計數。
1 //讀者計數用的互斥信號量 2 int readMutex = 1; 3 //寫者互斥訪問臨界區的互斥信號量 4 int writeMutex = 1; 5 //記錄讀者數目 6 int readcount = 0; 7 //讀者進程 8 reader(){ 9 while (true) { 10 //讀者來讀,需要記錄讀者的數量 11 //前提是互斥的訪問計數器 12 p(readMutex); 13 //判斷當前是否有讀者在讀取數據,依據此,決定寫者是否能進 14 if (0 == readcount) { 15 //如果當前無讀者,那么寫者可進寫入數據操作,但是這是讀者優先算法,那么寫者第一次來寫不能進,要保證讀者優先! 16 p(writeMutex); 17 } 18 //如果計數器不為0,那么要保證寫者不能進,且讀者進的時候,要保證優先,也就是可以插讀者的隊 19 readcount++; 20 //計數器使用完畢,則可以釋放使用權 21 v(readMutex); 22 //進行讀取數據的操作 23 read(); 24 //讀取完畢之后,讀者離開,再次占據計數器使用權,安全計數 25 p(readMutex); 26 readcount--; 27 //判斷是否計數器為0 28 if (0 == readcount) { 29 //此時,讀者再次為0,那么寫者可以進入寫數據了,因為不是第一次來 30 v(writeMutex); 31 } 32 //計數器使用完畢,釋放使用權 33 v(readMutex); 34 }// end of while 35 }// end of reader 36 //寫者進程 37 writer(){ 38 while (true) { 39 //保證讀者和其他寫者不能進入 40 p(writeMutex); 41 //寫操作 42 write(); 43 //完畢之后,釋放使用權 44 v(writeMutex); 45 } 46 }
二、公平情況算法(按照先來后到的順序)
讀者想進的時候,有寫者正在寫(或者正在等待寫),讀者就不能進(讀者等待),只有寫者走了,讀者才能進。和一相比,需要多一個信號量 wmutex=1,表示是否存在寫者正在寫或者等待寫,如果存在,讀者就等待,讀者不能插隊了。
1 //讀者計數用的互斥信號量 2 int readMutex = 1; 3 //寫者互斥訪問臨界區的互斥信號量 4 int writeMutex = 1; 5 //記錄讀者數目 6 int readcount = 0; 7 //判斷寫者是否存在的互斥信號量 8 int wmutex = 1; 9 //讀者進程 10 reader(){ 11 while (true) { 12 //不同於讀者優先算法,讀者來的時候,不能插隊了,如果有寫者在(或者等待),讀者也需要等待,設置標識信號量 13 //占用 wmutex 14 p(wmutex); 15 //讀者來讀,需要記錄讀者的數量 16 //前提是互斥的訪問計數器 17 p(readMutex); 18 //判斷當前是否有讀者在讀取數據,依據此,決定寫者是否能進 19 if (0 == readcount) { 20 //如果是第一個讀者來了,因為是公平算法,那么同樣需要阻止其他寫者進! 21 p(writeMutex); 22 } 23 readcount++; 24 //計數器使用完畢,則可以釋放使用權 25 v(readMutex); 26 //釋放 wmutex,如果不釋放,則后續讀者無法進入,or 寫者無法等 27 v(wmutex); 28 //進行讀取數據的操作 29 read(); 30 //讀取完畢之后,讀者離開,再次占據計數器使用權,安全計數 31 p(readMutex); 32 readcount--; 33 //判斷是否計數器為0 34 if (0 == readcount) { 35 //此時,讀者為0,那么寫者可以不用等待,從而進入寫數據了 36 v(writeMutex); 37 } 38 //計數器使用完畢,釋放使用權 39 v(readMutex); 40 }// end of while 41 }// end of reader 42 //寫者進程 43 writer(){ 44 while (true) { 45 //避免讀者優先,如果寫者是首個來的,那么同樣需要阻止其他讀者插隊,占用 46 p(wmutex); 47 //已經有一個寫者的時候,同樣,需保證讀者和其他寫者不能進入 48 p(writeMutex); 49 //寫操作 50 write(); 51 //完畢之后,釋放使用權 52 v(writeMutex); 53 //釋放寫者存在標志,說明讀者可以進 54 v(wmutex); 55 } 56 }
因為有了 wmutex,所以當第一個寫者進的時候,占用 p(wmutex),阻止了讀者進入,避免了讀者優先,v(wmutex)之后,讀者才可以進入,控制了進程,可以按照先來后到的順序來執行。
三、寫者優先算法(寫者可以插隊的算法)
類似讀者優先算法,同理,這里是寫者可以插隊,多用一個 readable 信號量,控制寫者可以優先於讀者進入臨界區,一個整數 writecount 統計寫者,而 wmutex 控制寫者互質訪問 writecount
1 //讀者計數用的互斥信號量 2 int readMutex = 1; 3 //寫者計數用的互斥變量 4 int writeCountMutex = 1; 5 //寫者互斥訪問臨界區的互斥信號量 6 int writeMutex = 1; 7 //記錄讀者數目 8 int readcount = 0; 9 //記錄寫者數目 10 int writecount = 0; 11 //判斷寫者是否存在的互斥信號量 12 int readable = 1; 13 //讀者進程 14 reader(){ 15 while (true) { 16 //占用 readable,可知道是否存在寫者 17 p(readable); 18 //讀者來讀,需要記錄讀者的數量 19 //前提是互斥的訪問計數器 20 p(readMutex); 21 //判斷當前是否有讀者在讀取數據,依據此,決定寫者是否能進 22 if (0 == readcount) { 23 //如果是第一個讀者來了,那么同樣需要阻止其他寫者進!只不過寫者優先說的是寫者可以插隊 24 p(writeMutex); 25 } 26 readcount++; 27 //計數器使用完畢,則可以釋放使用權 28 v(readMutex); 29 //釋放 readable果不釋放,則后續讀者無法進入,or 寫者無法等 30 v(readable); 31 //進行讀取數據的操作 32 read(); 33 //讀取完畢之后,讀者離開,再次占據計數器使用權,安全計數 34 p(readMutex); 35 readcount--; 36 //判斷是否計數器為0 37 if (0 == readcount) { 38 //此時,讀者為0,那么寫者可以不用等待,從而進入寫數據了 39 v(writeMutex); 40 } 41 //計數器使用完畢,釋放使用權 42 v(readMutex); 43 }// end of while 44 }// end of reader 45 //寫者進程,注意這里是寫者優先算法 46 writer(){ 47 while (true) { 48 //占用寫者計數器 49 p(writeCountMutex); 50 //如果是首個寫者,則組織其他讀者進入 51 if (0 == writecount) { 52 p(readable); 53 } 54 writecount++; 55 //停止占用計數器 56 v(writeCountMutex); 57 //寫者互斥訪問臨界區 58 p(writeMutex); 59 //寫操作 60 write(); 61 //完畢之后,釋放使用權 62 v(writeMutex); 63 //再次占用計數器 64 p(writeCountMutex); 65 writecount--; 66 //寫者沒了,那么讀者可以進 67 if (0 == writecount) { 68 v(readable); 69 } 70 //釋放寫者存在標志,說明讀者可以進 71 v(writeCountMutex); 72 } 73 }
注意:當第一個寫者來的時候,便占用了 readable,一直占據,后續讀者就會等待,而后續寫者可以插隊!因為 if 語句存在的原因!直到寫者再次為0,釋放了 readable,那么讀者才可以申請進入資源區,然后轉而是讀者去占用 readable,阻止后續的寫者進入。
PV 問題總結:
1、先確定進程是否可以同時執行,不能同時執行,則互斥訪問之。
2、在並發進程間分清楚是互斥還是同步問題。同類進程同時只能訪問一個資源叫互斥訪問,不同類進程按照順序的訪問,叫同步訪問。前者是間接的關系,后者是直接的關系。前驅進程加 v(釋放資源操作),用來喚醒后繼進程。后繼進程加 p 操作(申請資源),用來保證在前驅進程之后執行。
3、畫流程圖,說明變量的含義
4、pv 操作一定成對兒的出現
5、互斥 pv 操作在同一進程內要夾住臨界區,同步 pv 操作在不同類進程之間,v 在前驅,p 在后繼
6、如果有計數,要給計數變量加信號量mutex,互斥訪問。
死鎖問題
死鎖:當某個進程提出申請資源后,使得有關進程在無外力協助下,永遠分配不到必需的資源而無法繼續運行,這就產生了一種特殊的現象——死鎖。在許多實時應用中,比如計算機控制運輸和監視系統方面,死鎖問題也極為重要。總得來說就是:2個或者2個以上的進程,在系統里無限等待,無法執行,且無外力作用下,他們無法主動去獲得各自的資源!死鎖進程是系統中當前進程集合的一個子集。
死鎖產生的原因 :
1、競爭的資源不足以滿足所有的進程使用(死鎖產生的根本原因)
2、進程對資源的請求或者釋放的順序不當(死鎖產生的重要原因)
比如:假定有兩個進程Pl和P2都要修改文件F,修改時都需要一條暫時存放信息的磁帶,而只有一台磁帶機T可用。又假定由於某種原因,在進行修改之前,P2需要一暫存磁帶(例如為了修改,要重新組織輸入數據)。設F和T都是可重用資源,它們分別表示允許更新文件和允許使用磁帶機。於是Pl和P2。可有如下形式:
分析:從上面的申請-釋放過程可以看出,進程Pl和P2有可能“同時”分別到達rl和r2處,例如,P2首先得到T,然后Pl得到F,接着Pl到達r1,最后P2到達r2,此時,若Pl繼續運行,則占有F的進程Pl將阻塞在T上,若P2繼續運行,則占有T的進程P2將阻塞在F上,如果P2不能前進,則P1也不能繼續下去,反之亦然。我們說這兩個進程處在死鎖狀態。
產生死鎖有四個必要條件:互斥條件、不剝奪的條件、請求和保持資源的條件(部分分配資源條件)、環路等待條件。這四個缺一不可。
處理死鎖的4種基本方法:
鴕鳥法(不管不顧,任其自由發展,可以使用在死鎖很少很少發生的系統里)、預防死鎖(從進程調度上入手,去破壞死鎖產生的四個必要條件的一個或者多個)、避免死鎖(提前防止系統進入死鎖狀態,不破壞死鎖產生的四個必要條件,比如使用銀行家算法)、檢測和接觸死鎖(被動方式,發生了死鎖再去檢測處理)
預防死鎖的的四中方法:
1、破壞互斥條件,比如改變受資源本身限制的打印機
2、破壞請求與保持條件:每個進程在運行之前,必須預先提出自己所要使用的全部資源,調度程序在該進程所需要的資源末得到滿足之前,不讓它們投入運行,並且當資源一旦分配給某個進程之后,那么在該進程的整個運行期間相應資源一直被它占有,這就破壞了產生死鎖的請求與保持條件。
3、破壞環路等待條件:對系統提供的每一項資源,由系統設計者將它們按類型進行線性排隊,並賦予不同的序號。
例如,設卡片輸入機為1,打印機為2,磁帶機為3,磁盤機為4,……所有的進程都只能嚴格地按照編號遞增(或遞減)的次序去請求資源,亦即,只有低編號的資源要求滿足后,才能對高編號資源提出要求;釋放資源時,應按編號遞減的次序進行。由此可以看出,對資源請求采取了這種限制之后,所形成的進程—資源圖不可能再產生環路。
4、破壞不剝奪條件:使其已經分配的資源變為可剝奪的即可,就是說分配了,還可以要回來,重新分配。這樣也可以預防死鎖。
避免死鎖:使用銀行家算法
為了避免死鎖發生,操作系統必須根據預先掌握的關於資源用法的信息控制資源分配,使得共同進展路徑的下一步不致於進入危險區,即只要有產生死鎖的可能性,就避免把一種資源分配給一個進程。總得來說就是,把系統分為安全和不安全狀態,允許進程動態的去申請資源,提前估計分配資源的安全性,安全則進入,不安全就不進入。
注意:安全序列不唯一,且不一定不安全序列就必然會發生死鎖,只是可能發生死鎖而已。
死鎖避免的算法:銀行家算法和安全性算法
解除死鎖(一旦發生了死鎖的時候,檢測到了死鎖存在,可以被動的解除死鎖)
1.資源剝奪法
(1)還原算法。即恢復計算結果和狀態。
(2)建立檢查點主要是用來恢復分配前的狀態。
2.撤消進程法
活鎖和死鎖的區別
前面已經提到死鎖定義:
一組進程中的每一個進程,均無限期地等待此組進程中某個其他進程占有的,因而永遠無法得到的資源,這種現象稱為進程死鎖。
所以可以得到以下結論:
參與死鎖的進程至少有二個,每個參與死鎖的進程均等待資源 ,參與死鎖的進程中至少有兩個進程占有資源,死鎖進程是系統中當前進程集合的一個子集。
在一個動態系統中,資源請求與釋放是經常性發生的進程行為.對於每類系統資源,操作系統需要確定一個分配策略,當多個進程同時申請某類資源時,由分配策略確定資源分配給進程的次序。
資源分配策略可能是公平的,能保證請求者在有限的時間內獲得所需資源;資源分配策略也可能是不公平的,即不能保證等待時間上界的存在。在后一種情況下,即使系統沒有發生死鎖,某些進程也可能會長時間等待.當等待時間給進程推進和響應帶來明顯影響時,稱發生了進程飢餓,當飢餓到一定程度的進程所賦予的任務即使完成也不再具有實際意義時稱該進程被餓死。
考慮一台打印機分配的例子:當有多個進程需要打印文件時,系統按照短文件優先的策略排序,該策略具有平均等待時間短的優點,似乎非常合理,但當短文件打印任務源源不斷時,長文件的打印任務將被無限期地推遲,導致飢餓以至餓死。
與飢餓相關的另外一個概念稱為活鎖(live lock) 在忙式等待條件下發生的飢餓,稱為活鎖。例如不公平的互斥算法。
不進入等待狀態的等待稱為忙式等待,另一種等待方式是阻塞式等待,進程得不到共享資源時將進入阻塞狀態,讓出CPU給其他進程使用。
忙等待和阻塞式等待的相同之處在於進程都不具備繼續向前推進的條件,不同之處在於處於忙等待的進程不主動放棄CPU,盡管CPU可能被剝奪,因而是低效的,而處於阻塞狀態的進程主動放棄CPU ,因而是高效的。
活鎖是數據資源分配引起的,而餓死是處理器資源分配引起的。
不同領域中,本質應該是一樣的。活鎖的進程是在不主動放棄CPU的情況下無法完成工作,就如計算機網絡中說的報文圍繞目的節點轉悠,卻不能真正到達目的節點。
而餓死則是放棄CPU讓其他進程工作,但這一讓就成了無限期的讓,從而導致了餓死。
舉例子來說明什么是活鎖:
如果事務T1封鎖了數據R,事務T2又請求封鎖數據R,於是T2就等待。之后呢,T3也請求封鎖R,當T1釋放了R上的封鎖之后系統首先批准了T3的請求,T2仍然等待。然后T4又請求封鎖R,當T3釋放了R上的封鎖之后系統又批准了T4的請求,...,T2有可能永遠等待,這就是活鎖的情形,就是上面說的發生在忙等情景下的飢餓。
避免活鎖的簡單方法:采用先來先服務的策略。
舉例子說明什么是死鎖:
如果事務T1封鎖了數據R1,T2封鎖了數據R2,然后T1又請求封鎖R2,因T2已封鎖了R2,於是T1等待T2釋放R2上的鎖。接着T2又申請封鎖R1,因T1已封鎖了R1,T2也只能等待T1釋放R1上的鎖。這樣就出現了T1在等待T2,而T2又在等待T1的局面,T1和T2兩個事務永遠不能結束,形成死鎖。
歡迎關注
dashuai的博客是終身學習踐行者,大廠程序員,且專注於工作經驗、學習筆記的分享和日常吐槽,包括但不限於互聯網行業,附帶分享一些PDF電子書,資料,幫忙內推,歡迎拍磚!