Module 3.1 Process Synchronization
Module 3.1 Process Synchronization
• Background
• The Critical-Section Problem
• Synchronization Hardware
• Semaphores
• Classical Problems of Synchronization
• Critical Regions
• Monitors
• Synchronization in Solaris 2 & Windows 2000
Background
• Concurrent access to shared data may result in data
inconsistency.
• Maintaining data consistency requires mechanisms to
ensure the orderly execution of cooperating processes.
• Shared-memory solution to bounded-buffer problem
allows at most n – 1 items in buffer at the same time.
A solution, where all N buffers are used is not simple.
– Suppose that we modify the producer-consumer code by
adding a variable counter, initialized to 0 and incremented
each time a new item is added to the buffer
Processes
1.Serial
2.Parallel
Parallel Process
Independent Interdependent
(cooperative)
Process Synchronization
• P1 • P2
{.. {..
….. …..
….. …..
….. …..
count++ count--
….. …..
….. …..
….. …..
} }
• Shared data
#define BUFFER_SIZE 10
typedef struct {
...
} item;
item buffer[BUFFER_SIZE];
int in = 0;
int out = 0;
int counter = 0;
Bounded-Buffer
• Producer process
item nextProduced;
while (1) {
while (counter == BUFFER_SIZE)
; /* do nothing */
buffer[in] = nextProduced;
in = (in + 1) % BUFFER_SIZE;
counter++;
}
Bounded-Buffer
• Consumer process
item nextConsumed;
while (1) {
while (counter == 0)
; /* do nothing */
nextConsumed = buffer[out];
out = (out + 1) % BUFFER_SIZE;
counter--;
}
Bounded Buffer
• The statements
counter++;
counter--;
do {
acquire lock
critical section
release lock
remainder section
} while (true);
Semaphores
• Synchronization tool that does not require busy waiting.
• Semaphore S – integer variable
• can only be accessed via two standard atomic operations
• The operations were originally termed P (for wait from
Dutch proberen to test) and V(for signal from verhogen to
increment)
wait (S):
while S 0 do no-op;
S--;
signal (S):
S++;
Critical Section of n Processes
• Shared data:
semaphore mutex; //initially mutex = 1
• Process Pi:
do {
wait(mutex);
critical section
signal(mutex);
remainder section
} while (1);
Semaphore Usage
• Counting semaphore – integer value can range over an unrestricted
domain
• Binary semaphore – integer value can range only between 0 and 1
• Same as a mutex lock
• Can solve various synchronization problems
• Consider P1 and P2 that require S1 to happen before S2
• Create a semaphore “synch” initialized to 0
P1:
S1;
signal(synch);
P2:
wait(synch);
S2;
• Can implement a counting semaphore S as a binary semaphore
Semaphore Implementation
• Must guarantee that no two processes can execute
the wait() and signal() on the same semaphore at
the same time
• Thus, the implementation becomes the critical section
problem where the wait and signal code are placed in
the critical section
• Could now have busy waiting in critical section
implementation
• But implementation code is short
• Little busy waiting if critical section rarely occupied
• Note that applications may spend lots of time in
critical sections and therefore this is not a good
solution
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 invoking the operation on the
appropriate waiting queue
wakeup – remove one of processes in the waiting queue and
place it in the ready queue
typedef struct{
int value;
struct process *list;
} semaphore;
Implementation with no Busy waiting (Cont.)
wait(semaphore *S) {
S->value--;
if (S->value < 0) {
add this process to S->list;
block();
}
}
signal(semaphore *S) {
S->value++;
if (S->value <= 0) {
remove a process P from S->list;
wakeup(P);
}
}
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
P0 P1
wait(S); wait(Q);
wait(Q); wait(S);
... ...
signal(S); signal(Q);
signal(Q); signal(S);
do {
...
/* produce an item in next_produced */
...
wait(empty);
wait(mutex);
...
/* add next produced to the buffer */
...
signal(mutex);
signal(full);
} while (true);
Bounded Buffer Problem (Cont.)
• The structure of the consumer process
Do {
wait(full);
wait(mutex);
...
/* remove an item from buffer to next_consumed
*/
...
signal(mutex);
signal(empty);
...
/* consume the item in next consumed */
...
} while (true);
Readers-Writers Problem
A datadase 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
Several variations of how readers and writers are considered – all
involve some form of priorities
Shared Data
Database
Semaphore rw_mutex initialized to 1
Semaphore mutex initialized to 1
Integer read_count initialized to 0
Readers-Writers Problem (Cont.)
• The structure of a writer process
do {
wait(rw_mutex);
...
/* writing is performed */
...
signal(rw_mutex);
} while (true);
Readers-Writers Problem (Cont.)
The structure of a reader process
do {
wait(mutex);
read_count++;
if (read_count == 1)
wait(rw_mutex);
signal(mutex);
...
/* reading is performed */
...
wait(mutex);
read count--;
if (read_count == 0)
signal(rw_mutex);
signal(mutex);
} while (true);
Dining-Philosophers Problem
// eat
signal (chopstick[i] );
signal (chopstick[ (i + 1) % 5] );
// think
} while (TRUE);
monitor monitor-name
{
// shared variable declarations
procedure P1 (…) { …. }
initialization_code() {
for (int i = 0; i < 5; i++)
state[i] = THINKING;
}
}
Solution to Dining Philosophers (Cont.)
DiningPhilosophers.pickup(i);
EAT
DiningPhilosophers.putdown(i);
if (x_count > 0) {
next_count++;
signal(x_sem);
wait(next);
next_count--;
}
Solaris 2 Synchronization
• Implements a variety of locks to support multitasking,
multithreading (including real-time threads), and
multiprocessing.