Process Synchronization - Class
Process Synchronization - Class
Background
The Critical-Section Problem
Synchronization Hardware
Semaphores
Classical Problems of Synchronization
Critical Regions
Monitors
Message Passing
Synchronization in Solaris 2 & Windows 2000
inconsistency.
#define BUFFER_SIZE 10
typedef struct {
...
} item;
item buffer[BUFFER_SIZE];
int in = 0;
int out = 0;
int counter = 0;
item nextProduced;
while (1) {
; /* do nothing */
buffer[in] = nextProduced;
in = (in + 1) % BUFFER_SIZE;
counter++;
Consumer process
item nextConsumed;
while (1) {
while (counter == 0)
; /* do nothing */
nextConsumed = buffer[out];
counter--;
The statements
counter++;
counter--;
register1 = counter
register1 = register1 + 1
counter = register1
register2 = counter
register2 = register2 – 1
counter = register2
interleaved.
The value of count may be either 4 or 6, where the correct result should be 5.
Race Condition
Each process has a code segment, called critical section, in which the shared data is accessed.
Each process must request permission to enter its critical section. The section of code
Problem – ensure that when one process is executing in its critical section, no other
some processes that wish to enter their critical section, then the
selection of the processes that will enter the critical section next
Process Pi
do {
critical section
remainder section
} while (1);
Meets all three requirements; solves the critical-section problem for two processes.
Semaphore
Synchronization tool that does not require busy waiting
6.26
Semaphore as
General Synchronization Tool
Counting semaphore – integer value can range over an unrestricted
domain
Binary semaphore – integer value can range only between 0
and 1; can be simpler to implement
6.27
Semaphore Implementation
with no Busy waiting
With each semaphore there is an associated waiting
queue. Each entry in a waiting queue has two data items:
value (of type integer)
pointer to next record in the list
Two operations:
block – place the process in appropriate waiting
queue.
wakeup – remove one of processes in the waiting
queue and place it in the ready queue.
6.28
Semaphore Implementation with
no Busy waiting (Cont.)
Implementation of wait:
wait(semaphore *S) {
S->value--;
if (S->value < 0) {
add this process to S->list;
block();
}
}
Implementation of signal:
signal(semaphore *S) {
S->value++;
if (S->value <= 0) {
remove a process P from S->list;
wakeup(P);
}
}
6.29
Deadlock and Starvation
Deadlock – two or more processes are waiting indefinitely for an
event that can be caused by only one of the waiting processes
Let S and Q be two semaphores initialized to 1
P0 P1
wait (S); wait (Q);
wait (Q); wait (S);
. .
. .
. .
signal (S); signal (Q);
signal (Q); signal (S);
6.30
Classical Problems of Synchronization
Bounded-Buffer Problem
Dining-Philosophers Problem
6.31
Bounded-Buffer Problem
6.32
Bounded Buffer Problem (Cont.)
do {
// produce an item in nextp
wait (empty);
wait (mutex);
// add the item to the buffer
signal (mutex);
signal (full);
} while (TRUE);
6.33
Bounded Buffer Problem (Cont.)
do {
wait (full);
wait (mutex);
// remove an item from buffer to
nextc
signal (mutex);
signal (empty);
// consume the item in nextc
} while (TRUE);
6.34
Readers-Writers Problem
A data set is shared among a number of concurrent processes.
Readers – only read the data set; they do not perform any
updates
Writers – can both read and write
Problem – allow multiple readers to read at the same time. Only
one single writer can access the shared data at the same time.
Shared Data
Data set
Semaphore mutex initialized to 1 (controls access to
readcount)
Semaphore wrt initialized to 1 (writer access)
Integer readcount initialized to 0 (how many processes are
reading object)
6.35
Readers-Writers Problem (Cont.)
do {
wait (wrt) ;
// writing is performed
signal (wrt) ;
} while (TRUE);
6.36
Readers-Writers Problem (Cont.)
The structure of a reader process
do {
wait (mutex) ;
readcount ++ ;
if (readcount == 1)
wait (wrt) ;
signal (mutex)
// reading is performed
wait (mutex) ;
readcount - - ;
if (readcount == 0)
signal (wrt) ;
signal (mutex) ;
} while (TRUE);
6.37
Dining-Philosophers Problem
Shared data
Bowl of rice (data set)
Semaphore chopstick [5] initialized to 1
6.38
Dining-Philosophers Problem (Cont.)
The structure of Philosopher i:
do {
wait ( chopstick[i] );
wait ( chopStick[ (i + 1) % 5] );
// eat
signal ( chopstick[i] );
signal (chopstick[ (i + 1) % 5] );
// think
} while (TRUE);
6.39
More Problems with Semaphores
6.40
Monitors
A high-level abstraction that provides a convenient and
effective mechanism for process synchronization
Only one process may be active within the monitor at a time
monitor monitor-name
{
// shared variable declarations
procedure P1 (…) { …. }
…
procedure Pn (…) {……}
Initialization code ( ….) { … }
…
}
}
6.41
Schematic view of a Monitor
6.42
Condition Variables
condition x, y;
Two operations on a condition variable:
x.wait () – a process that invokes the operation is
suspended.
x.signal () – resumes one of processes (if any)
that
invoked x.wait ()
6.43
Monitor with Condition Variables
6.44
Solution to Dining Philosophers
monitor DP
{
enum { THINKING; HUNGRY, EATING) state [5] ;
condition self [5];
initialization_code() {
for (int i = 0; i < 5; i++)
state[i] = THINKING;
}
}
6.46
Bakery Algorithm
Shared data:
boolean lock = false;
Process Pi
do {
while (TestAndSet(lock)) ;
critical section
lock = false;
remainder section
}
Process P
i
do {
key = true;
while (key == true)
Swap(lock,key);
critical section
lock = false;
remainder section
}
These two solutions in Figure 7.7 and Figure 7.9 don’t
satisfy the bounded-waiting requirement.
The algorithm in Figure 7.10 satisfies all the critical-
section requirement.
1. Disable interrupts.
2. Shared lock variable.
3. Strict alteration. See Ex6.c. See the manual entry for
shared memory allocation: man shmget and Ex6.c and
programs in shm directory.
4. Peterson's solution [1981]. Challenge: modify the code in
Ex6.c to implement Peterson's solution for 2 processes.
Note several variables are shared:
shared int turn = 0;
shared int flag[2] = {FALSE,FALSE};
5. Hardware solution: Test-and-Set Locks (TSL).
signal (S):
S++;
Note: These operations were originally termed P (wait, proberen)
and V (signal, verhogen). In order to distinguish with the C/UNIX
function wait and signal, in our programming examples we use
DOWN and UP to indicate wait and signal respectively.
Process Pi:
do {
wait(mutex);
critical section
signal(mutex);
remainder section
} while (1);
Data structures:
binary-semaphore S1, S2;
int C:
Initialization:
S1 = 1
S2 = 0
C = initial value of semaphore S
signal operation
wait(S1);
C ++;
if (C <= 0)
signal(S2);
else
signal(S1);
DOWN(mutex);
- critical section -
UP(mutex);
S1 S2
A ----> B ----> D
| ^
| S1 S3 |
+-----> C ------+
Process A:
----------
- do work of A
UP(S1); /* Let B or C start */
UP(S1); /* Let B or C start */
Process B:
----------
DOWN(S1); /* Block until A is finished */
- do work of B
UP(S2);
Process C:
----------
DOWN(S1);
- do work of C
UP(S3);
Process D:
----------
DOWN(S2);
DOWN(S3);
- do work of D
S1 S2
A ----> B ----> D
| ^
| S3 S4 |
+-----> C ------+
Process A:
----------
- do work of A
UP(S1); /* Let B start */
UP(S3); /* Let C start */
Process B:
----------
DOWN(S1); /* Block until A is finished */
- do work of B
UP(S2);
Process C:
----------
DOWN(S3);
- do work of C
UP(S4);
Process D:
----------
DOWN(S2);
DOWN(S4);
- do work of D
Bounded-Buffer Problem
Dining-Philosophers Problem
Shared data
Initially:
do {
…
produce an item in nextp
…
wait(not_full);
wait(mutex);
…
add nextp to buffer
…
signal(mutex);
signal(not_empty);
} while (1);
do {
wait(not_empty)
wait(mutex);
…
remove an item from buffer to nextc
…
signal(mutex);
signal(not_full);
…
consume the item in nextc
…
} while (1);
Initially
wait(wrt);
…
writing is performed
…
signal(wrt);
wait(mutex);
readcount++;
if (readcount == 1)
wait(rt);
signal(mutex);
…
reading is performed
…
wait(mutex);
readcount--;
if (readcount == 0)
signal(wrt);
signal(mutex):
Philosopher i:
do {
wait(chopstick[i])
wait(chopstick[(i+1) % 5])
…
eat
…
signal(chopstick[i]);
signal(chopstick[(i+1) % 5]);
…
think
…
} while (1);
Shared data:
struct buffer {
int pool[n];
int count, in, out;
}
monitor monitor-name
{
shared variable declarations
procedure body P1 (…) {
...
}
procedure body P2 (…) {
...
}
procedure body Pn (…) {
...
}
{
initialization code
}
}
void putdown(int i) {
state[i] = thinking;
// test left and right neighbors
test((i+4) % 5);
test((i+1) % 5);
}
Variables
semaphore mutex; // (initially = 1)
semaphore next; // (initially = 0)
int next-count = 0;
x-count++;
if (next-count > 0)
signal(next);
else
signal(mutex);
wait(x-sem);
x-count--;
if (x-count > 0) {
next-count++;
signal(x-sem);
wait(next);
next-count--;
}