Transcript
Page 1: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Operating Systems Lecture NotesOperating Systems Lecture Notes

SynchronizationSynchronization

Matthew DaileySome material © Silberschatz, Galvin, and Gagne, 2002

Page 2: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

SynchronizationSynchronization

WHATWHAT: To synchronize is to make things happen at the same time.

HOWHOW: By making one process wait for another.

This guarantees that certain points in each process occur at the same

time.

WHYWHY: — 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

Page 3: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Why do we need to synchronize?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 */

Page 4: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Example continuedExample 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 asregister1 = counterregister1 = register1 + 1counter = register1

Suppose the C statement “counter--” is implemented in assembly asregister2 = counterregister2 = register2 - 1counter = register2

Page 5: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Example continued: interleaved executionExample continued: interleaved execution

If the producer and consumer run concurrently, execution can be interleaved:

T0: producer executes register1 = counter (reg1 = 5)T1: producer executes register1 = register1 + 1 (reg1 = 6)T2: consumer executesregister2 = counter (reg2 = 5)T3: consumer executesregister2 = register2 - 1 (reg2 = 4)T4: producer executes counter = register1 (counter = 6)T5: consumer executescounter = register2 (counter = 4)

But the correct answer at the end should be counter = 5!!

Page 6: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Race ConditionsRace 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.

Page 7: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Critical Section ProblemCritical Section Problem

Suppose we have n processes, P0, P1, …, Pn-1.

Each process has a special segment of code– Called the critical section– That updates shared data

GOALGOAL: 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.

Page 8: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Let’s look at the code againLet’s look at the code againProducer:

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.

Page 9: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Let’s look at the code againLet’s look at the code againProducer:

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.

Page 10: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Critical Section ProblemCritical 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

Page 11: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Critical Section [CS] ProblemCritical 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.

Page 12: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Solution 1: Take Turns?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;

Page 13: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Solution 1: Does it work?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!

Page 14: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Solution 2: State intention?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;

Page 15: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Solution 2: Does it work?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

Page 16: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Solution 3: Combination of 1 & 2Solution 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;

Page 17: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Proof of Proof of Mutual ExclusionMutual Exclusion in Solution 3 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)

Page 18: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Other ways to get mutual exclusionOther 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

Page 19: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Test-and-set instructionTest-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

Page 20: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Mutual Exclusion with TestAndSetMutual 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

Page 21: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

SemaphoresSemaphoresDefinition: 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.

Page 22: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Mutual Exclusion with Semaphores 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);

Page 23: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

More on SemaphoresMore 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!

Page 24: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Semaphore ImplementationSemaphore 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().

Page 25: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

MonitorsMonitors

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.

Page 26: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

What is a Monitor?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.

Page 27: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Monitor SyntaxMonitor Syntaxmonitor monitor-name {

shared variable declarations

procedure body P1 ( . . . ) {. . .

}

procedure body P2 ( . . . ) {. . .

}

. . .{

initialization code}

}

Page 28: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Monitor SemanticsMonitor 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 ascondition 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.

Page 29: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Monitor SchematicMonitor Schematic

Page 30: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Monitor With Condition VariablesMonitor With Condition Variables

Page 31: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Monitor Implementation IssueMonitor 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.

Page 32: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Solving Dining Philosophers With a MonitorSolving 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;

}}

}

Page 33: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Solving Dining Philosophers With a MonitorSolving 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/processdoes: */

dp.pickup(i); …

eat…

dp.putdown(i);

Page 34: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

Dining Philosophers ExampleDining 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?

Page 35: Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002

What have we learned?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!]


Recommended