400 likes | 608 Views
Background. Concurrent access to shared data may result in data inconsistency Maintaining data consistency requires mechanisms to ensure the orderly execution of cooperating processes Recall Producer/Consumer problem in Chapter 4.
E N D
Background • Concurrent access to shared data may result in data inconsistency • Maintaining data consistency requires mechanisms to ensure the orderly execution of cooperating processes • Recall Producer/Consumer problem in Chapter 4. • New solution adds a variable to track number of items in the buffer.
Race Condition global shared int counter = 0, BUFFER_SIZE = 10 ; Producer: while (1) { while (counter == BUFFER_SIZE) ; // do nothing // produce an item and put in nextProduced buffer[in] = nextProduced; in = (in + 1) % BUFFER_SIZE; counter++; }
Race Condition Consumer: while (1) { while (counter == 0) ; // do nothing nextConsumed = buffer[out]; out = (out + 1) % BUFFER_SIZE; counter--; // consume the item in nextConsumed }
Executed Separately work as expected. • Execute concurrently, causes race condition.
Consider this execution interleaving: S0: producer execute register1 = counter {register1 = 5}S1: producer execute register1 = register1 + 1 {register1 = 6} S2: consumer execute register1 = counter {register1 = 5} S3: consumer execute register1 = register1 - 1 {register1 = 4} S4: producer execute counter = register1 {counter = 6 } S5: consumer execute counter = register1 {counter = 4}
Solution to Critical-Section Problem • Mutual Exclusion - If process Pi is executing in its critical section, then no other processes can be executing in their critical sections. • No assumptions may be made about speed or number of CPUs. • No process executing outside of its critical section can block another process from entering its critical section. • No process should have to wait forever to enter its critical section.
Program Model while(1) { <entry section> <critical section> <remainder> }
Attempt 1 shared int turn = 0 ; while (1) while(1) { <enter critical> { <enter critical> while (turn !=0) ; while(turn !=1); critical region() ; critical_region() ; turn = 1 ; turn = 0 ; <remainder> <remainder> } }
Attempt 1 shared int turn = 0 ; while (1) while(1) { <enter critical> { <enter critical> while (turn !=0) ; while(turn !=1); critical_region() ; critical_region() turn = 1 ; turn = 0 ; <remainder> <remainder> } } • P0 accesses CR sets turn to 1. • P1 accesses CR sets turn to 0. In remainder section reads binary equivalent of War and Peace. • P0 enters CR sets turn to 1. • P0 attempts to enter CR but cannot (until after book report due).
Does it meet requirements? • Mutual Exclusion? • Violates rule three.
Attempt 2: Using flag to indicate interest global shared int flag[2] = {0,0} ; while(1) while(1) { flag[0] = 1 ; { flag[1] =1 ; while(flag[1] == 1) ; while(flag[0] ==1 ) ; //busy wait <critical section> <critical section> flag[0] = 0 ; flag[1] = 0 ; <remainder <remainder> } }
Attempt 2: Using flag to indicate interest global shared int flag[2] = {0,0} ; while(1) while(1) { flag[0] = 1 ; { flag[1] =1 ; while(flag[1] == 1) ; while(flag[0] ==1 ) ; //busy wait <critical section> <critical section> flag[0] = 0 ; flag[1] = 0 ; <remainder <remainder> } } • P0 sets flag[0] = 1 gets preempted. • P1 sets flag[1] = 1 gets preempted.
Problem? • Mutual Exclusion? • Progress? • Bounded Wait?
Attempt 2.1: Using flag to indicate interest global shared int flag[2] = {0,0} ; while(1) while(1) {while(flag[1] ==1 ) ; { while(flag[0] == 1) ; flag[0] = 1 ; flag[1] =1 ; <critical section> <critical section> flag[0] = 0 ; flag[1] = 0 ; <remainder <remainder> } }
Attempt 2.1: Using flag to indicate interest global shared int flag[2] = {0,0} ; while(1) while(1) {while(flag[1] ==1 ) ; { while(flag[0] == 1) ; flag[0] = 1 ; flag[1] =1 ; <critical section> <critical section> flag[0] = 0 ; flag[1] = 0 ; <remainder <remainder> } } • P0 reads flag[1] =0 ; Gets preempted. • P1 reads flag[0] = 0 ; Gets preempted. • P0 sets flag[0] = 1 and enters critical_region. • P1 sets flag[1] = 1 and enters critical_region.
Problem? • Mutual Exclusion? • Progress? • Bounded Wait?
Correct for Two Processes while(1) while(1) { flag[0] = 1 ; { flag[1] = 1 ; turn = 1 ; turn = 0 ; while(flag[1]&&turn==1) ; while(flag[0]&&turn==0); <critical section> <critical section> flag[0] = 0 ; flag[1] = 0 ; } }
Correct for Two Processes while(1) while(1) { flag[0] = 1 ; { flag[1] = 1 ; turn = 1 ; turn = 0 ; while(flag[1]&&turn==1) ; while(flag[0]&&turn==0); <critical section> <critical section> flag[0] = 0 ; flag[1] = 0 ; } } • P0 sets flag to 1 and turn to 1. Gets preempted. • e.g., reg1 = 1 // ready to store value of 1 to turn. • P1 sets flag to 1 and turn to 0. Gets preempted. • e.g., reg1 = 0 //ready to store value of 0 to turn. • What happens if P0 stores value of turn = 1 and falls into while state?? • What happens if P1 then stores value of turn = 0 ?
Goal of Critical Sections (Regions) Mutual exclusion using critical regions
Hardware Solutions • Many systems provide hardware support to critical section code. • Simple: Disable interrupts during critical section. • Would this provide mutual exclusion?? • Problems:
Hardware Solutions • Many systems provide hardware support to critical section code. • Simple: Disable interrupts during critical section. • Would this provide mutual exclusion?? • Problems: • Do not want to do this at user level. • Does not scale to multiprocessor system. • OK for OS to do so for short periods of time, not users.
Most machines provide special atomic hardware instructions • Atomic = non-interruptable • Either test memory word and set value • Or swap contents of two memory words
Test and Set Instruction (Busy Waiting) int lock = 0 ;// global integer var initially set to 0. This is a software description of the TestandSet hardware instruction. int TestAndSet (int lock) { return_val = lock ; lock = 1 ; return (return_val); }
Usage: shared int lock = 0 ; while(TestAndSet(lock)) ; //busy wait! <critical section> lock = 0 ;
Solutions that minimize busy waiting Producer-consumer problem (Revisited)
Deadlock? Producer: If (count = N) //assume condition true. Preempted Consumer: N-- ; If (count == N-1) wakeup(producer) ; Producer: Sleep() ; //taking the dirt nap
Semaphore • Synchronization tool that does not require busy waiting (spin lock) • Semaphore S – integer variable • Two standard operations modify S: wait() and signal() • Originally called P() andV(), called up and down in text. • Can only be accessed via two indivisible (atomic) operations
typedef struct { int count ; // for mutual exclusion between 2 processes initialize to 1. PCB *Sem_Q ; } Semaphore ;
Use of Semaphores struct Semaphore exclusion = 1 ; while (1) { wait(exclusion) ; //Note: Not a busy wait! <critical section> signal(exclusion) ; <remainder section> }
Semaphore Sem = 1 ; //Initialize counter variable wait(Sem) {Sem.count-- ; if (Sem.count < 0) { Place process on SemQ ; Block process ; } }
Signal(Sem) { Sem.count++ ; if (Sem.count <= 0 ) //someone is sleeping on S.Q { Remove process from SemQ; Place on ready queue ; } }
Example Semaphore Sem_Baby = 1 ; Process Operation Sem.count Result PO wait 0 Passes through P1 wait -1 Blocked P 0 signal 0 removes P1 from SemQ and it passes through the semaphore.
Ensuring Mutual Exclusion of Sem Code. shared int lock = 0 ; Semaphore Sem = 1 ; //Initialize count wait(Sem) { while(TestAndSet(lock)) ; //busy loop. Why not a problem?? Sem.count-- ; if (Sem.count < 0) { Place process on SemQ Block process and set lock to 0 } else lock = 0 ; }
Signal(Sem) { while (TestAndSet(lock)) ; //busy wait Sem.count++ ; if (Sem.count <= 0 ) { Remove process from SemQ; Place on ready queue ; } lock = 0 ; }
Deadlock and Starvation • Deadlock – two or more processes are waiting indefinitely for an event that can be caused by only one of the waiting processes • Let S and Q be two semaphores initialized to 1
Semaphore Q, S = {1,1} ; P1: P2: wait(S) ; wait(Q) ; wait(Q) ; wait(S) ; <CS> <CS> signal S ; signal(Q) ; signal Q ; signal(S) ;
Semaphore Q, S = {1,1} ; P0: wait(S) ; P1: wait(Q) ; P0: wait(Q) ; P1: wait(S) ;
Semaphores for Synchronization • Want Statement S1 in P0 to execute before statement S2 in P1. Semaphore Syncher = 0 ; P0: P1: S1 ; wait(Syncher) ; signal (Syncher) ; S2 ;
Semaphores for Synchronization • Want Statement S1 in P0 to execute before statement S2 in P1 before S3 in P2. Semaphore Syncher = 0 ; Semaphore Synch1 = 0 ; P0: P1: P2: S1 ; wait(Syncher) ; wait(Synch1) signal (Syncher) ; S2 ; S3 ; signal(Synch1) ;