Download presentation
Presentation is loading. Please wait.
Published byDwayne Adams Modified over 9 years ago
1
Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002
2
Synchronization WHAT WHAT :To synchronize is to make things happen at the same time. HOW HOW :By making one process wait for another. This guarantees that certain points in each process occur at the same time. WHY WHY :— Ensure consistency of shared data (prevent race conditions) — Make processes wait for resources to become available This is arguably the most important topic in the course! Readings: Silberschatz et al., chapter 7
3
Why do we need to synchronize? EXAMPLE: Bounded buffer problem with shared memory Producer: while ( 1 ) { /* produce item -> nextProduced */ while ( counter == BUFFER_SIZE ) ; /* do nothing */ buffer[in] = nextProduced; in = ( in + 1 ) % BUFFER_SIZE; counter++; } Consumer: While ( 1 ) { while ( counter == 0 ) ; /* do nothing */ nextConsumed = buffer[out]; out = ( out + 1 ) % BUFFER_SIZE; counter--; /* consume item -> nextConsumed */
4
Example continued Producer and consumer code is correct if they do not run concurrently. But with concurrency, the answer could be incorrect! Suppose the C statement “counter++” is implemented in assembly as register1 = counter register1 = register1 + 1 counter = register1 Suppose the C statement “counter--” is implemented in assembly as register2 = counter register2 = register2 - 1 counter = register2
5
Example continued: interleaved execution If the producer and consumer run concurrently, execution can be interleaved: T0:producer executesregister1 = counter(reg1 = 5) T1:producer executesregister1 = register1 + 1(reg1 = 6) T2:consumer executesregister2 = counter(reg2 = 5) T3:consumer executesregister2 = register2 - 1(reg2 = 4) T4:producer executescounter = register1(counter = 6) T5:consumer executescounter = register2(counter = 4) But the correct answer at the end should be counter = 5!!
6
Race Conditions If we swapped T4 and T5 in the previous interleaving: –Result would be counter = 6 –The outcome depends on the order of execution! A race condition is when the outcome depends on the particular order in which instructions execute. To guard against race conditions we must synchronize the processes/threads This is not a toy example! It happens all the time in complex multitasking systems.
7
Critical Section Problem Suppose we have n processes, P 0, P 1, …, P n-1. Each process has a special segment of code –Called the critical section –That updates shared data GOAL GOAL : design a protocol such that when one process is executing in its critical section, no other process is allowed into its critical section. Another way of saying it: –Critical section execution must be mutually exclusive in time.
8
Let’s look at the code again Producer: while ( 1 ) { /* produce item, then put in nextProduced */......... while ( counter == BUFFER_SIZE ) ; /* do nothing */ buffer[in] = nextProduced; in = ( in + 1 ) % BUFFER_SIZE; counter++; } Consumer: while ( 1 ) { while ( counter == 0 ) ; /* do nothing */ nextConsumed = buffer[out]; out = ( out + 1 ) % BUFFER_SIZE; counter--; /* consume item in nextConsumed */......... } Where are the critical sections? We have shown that counter needs to be protected in a critical section. With careful analysis, you will see that buffer does not need protection.
9
Let’s look at the code again Producer: while ( 1 ) { /* produce item, then put in nextProduced */......... while ( counter == BUFFER_SIZE ) ; /* do nothing */ buffer[in] = nextProduced; in = ( in + 1 ) % BUFFER_SIZE; counter++; } Consumer: while ( 1 ) { while ( counter == 0 ) ; /* do nothing */ nextConsumed = buffer[out]; out = ( out + 1 ) % BUFFER_SIZE; counter--; /* consume item in nextConsumed */......... } If the critical section execution are mutually exclusive in time, then the interleaving of the counter updates will not happen. Then, the answer will always be correct.
10
Critical Section Problem Assume the following structure for every process Pi: While ( not done ) { Remainder section Critical section Remainder section } Exit section Exit releases the barrier. It no longer blocks other processes out of their critical sections. Entry section is a barrier. It blocks processes out when one process is in its critical section. Entry section
11
Critical Section [CS] Problem The critical section problem: How to protect a critical section? Solutions to the CS problem must satisfy: – Mutual Exclusion : When process I (Pi) is executing its CS, no other process can execute in its CS. – Progress : No process in the remainder section is allowed to block processes wanting to enter their CS. – Bounded waiting : Must be able to guarantee that, once Pi requests entry to its CS, no more than k other processes will be allowed to enter before Pi does. We assume nothing about relative speed of the n processes.
12
Solution 1: Take Turns? Code for P0: /* Critical section */ Code for P1: /* Critical section */ Uses a shared integer variable named turn ENTRY EXIT while ( turn != 0 ); turn = 1; ENTRY EXIT while ( turn != 1 ) ; turn = 0;
13
Solution 1: Does it work? Mutual exclusion Bounded waiting Progress –Neither of the processes can enter until it is their “turn” –The other process only changes “turn” when done with CS –If P0 has to wait, P1 enters its CS at most once before P0 gets its chance –If turn == 1 but P1 is in its remainder section, P0 cannot enter its CS!
14
Solution 2: State intention? Code for P0: /* Critical section */ Uses an array of booleans, flag[] ENTRY EXIT flag[0] = TRUE; while (flag[1] == TRUE) ; flag[0] = FALSE; Code for P1: /* Critical section */ ENTRY EXIT flag[1] = TRUE; while (flag[0] == TRUE) ; flag[1] = FALSE;
15
Solution 2: Does it work? Mutual exclusion Bounded waiting Progress –If P0 is in CS, then flag[0] == TRUE –P1 will never enter CS if flag[0] == TRUE –Suppose P0 sets flag[0] == TRUE –Then P1 sets flag[1] to TRUE –Both P0 and P1 will wait indefinitely –Same as above
16
Solution 3: Combination of 1 & 2 Uses int turn=0; AND boolean flag[2] = {FALSE,FALSE}; Code for P0: /* Critical section */ ENTRY flag[0] = TRUE; turn = 1; while (flag[1] && turn == 1) ; EXIT flag[0] = FALSE; Code for P1: /* Critical section */ ENTRY flag[1] = TRUE; turn = 0; while (flag[0] && turn == 0) ; EXIT flag[1] = FALSE;
17
Proof of Mutual Exclusion in Solution 3 Assume towards a contradiction that P0 and P1 are in CS simultaneously. Since P0 and P1 are both in CS, flag = { TRUE, TRUE }. Without loss of generality, assume P1 entered CS at some time t, while P0 was already in its CS. Since flag[0]= TRUE at time t, it must be the case that turn=1 at time t (otherwise P1 could not enter CS). This implies P0 set turn=1 after P1 set turn=0. But if that is true, then P0 would have to wait at the while loop. This means P1 entered its CS before P0 did… contradiction! Mutual exclusion follows. P0 and P1 cannot be in CS simultaneously. (See text for proof of bounded wait and progress)
18
Other ways to get mutual exclusion For more than 2 processes, the CS problem is harder. The hardware can help us achieve mutual exclusion. Idea 1: disable interrupts during the CS –Simple and easy to implement –Restrictive: No other process in the entire system can run –User processes can have a very long CS Better idea: an atomic test-and-set instruction
19
Test-and-set instruction Pseudocode: boolean TestAndSet( boolean *pTarget ) { boolean rv = *pTarget; *pTarget = TRUE; return rv; } Basic idea: runs the entire procedure atomically (i.e. disallow interrupts) Can use this to implement a lock for mutual exclusion
20
Mutual Exclusion with TestAndSet A lock is also known as a mutex. Code for P0: /* Critical section */ Code for P1: /* Critical section */ ENTRY EXIT while (TestAndSet(&locked)) ; locked = FALSE; ENTRY EXIT while (TestAndSet(&locked)) ; locked = FALSE; Uses boolean locked; AND TestAndSet function
21
Semaphores Definition: A semaphore is an abstract data type with operations allowing mutual exclusion. A semaphore S is just an integer that can only be modified by three operations: init(S,i), wait(S), and signal(S) init(S,i) {wait(S) {signal(S) { S = i;while( S <= 0 ); S++; }S--;} } If the semaphore’s value is 0 when wait is called, we have to wait for another process to signal the semaphore (incrementing the value to 1). Then we will be able to decrement the value to 0 again and enter our critical section.
22
Mutual Exclusion with Semaphores Code for P0: /* Critical section */ Code for P1: /* Critical section */ Uses a semaphore, semaphore mutex = 1; ENTRY EXIT wait(mutex); signal(mutex); ENTRY EXIT wait(mutex); signal(mutex);
23
More on Semaphores Semaphores are the most common (thus most important) synchronization primitive in used in modern multithreaded applications. You will probably use semaphores in many projects in your future careers!
24
Semaphore Implementation The busy-wait in the wait(S) operation is inefficient. Also, to ensure bounded waiting and progress, we need to enforce an ordering on the set of waiting processes. So the implementation of wait(S) actually blocks the calling process and puts it on a queue of processes waiting for S. The implementation of signal(S) unblocks the first waiting process at the head of the queue. So wait() and signal() therefore have their own critical sections. Typically the OS briefly disables interrupts during the critical sections of wait() and signal().
25
Monitors Semaphores: a low-level synchronization structure –Widely used for many synchronization tasks –Can be used to solve the critical section problem if wait() and signal() calls are used correctly –But error-prone. What if I forget to put wait() and signal() around my critical section? Monitors: a higher-level synchronization structure –Solves the critical section problem automatically! –Can be used for other synchronization tasks too.
26
What is a Monitor? Like a C++ class, or like a C struct that can only be modified by particular functions. A monitor has: –Some private data –Functions that manipulate that private data Access rules for monitors: –Processes cannot directly access internal monitor data. –Monitor functions cannot access external data.
27
Monitor Syntax monitor monitor-name { shared variable declarations procedure body P1 (... ) {... } procedure body P2 (... ) {... }... { initialization code }
28
Monitor Semantics Only one process at a time can be active within a monitor. This means an entry queue is required. Additional variable type: the condition variable. Declared as condition x, y; Condition variables have two operations: –x.wait() : suspends calling process until some other process invokes x.signal() –x.signal() : resumes one (and only one) suspended process. Has no effect if there are no suspended processes.
29
Monitor Schematic
30
Monitor With Condition Variables
31
Monitor Implementation Issue What exactly should happen when P calls x.signal() while Q is waiting on x ? We said only one process can be active in a monitor at one time, so we have to enforce either of the following: –P waits until Q either leaves the monitor or Q waits for another condition –Q waits until P either leaves the monitor or P waits for another condition Different systems may implement x.signal() either way.
32
Solving Dining Philosophers With a Monitor monitor dp { enum { thinking, hungry, eating } state[5]; condition self[5]; void pickup(int i); void putdown(int i); void test(int i); void init() { for ( int i=0; i<5; i++) { state[I] = thinking; }
33
Solving Dining Philosophers With a Monitor void pickup(int i) { state[i] = hungry; test(i); if (state[i] != eating) self[i].wait(); } void putdown(int i) { state[i] - thinking; 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(); } /*Each philosopher thread/process does: */ dp.pickup(i); … eat … dp.putdown(i);
34
Dining Philosophers Example Try the following sequence with the monitor solution: –T0: Philosopher 0 gets hungry –T1: Philosopher 3 gets hungry –T2: Philosopher 1 gets hungry –T3: Philosopher 0 finished eating What is the state of each philosopher thread and each condition variable at time T4?
35
What have we learned? How race conditions can lead to incorrect results. The critical section (CS) problem: –Requirements for solutions –Not-quite-correct software solutions –A correct software solution to the 2-process CS problem –A hardware-supported solution to the CS problem Mutual exclusion as an important goal: –Solutions: TestAndSet, Semaphores, Monitors [Monitors can do much more!]
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.