Chapter 6 (a): Synchronization Silberschatz, Galvin and Gagne ©2007 – 7

advertisement
Chapter 6 (a): Synchronization
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
Silberschatz, Galvin and Gagne ©2007
Module 6: Process Synchronization










Background
Producer/Consumer Again
Race Conditions
Scheduler Assumptions
The Critical-Section Problem
Critical Section Goals
Deriving a Solution
Peterson’s Algorithm
Bakery Algorithm
Hardware Support
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.2
Silberschatz, Galvin and Gagne ©2007
Background



Concurrent access to shared data may result in data
inconsistency
Maintaining data consistency requires mechanisms to
ensure the orderly execution of cooperating
processes
Suppose that we wanted to provide a solution to the
consumer-producer problem that fills all the buffers.
 Have an integer count that tracks the number of full
buffers.
 Initially, count is set to 0.
 Producer increments count after producing a buffer
 Consumer decrements after consuming a buffer
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.3
Silberschatz, Galvin and Gagne ©2007
Producer-Consumer
•
Producer
•
while (true) {
Consumer
while (true) {
/* produce an item and */
/* put in nextProduced */
while (count == 0)
; // do nothing b/c empty
while (count == BUFFER_SIZE)
; // do nothing b/c full
nextConsumed = buffer[out];
out = (out + 1) % BUFFER_SIZE;
count--;
buffer [in] = nextProduced;
in = (in + 1) % BUFFER_SIZE;
count++;
/* consume the item */
/* in nextConsumed */
}
}
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.4
Silberschatz, Galvin and Gagne ©2007
Race Condition

count++ could be implemented as

register1 = count
register1 = register1 + 1
count = register1
count-- could be implemented as

register2 = count
register2 = register2 - 1
count = register2
Consider this execution interleaving with “count = 5” initially:
S0: producer execute register1 = count
{register1 = 5}
S1: producer execute register1 = register1 + 1 {register1 = 6}
S2: consumer execute register2 = count
{register2 = 5}
S3: consumer execute register2 = register2 - 1 {register2 = 4}
S4: producer execute count = register1
{count = 6 }
S5: consumer execute count = register2
{count = 4}
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.5
Silberschatz, Galvin and Gagne ©2007
What happened?



Threads (and sometimes processes) share global
memory
When a process contains multiple threads, they have
 Private registers and stack memory (the context
switching mechanism needs to save and restore
registers when switching from thread to thread)
 Shared access to the remainder of the process “state”
This can result in race conditions
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.6
Silberschatz, Galvin and Gagne ©2007
Two threads, one counter
Popular web server
 Uses multiple threads to speed things up.
 Simple shared state error:
 each thread increments a shared counter to track number of hits
…
hits = hits + 1;
…

What happens when two threads execute concurrently?
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.7
Silberschatz, Galvin and Gagne ©2007
Shared counters

Possible result: lost update!
hits = 0
time
read hits (0)
hits = 0 + 1
hits = 1


T2
T1
read hits (0)
hits = 0 + 1
One other possible result: everything works.
 Difficult to debug
Called a “race condition”
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.8
Silberschatz, Galvin and Gagne ©2007
Race conditions


Def: a timing dependent error involving shared state
 Whether it happens depends on how threads scheduled
 In effect, once thread A starts doing something, it needs to
“race” to finish it because if thread B looks at the shared
memory region before A is done, it may see something
inconsistent
Hard to detect:
 All possible schedules (permutations) have to be safe
 Number of possible schedule permutations is huge
 Some bad schedules? Some that will work sometimes?
 Intermittent  Unpredictable
 Timing dependent = small changes can hide bug

If a bug is deterministic and repeatable, celebrate!
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.9
Silberschatz, Galvin and Gagne ©2007
Scheduler Assumptions
Process b:
while(i > -10)
i = i - 1;
print “B won!”;
Process a:
while(i < 10)
i = i +1;
print “A won!”;
If i is shared, and initialized to 0
 Who wins?
 Is it guaranteed that someone wins?
 What if each thread runs on a separate, identical speed CPU?
 executing in parallel
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.10
Silberschatz, Galvin and Gagne ©2007
Scheduler Assumptions

Normally we assume that
 A scheduler always gives every executable thread
opportunities to run
 In effect, each thread makes finite progress
 But schedulers aren’t always fair
 Some threads may get more chances than others
 To reason about worst case behavior we sometimes
think of the scheduler as an adversary trying to “mess
up” the algorithm
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.11
Silberschatz, Galvin and Gagne ©2007
Critical Section Problem

Problem: Design a protocol for processes to cooperate, such that only one
process is in its critical section at any time
 How to make multiple instructions seem like one?
Process 1
CS1
Process 2
CS2
Time 
Processes progress with non-zero speed, no assumption on clock speed
Used extensively in operating systems:
Queues, shared variables, interrupt handlers, etc.
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.12
Silberschatz, Galvin and Gagne ©2007
Critical-Section Problem
1.
2.
3.
4.
Race Condition - When there
is concurrent access to
shared data and the final
outcome depends upon order
of execution.
Critical Section - Section of
code where shared data is
accessed.
Entry Section - Code that
requests permission to enter
its critical section.
Exit Section - Code that is run
after exiting the critical
section
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.13
Silberschatz, Galvin and Gagne ©2007
Critical Section Goals

Threads do some stuff but eventually might try to access
shared data
time
T2
T1
CSEnter();
Critical section
CSExit();
T2
T1
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
CSEnter();
Critical section
CSExit();
6.14
Silberschatz, Galvin and Gagne ©2007
Critical Section Goals

Perhaps they loop (perhaps not!)
T2
T1
CSEnter();
Critical section
CSExit();
T2
T1
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
CSEnter();
Critical section
CSExit();
6.15
Silberschatz, Galvin and Gagne ©2007
Critical Section Goals

We would like
 Safety (aka mutual exclusion)
 No more than one thread can be in a critical section at any time.
 Liveness (aka progress)
 A thread that is seeking to enter the critical section will eventually
succeed
 Bounded waiting
 A bound must exist on the number of times that other threads are
allowed to enter their critical sections after a thread has made a
request to enter its critical section and before that request is granted
 Assume that each process executes at a nonzero speed
 No assumption concerning relative speed of the N processes

Ideally we would like fairness as well
 If two threads are both trying to enter a critical section, they have equal
chances of success
 … in practice, fairness is rarely guaranteed
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.16
Silberschatz, Galvin and Gagne ©2007
Solving the problem

A first idea:
 Have a boolean flag, inside. Initially false.
CSEnter()
{
while(inside) continue;
inside = true;
}

CSExit()
{
inside = false;
}
Code is not safe: thread 0 could
finish the while test when inside is
false, but then thread 1 might call
CSEnter() before thread 0 can set
inside to true!
Now ask:
 Is this Safe? Live? Bounded waiting?
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.17
Silberschatz, Galvin and Gagne ©2007
Solving the problem: Take 2

A different idea (assumes just two threads):
 Have a boolean flag, inside[i]. Initially false.
CSEnter(int i)
{
inside[i] = true;
while(inside[j]) continue;
}

CSExit(int i)
{
inside[i] = false;
}
Code isn’t live (doesn’t guarantee progress): with
bad luck, both threads could be looping, with 0
looking at 1, and 1 looking at 0
Now ask:
 Is this Safe? Live? Bounded waiting?
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.18
Silberschatz, Galvin and Gagne ©2007
Solving the problem: Take 3

Another broken solution, for two threads
 Have a turn variable, turn, initially 1.
CSEnter(int i)
{
while(turn != i) continue;
}
CSExit(int i)
{
turn = i ^ 1;
}
Code isn’t live: thread 1 can’t enter unless
thread 0 did first, and vice-versa. But perhaps
one thread needs to enter many times and the
other fewer times, or not at all

Now ask:
 Is this Safe? Live? Bounded waiting?
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.19
Silberschatz, Galvin and Gagne ©2007
Peterson’s Algorithm (1981)
CSExit(int i)
{
inside[i] = false;
}
CSEnter(int i)
{
inside[i] = true;
turn = J;
while(inside[J] && turn == J)
continue;
}

Now ask:
 Is this Safe? Live? Bounded waiting?
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.20
Silberschatz, Galvin and Gagne ©2007
Peterson’s Solution





Two process solution
Assume that the LOAD and STORE instructions
are atomic; that is, cannot be interrupted.
The two processes share two variables:
 int turn;
 Boolean inside[2]
The variable turn indicates whose turn it is to
enter the critical section.
The inside array is used to indicate if a process
is ready to enter the critical section. inside[i] =
true implies that process Pi is ready!
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.21
Silberschatz, Galvin and Gagne ©2007
Analyzing Peterson’s Algorithm

Safety (by contradiction):
 Assume that both processes (Alice and Bob) are in
their critical section (and thus have their inside flags
set). Since only one, say Alice, can have the turn,
the other (Bob) must have reached the while() test
before Alice set her inside flag.
 However, after setting his inside flag, Alice gave
away the turn to Bob. Bob has already changed the
turn and cannot change it again, contradicting our
assumption.
Liveness & Bounded waiting => the turn variable.
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.22
Silberschatz, Galvin and Gagne ©2007
Generalize to N Threads?

Obvious approach won’t work:
CSExit(int i)
CSEnter(int i)
{
{
inside[i] = false;
inside[i] = true;
}
for(J = 0; J < N; J++)
while(inside[J] && turn == J)
continue;
}

Issue: Who’s turn is next?
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.23
Silberschatz, Galvin and Gagne ©2007
Bakery “concept”

Think of a popular store with a crowded
counter, perhaps the cheese line at Zabar’s
 People take a ticket from a machine
 If nobody is waiting, tickets don’t matter
 When several people are waiting, ticket
order determines order in which they can
make purchases
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.24
Silberschatz, Galvin and Gagne ©2007
Bakery Algorithm: “Take 1”


int ticket[n];
int next_ticket;
CSEnter(int i)
{
ticket[i] = ++next_ticket;
for(J = 0; J < N; J++)
while(ticket[J] && ticket[J] < ticket[i])
continue;
}

CSExit(int i)
{
ticket[i] = 0;
}
Oops… access to next_ticket is a problem!
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.25
Silberschatz, Galvin and Gagne ©2007
Bakery Algorithm: “Take 2”

int ticket[n];
Just add 1 to the max!
CSEnter(int i)
CSExit(int i)
{
{
ticket[i] = max(ticket[0], … ticket[N-1])+1;
ticket[i] = 0;
for(J = 0; J < N; J++)
}
while(ticket[J] && ticket[j] < ticket[i])
continue;
}

Clever idea: just add one to the max.

Oops… two could pick the same value!
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.26
Silberschatz, Galvin and Gagne ©2007
Bakery Algorithm: “Take 3”
If i, j pick same ticket value, id’s break tie:
(ticket[J] < ticket[i]) || (ticket[J]==ticket[i] && J<i)
Notation: (B,J) < (A,i) to simplify the code:
(B<A || (B==A && J<i)), e.g.:
(ticket[J],J) < (ticket[i],i)
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.27
Silberschatz, Galvin and Gagne ©2007
Bakery Algorithm: “Take 4”


int ticket[N];
boolean picking[N] = false;
CSEnter(int i)
CSExit(int i)
{
{
ticket[i] = max(ticket[0], … ticket[N-1])+1;
ticket[i] = 0;
for(J = 0; J < N; J++)
}
while(ticket[J] && (ticket[J],J) < (ticket[i],i))
continue;
}

Oops… i could look at J when J is still storing its ticket,
and yet J could have a lower id than me (i)!
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.28
Silberschatz, Galvin and Gagne ©2007
Bakery Algorithm: Almost final


int ticket[N];
boolean choosing[N] = false;
CSEnter(int i)
CSExit(int i)
{
{
choosing[i] = true;
ticket[i] = 0;
ticket[i] = max(ticket[0], … ticket[N-1])+1;
}
choosing[i] = false;
for(J = 0; J < N; J++) {
while(choosing[J]) continue;
while(ticket[J] && (ticket[J],J) < (ticket[i],i))
continue;
}
}
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.29
Silberschatz, Galvin and Gagne ©2007
Bakery Algorithm: Issues?


What if we don’t know how many threads might be
running?
 The algorithm depends on having an agreed upon
value for N
 Somehow would need a way to adjust N when a
thread is created or one goes away
Also, technically speaking, ticket can overflow!
 Solution: Change code so that if ticket is “too big”, set
it back to zero and try again.
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.30
Silberschatz, Galvin and Gagne ©2007
Bakery Algorithm: Final


int ticket[N]; /* Important: Disable thread scheduling when changing N */
boolean choosing[N] = false;
CSEnter(int i)
{
CSExit(int i)
do {
{
ticket[i] = 0;
ticket[i] = 0;
choosing[i] = true;
}
ticket[i] = max(ticket[0], … ticket[N-1])+1;
choosing[i] = false;
} while(ticket[i] >= MAXIMUM);
for(J = 0; J < N; J++) {
while(choosing[J]) continue;
while(ticket[J] && (ticket[J],J) < (ticket[i],i))
continue;
}
}
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.31
Silberschatz, Galvin and Gagne ©2007
Approaches to Critical Sections




Everything we’ve seen so far is a software-only solution that
relies on reads and writes being atomic
 Atomic = non-interruptible
Another approach: disable interrupts
 Disable interrupts briefly when calling CSEnter() and CSExit()
 Currently running code would execute without preemption
 Available only in the kernel (why?)
 Generally doesn’t work on multiprocessor systems
 Operating systems using this not broadly scalable
Modern machines provide hardware “help”: atomic instructions
 Either test memory word and set value (test and set)
 Or swap contents of two memory words (compare and swap)
Idea is to provide a mechanism for critical sections: a lock
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.32
Silberschatz, Galvin and Gagne ©2007
Critical Section Using Locks
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.33
Silberschatz, Galvin and Gagne ©2007
TestAndSet Instruction

Definition:
boolean TestAndSet (boolean *target)
{
boolean rv = *target;
*target = TRUE;
return rv:
}
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.34
Silberschatz, Galvin and Gagne ©2007
Solution using TestAndSet


Shared boolean variable lock., initialized to false.
Solution:
while (true) {
while ( TestAndSet (&lock ))
; /* do nothing
//
critical section
lock = FALSE;
//
remainder section
}
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.35
Silberschatz, Galvin and Gagne ©2007
Critical Sections using TestAndSet
cs_enter:
TSL REGISTER, LOCK // copy lock to reg and set to 1
CMP REGISTER, #0 // was lock 0?
JNE cs_enter
// if so, loop
RET
// otherwise return, in critical sec
cs_exit:
STORE LOCK #0
RET
// set lock to 0
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.36
Silberschatz, Galvin and Gagne ©2007
Swap Instruction

Definition:
void Swap (boolean *a, boolean *b)
{
boolean temp = *a;
*a = *b;
*b = temp:
}
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.37
Silberschatz, Galvin and Gagne ©2007
Solution using Swap


Shared Boolean variable lock initialized to FALSE; Each process has a
local Boolean variable key.
Solution:
while (true) {
key = TRUE;
while ( key == TRUE)
Swap (&lock, &key );
//
critical section
lock = FALSE;
//
remainder section
}
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.38
Silberschatz, Galvin and Gagne ©2007
Critical Sections using Swap
cs_enter:
MOVE REGISTER, #1
XCHG REGISTER, LOCK
CMP REGISTER, #0
JNE cs_enter
RET
//
//
//
//
//
cs_exit:
MOVE LOCK, #0
RET
// store a zero in lock
// return to caller
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
put 1 in register
swap reg and lock contents
was lock zero?
if not, loop
otherwise return, in cs
6.39
Silberschatz, Galvin and Gagne ©2007
Providing Critical Sections to Users


Everything we’ve seen so far involves busy waiting
 Also known as spin locks
 Acceptable for short waits (e.g., interrupts)
 Need a general-purpose mechanism that allows
sleeping
Would like to provide higher-level abstractions to users
 CSEnter and CSExit are possibilities
 Operating systems have offer other primitives
 E.g., semaphores, condition variables, mutexes
 Built out of low-level critical section operations
 More in next class
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
6.40
Silberschatz, Galvin and Gagne ©2007
End of Chapter 6 (a)
Operating System Concepts with Java – 7th Edition, Nov 15, 2006
Silberschatz, Galvin and Gagne ©2007
Download