Too Much Pizza Person A Person B 3:00 Look in fridge. Pizza! 3:05 Leave for store. Look in fridge. Pizza! 3:10 Arrive at store. Leave for store. Operating System I 3:15 Buy pizza. Arrive at store. 3:20 Arrive home. Buy pizza. 3:25 Put away pizza. Arrive home. Process Synchronization 3:30 Put pizza away. Oh no! Producer Consumer Cooperating Processes ! Consider: print spooler ! Model for cooperating processes – Enter file name in spooler queue ! Producer “produces” and item that – Printer daemon checks queue and prints consumer “consumes” ! Bounded buffer (shared memory) A B free 9 item buffer[MAX]; /* queue */ ... ... int counter; /* num items */ letter hw1 lab1.c (empty) 6 7 8 9 ! “Race conditions” (ugh!) Producer Consumer item i; /* item produced */ item i; /* item consumed */ int in; /* put next item */ int out; /* take next item */ while (1) { while (1) { while (counter == 0) {/*no-op*/} produce an item while (counter == MAX){/*no-op*/} item = buffer[out]; buffer[in] = item; out = (out + 1) % MAX; in = (in + 1) % MAX; counter = counter - 1; counter = counter + 1; consume the item } } 1
Trouble! Critical Section R1 = counter P: {R1 = 5} ! Mutual Exclusion P: R1 = R1 + 1 {R1 = 6} – Only one process inside critical region R2 = counter {R2 = 5} C: ! Progress C: R2 = R2 -1 {R2 = 4} – No process outside critical region may block other processes wanting in counter = R1 P: {counter = 6} ! Bounded Waiting C: counter = R2 {counter = 4} – No process should have to wait forever (starvation) ! Note, no assumptions about speed! Second Try First Try: Strict Alternation int flag[1]; /* boolean */ int turn; /* shared, i or j */ while(1) { while(1) { flag[i] = true; while (turn <> i) { /* no-op */} while (flag[j]) { /* no-op */} /* critical section */ /* critical section */ turn = j flag[i] = false; /* remainder section */ /* remainder section */ } } Third Try: Peterson’s Solution Multiple-Processes int flag[1]; /* boolean */ int turn; ! “Bakery Algorithm” while(1) { ! Common data structures flag[i] = true; boolean choosing[n]; turn = j; int num[n]; while (flag[j] && turn==j){ } ! Ordering of processes /* critical section */ – If same number, can decide “winner” flag[i] = false; /* remainder section */ } 2
Multiple-Processes Synchronization Hardware choosing[i] = true; ! Test-and-Set: returns and modifies atomically num[i] = max(num[0],num[1] …)+1 choosing[i] = false; int Test_and_Set(int target) { for (j=0; j<n; j++) { int temp; while(choosing[j]) { } temp = target; while( num[j]!=0 && target = true; (num[j],j)<(num[i],i) ) {} return temp; } } /* critical section */ num[i] = 0; Synchronization Hardware Semaphores ! Does not require “busy waiting” while(1) { ! Semaphore S (shared, often initially =1) while (Test_and_Set(lock)) { } – integer variable /* critical section */ – accessed via two (indivisible) atomic operations lock = false; wait(S): S = S - 1 /* remainder section */ if S<0 then block(S) } signal(S): S = S + 1 if S<=0 then wakeup(S) Critical Section w/Semaphores Semaphore Implementation semaphore mutex; /* shared */ ! How do you make sure the signal and the wait operations are atomic? while(1) { wait(mutex); /* critical section */ signal(mutex); /* remainder section */ } 3
Trouble! Semaphore Implementation signal(S) wait(S) /* cr */ /* cr */ ! Disable interrupts – Why is this not evil? wait(S) wait(S) – Multi-processors? ! Use correct software solution /* cr */ ! Use special hardware, i.e.- Test-and-Set Process A Process B wait(S) wait(Q) wait(Q) wait(S) … … Classical Synchronization Dining Philosophers Problems ! Bounded Buffer ! Phisolophers – Think ! Readers Writers – Sit ! Dining Philosophers – Eat – Think ! Need 2 chopsticks to eat Dining Philosophers Philosopher i: while (1) { /* think… */ Other Solutions? wait(chopstick[i]); wait(chopstick[i+1 % 5]); /* eat */ signal(chopstick[i]); signal(chopstick[i+1 % 5]); } 4
Other Solutions Readers-Writers ! Allow at most N-1 to sit at a time ! Readers only read the content of object ! Allow to pick up chopsticks only if both are ! Writers read and write the object available ! Critical region: ! Asymmetric solution (odd L-R, even R-L) – No processes – One or more readers (no writers) – One writer (nothing else) ! Solutions favor Reader or Writer Readers-Writers Readers-Writers Shared: Reader: semaphore mutex, wrt; wait(mutex); int readcount; readcount = readcount + 1; if (readcount==1) wait(wrt); signal(mutex); Writer: wait(wrt) /* read stuff */ /* write stuff */ wait(mutex); signal(wrt); readcount = readcount - 1; if (readcount==0) signal(wrt); signal(mutex); “Critical Region” “Critical Region” ! High-level construct ! Deadlocks still possible: region X do S – Process A: Process A Process B X is shared variable region X do wait(x-mutex) ... region Y do S1; ... wait(y-mutex) S is sequence of statements ... wait(x-mutex) – Process B: ! Compiler says: wait(y-mutex) ... region Y do wait(x-mutex) region X do S2; S X signal(x-mutex) A “cycle” B Y 5
Bounded Buffer Conditional Critical Regions Shared: ! High-level construct struct record { region X when B do S item pool[MAX]; X is shared variable int count, in, out; B is boolean expression (based on c.r.) }; S is sequence of statements struct record buffer; Bounded Buffer Producer Bounded Buffer Consumer region buffer when (count < MAX){ region buffer when (count > 0){ pool[in] = i; /* next item*/ nextc = pool[out]; in = in + 1; out = (out + 1) % n; count = count + 1; count = count - 1; } } Monitors Monitor Producer-Consumer ! High-level construct monitor ProducerConsumer { ! Collection of: condition full, empty; /* not semphores */ integer count; – variables – data structures /* function prototypes */ – functions void producer(); – Like C++ classs void consumer(); ! One process active inside void enter(item i); item remove(); ! “Condition” variable } – not counters like semaphores 6
Monitor Producer-Consumer Monitor Producer-Consumer void producer() { void enter (item i) { item i; if (count == N) wait(full); while (1) { /* add item i */ /* produce item i */ count = count + 1; ProducerConsumer .enter(i); if (count == 1) then signal(empty); } } } item remove () { void consumer() { if (count == 0) then wait(empty); item i; /* remove item into i */ while (1) { count = count - 1; i = ProducerConsumer .remove(); if (count == N-1) then signal(full); /* consume item i */ return i; } } } Ex: Cond. Crit. Region w/Sem Other IPC Synchronization region X when B do S { wait(x-mutex); ! Sequencers if (!B) { ! Path Expressions x-count = x-count + 1; ! Serializers signal(x-mutex); ! ... wait(x-delay); ! All essentially equivalent in terms of /* wakeup loop */ semantics. Can build each other. x-count = x-count -1 } /* remainder */ Ex: Wakeup Loop Ex: Remainder while (!B) { S; x-temp = x-temp + 1; if (x-count > 0) { if (x-temp < x-count) x-temp = 0; signal(x-delay); signal(x-delay); else } else signal(x-mutex); signal(x-mutex); wait(x-delay); } 7
Trouble? Message Passing ! Monitors and Regions attractive, but ... ! Communicate information from one process to another via primitives: – Not supported by C, C++, Pascal ... send(dest, &message) N semaphores easy to add ! Monitors, Semaphores, Regions ... receive(source, &message) – require shared memory ! Receiver can specify ANY – break on multiple CPU (w/own mem) ! Receiver can block (or not) – break distributed systems ! Message Passing! Producer-Consumer Consumer Mailbox void Producer() { while (TRUE) { /* produce item */ void Consumer { build_message(&m, item); for (i=0; i<N; i++) send(consumer, &m); send(producer, &m); /* N empties */ receive(consumer, &m); /* wait for ack */ while(1) { }} receive(producer, &m); void Consumer { extract_item(&m, &item); while(1) { send(producer, &m); /* ack */ receive(producer, &m); “Rendezvous” /* consume item */ extract_item(&m, &item); } send(producer, &m); /* ack */ } /* consume item */ }} New Troubles ! Scrambled messages ( checksum ) ! Lost messages ( acknowledgements ) ! Lost acknowledgements ( sequence no .) New Troubles with Messages? ! Process unreachable (down, terminates) ! Naming ! Authentication ! Performance (from copying, message building) 8
Recommend
More recommend