操作系統學習8同步互斥問題

回顧一下同步互斥的概念:

現代操作系統基本都是多任務操作系統,即同時有大量可調度實體在運行。在多任務操作系統中,同時運行的多個任務可能:

  • 都需要訪問/使用同一種資源
  • 多個任務之間有依賴關係,某個任務的運行依賴於另一個任務

這兩種情形是多任務編程中遇到的最基本的問題,也是多任務編程中的核心問題,同步和互斥就是用於解決這兩個問題的。


互斥:是指散步在不同任務之間的若干程序片斷,當某個任務運行其中一個程序片段時,其它任務就不能運行它們之中的任一程序片段,只能等到該任務運行完這個程序片段後纔可以運行。最基本的場景就是:一個公共資源同一時刻只能被一個進程或線程使用,多個進程或線程不能同時使用公共資源。


同步:是指散步在不同任務之間的若干程序片斷,它們的運行必須嚴格按照規定的某種先後次序來運行,這種先後次序依賴於要完成的特定的任務。最基本的場景就是:兩個或兩個以上的進程或線程在運行過程中協同步調,按預定的先後次序運行。比如 A 任務的運行依賴於 B 任務產生的數據。


顯然,同步是一種更爲複雜的互斥,而互斥是一種特殊的同步。也就是說互斥是兩個任務之間不可以同時運行,他們會相互排斥,必須等待一個線程運行完畢,另一個才能運行,而同步也是不能同時運行,但他是必須要安照某種次序來運行相應的線程(也是一種互斥)!因此互斥具有唯一性和排它性,但互斥並不限制任務的運行順序,即任務是無序的,而同步的任務之間則有順序關係。


一般涉及到同步和互斥的問題,都是線程級別之間的問題。因爲一般情況下進程都是認爲有各自的數據空間。但是其實對於現代操作系統來說,很多時候進程和線程直接的概念並沒有那麼明晰。


1、同步的問題,主要是進程或線程有一些共享資源的情況下要考慮的。爲了解決不確定性,要引入同步互斥。

系統缺陷:結果依賴於併發執行或者事件的順序和時間,這就是不確定性和不可重複性。這種現象稱爲競態。這是我們要極力避免的。

最好的方法是讓指令不會被打斷。所以必須使用原子操作。 


2、臨界區、互斥、死鎖、飢餓的概念:


3、臨界區的特點(屬性):


4、如何實現對臨界區代碼的保護(也就是實現互斥的機制)?

(1)禁用硬件中斷。

回顧一下併發概念:併發,在操作系統中,是指一個時間段中有幾個程序都處於已啓動運行到運行完畢之間,且這幾個程序都是在同一個處理機上運行,但任一個時刻點上只有一個程序在處理機上運行



另外,屏蔽中斷在多CPU的機制下是無法解決的,因爲如果某個資源是兩個CPU並行執行,那是無法成功的,因爲一個CPU屏蔽中斷的能力只是對於該CPU而言,而不是對於其他CPU。因此在多CPU的時候,中斷是無法解決互斥問題的。

(2)基於軟件的解決方法

即基於一些軟件和編程上的設計使之滿足臨界區的屬性。

(3)更高級的抽象

5、信號量(Semaphore)

背景:


確保同步的各個機制:


但是,這樣是不夠的。例如,我們希望臨界區內可以有多個線程在執行等。這樣的話,有哪些更高級的同步手段?可以通過信號量來實現這個機制。信號量在早起的操作系統中是主要的同步原語,例如原Unix中,現在很少用了。

信號量是什麼呢?信號量是一個整形,此處稱爲sem。sem只有兩個原子操作:P()和V()


其實很類似鐵路上的信號燈。

6、信號量的使用


一般信號量被初始化爲一個正數。如果有進程想進入臨界區,則執行P(),如 果有進程要退出臨界區,則執行V()。

信號量完全可以完成鎖的功能。鎖與信號量最大的區別是鎖在臨界區中只有一個進程,而信號量可以有多個。


(1)使用二進制信號量,也就是隻有0和1兩個值

1)使用二進制信號量實現互斥,其實就跟鎖一樣。把信號量初始值設爲1,在進入臨界區之前進行P操作,也就是獲取鎖。結束後進行V操作,也就是放棄鎖


2)實現調度約束,也就是同步操作

信號量初值設成0。在這個例子中,線程A必須要等到線程B執行到某個程度後再執行。


(2)一般/計數信號量

解決這個問題,一般是一個線程在等待另一個線程處理事情。下面是個例子:



7、信號量的實現

信號量使用一個整形和一個隊列來實現。等待隊列是一系列等待的線程。


信號量的優缺點:


雖然信號量大量使用在實際的操作系統中,但是還是略顯複雜,容易發生各種死鎖等出錯現象。有沒有更好更簡單更方便的方法來讓開發者實現同步互斥呢?這裏就需要使用管程。

8、管程(monitor)

什麼是管程:包含了一系列共享變量以及針對這些變量操作的函數的組合和模塊。

管程比信號量抽象程度更高,但是對於開發者來說也越容易使用。

引入管程機制的目的:1、把分散在各進程中的臨界區集中起來進行管理;2、防止進程有意或無意的違法同步操作;3、便於用高級語言來書寫程序,也便於程序正確性驗證


9、解決同步互斥問題總結:



下面講死鎖


10、死鎖的四個條件(如果四個條件同時成立,可能會有死鎖,但是不湊齊這四個條件一定沒有死鎖):



對於圖2來說,沒有滿足持有並等待條件,因爲資源R1和R2有兩個實例,而P4和P2可以持有R2和R1並且執行,執行完後,資源就釋放了,就可以繼續進行下去了。所以雖然這裏滿足了循環等待條件,但是並不會有死鎖。

11、死鎖的處理

死鎖預防->死鎖避免->死鎖檢測->死鎖恢復。他們的條件是逐步減弱的。一般來說,死鎖檢測和死鎖恢復是結合在一起使用的,畢竟檢測到了有死鎖,當然要恢復。

對於一個操作系統來說,一般這樣設計:


爲什麼大部分都忽略這個問題呢?判斷是否出現死鎖以及恢復的開銷很大;如果設置一系列的約束不能進入死鎖狀態,會令操作系統的功能受到很大限制,很多任務無法完成。這被稱爲鴕鳥辦法,忽略死鎖這個問題,如果遇到了這個問題,再想辦法解決。

(1)死鎖預防

也就是讓死鎖絕對不會出現。思路就是,打破死鎖四個條件中的某個條件就可以了。

1)互斥:把資源變成可以共同佔用的

2)佔用並等待:保證當一個進程請求資源時,它不持有其他任何資源。會造成資源利用率低,可能會發生飢餓

3)無搶佔:如果進程佔有某些資源,並請求其他不能被立即分配的資源的時候,釋放當前正在佔有的資源

4)循環等待:對所有資源類型進行排序,並要求每個進程按照資源的順序進行申請

(2)死鎖避免

相比死鎖預防更小的限制程度。它不是通過一些機制來絕對預防死鎖,而是在進程申請資源的時候,判斷是否會產生死鎖。如果有可能出現死鎖,不能分配資源給這個進程。所以需要系統具有一些額外的先驗信息



這裏的安全狀態,不止是不會陷入死鎖的狀態,而是比不會陷入死鎖更大的一個狀態。


有一個著名的死鎖避免算法:銀行家算法。歸根到底,銀行家算法就是判斷一個序列是否安全的算法。


銀行家算法的具體實現:


存在關係Need[i,j]=Max[i,j]-Allocation[i,j]

對於具體的算法實現來說,首先是安全狀態判斷算法


然後是整體的銀行家算法


(3)死鎖檢測

允許系統進入死鎖狀態,隔一段時間調用死鎖檢測算法,如果系統進入死鎖了,就啓動恢復機制

1)怎麼檢測系統是否進入死鎖?

常用方法:建立資源分配圖,把資源的R去掉,如果產生環了就是進入死鎖狀態了


檢測算法的使用需要考慮什麼問題:


一個概念,回滾:回滾(Rollback)指的是程序或數據處理錯誤,將程序或數據恢復到上一次正確狀態的行爲。回滾包括程序回滾和數據回滾等類型。

(4)死鎖恢復

關鍵是恢復的進程,需要定一種規則




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章