SlideShare a Scribd company logo
OS第四次讀書會
2017/05/18
TA 鄭人瑋
Ch6 同步(Synchronization)
2
Case:Shared Memory
• 多個process共享同個記憶體或資料做各自的事情
• 若沒有做互斥存取的處理,會因Process的執行順序不同而產生
不同的結果(Race Condition)
3
Race Condition例子(1/2)
• 假設當前有個變數Sum = 10,當前有兩個Process (P1, P2)要對
Sum做處理…
4
P1:Sum = Sum + 1
P2:Sum = Sum - 1
Sum = 10
 執行Sum-1
 P2將結果傳回Sum
 執行Sum+1
 P1將結果傳回Sum
Race Condition例子(2/2)
5
• 理想上的執行順序為如下,Sum最後結果會是10:
 執行Sum+1 (S.M.中的Sum尚未變動)
 P1將結果傳回Sum (Sum = 10→11)
 執行Sum-1 (S.M.中的Sum尚未變動)
 P2將結果傳回Sum (Sum = 11→10)
• 但P1, P2不保證在執行過程中不被中斷,因此這四個執行順序有
可能會隨機而產生不同的結果。
• e.g. 假設執行順序為➀➂➁➃,則Sum最終結果為9
同步的用意
當發生狀況或是資料變動時,作業系統以及
其他process要如何反應
6
同步的兩種做法
需要對共享資料之存取進行管制,即同時段只能有一個行程使用該
變數。
1. 不中斷 (Disable Interrupt)
2. 設定臨界區 (Critical Section Design)
7
不中斷 (Disable Interrupt)
• 簡單的方法,在欲執行某process之前先Disable Interrupt,待執行完
畢後再恢復中斷功能,執行的資料就不會被其他process 所干涉。
• 缺點是可能會讓更緊急的事情無法做,會影響Performance,且在多
核心的電腦會有溝通的時間差。
(Disable Interrupt)
 執行Sum+1
 P1將結果傳回Sum
(Enable Interrupt)
(Disable Interrupt)
 執行Sum-1
 P2將結果傳回Sum
(Enable Interrupt)
8
設定臨界區 (Critical Section Design)
• 在Process的內部重要的地方設一個臨界區(C.S.)隔離起來,提供
互斥存取的功能,確保完成該區域之前只有一個process能進去。
9
P1
do{
(入口)
Sum = Sum +1 (C.S.)
(出口)
…
其他部分
}while(1);
P2
do{
(入口)
Sum = Sum -1 (C.S.)
(出口)
…
其他部分
}while(1);
同步的三要件
1. Mutual Exclusion (互斥存取)
• 在任一時間點只允許一個process進入C.S.,不允許多個process同一時
間在各自的C.S.活動
2. Progress (進行)
 若有個process正等待進入C.S.且其他process沒有意願進入C.S.內,則
此process可以在有限時間內進入
 若有多個process想要進入C.S.,則可以在有限時間內挑選一個process
進入C.S.(No Deadlock)
3. Bounded Waiting (有限等待)
• 假設有n個process等待進入C.S.,則最長需等待(n-1)個process才能進
入C.S.,也就是說不能有process一直佔用C.S. (No Starvation)
10
軟體程式解決同步問題
11
用程式設計Critical Section
• 需要天才的腦袋,很難達成…
1. 利用turn變數
2. 利用flag[]陣列
3. turn + flag[] (Peterson’s Solution)
12
利用turn變數
• turn=i代表現在輪到Pi進入C.S.執行
• 若turn不是自己的就會被入口處的迴圈擋住
13
Pi
do{
(入口)
while(turn != i){ }
Pi進入臨界區執行(C.S.)
(出口)
turn = j
…
其他部分
}while(1);
Pj
do{
(入口)
while(turn != j){ }
Pj進入臨界區執行(C.S.)
(出口)
turn = i
…
其他部分
}while(1);
分析同步三要件
• 滿足Mutual Exclusion
 turn變數將其他process鎖在入口區的while迴圈
• 不滿足Progress
 假設當前turn = i,但Pi不想進入C.S.而Pj想,Pj就會被Pi阻礙
• 滿足Bounded Waiting
 假設當前Pi與Pj都想進入C.S.,且Pi搶先進入,則Pj需等待Pi結束後才
能進去
 若Pi執行完畢後又想搶先進入C.S.,但此時turn = j,Pj已可以進入,因
此對Pj而言只需要等待(2-1=1)個process執行時間
14
利用flag[]陣列
• flag[]初始值為false
• flag[i] = true代表Pi想要進入C.S.
15
Pi
do{
(入口)
flag[i] = true
while(flag[j]){ }
Pi進入臨界區執行(C.S.)
(出口)
flag[i] = false
…
其他部分
}while(1);
Pj
do{
(入口)
flag[j] = true
while(flag[i]){ }
Pj進入臨界區執行(C.S.)
(出口)
flag[j] = false
…
其他部分
}while(1);
分析同步三要件
• 滿足Mutual Exclusion
 當flag[i] = true時,Pj就會被擋在入口的while迴圈
• 不滿足Progress
 假設flag[i]與flag[j]同時都變為true,則Pi, Pj皆會卡在while迴圈而產
生deadlock
• 滿足Bounded Waiting
 假設當前Pi與Pj都想進入C.S.,且Pi搶先進入(flag[i] = true),則Pj需等
待Pi結束後(flag[i] = false)才能進去
 若Pi執行完畢後又想搶先進入C.S.,但此時flag[j] = true,Pj已可以進
入,因此對Pj而言只需要等待(2-1=1)個process執行時間
16
Peterson’s Solution
17
Pi
do{
(入口)
flag[i] = true
turn = j
while(flag[j] && turn == j){ }
Pi進入臨界區執行(C.S.)
(出口)
flag[i] = false
…
其他部分
}while(1);
Pj
do{
(入口)
flag[j] = true
turn = i
while(flag[i] && turn == i){ }
Pj進入臨界區執行(C.S.)
(出口)
flag[j] = false
…
其他部分
}while(1);
分析同步三要件
• 滿足Mutual Exclusion
 若Pi, Pj皆想進入C.S.,則flag[i] = flag[j]= true,然turn只會是i或j,因此其
中之一必會擋在while迴圈
• 滿足Progress
 若Pi不想進入C.S.,flag[i] = false,此時若Pj想進入C.S.,則while(flag[i] &&
turn == i)不會阻擋Pj進入C.S.
 若Pi, Pj皆想進入C.S.,flag[i] = flag[j]= true ,同Mutual Exclusion特性會
只讓Pi或Pj進入C.S.
• 滿足Bounded Waiting
 假設當前Pi與Pj都想進入C.S.,且Pi搶先進入(flag[i] = true),則Pj會卡在
while迴圈,需等待Pi結束後(flag[i] = false)才能進去
 若Pi執行完畢後若又想搶先進入C.S.,但此時flag[j] = true,且turn = j,Pi
會被擋在while迴圈,Pj已可以進入,因此對Pj而言只需要等待(2-1=1)個
process執行時間
18
硬體的同步指令
19
硬體指令的C.S. Design
• 與前面方法最大的不同是硬體預設指令屬於atomic instruction,
意即不會被中斷
• 可以很容易以呼叫函數的方式滿足Mutual Exclusion,但要滿足
Bounded Waiting仍然要花許多心思處理
1. Test_and_Set()
2. Compare_and_Swap()
20
Test_and_Set()
• Test_and_Set(boolean &target){
boolean rv = *target;
*target = true;
return rv
}
• 簡單來說,此函數的用意就是return原來input value,並將
input value改成true
21
實作Test_and_Set()
• lock變數初始值為false
22
Pi
do{
(入口)
while(Test_and_Set(&lock)){ }
Pi進入臨界區執行(C.S.)
(出口)
lock = false
…
其他部分
}while(1);
Pj
do{
(入口)
while(Test_and_Set(&lock)){ }
Pj進入臨界區執行(C.S.)
(出口)
lock = false
…
其他部分
}while(1);
分析同步三要件
• 滿足Mutual Exclusion
 類似turn的原理,當Pi先進入C.S.,lock會被改為true,此時Pj欲進入
C.S.就會被卡在while迴圈
• 滿足Progress
 若Pi不想進入C.S.,就不會執行Test_and_Set(),此時lock = false,
若Pj想進入C.S.,則while(Test_and_Set(&lock))不會阻擋Pj進入C.S.
 若Pi, Pj皆想進入C.S.,同Mutual Exclusion特性,在其中之一的
process從C.S.出來執行lock = false之前,會只讓Pi或Pj進入C.S.
• 不滿足Bounded Waiting
 假設Pi當前進入C.S.執行(lock = true),執行完後執行lock = false,此
時Pi有可能還會繼續搶到lock的主權而導致Pj永遠被卡在while迴圈
(Starvation)
23
Compare_and_Swap()
• Compare_and_Swap(int *lock, int expected, int new_value){
int tmp = *lock;
if(*lock == expected){
*lock = new_value;
}
return tmp;
}
• 簡單來說,函數用意是將lock值從expected轉換成new_value
(e.g. 0 轉換成 1)
24
實作Compare_and_Swap()
• lock變數初始值為0
25
Pi
do{
(入口)
while(Compare_and_Swap(&lock,0,1)){ }
Pi進入臨界區執行(C.S.)
(出口)
lock = 0
…
其他部分
}while(1);
Pj
do{
(入口)
while(Compare_and_Swap(&lock,0,1)){ }
Pj進入臨界區執行(C.S.)
(出口)
lock = 0
…
其他部分
}while(1);
分析同步三要件
• 滿足Mutual Exclusion
 類似Test_and_Set()的原理,當Pi先進入C.S.,lock會被swap為1,此
時Pj欲進入C.S.會因為lock≠expected,就會被卡在while迴圈
• 滿足Progress
 與Test_and_Set()同,因為lock功能是相同的
• 不滿足Bounded Waiting
 與Test_and_Set()同,Pi有可能還會繼續搶到lock的主權而導致Pj永遠
被卡在while迴圈(Starvation)
26
OS提供同步的方法
27
OS提供解決同步問題的方法
• OS提供一些資料形態來達成同步機制
1. Mutex lock
2. Semaphore
28
Mutex lock
• 一種抽象的概念,利用acquire()及release()以busy waiting限制
要進入C.S.的process,達成Mutual Exclusion。(又稱為spin
lock)
• available預設為true
• Mutex lock就好像一間廁所的鑰匙,有人使用就會拿走鑰匙
(available = false),等使用完再釋放出鑰匙使用權(available =
true)
29
30
單純使用mutex lock會
不滿足Bounded Waiting!
(因為有可能有個process
一直搶到lock)
Semaphore
• 是一種解決同步問題及臨界區設計的資料形態
• C語言宣告:Semaphore *S = make_semaphore(1);
• 與mutex lock類似,也提供兩種方法wait(S), signal(S)來控制要
進入C.S.的process
31
wait() and signal() (1/2)
• 最原始的方法本身幾乎與mutex lock無異,是以busy waiting的
方式實現C.S. Design
32
wait(*S){
while(S<=0){}
S = S-1;
}
signal(*S){
S = S+1;
}
wait() and signal() (2/2)
• 修正後的wait() and signal()方法以block()及wakeup()方法,以
sleep(暫時釋放CPU主控權)的方式實現C.S. Design
33
wait(*S){
S = S-1;
if(S < 0){
block();
//將Process放在Waiting Queue等待
}
}
signal(*S){
S = S+1;
if(S <= 0){
wakeup();
//將Process從Waiting Queue拿到Ready Queue
}
}
Semaphore實作方式
Semaphore mutex = 1;
do{
wait(mutex);
Pi進入C.S.執行
signal(mutex);
…
其他部分程式
}while(true)
34
不會像mutex lock會產生
starvation,因為block()跟
wakeup()將process放在queue
(FIFO),因此就算Pi執行完畢後又
要回去搶鑰匙,還是得排隊!
兩種不同的Semaphore
1. Binary Semaphore
• S變數只能為0或1,與mutex lock做法雷同
• S=1代表有1把鑰匙可以用,S=0代表沒有鑰匙可以用
2. Counting Semaphore
• S可以為1以上的數字,也可以為負
• S = n代表有n把鑰匙可以用,S=-n代表目前有n個process在waiting
queue中等待
35
Mutex lock以及Semaphore的差別
• 雖然從Binary Semaphore來看,S=1就等於Mutex lock中的
true,而S=0則為false
• 然mutex lock只能從正在C.S.中的process釋放鑰匙使用權,而
Semaphore則有可能從其他process釋放使用權(因為只要能執行
S = S+1即可)
• 因此通常mutex lock會用來保護C.S.,而Semaphore則會用來處
理更進階的同步問題
36
經典的同步問題
37
一.生產者消費者問題
• Producer會不斷地放東西至Bounded Buffer
• Consumer會不斷地從Bounded Buffer拿東西
• 需要同步的問題:
1. Buffer為空時,Consumer需要等待
2. Buffer為滿時,Prodecer需要等待
38
空間為n的Buffer生產者 消費者
Semaphore宣告
• Semaphore *mutex = make_semaphore(1);
 mutex用來控制Producer及Consumer在Buffer中的存取
 初始值為1
• Semaphore *empty = make_semaphore(n);
 empty用來表示目前有多少Buffer的空間可用
 若empty <= 0 時表示Buffer已滿,Producer就要等待
 初始值為n
• Semaphore *full = make_semaphore(0);
 full用來表示目前有多少Buffer空間已被使用
 若full = 0 時表示Buffer已空,Consumer就要等待
 初始值為0
39
Semaphore實作
40
Producer
do{
wait(empty);
wait(mutex);
Producer放東西到Buffer
signal(mutex);
signal(full);
}while(true)
Consumer
do{
wait(full);
wait(mutex);
Consumer到Buffer拿東西
signal(mutex);
signal(empty);
}while(true)
二.讀寫問題
• 有多個Writer與多個Reader對同一份文件進行讀寫
• Reader:讀取文件,且可以多個Reader同時讀取
• Writer:可以讀/寫文件,同一時間只能有一個Writer更新
• 需要同步的問題:
1. 同一時間只能有一個Writer進入文件更新
2. 當有Reader在讀取文件時,Writer需要等待,不能進入文件
41
文件
Writer
Writer
Writer
Reader
Reader
Reader
初始值宣告
• reader_count = 0;
 用來計算目前有幾個Reader的變數
 初始值為0
• Semaphore *rw_mutex = make_semaphore(1);
 用來控制Reader/Writer進入文件的Semaphore
 當rw_mutex = 0時代表有其他人進入文件需要等待
 初始值為1
• Semaphore *mutex = make_semaphore(1);
• 用來控制reader_count增減的Semaphore
• 當mutex = 0時代表reader_count正在變動需要等待
• 初始值為1
42
Semaphore實作
43
Writer
do{
wait(rw_mutex);
Writer進入文件讀寫
signal(rw_mutex);
}while(true)
Reader
do{
wait(mutex);
reader_count ++;
if(reader_count == 1){
wait(rw_mutex)
}
signal(mutex);
Reader進入文件讀取
wait(mutex);
reader_count--;
if(reader_count == 0){
signal(rw_mutex)
}
signal(mutex);
}while(true)
Reader程式修正(1/2)
• 上述方法會有Starvation問題:當有無限個Reader進入讀取時
Writer會沒機會進入文件!
• 解決方法:
Semaphore *max_reader = make_semaphore(k);
新增一個Semaphore,限制最多k個Reader讀取就要讓Writer寫入
但在不常讀寫的檔案中會受到限制→可用timestemp解決
44
讀寫問題程式修正(2/2)
45
Reader
do{
wait(mutex);
reader_count ++;
if(reader_count == 1){
wait(rw_mutex)
}
signal(mutex);
wait(max_reader);
Reader進入文件讀取
wait(mutex);
reader_count--;
if(reader_count == 0){
signal(rw_mutex)
}
signal(mutex);
}while(true)
Writer
do{
wait(rw_mutex);
Writer進入文件讀寫
signal(max_reader);
signal(rw_mutex);
}while(true)
三.哲學家問題
• 有5位哲學家圍著在圓桌,每人左右各放一支筷子。
• 哲學家不是思考(等待)就是吃飯
• 吃飯時需要身邊兩支筷子
• 需要同步的問題:
1. 哲學家想吃時但身旁任一筷子有人拿走就要等待(思考)
46
Semaphore宣告
• Semaphore *chopstick[0..4] = make_semaphore(1);
1. 每支筷子都設一個Semaphore
2. 當chopstick[0] = 0代表第一支筷子有人使用
3. 初始值皆設為1
47
chopstick[0] chopstick[1]
chopstick[2]
chopstick[3]
chopstick[4]
Semaphore實作
48
Philosopher[i]
do{
wait(chopstick[i]);
wait(chopstick[(i+1)%5]);
Philosopher[i]吃飯
signal(chopstick[i]);
signal(chopstick[(i+1)%5]);
}while(true)
實作分析
• 上述實作其實是有問題的!→Deadlock
• 考率極端狀況
1. 若每個哲學家都拿起右邊的筷子(chopstick[i]),則會造成所有的哲學
家都陷入沉思等待
2. 當第一、三個哲學家分別持續拿著第一、二雙筷子時(chopstick[0],
chopstick[1]),會造成第二個哲學家無筷子可用
49
解決方法(1/2)
1. 只允許最多(n-1)個哲學家吃,意即最多只能四位拿筷子→可解
決Deadlock,但會有Starvation發生
2. 除非哲學家能夠順利拿到左右邊兩支筷子,否則不讓哲學家拿
筷子吃飯→打破Hold and Wait,一樣會有Starvation
3. 奇數者先取左邊再取右邊;偶數者先取右邊再取左邊→打破
Circular Waiting,一樣會有Starvation
4. Chandy/Misra Solution:先將筷子湊成對,要吃的領筷子券,
吃完再釋放→筷子券搶奪一樣會有Starvation
5. 利用Monitor
50
為何要使用Monitor?
• Programmer很多時候會誤用Semaphore而造成程式違反
Mutual Exclution甚至是產生Deadlock (e.g. 把signal(), wait()弄
反)
• Monitor是為了解決同步問題所建立的高階資料結構,分成已下
三架構:
1. 一組Procedures :供外界呼叫使用
2. 共享資料區:此區域會宣告一些共享變數,且只提供給Monitor內各
Procedure所共用,外界Process不得直接使用。
3. 初始區: 設定某些共享資料變數的初値
• Programmer可以專注在同步問題,而不用費心思去解決互斥問
題
51
Monitor實作哲學家問題(定義)
52
monitor DiningPhilosophers{
enum { THINKING; HUNGRY, EATING) state [5] ;
condition self [5];
void pickup (int i) {
state[i] = HUNGRY;
test(i);
if (state[i] != EATING) self [i].wait;
}
void putdown (int i) {
state[i] = THINKING;
// test left and right neighbors
test((i + 4) % 5);
test((i + 1) % 5);
}
void test (int i) {
if ( (state[(i + 4) % 5] != EATING) &&
(state[i] == HUNGRY) &&
(state[(i + 1) % 5] != EATING) ) {
state[i] = EATING ;
self[i].signal () ;
}
}
initialization_code() {
for (int i = 0; i < 5; i++)
state[i] = THINKING;
}
}
資料共享區
Procedures
初始區
Monitor實作哲學家問題(使用)
Philosopher[i]
do{
DiningPhilosophers.pickup (i);
philosopher[i]開始吃東西
DiningPhilosophers.putdown (i);
}while(true)
53
參考資料
• https://lifeandnote.wordpress.com/2014/08/26/%E7%AD%8
6%E8%A8%98-
os%E4%B8%AD%E7%9A%84%E7%B6%93%E5%85%B8%E5
%90%8C%E6%AD%A5%E5%95%8F%E9%A1%8C/
• http://sjchen.im.nuu.edu.tw/OS/97Spring/Ch_7.pdf
• http://isrc.asia.edu.tw/course/os/chapter6-2012.ppt
54
Q & A
55

More Related Content

PPTX
Os讀書會20170609
PPTX
Os讀書會20170504
PDF
超簡単!Apache TomcatをWindowsにインストール
PPTX
Coherenceを利用するときに気をつけること #OracleCoherence
PPTX
Wiresharkの解析プラグインを作る ssmjp 201409
PPTX
オレ流のOpenJDKの開発環境(JJUG CCC 2019 Fall講演資料)
PDF
【HinemosWorld2014】B2-3_Hinemos徹底解剖~ジョブ編~
PDF
iostatの見方
Os讀書會20170609
Os讀書會20170504
超簡単!Apache TomcatをWindowsにインストール
Coherenceを利用するときに気をつけること #OracleCoherence
Wiresharkの解析プラグインを作る ssmjp 201409
オレ流のOpenJDKの開発環境(JJUG CCC 2019 Fall講演資料)
【HinemosWorld2014】B2-3_Hinemos徹底解剖~ジョブ編~
iostatの見方

What's hot (20)

PDF
Java Concurrency by Example
PDF
Redmine + MySQL 応答性能の調査結果と対策
PDF
PWNの超入門 大和セキュリティ神戸 2018-03-25
PDF
Linuxのユーザーランドをinitから全てまるごとgolangで書く
PPTX
徳丸本に載っていないWebアプリケーションセキュリティ
PPTX
git講座用パワーポイント
PDF
Bonfire API #1 APIのリトライ処理
ODP
Format string Attack
PPTX
PHP と SAPI と ZendEngine3 と
PDF
20221202-WHOIS教室 Ver3.0.pdf
PPTX
KOCOON – KAKAO Automatic K8S Monitoring
PPTX
Dbts 分散olt pv2
PDF
PDF
OpenJDK トラブルシューティング #javacasual
PDF
雑なMySQLパフォーマンスチューニング
PDF
React native felicaの紹介
PDF
Play with FILE Structure - Yet Another Binary Exploit Technique
PDF
Interpreter, Compiler, JIT from scratch
PPTX
jcmd をさわってみよう
PDF
PHP7の内部実装から学ぶ性能改善テクニック
Java Concurrency by Example
Redmine + MySQL 応答性能の調査結果と対策
PWNの超入門 大和セキュリティ神戸 2018-03-25
Linuxのユーザーランドをinitから全てまるごとgolangで書く
徳丸本に載っていないWebアプリケーションセキュリティ
git講座用パワーポイント
Bonfire API #1 APIのリトライ処理
Format string Attack
PHP と SAPI と ZendEngine3 と
20221202-WHOIS教室 Ver3.0.pdf
KOCOON – KAKAO Automatic K8S Monitoring
Dbts 分散olt pv2
OpenJDK トラブルシューティング #javacasual
雑なMySQLパフォーマンスチューニング
React native felicaの紹介
Play with FILE Structure - Yet Another Binary Exploit Technique
Interpreter, Compiler, JIT from scratch
jcmd をさわってみよう
PHP7の内部実装から学ぶ性能改善テクニック
Ad

Os讀書會20170518