程式的前三個部分(程式的基本概念、程式控制、執行緒)請閱讀 作業系統-4-程式管理(一)
四、程式同步
概念:程式同步的主要任務是使併發執行的各程式之間能有效的共享資源和相互合作,從而使程式的執行具有可再現性。
1 程式同步的基本概念
(1)程式之間的兩種制約關係:間接制約關係:系統資源競爭,程式間彼此無關
直接制約關係:程式間合作,彼此相關
(2)資源競爭需解決的兩個問題:死鎖(Deadlock)問題、飢餓(Starvation)問題(既要解決飢餓問題,又要解決死鎖問題。)
(3)程式互斥:概念:程式互斥指若干程式要使用同一共享資源時,任何時刻最多允許一個程式使用,其他程式必須等待,直到佔有資源的程式釋放該資源。
作用:程式互斥是解決程式間競爭關係(間接制約關係)的手段。
(4)程式合作:概念:程式合作是指某些程式為完成同一任務需要分工協作。
作用:程式合作是解決程式間協作關係(直接制約關係)的手段。
(5)程式合作與程式互斥的區別:程式同步指兩個以上程式基於某個條件來協調它們的活動。一個程式的執行依賴於協作程式的訊息或訊號,
當一個程式沒有得到來自於協作程式的訊息或訊號時需等待,直到訊息或訊號到達才被喚醒。
程式互斥是一種特殊的程式同步關係,即依次使用互斥共享資源,是對程式使用資源次序上的一種協調。
(6)臨界資源(Critical Resource/CR):一次僅允許一個程式訪問的資源。如:程式P1、P2共享一臺印表機,若讓它們交替使用則得到的結果肯定是不可理解的。
臨界資源可能是硬體,也可能是軟體:變數,資料,表格,佇列等。
併發程式對臨界資源的訪問必須作某種限制,否則就可能出現與時間有關的錯誤,如:聯網售票。
(7)臨界區(Critical Section/CS): 臨界段,在每個程式中,訪問臨界資源的那段程式。
與同一變數有關的臨界區分散在各程式的程式段中,而各程式的執行速度不可預知。
如果能保證程式在臨界區執行時,不讓另一個程式進入臨界區,即各程式對共享變數的訪問是互斥的,就不會造成與時間有關的錯誤。
與時間有關的錯誤: 一飛機訂票系統,兩個終端,執行T1、T2程式
T1 : T2:
... ...
Read(x); Read(x);
if x>=1 then if x>=1 then
x:=x-1; x:=x-1;
write(x); write(x);
... ...
注意:臨界區是對某一臨界資源而言的,對於不同臨界資源的臨界區,它們之間不存在互斥。
如程式段A、B有關於變數X的臨界區,而C、D有關於變數Y的臨界區,
那麼,A、B之間需要互斥執行,C、D之間也要互斥執行,而A與C、B與D之間不用互斥執行。
臨界區的排程原則:一次至多允許一個程式進入臨界區內
一個程式不能無限地停留在臨界區內
一個程式不能無限地等待進入臨界區
(8)同步機制應遵循的規則:空閒讓進、忙則等待、有限等待、讓權等待
用軟體方法解決程式互斥問題:(設兩個程式Pi、Pj使用臨界資源。)
演算法一分析:該演算法規定了程式必須輪流執行;若某程式不需要進入CS,而另一程式要求連續進入CS,該演算法不能解決;破壞了“空閒讓進”的準則;
演算法二分析:該演算法解決了演算法一“空閒讓進”的問題;
該演算法中,若多個程式同時要求進入CS時,都發現對方程式的標誌為“假”,會同時進入CS,這樣,破壞了“忙則等待”的準則;
演算法三分析:該演算法解決了演算法二“忙則等待”的問題;
該演算法中,若多個程式同時要求進入CS時,都將自己的標誌設為“真”,這樣,互相謙讓,誰也不會進入CS。這樣,破壞了“空閒讓進”的準則;
演算法四分析:滿足空閒讓進、忙則等待、有限等待、讓權等待四個規則
生產者消費者問題:
問題分析:利用一個陣列表示具有n個緩衝區的迴圈緩衝池;
用輸入指標in指示下一個可投放產品的緩衝區,每當生產者程式生產並投放一個產品,輸入指標加1 (in:=(in+1) mod n);
用指標out指示下一個可從中獲取產品的緩衝區,每當消費者程式取出一個產品,輸出指標加1 (out:=(out+1) mod n) ;
----------------------------------------------------------------------------------------------------------------
2 訊號量機制
概念:1965年,由荷蘭學者Dijkstra提出,P、V操作分別是荷蘭語的test (Proberen) 和increment (Verhogen) 。
訊號量機制是一種卓有成效的程式同步機制。經歷整型訊號量、記錄型訊號量,發展為“訊號量集”機制。
P、V操作是原語。
訊號量的值除初始化外,只能由P、V原語修改。(wait、signal)
型號量的分類:訊號量按其用途分為:公用訊號量、私有訊號量
訊號量按其取值分為:二元訊號量、一般訊號量
(1)整型訊號量:定義為一個整型量,由兩個標準原子操作wait(S)(P操作)和signal(S)(V操作)來訪問。
P(S) 或 wait(S): while S≤0 do no-op;
S:=S-1;
V(S) 或 signal(S): S:=S+1;
整型訊號量出現“忙等”現象。
P(S) 或 wait(S): S:=S-1; S>0:可用資源數量
if (S<0) block(); S<0:|S|表示等待使用資源的程式數量
V(S) 或 signal(S): S:=S+1;
if (S<=0) wakeup();
利用整型訊號量實現互斥:
(2)記錄型訊號量:記錄型訊號量機制採取“讓權等待”策略,即當程式不能申請到資源時,讓出CPU使用權。避免了整型訊號量出現的“忙等”現象。
記錄型訊號量需要一個用於代表資源數目的整型變數value,一個用於連結所有等待程式的程式連結串列queue。
資料結構定義如下: struct semaphore {
int value;
pointer_PCB queue;
};
訊號量說明:struct semaphore s;
P操作:P(s){
s.value = s.value -1 ;
if (s.value < 0){
該程式狀態置為等待狀態
將該程式的PCB插入相應的等待佇列末尾s.queue;
}
}
V操作:V(s){
s.value = s.value +1 ;
if (s.value <= 0){
喚醒相應等待佇列s.queue中等待的一個程式
改變其狀態為就緒狀態,並將其插入就緒佇列
}
}
程式對CR進行申請:begin
repeat
P(S);
CS;
V(S);
remainder;
until false;
end
(3)AND型訊號量
AND訊號量是針對程式間共享多個資源;
例如:兩個程式A、B要求訪問共享資料D、E。為D、E分別設定用於互斥的訊號量Dmutex、Emutex,初值為1。
process A: process B:
wait(Dmutex); wait(Emutex);
wait(Emutex); wait(Dmutex);
此處出現死等現象
AND同步機制的基本思想是:將程式在整個執行過程中需要的所有資源,一次性全部地分配給程式,待程式使用完後再一起釋放。
只要尚有一個資源未分配給程式,其它所有可能為之分配的資源,也不分配。
實現時在wait操作中,增加一個“AND”條件,故稱為AND同步,或同時wait操作(Swait)。
-------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------
(4)訊號量集
程式對訊號量Si的測試值為ti(表示訊號量的判斷條件,要求Si >= ti;即當資源數量低於ti時,便不予分配)
佔用值為di(表示資源的申請量,即Si=Si-di)
對應的P、V原語格式為:
Swait(S1, t1, d1; ...; Sn, tn, dn);
Ssignal(S1, d1; ...; Sn, dn);
一般“訊號量集”可以用於各種情況的資源分配和釋放,幾種特殊情況:
(1)Swait(S, d, d)表示每次申請d個資源,當少於d個時,便不分配
(2)Swait(S, 1, 1)表示記錄型訊號量或互斥訊號量
(3)Swait(S, 1, 0)可作為一個可控開關(當S1時,允許多個程式進入特定區域;當S=0時,禁止任何程式進入該特定區域)
(4)一般“訊號量集”未必成對使用。如:一起申請,但不一起釋放!
五、經典的程式同步問題(等我後續單獨寫部落格)
1 生產者/消費者問題
2 讀者/寫者問題
3 哲學家進餐問題
六、管程機制
引入管程機制的原因:減少大量的同步操作分散在各個程式中。
解決的辦法——引入管程:為每個可共享資源設立一個專門的管程,來統一管理各程式對該資源的訪問。
管程(Monitor)的定義:一個管程包含一個資料結構和能為併發程式所執行(在該資料結構上)的一組操作,這組操作能同步程式和改變管程中的資料。
管程的三個部分:侷限於管程內使用的共享變數的定義
對侷限於管程的資料初始化
對該資料結構進行操作的一組過程
管程的特點:(1)區域性資料變數只能被管程的過程訪問,任何外部過程都不能訪問。
(2)一個程式通過呼叫管程的一個過程進入管程。
(3)任何時候,只能有一個程式在管程中執行,呼叫管程的任何其他程式都被掛起,以等待管程變成可用的。(由編譯器控制一次只能有一個程式呼叫管程)
(4)為了保證共享變數的資料一致性,管程應互斥使用。
(5)管程通常是用於管理資源的,因此管程中有程式等待佇列和相應的等待和喚醒操作。
(6)在管程入口有一個等待佇列,稱為入口等待佇列。當一個已進入管程的程式等待時,就釋放管程的互斥使用權;
當已進入管程的一個程式喚醒另一個程式時,兩者必須有一個退出或停止使用管程。
(7)在管程內部,由於執行喚醒操作,可能存在多個等待程式(等待使用管程),稱為緊急等待佇列,它的優先順序高於入口等待佇列。
描述:一個程式進入管程之前要先申請,一般由管程提供一個enter()過程;
離開時釋放使用權,如果緊急等待佇列不空,則喚醒第一個等待者,一般也由管程提供外部過程leave()。
管程內部有自己的等待機制。管程可以說明一種特殊的條件型變數:var c:condition;實際上是一個指標,指向一個等待該條件的PCB佇列。
條件變數實際上是區分阻塞的原因。
條件變數:管程利用wait原語讓程式等待,等待的原因可用條件變數condition區別。它們應置於wait和signal之前。
例如:var X:condition;X.signal表示重新啟動一個被阻塞的程式,但如果沒有被阻塞的程式,則X.signal不產生任何後果。
利用管程解決生產者-消費者問題(待我後續釋出部落格)
七、程式通訊
1 概念
(1)所謂程式通訊是指程式之間資訊交換。
(2)P、V操作實現的是程式之間的低階通訊,所以P、V為低階通訊原語。它只能傳遞簡單的訊號,不能傳遞交換大量資訊。
(3)若要在程式間傳遞大量資訊可以用Send、 Receive原語(高階通訊原語)。
(4)訊號量機制作為同步工具成效顯著,但作為通訊工具,存在不足:
效率低。生產者一次只能放一個產品(訊息)到緩衝區,消費者一次只能取一個產品(訊息);
通訊對使用者不透明。
使用者用低階通訊工具不夠方便,因為資料結構設定、傳送、程式互斥、同步等都由程式設計師負責。
2 程式通訊型別
高階通訊:使用者可直接利用OS提供的一組通訊命令高效傳送大量資料。分為:
(1)共享儲存器系統(Shared-Memory System) :程式間通過共享某些資料結構或共享儲存區進行通訊。
基於共享資料結構的通訊方式:多個程式通過公用某些資料結構交換資訊。如:生產者-消費者問題中的有界緩衝區。
該方式低效。因資料結構設定、同步等由程式設計師負責。
基於共享儲存區的通訊方式:高階通訊,在儲存器中劃出一塊共享儲存區,程式在通訊前,向系統申請共享儲存區中的一個分割槽,
並指定該分割槽的關鍵字,若系統已經給其它程式分配了這樣的分割槽,則將該分割槽的描述符返回給申請者。
接著,申請者把獲得的共享儲存分割槽連線到本程式上,此後可讀寫該分割槽。
注:以上兩種方式的同步互斥都要由程式自己負責。
(2)訊息傳遞系統(Message Passing System):程式間的資料交換以格式化的訊息為單位,程式設計師利用系統的通訊原語實現通訊。 如:網路中的報文。
作業系統隱藏了通訊的實現細節,簡化了通訊程式編制的複雜性,因而得到廣泛應用。
訊息傳遞系統可分為:
直接通訊(也稱為訊息緩衝通訊):傳送程式直接把訊息傳送給接收者,並將它掛在接收程式的訊息緩衝佇列上。接收程式從訊息緩衝佇列中取得訊息。
間接通訊:傳送程式將訊息傳送到某種中間實體中(信箱),接收程式從(信箱)中取得訊息。也稱信箱通訊。在網路中稱為電子郵件系統。
兩種通訊方式的主要區別:前者需要兩程式都存在,後者不需要。
(3)管道(Pipe)通訊 (共享檔案方式):管道是最初的UNIX IPC形式,它能傳送大量資料,被廣泛採用。
所謂管道,是指用於連線一個讀程式和一個寫程式的檔案,稱pipe檔案。
向管道提供輸入的程式(稱寫程式),以字元流的形式將大量資料送入管道,而接受管道輸出的程式(讀程式)可從管道中接收資料。
管道可以是單工的,也可以是雙工的。
管道分類:無名管道只限於相同祖先的程式間使用,是單向的。比如:父程式與子程式之間,或同一父程式的兩個子程式間。
有名管道可在任意兩個程式間使用,有名檔案,有函式建立,並且建立程式即使終止了,該管道仍存在,是單向的,半雙工的。
管道與訊息佇列的區別:管道中的訊息是無界的
管道是外存的,訊息佇列是記憶體的
管道機制必須提供以下三方面能力:
互斥:一個程式讀/寫管道時,另一個讀/寫程式必須等待。
同步:寫程式寫入後,只有等到讀程式讀走資料後,才能再寫;同樣,讀程式無資料讀時,只有等到寫程式寫入後,被喚醒再讀。
確定對方是否存在:只有對方存在時才能通訊。
3 訊息傳遞通訊的實現方法
訊息通訊分為直接通訊和間接通訊。
直接通訊方式:通訊命令(原語):Send(Receiver,message);
Receive(Sender,message);
間接通訊方式(信箱方式):信箱的建立與撤消。建立者應給出信箱名、屬性(公用、私用或共享)等
信箱中訊息的傳送與接收:Send(mailbox,message);
Receive(mailbox,message);
信箱的種類(3類)
私用信箱:使用者程式為自己建立的信箱,是程式的一部分。只有自己有權讀,其他程式只能向信箱發訊息;可採用單向鏈路實現。
公用信箱:由作業系統建立,所有系統核准程式都可使用;採用雙向通訊鏈路實現。
共享信箱:由某程式建立並指明可共享,則擁有者和共享者可使用。
利用信箱通訊時,有四種關係:1:1 傳送和接收程式之間建立專用鏈路;
N:1 即客戶/伺服器;
1:N 傳送程式可用廣播方式向多個接收者發訊息;
N:N 建立一個公共信箱,多個程式都能向信箱中發消 息,也能從信箱中取訊息;
信箱使用規則:若傳送信件時信箱已滿,則傳送程式被置為“等信箱”狀態,直到信箱有空時才被喚醒
若取信件時信箱中無信,則接收程式被置為“等信件”狀態,直到有信件時才被喚醒
Send實現:send(MailBox,M):把信件M送到指定的信箱MailBox中
步驟:查詢指定信箱MailBox ;
若信箱未滿,則把信件M送入信箱且喚醒“等信件”者;
若信箱已滿,置傳送信件程式為“等信箱”狀態;
Receive實現:receive(MailBox,X):從指定信箱MailBox中取出一封信,存放到指定的地址X中
步驟:查詢指定信箱MailBox ;
若信箱中有信,則取出一封信存於X中且喚醒“等信箱”者;
若信箱中無信件,置接收信件程式“等信件”狀態;