550 likes | 558 Views
Learn about semaphores and mutual exclusion algorithms used in OS, avoid deadlock and wasted CPU time by efficient synchronization methods.
E N D
Synchronization: semaphoresand some more stuff Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
What's wrong with busy waiting? • Doesn't make sense for uni-processor • May cause deadlock • Wastes CPU time • But may be efficient if waiting-time is short The mutual exclusion algorithms we saw used busy-waiting. What’s wrong with that? Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Deadlock results What's wrong with busy waiting? Busy waiting may cause deadlock • Process A's priority is higher than process B's • Process B enters the CS • Process A needs to enter the CS, busy-waits for B to exit the CS • Process B cannot execute as long as the higher-priority process A is executing/ready Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Outline • Semaphores and the producer/consumer problem • Counting semaphores from binary semaphores • Event counters and message passing synchronization Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Semaphores Two atomic operations are supported by a semaphoreS: up(S) [the 'v' operation] • If there are blocked processes, wake-up one of them • Else S++ down(S) [the 'p' operation] • If S≤0 the process is blocked. It will resume execution only after it is woken-up • Else S-- • S is non-negative • Supported by Windows, Unix, … Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Semaphores: is the following correct? up(S) [the 'v' operation] • S++ • If there are blocked processes, wake-up one of them Two atomic operations are supported by a semaphoreS: down(S) [the 'p' operation] • If S≤0 the process is blocked. It will resume execution only after it is woken-up • S-- Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
A single up() freed 2 down()s Pseudo-code in previous slide is wrong Consider the following bad scneario: • S=0 and process A performs down(S) – A is blocked • Process B performs up(S) – S=1 A is ready • Process C performs down(S) – S=0 & C proceeds • Process A gets a time-slice and proceeds – S=-1 Operating Systems, 2011, Danny Hendler & Amnon Meisels
Implementing mutex with semaphores Shared data: semaphorelock; /* initially lock = 1 */ down(lock) Critical section up(lock) Yes Does the algorithm satisfy mutex? Does it satisfy deadlock-freedom? Does it satisfy starvation-freedom? Yes Depends… Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
More on synchronization using semaphores Three processes p1; p2; p3 semaphores s1 = 1, s2 = 0; p1p2p3 down(s1); down(s2); down(s2); A B C up(s2); up(s2); up(s1); Which execution orders of A, B, C, are possible? (A B* C)* Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
No guarantee for correct synchronization(when multiple semaphores/locks are used) P0P1 down(S); down(Q); down(Q); down(S); move1move2 up(S); up(Q); up(Q) up(S); 1 1 • Example: move money between two accounts which are protected by semaphores S and Q Does this work? Deadlock! Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Negative-valued semaphores up(S) • S++ • If there are blocked processes (i.e. S≤0), wake-up one of them Two atomic operations are supported by a semaphoreS: down(S) • S-- • If S<0 the process is blocked. It will resume execution only when S is non-negative -3 • If S is negative, then there are –S blocked processes Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Negative semaphore Implementation type semaphore = record value: integer; L: list of process; end; -3 L atomic down(S):S.value--; if (S.value < 0) { add this process to S.L;sleep; } atomic up(S): S.value++; if (S.value <= 0) { remove a process P from S.L;wakeup(P); } Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Implementing a spin-lock with TSL (test-set-lock) mutex_lock: TSL REG, mutex CMP REG, #0 JZE ok CALL thread_yield JMP mutex_lock ok: RET mutex_unlock: MOV mutex, #0 RET Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Implementing a negative semaphore with TSL -3 type semaphore = record value, flag: integer; L: list of process; end; L down(S):repeat until test-and-set(S.flag) S.value--; if (S.value < 0) { add this process to S.L;S.flag=0sleep; }else S.flag=0 up(S):repeat until test-and-set(S.flag)S.value++; if (S.value <= 0) { remove a process P from S.L;wakeup(P); } S.flag=0 Any problem with this code? In down(), resetting flag and sleeping should be atomic. Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
More on semaphore implementation • On a uni-processor, disabling interrupts may be used • TSL implementation works for multi-processors • On a multi-processor, we can use spin-lock mutual exclusion to protect semaphore access • The 2 operations that need to be performed atomically are performed as kernel entries to the process table Why is this better than busy-waiting in the first place? Busy-waiting is now guaranteed to be very short Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
out in Producer-Consumer Problem • Paradigm for cooperating processes, • producer process produces information that is consumed by a consumer process • Two versions • unbounded-bufferplaces no practical limit on the size of the buffer • bounded-bufferassumes that there is a fixed buffer size buffer Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Bounded Buffer buffer 0 1 2 item1 2 Out item2 3 item3 consumer 4 producer item4 5 6 In 7 Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Implementation using semaphores • Two processes or more use a shared buffer in memory • The buffer has finite size(i.e., it is bounded) • The producer writes to the buffer and the consumer reads from it • A full buffer stops the producer • An empty buffer stops the consumer Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Producer-Consumer implementation with semaphores #define N 100 /* Buffer size */ typedefintsemaphore; semaphoremutex = 1; /* access control to critical section */ semaphore empty = N; /* counts empty buffer slots */ semaphore full = 0; /* counts full slots */ void producer(void) { int item; while(TRUE) { produce_item(&item); /* generate something... */ down(&empty); /* decrement count of empty */ down(&mutex); /* enter critical section */ enter_item(item); /* insert into buffer */ up(&mutex); /* leave critical section */ up(&full); /* increment count of full slots */ } } Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Producer-Consumer implementation with semaphores void consumer(void){int item; while(TRUE) {down(&full); /* decrement count of full */down(&mutex); /* enter critical section */remove_item(&item); /* take item from buffer) */up(&mutex); /* leave critical section */up(&empty); /* update count of empty */consume_item(item); /* do something... */ }} Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Outline • Semaphores and the producer/consumer problem • Counting semaphores from binary semaphores • Event counters and message passing synchronization Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Binary Semaphore • Assumes only values 0 or 1 • Wait blocks if semaphore=0 • Signal (up operation) either wakes up a waiting process, if there is one, or sets value to 1 (if value is already 1, signal is “wasted”) • How can we implement a counting semaphore by using binary semaphores? Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Implementing a counting semaphore with binary semaphores (user space): take 1 down(S): down(S1); S.value--; if(S.value < 0){ up(S1); down(S2); } else up(S1); binary-semaphore S1 initially 1, S2 initially 0, S.value initially 1 up(S): down(S1); S.value++; if(S.value≤ 0) up(S2); up(S1) L1: L2: This code does not work. Why? Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Race condition for counting semaphore take 1 • Processes Q1 – Q4 perform down(S), Q2 – Q4 are preempted between lines L1 and L2: the value of the counting semaphore is now -3 • Processes Q5-Q7 now perform up(S): the value of the counting semaphore is now 0 • Now, Q2-Q4 wake-up in turn and perform line L2 (down S2) • Q2 runs but Q3-Q4 block. There is a discrepancy between the value of S and the number of processes waiting on it Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Implementing a counting semaphore with binary semaphores (user space): take 2 down(S): down(S1); S.value--; if(S.value < 0){ up(S1); //L1 down(S2); } //L2 up(S1); binary-semaphore S1 initially 1, S2 initially 0, S.value initially 1 up(S): down(S1); S.value++; if(S.value≤ 0) up(S2); else up(S1) Does this code work? Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
The effect of the added ‘else’ • up(S1) is performed by up(S) only if no process waits on S2 • Q5 leaves up(S) without releasing S1 • Q6 cannot enter the critical section that protects the counter • It can only do so after one of Q2-Q4 releases S1 • This generates a “lock-step” situation: an up(), a down(), an up()… • The critical section that protects the counter is entered alternately by a producer or a consumer Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Recall the bounded-buffer algorithm #define N 100 typedefintsemaphore; semaphoremutex = 1; semaphoreempty = N; semaphore full = 0; void producer(void) { int item; while(TRUE) { produce_item(&item); down(&empty); down(&mutex); enter_item(item); up(&mutex); up(&full); } } void consumer(void){int item; while(TRUE) {down(&full); down(&mutex); remove_item(&item);up(&mutex); up(&empty);consume_item(item); }} Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
A Problematic Scheduling Scenario Consider a Bounded buffer of 5 slots. Assume there are 6 processes each filling five slots in turn. 1 2 Empty.Value = 5 3 4 5 6 Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
A Problematic Scheduling Scenario 1. five slots are filled by the first producer 1 2 Empty.Value = 0 3 4 5 6 Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
A Problematic Scheduling Scenario 1. The second producer is blocked 1 2 Empty.Value = -1 3 4 5 6 Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
A Problematic Scheduling Scenario 1. The third producer is blocked 1 2 Empty.Value = -2 3 4 5 6 Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
A Problematic Scheduling Scenario 1. The fourth producer is blocked 1 2 Empty.Value = -3 3 4 5 6 Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
A Problematic Scheduling Scenario 1. The fifth producer is blocked 1 2 Empty.Value = -4 3 4 5 6 Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
A Problematic Scheduling Scenario 2. All blocked producers are waiting on S2 1 2 Empty.Value = -5 3 4 5 6 Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
A Problematic Scheduling Scenario 3. The consumer consumes an item and is blocked on Empty.S1 until a producer adds an item. 1 2 Empty.Value = -5 3 4 5 6 Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
A Problematic Scheduling Scenario 3. The consumer consumes an item and is blocked on S1 , one producer adds an item. 1 2 Empty.Value = -4 3 4 5 6 Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
A Problematic Scheduling Scenario 4. Consumer must consume, only then another producer wakes up and produces an item 1 2 Empty.Value = -3 3 4 5 6 Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
A Problematic Scheduling Scenario 4. Same as in step 3. 1 2 Empty.Value = -2 3 4 5 6 Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
A Problematic Scheduling Scenario 5. And again… 1 2 Empty.Value = -1 3 4 5 6 Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Implementing a counting semaphore with binary semaphores (user space): take 3 (P.A. Kearns, 1988) down(S) down(S1); S.value--; if(S.value < 0){ up(S1); //L1 down(S2); //L2 down(S1); S.wake--; if(S.wake > 0) then up(S2);} //L3 up(S1); binary-semaphore S1=1, S2=0, value initially 1, integer wake=0 up(S): down(S1); S.value++; if(S.value <= 0) { S.wake++; up(S2); } up(S1); Does THIS work? Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Correctness arguments (Kearns)… • The counter S.wake is used when processes performing down(S) are preempted between lines L1 and L2 • In such a case, up(S2) performed by processes during up(S) has no effect • However, these processes accumulate their waking signals on the (protected) counter S.wake • After preemption is over, any single process that wakes up from its block on down(S2) checks the value of S.wake • The check is again protected • For each count of the wake-up signals, the awakened process performs the up(S2) (in line L3) • Each re-scheduled process wakes up the next one Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Kearns' algorithm is wrong • Processes P0..P7 perform down(S), P0 goes through, P1..P7 are preempted just after line L2 of the operation • Processes P8..P11 perform up(S) and their up(S2) operations release, say, P1..P4 • Processes P5, P6, P7 are still waiting on S2 and S.wake = 4 • Processes P1..P4 are ready, just before line L3 • Each of P1..P3 will decrement S.wake in its turn, check that it's positive and signal one of P5..P7 • Four up operations have released 7 down operations Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Implementing a counting semaphore with binary semaphores (user space): take 4 (Hemmendinger, 1989) down(S) down(S1); S.value--; if(S.value < 0){ up(S1); down(S2); down(S1); S.wake--; if(S.wake > 0) then up(S2);} // L3 up(S1); binary-semaphore S1=1, S2=0, integer wake=0 up(S): down(S1); S.value++; if(S.value <= 0) { S.wake++; if (S.wake == 1) up(S2); } up(S1); This works Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Implementing a counting semaphore with binary semaphores (user space): take 5(Barz, 1983) down(S) down(S2); down(S1); S.value--; if (S.value>0) then up(S2); up(S1); binary-semaphore S1=1, S2=min(1, init_value), value=init_value up(S): down(S1); S.value++; if(S.value == 1) { up(S2); } up(S1); This works, is simpler, and was published earlier(!)… Can we switch the order of downs in down(S)? Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Correctness arguments… • The critical section is guarded by S1 and each of the operations down(S) and up(S) uses it to correctly update the value of S.value • After updating (and inside the critical section) both operations release the S2 semaphore only if value is positive • S.value is never negative, because any process performing down(S) is blocked at S2 • Signals cannot be 'wasted' Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Fairness of semaphores • Order of releasing blocked processes: • Weak – up() performing process enters after (one of the) blocked processes • Strong – An upper bound on the number of entries of process that performed up() if others are waiting • Unfair: • No guarantee on the number of times the up() performing process enters before the blocked • Open competition each time the lock is free • Imitating the Java 'wait' 'notify' mechanism • Or the spin-lock of XV6… Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Outline • Semaphores and the producer/consumer problem • Counting semaphores from binary semaphores • Event counters & message passing synchronization (העשרה) Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
Event Counters • Integer counters with three operations: • Advance(E): increment E by 1, wake up relevant sleepers • Await(E,v): wait until E ≥ v. Sleep if E < v • Read(E): return the current value of E • Counter value is ever increasing • The Read() operation is not required for the bounded-buffer implementation in the next slide Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels
producer-consumer with Event Counters(for a single producer and a single consumer) #define N 100typedef int event_counter; event_counter in = 0; /* counts inserted items */event_counter out = 0; /* items removed from buffer */ void producer(void){int item, sequence = 0; while(TRUE) { produce_item(&item);sequence = sequence + 1; /* counts items produced */await(out, sequence - N); /* wait for room in buffer */enter_item(item); /* insert into buffer */advance(&in); /* inform consumer */ }} Operating Systems, Spring 2016, Meni Adler, Danny Hendler & Amnon Meisels