Ch07 Process Synchronization

22
Course 7: S ynchronization

description

Process Synchronization

Transcript of Ch07 Process Synchronization

Page 1: Ch07 Process Synchronization

Course 7: Synchronization

Page 2: Ch07 Process Synchronization

6.2 Silberschatz, Galvin and GagneOperating System Concepts – 8th Edition

Course 6: Review Concurrency: multiprocessing, multitasking, or any

combination

Race condition: the situation that the result of a

concurrent execution is dependent on the

nondeterministic interleaving

Atomic instructions: its execution cannot be

interleaved with other instructions before its completion

Synchronization – mechanism by which processes

communicate with each other in order to agree on a

sequence of actions

Synchronization requirements – ex: dinning

philosophers

Mutual exclusion

No deadlocks

Starvation free

Critical section - piece of code that only one thread

can execute at once.

Page 3: Ch07 Process Synchronization

6.3 Silberschatz, Galvin and GagneOperating System Concepts – 8th Edition

Course 6: Review

Page 4: Ch07 Process Synchronization

6.4 Silberschatz, Galvin and GagneOperating System Concepts – 8th Edition

Course 6: Review

Ex: Too much milk

problem

Page 5: Ch07 Process Synchronization

6.5 Silberschatz, Galvin and GagneOperating System Concepts – 8th Edition

Course 7: Thread Synchronization

Peterson solution

Locks

Hardware locks

Higher level solutions

Semaphores

Monitors and condition variables

Page 6: Ch07 Process Synchronization

6.6 Silberschatz, Galvin and GagneOperating System Concepts – 8th Edition

Peterson solution

Page 7: Ch07 Process Synchronization

6.7 Silberschatz, Galvin and GagneOperating System Concepts – 8th Edition

Peterson solution

Peterson:

Page 8: Ch07 Process Synchronization

6.8 Silberschatz, Galvin and GagneOperating System Concepts – 8th Edition

Peterson solution - proof

Peterson’s algorithm combines the ideas of solution attempts II and III; If both

processes have set their enter-flag to true, then the value of turn decides who may

enter the critical section

Peterson’s algorithm satisfies mutual exclusion, proof:

Assume that both P1 and P2 are in their critical section and that P1 entered before P2

When P1 entered the critical section we have enter1 =true, and P2 must thus have seen turn

= 2 upon entering its critical section

P2 could not have executed line 2 after P1 entered, as this sets turn = 1 and would have

excluded P2, as P1 does not change turn while being in the critical section

However, P2 could not have executed line 2 before P1 entered either because then P1 would

have seen enter2 =true and turn = 1, although P2 should have seen turn = 2

Page 9: Ch07 Process Synchronization

6.9 Silberschatz, Galvin and GagneOperating System Concepts – 8th Edition

Peterson solution - proof

Peterson’s algorithm is starvation free, proof:

Assume P1 is forced to wait in the entry protocol forever. P2 can

eventually do only one of three actions:

1. Be in its non-critical section: then enter2 is false, thus allowing P1

to enter.

2. Wait forever in its entry protocol: impossible because turn cannot

be both 1 and 2

3. Repeatedly cycle through its code: then P2 will set turn to 1 at

some point and never change it back

Page 10: Ch07 Process Synchronization

6.10 Silberschatz, Galvin and GagneOperating System Concepts – 8th Edition

Peterson solution - conclusions Peterson solution works, but it’s really unsatisfactory

Really complex – even for this simple an example

Hard to convince yourself that this really works

While P1/P2 is waiting, it is consuming CPU time

This is called “busy-waiting”

There’s a better way

Have hardware provide better (higher-level) primitives than atomic load and store

Build even higher-level programming abstractions on this new hardware support

Page 11: Ch07 Process Synchronization

6.11 Silberschatz, Galvin and GagneOperating System Concepts – 8th Edition

Synchronization solutions

We are going to implement various higher-level synchronization primitives

using atomic operations

Everything is pretty painful if only atomic primitives are load and store

Need to provide primitives useful at user-level

Page 12: Ch07 Process Synchronization

6.12 Silberschatz, Galvin and GagneOperating System Concepts – 8th Edition

Locks

Page 13: Ch07 Process Synchronization

6.13 Silberschatz, Galvin and GagneOperating System Concepts – 8th Edition

Locks Suppose we have some sort of implementation of a lock (more in a

moment).

Lock.Acquire() – wait until lock is free, then grab

Lock.Release() – Unlock, waking up anyone waiting

These must be atomic operations – if two threads are waiting for the lock and both see it’s free, only one succeeds to grab the lock

Then, our “milk problem” is easy:

milklock.Acquire();

if (nomilk)

buy milk;

milklock.Release();

Once again, section of code between Acquire() and Release() called a “Critical Section”

Page 14: Ch07 Process Synchronization

6.14 Silberschatz, Galvin and GagneOperating System Concepts – 8th Edition

Interrupt Enable/Disable How can we build multi-instruction atomic operations?

Recall: dispatcher gets control in two ways.

Internal: Thread does something to relinquish the CPU

External: Interrupts cause dispatcher to take CPU

On a uniprocessor, can avoid context-switching by:

Avoiding internal events

Preventing external events by disabling interrupts

Consequently, naïve Implementation of locks:

LockAcquire { disable Ints; }

LockRelease { enable Ints; }

Problems with this approach:

Can’t let user do this! Consider following:LockAcquire();While(TRUE) {;}

Real-Time system—no guarantees on timing!

Critical Sections might be arbitrarily long

What happens with I/O or other important events?

“Reactor about to meltdown. Help?”

Page 15: Ch07 Process Synchronization

6.15 Silberschatz, Galvin and GagneOperating System Concepts – 8th Edition

Better Implementation of Locks by

Disabling Interrupts

Key idea: maintain a lock variable and impose mutual exclusion only during operations on that variable

int value = FREE;

Acquire() {

disable interrupts;

if (value == BUSY) {

put thread on wait queue;

Go to sleep();

// Enable interrupts?

} else {

value = BUSY;

}

enable interrupts;

}

Release() {

disable interrupts;

if (anyone on wait queue) {

take thread off wait queue

Place on ready queue;

} else {

value = FREE;

}

enable interrupts;

}

Page 16: Ch07 Process Synchronization

6.16 Silberschatz, Galvin and GagneOperating System Concepts – 8th Edition

Better Implementation of Locks by

Disabling Interrupts

Note: unlike previous solution, the critical section (inside Acquire()) is very short

User of lock can take as long as they like in their own critical section: doesn’t impact global machine behavior

Critical interrupts taken in time!

int value = FREE;

Acquire() {

disable interrupts;

if (value == BUSY) {

put thread on wait queue;

enable interrupts;

Go to sleep();

} else {

value = BUSY;

}

enable interrupts;

}

Page 17: Ch07 Process Synchronization

6.17 Silberschatz, Galvin and GagneOperating System Concepts – 8th Edition

Disabling Interrupts conclusions

Problems with previous solution:

Can’t give lock implementation to users

Doesn’t work well on multiprocessor

Disabling interrupts on all processors requires messages

and would be very time consuming

Alternative: atomic instruction sequences

These instructions read a value from memory and write a new

value atomically

Hardware is responsible for implementing this correctly

on both uniprocessors (not too hard)

and multiprocessors (requires help from cache coherence

protocol)

Unlike disabling interrupts, can be used on both uniprocessors

and multiprocessors

Page 18: Ch07 Process Synchronization

6.18 Silberschatz, Galvin and GagneOperating System Concepts – 8th Edition

Hardware Locks

Page 19: Ch07 Process Synchronization

6.19 Silberschatz, Galvin and GagneOperating System Concepts – 8th Edition

Read-Modify-Write Sequences

test&set (&address) { /* most architectures */result = M[address];M[address] = 1;return result;

}

swap (&address, register) { /* x86 */temp = M[address];M[address] = register;register = temp;

}

Page 20: Ch07 Process Synchronization

6.20 Silberschatz, Galvin and GagneOperating System Concepts – 8th Edition

Read-Modify-Write Sequences

test&set (&address) { /* most architectures */result = M[address];M[address] = 1;return result;

}

int value = 0; // Free

Acquire() {

while (test&set(value)); // while busy

}

//Critical section

Release() {

value = 0;

}

Simple explanation:

If lock is free, test&set reads 0 and sets value=1, so lock is now busy.

It returns 0 so while exits.

If lock is busy, test&set reads 1 and sets value=1 (no change). It

returns 1, so while loop continues

When we set value = 0, someone else can get lock

Busy-Waiting: thread consumes cycles while waiting

Page 21: Ch07 Process Synchronization

6.21 Silberschatz, Galvin and GagneOperating System Concepts – 8th Edition

Read-Modify-Write Sequences

Positives for this solution

Machine can receive interrupts

User code can use this lock

Works on a multiprocessor

Negatives

This is very inefficient because the busy-waiting thread will consume cycles waiting

Waiting thread may take cycles away from thread holding lock (no one wins!)

Priority Inversion: If busy-waiting thread has higher priority than thread holding lock no progress!

Priority Inversion problem with original Martian rover

Page 22: Ch07 Process Synchronization

End of Course 7