You are on page 1of 49

MODULE 3

Module 3
● Process synchronization
● Race conditions – Critical section problem
● Peterson’s solution, Synchronization hardware
● Mutex Locks, Semaphores, Monitors
● Synchronization problems -Producer Consumer, Dining Philosophers and
Readers-Writers.

● Deadlocks
● Necessary conditions,
● Resource allocation graphs,
● Deadlock prevention,
● Deadlock avoidance – Banker’s algorithms,
● Deadlock detection, Recovery from deadlock.
Race Condition and
Critical Section Problem
Process Synchronization
● Processes executing concurrently in the OS may be
● independent process
● cooperating process

● Independent process
● They cannot affect or be affected by other processes executing in the
system.

● Cooperating process
● They can affect or be affected by other processes, executing in the
system.
● Share – resources, code, variable, memory etc
P1 P2 ● counter++ could be implemented as
{ {
---- ---- register1 = counter
register1 = register1 + 1
---- ----
counter = register1
counter++; ----
---- counter--; ● counter-- could be implemented as
---- ----
register2 = counter
---- ----
register2 = register2 - 1
} } counter = register2

Case Execution order Final value of


counter
Case 1 P1; P2 5
Case 2 P1: 1,2; P2: 1,2,3; P1:3 6
Case 3 P1:1,2; P2:1,2; P1:3; P2:3 4

Race Condition
Race condition is a situation where several process access and manipulate same data
concurrently and the outcome of execution depends on the particular order in which access
takes place .
● Why we need Process synchronization mechanisms?
● Concurrent access to shared data may result in data inconsistency
● Maintaining data consistency requires mechanisms to ensure the
orderly execution of cooperating processes – Process
synchronization mechanisms
Critical Section Problem
● The regions of a program that try to access shared resources and may
cause race conditions are called critical section.
● Portion of code Changing common variables, updating a table, writing a
file and so on
● To avoid race condition among the processes, we need to assure that
only one process at a time can execute within the critical section.
● The critical section problem is used to design a set of protocols
which can ensure that the Race condition among the processes will
never arise.

P1 P2
{ {
---- ----
---- ----
counter++; ----
---- counter--;
---- ----
---- ----
Critical Section
● When one process is in critical section, no other may be in its critical section

● General structure of process Pi

P1
{
----
----
counter++;
----
----
----
}
Solution to critical section problem must satisfy the following 3
requirements:

1. Mutual Exclusion
● If process Pi is executing in its critical section, then no other processes can be
executing in their critical sections

2. Progress
● If no process is executing in its critical section and there exist some processes that
wish to enter their critical section, then the selection of the processes that will
enter the critical section next cannot be postponed indefinitely
● Progress means that if one process doesn't need to execute into critical section
then it should not stop other processes to get into the critical section.

3. Bounded Waiting
- 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
Peterson’s Solution
Peterson’s Solution
● A classic software based solution to CSP

● Two process solution, Pi and Pj

● Humble algorithm – if Pi wants to enter in CS, it allows Pj to enter in CS first.

● To implement Peterson’s solution, 2 data items to be shared between the 2


processes.
● int turn
4 The variable turn indicates whose turn it is to enter the critical section
4 if turn =j, implies it is the turn of process pj to enter the CS

● Boolean flag[2]
4 The flag array is used to indicate if a process is ready to enter the critical
section.
4 flag[i] = true implies that process P is ready
i
Algorithm for Process Pi

do {
flag[i] = true;
turn = j;
while (flag[j] && turn = =
j);
critical section
flag[i] = false;
remainder section
} while (true);
Peterson’s Solution (Cont.)
Peterson’s Solution (Cont.)

● Provable that the three CS requirement are met:


o Mutual exclusion is preserved
o Progress requirement is satisfied
o Bounded-waiting requirement is met
Peterson’s Solution (Cont.)

● May not work correctly on modern computer architecture

● But it provides a good algorithmic description of solving the critical-


section problem and illustrates some of the complexities involved in
designing software that addresses the requirements of mutual
exclusion, progress, and bounded waiting.
Semaphore
Semaphore

● Semaphore is a software based process synchronization mechanism

● Proposed by Edger Dijsktra


● Semaphore S – integer variable

● Can only be accessed via two indivisible (atomic) operations

wait( )

● P(), from the Dutch word proberen, which means to test.

signal( )

● V(), from the Dutch word verhogen, which means to


increment.
● Definition of the wait() operation

wait(S)
{
while (S <= 0); // busy wait
S--;
}

● Definition of the signal() operation

signal(S)
{
S++;
}
S=1
Types of Semaphore

● Binary semaphore – value of semaphore can range only between 0 and 1


● Also known as mutex lock
● Initialized S as 1
● S=1, it is free
● S=0, some process is executing in CS so requesting process has to wait.

● Counting semaphore – integer value can range over an unrestricted


domain
● Used to control access to a resource that has multiple instances.
Semaphore Implementation
● While implementing, main problem with this approach – it requires busy
waiting

● Busy waiting
● While a process is in its CS, any other process that tries to enter its CS
must loop continuously in the entry code.
● Busy waiting wastes CPU cycles that some other process might be able to
use productively.

● This type of semaphore is called spinlock


● Because process spins while waiting for the lock.
● To overcome busy waiting, we can modify the definition of wait() and signal()
semaphore operations.

● Modification on wait()
● When a process executes the wait ( ) operation and finds that the semaphore
value is not positive, it must wait.
● However, rather than engaging in busy waiting, the process can block itself.
● The block() places a process into waiting queue associated with the semaphore,
and the state of the process is switched to the waiting state
● Then the control is transferred to CPU scheduler, which selects another process
to execute.
● Modification on signal()
● A process that is blocked, waiting on a semaphore S, should be restarted when
some other process executes a signal() operation.
● Wakeup() operation changes the process from the waiting state to the ready state.
Semaphore Implementation with no Busy
waiting
● Semaphore should be defined as follows:
typedef struct{
int value;
struct process
*list;
} semaphore;

wait(semaphore *S)
{
S->value--;
if (S->value < 0)
{
add this process to S- signal(semaphore *S) {
>list; S->value++;
block(); if (S->value <= 0) {
} remove a process P
} from
S->list;
wakeup(P);
}
Deadlock and Starvation
● The implementation of semaphore with a waiting queue may result in
Deadlock
● A situation where 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);
Classical Problems of Synchronization

● Classical problems of synchronization:

● Bounded-Buffer Problem/Producer consumer problem

● Dining-Philosophers Problem

● Readers and Writers Problem


Bounded-Buffer Problem
● Also known as producer consumer problem

● The producer tries to insert data into an empty slot of a buffer.


● The consumer tries to remove data from a filled slot in the buffer.
● Problem
● The producer must not insert data when the buffer is full.
● The consumer must not remove data when the buffer is empty.
● The producer and consumer should not insert and remove data simultaneously.
Solution to the Bounded-Buffer Problem
using semaphore
We will make use of 3 semaphores:

●Empty
● Counting semaphore, used to keep track of empty slots
● initial value is the number of slots in the buffer

●Full
● Counting semaphore, used to keep track of number of filled slots in buffer.
● initialized to the value 0

●Mutex
● Binary semaphore which is used to acquire and release lock
● initialized to the value 1
The structure of the producer process:

do {
...
/* produce an item */
...
wait(empty); //wait until empty >0 and then decrement empty

wait(mutex); //acquire lock


...
/* add produced item to the buffer */
...
signal(mutex); //release lock
signal(full); //increment full
} while (true);
The structure of the consumer process:

do {
wait(full); //wait until full>0 and then decrmnt full
wait(mutex); //acquire lock
...
/* remove an item from buffer */
...
signal(mutex); //release lock
signal(empty); //increment empty
...
/* consume the item */
...
} while (true);
Producer Consumer
do {
... do {
/* produce an item */ wait(full);
...
wait(empty); wait(mutex);
wait(mutex); ... /* remove an item
/* add produced item to from buffer */
the buffer */ ...
... signal(mutex);
signal(mutex); signal(empty);
signal(full); ...
} while (true); /*consume item*/
...
} while (true);
Dining-Philosophers Problem
● Five philosophers are sitting around a
circular table and their job is to think and
eat alternatively.
● Bowl of noodles/rice is placed in front
along with five chopsticks for each of the
philosophers.
● To eat, a philosopher needs both their
right and a left chopstick. A philosopher
can only eat if both immediate left and
right chopsticks of the philosopher is
available.
● In case if both immediate left and right
chopsticks of the philosopher are not
available then the philosopher puts down
their (either left or right) chopstick and
● Philosophers – processes starts thinking again.
● Chop stick – resources shared between processes
● Bowl – the data the processes want to access
Solution of Dining Philosopher Problem
● Use a semaphore to represent a chopstick

● Since, 5 chopsticks – 5 semaphore is required


● Semaphore chopstick[5] initialized to 1 (free)
● Binary semaphore

● A chopstick can be picked up by executing a wait operation on


the semaphore and released by executing a signal operation on
the semaphore.
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);

Any Problem??
wait(S)
signal(S)
{
{
while (S <= 0);
S++;
S--;
}
}
Problem:
● Although this solution guarantees that no two neighbors are eating simultaneously,
it could still create a deadlock.

● Suppose that all five philosophers become hungry simultaneously and each grabs
their left chopstick. All the elements of chopstick will now be equal to 0.

● When each philosopher tries to grab his right chopstick, he will be delayed forever.
Possible remedies:
1. Allow at most four philosophers to be sitting simultaneously at the table. (count of
chopstick remains 5 itself)

2. Allow a philosopher to pick up his chopsticks only if both chopsticks are available.
(to do this, she must pick them up in a critical section)

3. Use an asymmetric solution – that is,


1. an odd numbered philosopher picks up his left chopstick first and then the
right chopstick
2. Whereas, even numbered philosopher picks up his right chopstick first and
then the left one.
Readers-Writers Problem

● A data set/database is shared among a number of concurrent processes

● Some of these processes may want to

● only read the data set; they do not perform any updates - Readers

● both read and write - Writers

● If two readers access the data simultaneously, no adverse affect.

● But, if a writer and some other process(reader or writer)access the data


simultaneously, it will result in some problem.

● To overcome this – writers have exclusive access to shared data item.


Solution to Readers-Writers Problem
● Using semaphore
● An integer variable and 2 semaphores
● read_count
● Integer variable initialized to 0
● Keep track of how many processes are currently reading the object
● Semaphore mutex
● Initialized to 1
● Used to ensure mutual exclusion when readcount is updated
● That is, when any reader enters or exit from the critical section.
● Used only by reader process
● Semaphore rw_mutex
● initialized to 1
● Common to both reader and writer process
Structure of a writer process

do {
wait(rw_mutex);
...
/* writing is performed */
...
signal(rw_mutex);
} while (true);
R1
Database
Readcount =
R2
Mutex=1 rw_mutex=0
R3
do {
w4
wait(mutex);
read_count++;// number of readers has now increased by 1
if (read_count == 1)
wait(rw_mutex);//ensure no writer can enter if there
is even one reader
signal(mutex); //other readers can enter while this
current reader is inside the critical section.

/* reading is performed */
wait(mutex);
read count--; // a reader wants to leave
if (read_count == 0) // no reader is left in the critical section
signal(rw_mutex); //writers can enter
signal(mutex); //reader leaves
} while (true);
Assignment

● Synchronization hardware
● Mutex Locks
Monitors

● A high-level abstraction that provides a convenient and effective mechanism for


process synchronization

● Syntax of monitor

monitor monitor-name
{
//shared variable
declarations
procedure P1 (…)
{ ….. }
…………..
procedure Pn (…)
{……}
Initialization code (…) { … }
❑A procedure defined within a monitor can access only those

variables declared locally within the monitor and its formal

parameters.

❑Similarly, the local variables of a monitor can be accessed by

only the local procedure.

❑The monitor constructs ensures that only one process may be

active within the monitor at a time


● Condition variables: condition x, y;

● Two operations are allowed on a condition variable:

● x.wait() – a process that invokes the operation is suspended until


x.signal()
● x.signal() – resumes one of processes (if any) that invoked
x.wait()
4 If no x.wait() on the variable, then it has no effect on the variable
Schematic view of a Monitor
Dining Philosophers Solution using Monitors

● This solution imposes the restriction that a philosopher may pick up his
chopsticks only if both of them are available.

● To code this solution, we need to distinguish among three states in which we may
find a philosopher. For this purpose, we introduce the following data structure
enum {thinking, hungry, eating} state [5];

● Philosopher I can set the variable state[i]=eating, only if his two neighbours are
not eating:
state[(i + 4) % 5] != EATING) && (state[(i + 1) % 5] != EATING)

● We also need to declare condition self[5]; where philosopher i can delay himself
when he is hungry but is unable to obtain the chopsticks he needs.
monitor DiningPhilosophers
{
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; //present philosopher has to
wait
}

void putdown (int i)


{
state[i] = THINKING;
// test left and right neighbors want to eat
test((i + 4) % 5);
test((i + 1) % 5);
}
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()
{
for (int i = 0; i < 5; i++)
state[i] = THINKING;
}
}
● Each philosopher i invokes the operations pickup() and putdown() in
the following sequence:

DiningPhilosophers.pickup(i);

EAT

DiningPhilosophers.putdown(i);

● No deadlock, but starvation is possible

You might also like