CSE 421/521 - Operating Systems Roadmap Fall 2011 • Process Synchronization • Race Conditions Lecture - VIII • Critical-Section Problem Process Synchronization - I – Solutions to Critical Section – Different Implementations • Semaphores • Classic Problems of Synchronization Tevfik Ko ş ar University at Buffalo September 22 nd , 2011 1 2 Shared Variables: count=0, buffer[] Background Producer: while (true){ /* produce an item and put in nextProduced • Concurrent access to shared data may result in data while (count == BUFFER_SIZE) inconsistency ; // do nothing • Maintaining data consistency requires mechanisms buffer [in] = nextProduced; to ensure the orderly execution of cooperating in = (in + 1) % BUFFER_SIZE; processes count++; • Consider consumer-producer problem: } – Initially, count is set to 0 Consumer: – It is incremented by the producer after it produces a new while (1) { buffer while (count == 0) – and is decremented by the consumer after it consumes a ; // do nothing buffer. nextConsumed = buffer[out]; out = (out + 1) % BUFFER_SIZE; count--; } /* consume the item in nextConsumed 3 4 Race Condition Race Condition ✦ Race condition : The situation where several processes access and • count++ could be implemented as manipulate shared data concurrently. The final value of the shared register1 = count data depends upon which process finishes last. register1 = register1 + 1 count = register1 • count-- could be implemented as ✦ To prevent race conditions, concurrent processes must be register2 = count synchronized. register2 = register2 - 1 – Ensure that only one process at a time is manipulating the count = register2 variable counter . • Consider this execution interleaving with “count = 5” initially: ✦ The statements S0: producer execute register1 = count {register1 = 5} • count++; S1: producer execute register1 = register1 + 1 {register1 = 6} • count--; S2: consumer execute register2 = count {register2 = 5} S3: consumer execute register2 = register2 - 1 {register2 = must be performed atomically. 4} S4: producer execute count = register1 {count = 6 } ✦ Atomic operation means an operation without interruption. S5: consumer execute count = register2 {count = 4} 5 6
Race Condition Race Condition " Significant race conditions in I/O & variable sharing " Significant race conditions in I/O & variable sharing char chin, chout; //shared char chin, chout; //shared char chin, chout; //shared char chin, chout; //shared void echo() void echo() void echo() void echo() A B A B { { { { do { do { do { do { chin = getchar(); chin = getchar(); chin = getchar(); chin = getchar(); 1 4 1 2 chout = chin; chout = chin; chout = chin; chout = chin; 2 5 5 3 putchar(chout); putchar(chout); putchar(chout); putchar(chout); 3 6 6 4 } } } } while (...); lucky while (...); while (...); unlucky while (...); CPU CPU } } } } scheduling scheduling ! # > ./echo > ./echo > ./echo > ./echo Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! ee.... Single-threaded echo Multithreaded echo (lucky) Single-threaded echo Multithreaded echo (unlucky) 7 8 Race Condition Race Condition " Significant race conditions in I/O & variable sharing " Significant race conditions in I/O & variable sharing void echo() void echo() $ in this case, replacing the global variables with local variables { { did not solve the problem char chin, chout; char chin, chout; A B do { do { $ we actually had two race conditions here: chin = getchar(); chin = getchar(); 1 2 % one race condition in the shared variables and the order of chout = chin; chout = chin; 5 3 value assignment putchar(chout); putchar(chout); 6 4 } } % another race condition in the shared output stream: which while (...); unlucky while (...); CPU thread is going to write to output first (this race persisted } } scheduling even after making the variables local to each thread) # > ./echo > ./echo ==> generally, problematic race conditions may occur whenever Hello world! Hello world! resources and/or data are shared (by processes unaware of each Hello world! eH.... other or processes indirectly aware of each other) Single-threaded echo Multithreaded echo (unlucky) 9 10 Critical Section/Region Critical Section " The “indivisible” execution blocks are critical regions • Critical section/region: segment of code in which the $ a critical region is a section of code that may be executed by process may be changing shared data (eg. common only one process or thread at a time variables) A • No two processes should be executing in their critical common critical region B sections at the same time --> prevents race conditions • Critical section problem: design a protocol that the $ although it is not necessarily the same region of memory or processes use to cooperate section of program in both processes A A’s critical region B B’s critical region ==> but physically different or not, what matters is that these regions cannot be interleaved or executed in parallel (pseudo or real) 11 12
Solution to Critical-Section Problem Solution to Critical-Section Problem A solution to the critical-section problem must satisfy the following requirements: 3. Bounded Waiting - A bound must exist on the number 1. Mutual Exclusion - If process P i is executing in its of times that other processes are allowed to enter their critical sections after a process has made a request to critical section, then no other processes can be enter its critical section and before that request is executing in their critical sections granted 2. Progress - If no process is executing in its critical ! Assume that each process executes at a nonzero speed section and there exist some processes that wish to ! No assumption concerning relative speed of the N processes enter their critical section, then the selection of the processes that will enter the critical section next cannot be postponed indefinitely 13 14 Critical Section Mutual Exclusion " We need mutual exclusion from critical regions " Desired effect: mutual exclusion from the critical region critical regions can be protected from concurrent access by $ thread A reaches the gate A padding them with entrance and exit gates (we’ll see how later): 1. critical region to the critical region (CR) a thread must try to check in, then it must check out B before B void echo() void echo() thread A enters CR first, A 2. { { HOW is this achieved?? preventing B from entering A B char chin, chout; char chin, chout; B (B is waiting or is blocked) do { do { enter critical region? enter critical region? thread A exits CR; thread 3. chin = getchar(); A chin = getchar(); B can now enter chout = chin; chout = chin; B putchar(chout); putchar(chout); exit critical region exit critical region thread B enters CR } } 4. A while (...); while (...); B } } 15 16 Mutual Exclusion Mutual Exclusion " Implementation 1 — disabling hardware interrupts " Implementation 1 — disabling hardware interrupts & $ it works, but not reasonable! thread A reaches the gate A 1. critical region $ what guarantees that the user to the critical region (CR) B void echo() before B process is going to ever exit the { critical region? as soon as A enters CR, it char chin, chout; A 2. do { disables all interrupts, $ meanwhile, the CPU cannot B disable hardware interrupts thus B cannot be interleave any other task, even chin = getchar(); chout = chin; scheduled unrelated to this race condition A putchar(chout); as soon as A exits CR, it $ the critical region becomes one enable hardware interrupts 3. B enables interrupts; B can } physically indivisible block, not while (...); be scheduled again logically } A thread B enters CR $ also, this is not working in multi- 4. B processors 17 18
Recommend
More recommend