Contents

advertisement
TDDI04
Concurrent Programming,
Operating Systems,
and Real-time Operating Systems
Contents
§ The Critical-Section Problem
§ Disable interrupts – for small critical sections and 1 CPU
§ Peterson’s Solution for 2 processes
§ Hardware Support for Synchronization
Process Synchronization
•
Atomic TestAndSet, Atomic Swap
§ Semaphores and Locks
§ Why and How to Avoid Busy Waiting
§ Classic Problems for Studying Synchronization
[SGG7] Chapter 6
•
Bounded Buffer, Readers/Writers, Dining Philosophers
§ Monitors
•
Copyright Notice: The lecture notes are mainly based on Silberschatz’s, Galvin’s and Gagne’s book (“Operating System
Concepts”, 7th ed., Wiley, 2005). No part of the lecture notes may be reproduced in any form, due to the copyrights
reserved by Wiley. These lecture notes should only be used for internal teaching purposes at the Linköping University.
Condition variables
Acknowledgment: The lecture notes are originally compiled by C. Kessler, IDA.
Klas Arvidsson, IDA,
Linköpings universitet.
Concerns both processes sharing memory and threads
(in the following we only write ”processes”, for brevity)
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
Background
§ And nondeterminism – result may depend on relative speed of processes
involved (”race conditions”)
while (true) {
while (count == BUFFER_SIZE)
;
§ Consider a process producing data to be read by an other process. (The
consumer-producer problem). This can be realized with
a shared memory buffer and some variables keeping track of total size,
used size (count), read position and write position. The count variable will
be shared by both processes:
// do nothing
buffer[in] = nextProduced;
in = (in + 1) % BUFFER_SIZE;
count++;
}
Producer increments count after it produces a new item
Consumer decrements count after it consumes an item.
Remaining variables can be read-only or modified by only one process.
© Silberschatz, Galvin and Gagne 2005
5.3
Consumer
// produce an item and put in nextProduced
§ Maintaining data consistency requires mechanisms to ensure the orderly
execution of cooperating processes
Initially, count is set to 0.
Producer
buffer
Producer:
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
out in
Producer/Consumer
§ Concurrent access to shared data may result in data inconsistency
•
•
•
© Silberschatz, Galvin and Gagne 2005
5.2
n Shared integer variable
count, initialized to 0, keep
track of the number of items
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
Consumer:
while (true) {
while (count == 0)
; // do nothing
nextConsumed = buffer[out];
out = (out + 1) % BUFFER_SIZE;
count--;
// … consume item in
nextConsumed
}
5.4
Race Conditions lead to Nondeterminism
Critical Section
§ count++ could be implemented in machine code as
§ Critical Section: A set of instructions, operating on
shared data or resources, that should be executed
by a single process without interruption
39: register1 = count
40: register1 = register1 + 1
41: count = register1
: load r1, _count
: add r1, r1, #1
: store _count, r1
§ count-- could be implemented as
22: register2 = count
23: register2 = register2 - 1
24: count = register2
•
•
NOT
ATOMIC!
§ Consider this execution interleaving, with “count = 5” initially:
39: producer executes register1 = count
{ register1 = 5 }
Context
40: producer executes register1 = register1 + 1 { register1 = 6 }
switch
22: consumer executes register2 = count
{ register2 = 5 }
23: consumer executes register2 = register2 - 1 { register2 = 4 }
41: producer executes count = register1
{ count = 6 }
24: consumer executes count = register2
{ count = 4 }
§ Compare to a different interleaving, e.g., 39,40,41,switch,22,23,24…
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.5
© Silberschatz, Galvin and Gagne 2005
© Silberschatz, Galvin and Gagne 2005
Atomicity of execution
Mutual exclusion: At most one process should
be allowed to operate inside at any time
• Consistency: inconsistent intermediate states of
shared data not visible to other processes outside
§ May consist of different program parts for different processes
•
Ex: producer and consumer code accessing count is 1 critical section
§ General structure, with structured control flow:
...
Entry of critical section C
… critical section C: operation on shared data
Exit of critical section C
…
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.6
© Silberschatz, Galvin and Gagne 2005
1
Requirements on a Solution to
the Critical-Section Problem
A First Attempt…
§ For 2 processes P0 P1
1. Mutual Exclusion
•
If process Pi is executing in its critical section C, then no other
processes can be executing in their critical sections C (accessing the
same shared data/resource)
2. Progress
•
If no process is executing in its critical section C and there exist some
processes that wish to enter their critical section C, then the selection
of the process that will enter C next cannot be postponed indefinitely
A bound must exist on the number of times that other processes are
allowed to enter their critical sections after a process 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
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
© Silberschatz, Galvin and Gagne 2005
5.7
Peterson’s Solution
§ Assumes that Load and Store
instructions execute atomically
§ Shared variables:
int turn;
bool ready[2] = { false, false };
§ The variable turn indicates
whose turn it is to enter the
critical section.
Code for process Pi
( j = 1 – i ):
do {
ready[i] = TRUE;
turn = j;
while ( ready[j] && turn == j)
; /* busy wait */
…. CRITICAL SECTION
ready[i] = FALSE;
…. REMAINDER SECTION
§ ready[i] = true ⇒ process Pi is
ready to enter, for i=0,1.
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
do {
flag[i] = TRUE;
while (flag[j]) ;
boolean flag[2] = { false, false };
•
flag[i] == TRUE ⇒
Pi ready to enter its
critical section, for i = 0, 1
§ Satisfies mutual exclusion,
but not progress requirement.
•
… critical section
flag[i] = false;
… remainder section
} while (TRUE);
P0 sets flag[0]=TRUE; context switch; P1 sets flag[1]=TRUE; …
⇒ infinite loop
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.8
© Silberschatz, Galvin and Gagne 2005
Hardware Support for Synchronization
§ For 2 processes P0 P1
•
•
(Notation: j = 1 – i )
§ Shared variables:
3. Bounded Waiting
•
n Code for process Pi :
§ Assumes that Load and Store
execute atomically
} while (TRUE);
5.9
© Silberschatz, Galvin and Gagne 2005
TestAndSet Instruction
§ Most systems provide hardware support for protecting critical sections
§ Uniprocessors – could disable interrupts
•
•
Currently running code would execute without preemption
Generally too inefficient on multiprocessor systems
> Operating systems using this are not broadly scalable
§ Modern machines provide special atomic instructions
•
•
TestAndSet: test memory word and set value atomically
Swap: swap contents of two memory words atomically
> Atomic = non-interruptable
> If multiple TestAndSet or Swap instructions are executed
simultaneously (each on a different CPU in a multiprocessor),
then they take effect sequentially in some arbitrary order.
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.10
© Silberschatz, Galvin and Gagne 2005
Swap Instruction
§ Definition in pseudocode:
§ Definition in pseudocode:
void Swap (boolean *a, boolean *b)
{
boolean temp = *a;
atomic
*a = *b;
*b = temp;
}
boolean TestAndSet (boolean *target)
{
atomic
boolean rv = *target;
*target = TRUE;
return rv;
// return the OLD value
}
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.11
© Silberschatz, Galvin and Gagne 2005
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.12
© Silberschatz, Galvin and Gagne 2005
2
TestAndSet using atomic swap
Solution using TestAndSet
§ Shared boolean variable lock, initialized to FALSE (= unlocked)
§ Definition using atomic swap:
§ do {
while ( TestAndSet (&lock ))
{
boolean rv = TRUE;
boolean tmp = *target;
*target = rv;
rv = tmp;
return rv;
// do nothing but spinning on the lock (busy waiting)
;
boolean TestAndSet (boolean *target)
// … critical section
atomic swap
lock = FALSE;
Swap(&rv, target);
// …
remainder section
// return the OLD value
}
} while ( TRUE);
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.13
© Silberschatz, Galvin and Gagne 2005
Solution using Swap
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
§ Semaphore S:
•
•
shared integer variable
two standard atomic operations to modify S:
wait() and signal(), originally called P() and V()
§ Provides mutual exclusion
§ wait (S) {
while S <= 0
;
critical section
// busy waiting…
S--;
}
lock = FALSE;
// remainder section
} while (TRUE);
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.15
Must be atomic
§ signal (S) {
S++;
}
© Silberschatz, Galvin and Gagne 2005
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.16
© Silberschatz, Galvin and Gagne 2005
Semaphore Implementation using a Lock
Semaphores and Locks
§ Counting semaphore – integer value, increment/decrement
§ Binary semaphore – value in { 0, 1 } resp., { FALSE, TRUE };
•
•
•
© Silberschatz, Galvin and Gagne 2005
Semaphore
§ Shared Boolean variable lock initialized to FALSE;
§ Each process has a local Boolean variable tmp.
§ do {
tmp = TRUE;
while ( tmp == TRUE)
Swap (&lock, &tmp );
// busy waiting…
//
5.14
§ Must guarantee that no two processes can execute wait(S)
and signal(S) on the same semaphore S at the same time
Also known as (mutex) locks
§ Thus, implementation becomes the critical section problem
where the wait and signal code are placed in the critical
section.
Called spinlocks if using busy waiting
§ Protect this critical section by a mutex lock L.
can be simpler to implement (e.g., using TestAndSet)
§ Can implement a counting semaphore S using a lock
§ Provides mutual exclusion:
Semaphore S;
// initialized to 1
wait (S);
… Critical Section code
signal (S);
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.17
© Silberschatz, Galvin and Gagne 2005
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.18
© Silberschatz, Galvin and Gagne 2005
3
Busy Waiting?
Eliminate Busy Waiting
§ Up to now, we used busy waiting in critical section
implementations (spinning on semaphore / lock)
§ With each semaphore there is an associated waiting queue.
Each entry in a waiting queue contains:
•
•
•
•
Mostly OK on multiprocessors (disable interrupts costly)
•
As applications may spend lots of time in critical sections,
this is not a good solution.
OK if critical section is very short and rarely occupied
•
•
Process table index, e.g. pid
Pointer to next entry
But accesses shared memory frequently …
For longer critical sections or high congestion,
waste of CPU time
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.19
© Silberschatz, Galvin and Gagne 2005
§ Two operations:
•
block – place the process invoking the operation on the
appropriate waiting queue.
•
wakeup – remove one of processes in the waiting queue
and place it in the ready queue.
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
© Silberschatz, Galvin and Gagne 2005
5.20
Semaphore Implementation w/o Busy Waiting
Problems: Deadlock and Starvation
§ typedef struct { int value; struct process *wqueue; } semaphore;
§ Deadlock – two or more processes are waiting
indefinitely for an event that can be caused
only by one of the waiting processes
§ Typical example: Nested critical sections
• Guarded by semaphores S and Q, initialized to 1
P0
P1
§ void wait ( semaphore *S ) {
S->value--;
if (S->value < 0) {
add this process to S->wqueue;
block(); // I release the lock on the critical section for S …
}
// … and release the CPU
}
§ void signal ( semaphore *S ) {
S->value++;
if (S->value <= 0) {
remove a process P from S->wqueue;
wakeup (P); // append P to ready queue
}
}
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.21
time
wait (S);
wait (Q);
wait (Q);
…
wait (S);
…
signal (S);
signal (Q);
signal (Q);
signal (S);
§ Starvation – indefinite blocking. A process may never be
removed from the semaphore queue in which it is suspended.
© Silberschatz, Galvin and Gagne 2005
Classical Problems of Synchronization
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.22
© Silberschatz, Galvin and Gagne 2005
Bounded-Buffer Problem
§ Bounded-Buffer Problem
out in
§ Readers and Writers Problem
Producer
Consumer
buffer
§ Dining-Philosophers Problem
§ Buffer with space for N items
§ Semaphore mutex initialized to the value 1
• Protects accesses to out, int, buffer
§ Semaphore full initialized to the value 0
• Counts number of filled buffer places
§ Semaphore empty initialized to the value N
• Counts number of free buffer places
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.23
© Silberschatz, Galvin and Gagne 2005
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.24
© Silberschatz, Galvin and Gagne 2005
4
Bounded Buffer
Producer:
do {
Readers-Writers Problem
Consumer:
do {
// … produce an item
Atomic
decrement;
block if no
place filled
(all empty)
wait (empty);
wait (mutex);
wait (full);
wait (mutex);
// … add item to buffer
// … remove an item from buffer
signal (mutex);
signal (full);
signal (mutex);
signal (empty); // atomic incr.
// … consume the removed item
} while (true);
} while (true);
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.25
© Silberschatz, Galvin and Gagne 2005
Readers-Writers Problem (Cont.)
§ 2 types of processes accessing a shared data set:
• Readers – only read accesses, no updates
• Writers – can both read and write.
§ Relaxed Mutual Exclusion Problem:
allow either multiple readers or one single writer
to access the shared data at the same time.
§ Solution: use
• Semaphore mutex initialized to 1.
• Semaphore wrt initialized to 1.
• Integer readcount initialized to 0.
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
// to protect readcount
// 0: someone inside
// count readers inside
5.26
© Silberschatz, Galvin and Gagne 2005
Readers-Writers Problem (Cont.)
§ The structure of a writer process:
do {
wait(wrt); // writers queue on wrt
§ The structure of a reader process:
do {
wait(mutex);
readcount++;
if (readcount == 1)
wait(wrt); // first reader queues on wrt,
signal(mutex); // and the others on mutex.
// … writing is performed
// … reading is performed
signal(wrt);
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
This solution
favors the
readers.
Writers may
starve.
wait(mutex);
readcount--;
if (readcount == 0)
signal(wrt);
signal(mutex);
} while (true);
} while(true);
5.27
© Silberschatz, Galvin and Gagne 2005
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.28
© Silberschatz, Galvin and Gagne 2005
3+1 Deadlock-Free Solutions of the
Dining-Philosophers Problem
Dining-Philosophers Problem
Shared data
§ Bowl of rice (data set)
§ Array of 5 semaphores chopstick [5],
all initialized to 1
§ Allow at most four philosophers to be sitting simultaneously at
the table
The structure of Philosopher i:
§ Allow a philosopher to pick up her/his chopsticks only if both
chopsticks are available (both in the same critical section)
do {
wait(chopstick[i]);
wait(chopstick[(i + 1) % 5]);
// … eat …
signal(chopstick[i]);
signal(chopstick[(i + 1)%5]);
// … think …
} while(true);
§ Odd philosophers pick up left chopstick first; even
philosophers pick up right chopstick first
(Asymmetric solution)
§ In Fork: Represent chopsticks as bits in a bitvector (integer)
and use bit-masked atomic operations to grab both chopsticks
simultaneously. Works for up to sizeof(int) philosophers.
[Keller, K., Träff: Practical PRAM Programming, Wiley 2001]
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.29
© Silberschatz, Galvin and Gagne 2005
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.30
© Silberschatz, Galvin and Gagne 2005
5
Monitors
Pitfalls with Semaphores
§ Monitor: high-level abstraction à la OOP that provides a convenient and
effective mechanism for process synchronization
§ Correct use of semaphore operations:
• Protect all possible entries/exits of
control flow into/from critical section:
wait (mutex) …. signal (mutex)
§ Possible sources of synchronization errors:
• Omitting wait (mutex) or signal (mutex) (or both) ??
• wait (mutex) …. wait (mutex) ??
• wait (mutex1) …. signal (mutex2) ??
• Multiple semaphores with different orders of wait() calls ??
> Example: Each philosopher first grabs the chopstick to
its left -> risk for deadlock!
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.31
© Silberschatz, Galvin and Gagne 2005
Condition Variables
•
Encapsulate shared data, protecting semaphore, and all operations in a
single abstract data type
•
•
Only one process may be active within the monitor at a time
monitor monitor-name
{
… // shared variable declarations
initialization ( ….) { … }
procedure P1 (…) { …. }
…
procedure Pn (…) {……}
}
§ In Java realized as classes with
synchronized methods
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.32
© Silberschatz, Galvin and Gagne 2005
Monitor with Condition Variables
§ condition x;
§ Waiting queue associated with each condition variable
§ Two operations on a condition variable:
• wait(x) or x.wait ()
– process that invokes the operation is suspended.
•
x
signal(x) or x.signal ()
– resumes one of processes (if any) that invoked wait (x)
§ Awoken thread must re-apply for entry into the monitor.
Two scenarios:
•
•
Signal and wait: signal’ing thread steps aside for resumed
Signal and continue: signal’ing thread continues while
resumed thread waits for signal’er
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.33
© Silberschatz, Galvin and Gagne 2005
Example: Monitor with condition variable
monitor {
// Simple resource allocation monitor in pseudocode
boolean inUse = false; // simple shared state variable
condition available;
// condition variable
initialization { inUse = false; }
procedure getResource ( ) {
if (inUse)
wait( available ); // wait until available is signaled
inUse = true;
// resource is now in use
}
procedure returnResource ( ) {
inUse = false;
signal( available ); // signal a waiting thread to proceed
}
}
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.35
© Silberschatz, Galvin and Gagne 2005
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.34
© Silberschatz, Galvin and Gagne 2005
Monitor Solution to Dining Philosophers
monitor DiningPhilosopher {
enum { THINKING, HUNGRY, EATING } state [5] ;
condition self [5];
void pickup ( int i ) {
state[i] = HUNGRY;
test ( i );
if (state[i] != EATING)
self [i].wait();
}
void test ( int i )
{
if ( (state[(i+4)%5] != EATING)
&& (state[i] == HUNGRY)
&& (state[(i+1)%5] != EATING) )
{
state[i] = EATING;
self[i].signal ();
}
}
initialization_code()
void putdown ( int i ) {
{
state[i] = THINKING;
for (int i = 0; i < 5; i++)
test((i+4)%5); // left neighbor
state[i] = THINKING;
}
test((i+1)%5); // right neighbor
} // end monitor
}
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.36
© Silberschatz, Galvin and Gagne 2005
6
Synchronization Examples
§ Solaris
§ Windows XP
[SGG7]
Chapter 6.8
§ Linux
§ Pthreads
TDDI04, K. Arvidsson, IDA, Linköpings universitet.
5.37
© Silberschatz, Galvin and Gagne 2005
7
Download