1 / 41

第五章 並行:互斥與同步 Concurrency: Mutual Exclusion and Synchronization

第五章 並行:互斥與同步 Concurrency: Mutual Exclusion and Synchronization. 5.1 並行的原理 並行執行的問題 所有 process 可共享的全域性資源負擔著風險。例如:對全域性變數執行讀寫動作,讀寫順序變得很重要。 最理想的資源分配管理,對於系統而言是困難的。例如: process A 要求鎖住某特定 I/O 通道,但在使用該通道前遭暫停 (suspend) ,對系統而言,如此並沒有效益。 程式設計錯誤的確認變得困難。 ( 執行結果不具重複性 ). 簡單的範例:交錯執行共用變數的程式.

xannon
Download Presentation

第五章 並行:互斥與同步 Concurrency: Mutual Exclusion and Synchronization

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第五章 並行:互斥與同步Concurrency: Mutual Exclusion and Synchronization 5.1 並行的原理 • 並行執行的問題 • 所有process可共享的全域性資源負擔著風險。例如:對全域性變數執行讀寫動作,讀寫順序變得很重要。 • 最理想的資源分配管理,對於系統而言是困難的。例如:process A 要求鎖住某特定I/O通道,但在使用該通道前遭暫停(suspend),對系統而言,如此並沒有效益。 • 程式設計錯誤的確認變得困難。(執行結果不具重複性)

  2. 簡單的範例:交錯執行共用變數的程式 Process P1Process P2 chin = getchar(); chin = getchar(); chout = chin; putchar(chout); chout = chin; putchar(chout); void echo() { chin = getchar(); chout = chin; putchar(chout); } (chin = x) (chin = y) (chout = y)

  3. 簡單的範例 (續):臨界區間的阻擋 Process P1Process P2 chin = getchar(); (P1仍在echo程序,P2被阻擋) chout = chin; putchar(chout); (P1離開echo,P2的阻擋消除) chin = getchar(); chout = chin; putchar(chout); (chin = x) (chin = y)

  4. 簡單的範例 (續):在二個不同的處理器執行 Process P1Process P2 chin = getchar(); chin = getchar(); chout = chin; chout = chin; putchar(chout); putchar(chout); (chin = x) (chin = y)

  5. 作業系統考量:設計和管理的問題 • 作業系統有能力追蹤各個活動的process。(利用process的控制區塊) • 作業系統具有替活動的process分配和取回資源的能力。 • 這些資源包括:處理器時間、記憶體、檔案、I/O裝置。 • 作業系統必須保護每個process的資料和資源。 • process執行的結果必須和執行速度無關(speed independence),且不受其他process執行速度的影響。

  6. Process的交互作用 • 三種感知程度(degrees of awareness): • 無法感知彼此的存在: 獨立的process。 • 間接感知彼此的存在: 共享某些物件的存取權(例如,I/O緩衝區)。 • 直接感知彼此的存在: 設計來一起完成某些動作。 • Process彼此間對資源競爭的情形下,三個控制問題: • Mutual Exclusion (互斥):每次只有一個程式在臨界區域(critical section)。 • Deadlock (死結):例如:Process P1, P2; 資源 R1, R2; P1佔有R2, P2佔有R1。假設需要同時二個資源才能完成動作,則發生死結。 • Starvation (飢餓):假設P1, P2, P3需定期使用資源R,P1使用R時,P2、P3被耽擱(delayed)。當P1離開臨界區域時,假設P3獲得R的使用權,如此週而復始,P2永遠無法取得資源R。

  7. Mutual Exclusion(互斥)機制:進出臨界區域 /* program mutualexclusion */ const int n = …; {* number of processes *} void P(int i) { while(true) { entercritical(i); /* critical section */ exitcritical(i); /* remainder */ } } void main() { parbegin(P(R1), P(R2), …, P(Rn)); }

  8. 資料的一致性(coherence) • 考慮一個簿記應用程式的許多資料項目可能被修改 • 資料a 和 b,被維持在 a = b 的關係下。(例如: a = b = 1) • 考慮以下二個process: P1: a = a + 1; b = b + 1; P2: b = 2 * b; a = 2 * a; • 假設此時二個process分別要求了個別資料項(a及b)的互斥﹔ a = a + 1; b = 2 * b; b = b + 1; a = 2 * a; (結果: a = 4 及 b = 3)

  9. Mutual Exclusion的需求(Requirements) • 任何時間只允許一個process在臨界區域執行。 • Only one process at a time is allowed in the critical section for a resource • 在臨界區域停止執行的process,不准影響其他process執行。 • If a process halts in its critical section, it must not interfere with other processes. • 不准無止盡的耽擱一個要求進入臨界區域的process,不能有死結或飢餓發生 。 • A process requiring the critical section must not be delayed indefinitely; no deadlock or starvation • 當沒有任何Process處於臨界區域時,任何要求進入臨界區域的process必須立刻得到進入的允許。 • 不可對Process的相對速度和處理器的數目做任何假設。 • Process只能在臨界區域內停留一有限的時間。

  10. 5.2 Mutual Exclusion(互斥):軟體處理方式 • 任何對mutual exclusion的嘗試,都必須仰賴硬體上一些基本的排斥機制。 • 常見的機制:對記憶體位址的存取,一次只能有一個的限制。(在單一處理器或SMP皆如此) • 首先考慮2個process: P0 及 P1 • Dekker演算法前四次嘗試均不正確。(說明Mutual Exclusion問題的難度及可能的邏輯盲點) • Dekker演算法, 正確解法。 • Peterson演算法簡化Dekker演算法, 正確解法。

  11. Dekker演算法, 第一次嘗試 • 共享的全域變數: int turn = 0; (英文: It’s your turn.) 執行權在0, 1間交換。 • 忙碌等待 (busy-waiting): while-loop • 保證mutual exclusion • 缺點: • P0, P1必須全然輪流使用臨界區域,執行步調受到較慢的process影響。 • 如果一個process失敗(無論是臨界區域內或外),另一process將永遠被堵住。

  12. Dekker演算法, 第二次嘗試 • 共享全域變數: boolean flag[2] = {false, false}; • 每個process有自己的flag:flag[0]或flag[1]。每個process可檢查另一process的flag,但不能更改資料。 • 當一個process希望進入臨界區域時,它週期性檢查其他的flag直到其值為false(表示另一個process不在臨界區域內)。 • 此時它設定自己的flag為true ,然後進入臨界區域。離開時,設定自己的flag為false。

  13. 缺點:甚至不保證互斥。舉例: • P0執行到while,發現flag[1]為false • P1執行到while,發現flag[0]為false • P0設定flag[0]為true,進入臨界區域。 • P1設定flag[1]為true,進入臨界區域。

  14. Dekker演算法, 第三次嘗試 • 透過改變二行程式碼的位置,嘗試解決問題。不過仍然失敗! • 缺點:死結。舉例: • P0設定flag[0]為true。 • P1設定flag[1]為true 。 • P0執行到while,發現flag[1]為true,忙碌等待。 • P1執行到while,發現flag[0]為true,忙碌等待。

  15. Dekker演算法, 第四次嘗試

  16. 相當接近正確答案,但還有瑕疵: • P0設定flag[0]為true。 • P1設定flag[1]為true 。 • P0執行while (flag[1]為true) ,進入while迴圈主體。 • P1執行while (flag[0]為true) ,進入while迴圈主體。 • P0設定flag[0]為false。延遲。 • P1設定flag[1]為false。延遲。 • P0設定flag[0]為true。 • P1設定flag[1]為true。 • 嚴格來說不是死結(任何一個process改變相對執行速度,就可打破此無窮迴路)

  17. Dekker演算法, 正確解法 boolean flag [2]; int turn; void P0() { while (true) { flag [0] = true; while (flag [1]) if (turn == 1) { flag [0] = false; while (turn == 1) /* do nothing */; flag [0] = true; } /* critical section */; turn = 1; flag [0] = false; /* remainder */; } } void P1(){/*0改為1,1改為0,其餘相同*/} • 利用嘗試一的turn,避免相互禮讓的情形。 • P0想進入臨界區域時,更改flag[0]為真。 • 如果flag[1] 是假,P0進入臨界區域。 • 否則就由turn決定:turn=0,輪由P0堅持進入,P0不斷檢查flag[1] ,直到P1鬆手; • turn=1,P0將flag[0]改為假,鬆手讓P1前進。

  18. Peterson演算法 • Dekker演算法不易推廣到更多process,且證明其正確性需要很高的技巧。 • Peterson[1981]提供一簡單絕妙的解法。 • P0進入臨界區域的條件:flag[1]為false或turn=0 • P0設turn=1,相互禮讓。最後結果看禮讓的順序。 boolean flag [2]; int turn; void P0() { while (true) { flag [0] = true; turn = 1; while (flag [1] && turn == 1) /* do nothing */; /* critical section */; flag [0] = false; /* remainder */; } } void P1() {/* 0改為1, 1改為0, 其餘相同 */}

  19. 5.3 Mutual Exclusion(互斥):硬體支援 • 使中斷無效:在單一處理器機器,並行process只能交錯執行,只要防止process被中斷就可以了。 • 系統核心可以提供這種primitives。 • 因為臨界區域不准被中斷,可以保證mutual exclusion。 • 缺點: • 執行效能退化,處理器介入程式的能力受限制。 • 在多處理器上,不允許中斷,並不能保證mutual exclusion。 while (true) { /* disable interrupts, 關閉中斷 */ /* critical section */ /* enable interrupts, 打開中斷 */ /* 其餘程式 */ }

  20. 特殊的機器指令:測試和設定(test-and-set) • 如果i值為0,則i值改為1,並返回true;否則不改i值。 • 整個指令不能被中斷。 • 離開臨界區間後,將bolt (門閂)設成0。 /* program mutualexclusion */ const int n = /*number of processes */; int bolt; void P(int i) { while (true) { while (!testset (bolt)) /* do nothing */; /* critical section */; bolt = 0; /* remainder */ } } void main(){ bolt = 0; parbegin (P(1),P(2), …,P(n)); } boolean testset(int &i) { if (i == 0) { i = 1; return true; } else return false; }

  21. 交換的指令(exchange) • keyi: 區域變數 • 先設keyi = 1, 利用exchange(keyi, bolt)設定bolt = 1。 • 若keyi=0,表門閂未上鎖,即進入臨界區間。 /* program mutualexclusion */ int const n = /* number of processes**/; int bolt; void P(int i) { int keyi; while (true) { keyi = 1; while (keyi != 0) exchange (keyi, bolt); /* critical section */; exchange (keyi, bolt);/* bolt=0 */ /* remainder */ } } void main() { bolt = 0; parbegin (P(1), P(2), …, P(n)); } void exchange( int &reg, int &mem) { int temp; temp = mem; mem = reg; reg = temp; }

  22. 機器指令方法的特性 • 優點 • 無論幾個process,單一處理器或對稱式多處理器,它都適用。 • 可支援多個臨界區域,每個臨界區域可以用自己的變數。 • 缺點 • 採用忙碌等待(busy-waiting),每一process在等待進入臨界區域,都會消耗處理器的時間。 • 可能發生飢餓。隨機選擇下一個進入臨界區域的process,可能有process一直無法進入。 • 可能發生死結。P1進入臨界區域,P2有較高優先權,P1被迫中斷,若P2也要進入臨界區域,將等待P1,形成死結。

  23. 5.4 號誌(Semaphore) • 基本的原理:透過簡單的訊號,多個process可以彼此(複雜的)合作。 • 訊號化使用的特殊變數,叫做號誌[Dijkstra 1965] 。 • 傳遞訊號 s:signal(s) (Dijkstra原始論文中稱 P operation) • 接收訊號 s:wait(s) (V operation) • 號誌是一整數變數,以下三個動作: • 初設成非負數的值。 • wait動作會減少號誌的數值。如果數值成為負數,執行wait動作的process會被懸置(blocked)。 • signal動作會增加號誌的數值。如果數值不是正整數,則因wait動作懸置的process就會被解除懸置。 • wait 及 signal是atomic (最小的、原子的) ;不能被中斷。 • 二元號誌:只能0和1的數值。與一般號誌有相同表達能力。

  24. 號誌的定義、利用號誌達到互斥 struct semaphore { int count; queueType queue; }; void wait(semaphore s) { s.count--; if (s.count < 0) { place this process in s.queue; block this process; } } void signal(semaphore s) { s.count++; if (s.count <= 0) { remove a process P from s.queue; place process P on ready list; } } /* program mutualexclusion */ const int n = /* number of processes */; semaphore s = 1; void P(int i) { while (true) { wait(s); /* critical section */; signal(s); /* remainder */; } } void main() { parbegin (P(1), P(2), …, P(n)); } 圖5.9 利用號誌達到互斥 圖5.6 號誌的定義

  25. Figure 5.10 Processes Accessing Shared Data Protected by a Semaphore 25

  26. s.count  0: s.count是可以執行wait(s)而不會懸置的process的個數。 • s.count < 0: s.count的大小是懸置在s.queue的process的個數。

  27. 生產者/消費者問題(the producer/consumer problem) • 一個process產生出某種形式的資料,放在緩衝區;有一消費者從緩衝區中,一次拿出一個物件。 • 系統為避免緩衝區重疊使用,限制一次只允許一方(生產者或消費者)進出緩衝區。 producer: while (true) { /* produce item v; */ b[in] = v; in ++; } consumer: while (true) { while (in <= out) /* do nothing */ w = b[out]; out ++; /* consume item w */ } Figure 5.11 Infinite Buffer for the Producer/Consumer Problem (灰色表目前佔用)

  28. 有限循環緩衝區 Figure 5.15 Finite Circular Buffer for the Producer/Consumer Problem

  29. 用號誌解決生產者/消費者問題 /* program producerconsumer */ semaphore n = 0; semaphore s = 1; void producer() { while (true) { produce(); wait(s); append(); signal(s); signal(n); } } void consumer() { while (true) { wait(n); wait(s); take(); signal(s); consume(); } } void main() { parbegin (producer, consumer); } Figure 5.14 A Solution to the Infinite-Buffer Producer/Consumer Problem Using Semaphores

  30. 理髮店問題(a barbershop problem) 理髮師的狀態:理髮、收費、坐在椅子上睡覺等顧客上門 入口 三張椅子,三位理髮師 一個收銀機 站立的空間,消防法令規定顧客上限20人 四個顧客坐著等待 出口 Figure 5.19 The Barbershop

  31. 5.5 監督器(Monitors) • 號誌的缺點:wait 及 signal 動作很powerful且很有彈性,然而使用號誌很難製作出正確的解法。wait及signal動作可能分散在整個程式中,很難看出整個動作的影響效果。 • Monitor [Hoare 1974]是一個程式語言的架構(construct) ,提供和號誌相同的效果,並且容易控制。 • 監督器結合訊號(Monitor with Signal)特性: • 區域變數只能被monitor的程序所存取。 • process藉由呼叫monitor的程序以進入該monitor。 • 同時間內只有一個process可在monitor中執行;任何其他process都被暫停(阻擋),以等待monitor變回可用狀態。 • monitor使用條件變數(condition variable)來支援同步。 • cwait(c):目前呼叫的process等待條件c。 • csignal(c):因等待條件c而暫停的process之一繼續執行。若沒有process在等待,訊號將消失。

  32. Monitor的結構 等待進入的process 單一進入點 區域資料 執行cwait暫停自己 條件變數 程序 1 初始化程式碼 Figure 5.21 Structure of a Monitor 32

  33. /* program producerconsumer */ monitor boundedbuffer; char buffer [N]; /* space for N items */ int nextin, nextout; /* buffer pointers */ int count; /* number of items in buffer */ int notfull, notempty; /* for synchronization */ void append (char x) { if (count == N) cwait(notfull); /* buffer is full; avoid overflow */ buffer[nextin] = x; nextin = (nextin + 1) % N; count++; /* one more item in buffer */ csignal(notempty); /* resume any waiting consumer */ } void take (char x) { if (count == 0) cwait(notempty); /* buffer is empty; avoid underflow */ x = buffer[nextout]; nextout = (nextout + 1) % N; count--; /* one fewer item in buffer */ csignal(notfull); /* resume any waiting producer */ } 33

  34. void producer() { char x; while (true) { produce(x); append(x); } } void consumer() { char x; while (true) { take(x); consume(x); } } voidmain() { parbegin (producer, consumer); } Figure 5.22 A Solution to the Bounded-Buffer Producer/Consumer Problem Using a Monitor 34

  35. 5.6 訊息傳遞(Message Passing) • Process之間互動的基本需求: • 同步:mutual exclusion。 • 通訊:交換資訊。 • 訊息傳遞可同時提供這兩個功能 • 可在分散式系統、對稱式多處理器、單處理器的系統實作 • 訊息傳遞系統的形式 • 傳送:send(目的,訊息) • 接收:receive(來源,訊息) • 二個process間訊息的聯繫暗示某種程度的同步: • 接收者不能接收訊息直到其他process傳送訊息給它。

  36. 訊息系統針對Process間通訊的設計特質 • 傳送與接收都可為阻擋或未阻擋,常見的3種組合: • blocking send, blocking receive:稱為約會(rendezvous) ,允許process間緊密地同步。 • non-blocking send, blocking receive: 最有用的組合。 • non-blocking send, non-blocking receive:都不必等待。 • 一般訊息的格式

  37. 郵件信箱與訊息埠 信箱 • 彈性的使用訊息 • 系統另外提供建立信箱的服務 • 埠為Q1所擁有 埠

  38. 利用訊息達到互斥 /* program mutualexclusion */ const int n = /* number of processes */; void P(int i) { message msg; while (true) { receive (mutex, msg); /* critical section */; send (mutex, msg); /* remainder */; } } void main() { create_mailbox (mutex); send (mutex, null); parbegin (P(1), P(2), . . ., P(n)); } Figure 5.26 Mutual Exclusion Using Messages

  39. 利用訊息之生產者/消費者問題解法 const int capacity = /* buffering capacity */ ; null = /* empty message */ ; int i; void producer() { message pmsg; while (true) { receive (mayproduce, pmsg); pmsg = produce(); send (mayconsume, pmsg); } } void consumer() { message cmsg; while (true) { receive (mayconsume, cmsg); consume (cmsg); send (mayproduce, null); } }

  40. void main() { create_mailbox (mayproduce); create_mailbox (mayconsume); for (int i = 1; i <= capacity; i++) send (mayproduce, null); parbegin (producer, consumer); } Figure 5.27 A Solution to the Bounded-Buffer Producer/Consumer Problem Using Messages

  41. 5.7 讀取者/寫入者(古典)問題 • 問題定義: • 一資料區域由許多process所分享,許多process只讀資料區域(讀取者) ,而許多process只寫入資料區域(寫入者) 。必須滿足下列狀況 • 任何數目的讀取者可同時讀取檔案。 • 一次只有一個寫入者可以寫入檔案。 • 假如寫入者正在寫入檔案,則沒有任何讀取者可以讀取。

More Related