Unit 2 (New Syll)

You might also like

Download as doc, pdf, or txt
Download as doc, pdf, or txt
You are on page 1of 31

UNIT II

PROCESS SYNCHRONIZATION

Race Condition:
A situation where several processes access and manipulate the same data
concurrently and the outcome of the execution depend on the particular order in which
the access takes place is called a Race Condition. To guard against the race condition, we
need to ensure that only one process at a time can be manipulating a particular variable.
For this we need some form of synchronization of the process.

Example for Race condition:


Consider two processes A and B is to be run in a system. Let A be executed first
and writes a value eg:- 35 in the memory address 5. When the time slice ends the CPU is
switched to process B.

Now when process B is executing it updates the value to 55 in the same address 5
that was already used by process A.
When process A comes back to execute its next slice, it tries to read the value at address
5. It reads the value as 55 where its original value was 35. This gives a unexpected
result and causes Race condition.

Process A 2

3
Process B
4

:
:
:

Race Condition

The Critical-Section Problem:


Consider a system consisting of n processes. Each process has a segment of code
called a critical section, in which the process may be changing common variables,
updating a table, writing a file and so on. The important feature of this system is that
when one process is executing in its critical section, no other process is to be allowed to
execute in its critical section. The execution of critical sections by the processes is
mutually exclusive in time i.e., only one process is allowed to execute in a critical section
at any instant of time. Each process must request permission to enter its critical section.
The section of code implementing this request is the entry section. The critical section
may be followed by an exit section. The remaining code is the remainder section.

do
{
Entry Section
Critical Section
Exit Section
Reminder Section
}while(1);

General structure of a typical process Pi

A solution to the critical section problem must satisfy the following three requirements.

1. Mutual exclusion: If process Pi is executing in its critical section, then no other


process can be executing in their critical sections.

2. Progress: If there are no processes executing in the critical section, and some
processes wish to enter into critical section, then only those processes that are not
executing in their remainder section can participate in the decision on which will enter its
critical section next, and the selection cannot be postponed indefinitely.

3.Bounded waiting:
There exist bound on the number of times that other process are
allowed to enter their critical sections after a process has made a request to enter to its
critical section and before that request is granted.

There are some solutions for the critical section problem.

1.Two process solution:


This solution is applied to only two processes at a time. The process are
P0 and P1.For our convenience if we take Pi as one process then Pj is used to denote the
other process.(ie);j=1-i.

Algorithm1:
Let the process share the common integer variable turn initialized to 0 or 1.If
the turn ==i then process Pi is allowed execute its critical section.
Structure of Pi
do{ While process Pi is not allowed to
enter the critical section, do no
While(turn!=i ); operation else, enter the critical
section.

Critical section
Give turn to remaining process.
turn=j;

Reminder section Execute the reminder section of


}while(1); process, Pi.

This solution ensures that only one process at a time can be in its critical
section. However, it does not satisfy the progress requirement, since it requires strict
alteration of processes in the execution of the critical section. For example, if turn==0
and P1 is ready to enter its critical section, P1 cannot do so, even though P0 may be in its
remainder section.

Algorithm 2:

Problem in algorithm 1 is that no sufficient information is given on the state of each


process, it remembers only which process is allowed to enter its critical section. To
rectify this we replace the variable turn with an array:

boolean flag[2];

The elements of the array are initialized to false. If flag[i] is true then it indicates that Pi
is ready to enter the critical section.
Structure of process Pi is
do{
flag[i] = true;
while(flag[j];

Critical section

flag[j] = false;

Reminder section
}while(1);

Here, process Pi first sets flag[j] to be true and it is ready to enter its critical section. Then
Pi checks to verify that Pj is also not ready to enter its critical section. If Pj was ready
then Pi would wait until Pj has indicated that it no longer needed to be in the critical
section i.e., until flag[j] was false. At this point Pi would enter the critical section. On
exiting the critical section, Pi would set flag[I] to be false allowing Pj that is waiting to
enter its critical section.

In this solution, the mutual-exclusion requirement is satisfied. Unfortunately, the progress


requirement is not met.

Eg; Consider the following execution sequence,


To : P0 sets flag[0] = true.
T1 : P1 sets flag[1] = true.
Now P0 and P1 are looping forever in their responsible while statements.

Algorithm 3.

By combining the key ideas of both algorithms 1 and 2 we obtain a correct solution to the
critical section problem, where all the requirements are met.

The processes share two variables:


boolean flag[2];
int turn;
Initially flag[0] = flag[1] = false and the value of turn is either 0 or 1.

Structure of process Pi is
do{
flag[i] = true;
turn = j;
while(flag[j] && turn = j);

critical section

flag[j] = false;

reminder section
}while(1);

Enter the critical section, process Pi first sets flag[i] to be true and sets turn to the value j,
thus assuring that if the other process wishes to enter the critical section it can do so. If
both processes try to enter at the same time, turn will be set to both i and j at roughly the
same time. One of these cases will last; the other will occur, but will be overwritten
immediately. The eventual value of turn will decide which of the two processes is
allowed to enter its critical section first.

To prove this is correct, we need to show that


1. Mutual exclusion is preserved.
2. The progress requirement is satisfied.
3. The bound-waiting requirement is met.
To prove property 1:
 Pi enters its critical section only if either flag[j] = false or turn = i.
 If both processes can be executing in the critical section at the same time,
then flag[0] = flag[1] = true.

The above two observations imply that P0 and P1could not have successfully executed
their while statements at the same time, since the value of turn can be either 0 or 1 but not
both. Hence one of the processes, Pj, must have successfully executed the while
statement whereas Pi had to execute the statement turn ==j.

Since at that time flag[j]==true and turn==j, the condition will persist as long as Pj is in
its critical section and thus mutual exclusion is preserved.

To prove requirements 2 & 3:


 Process Pi can be prevented from the critical section only if it is stuck in the while
loop with the condition flag[j]==true & turn==j.
 If Pj is not ready to enter the critical section, then flag[j]==false & Pi can enter its
critical section.
 If Pj has set flag[j]to true and is also executing in its while statement then enter
turn==i or turn==j.
 If turn==i, then Pi will enter the critical section.
 If turn == j, then Pj will enter the critical section.

Once Pj exits its critical section, it will reset flag[j] to false, allowing Pi to enter critical
section. If Pj to resets flag[j]to true, it must also set turn to i. Since Pi does not change the
value of the variable turn while executing the while statement, Pi will enter the critical
section, i.e, progress satisfied, after at most one entry by Pj i.e, bounded waiting satisfied.

2.Multipe Process Solution:

Algorithm 3 solves the critical section problem from two processes. Now we will see
another algorithm to solve the critical section problem for n processes.

The algorithm is known as ‘Bakery Algorithm’ and is based on a scheduling algorithm


commonly used in bakeries, ice-cream stores, deli counters, motor-vehicle registries and
other locations where orders must be made out of chaos. This algorithm was developed
for distributed environment based and we shall concentrate on applying it for a
centralized environment

On entering the store, each customer receives a number. The customer with the lowest
number is served first. The customer with the lowest number is served next. The bakery
algorithm cannot guarantee that two processes do not receive the same number. In the
case of a tie the process with the lowest name is served first.
The common data structures are
boolean choosing[n];
int number[n];
These two data structures are initialized to false and 0 respectively, initially. For
convenience we define these notations:

 (a,b) < (c,d) if a<c or if a==c and b<d.


 max(a0,…, an-1)is a number, k, such that k>= ai for I = 0,…, n-1.

The structure of Pi is

do {
choosing[i] = true;
number[i] = max(number[0], number[1], …, number[n-1]) + 1;
choosing[i] = false;
for (j = 0; j<n; j++)
{
while (choosing[j]);
while ((number[j] 1= 0) && (number[j,j]<number[i,i]));

critical section
number[i] = 0;

remainder section

}while(1);

To prove that bakery algorithm is correct, we need first to show that, if Pi is in its
critical section and Pk(k != i) has already chosen its number k != 0, then
(number[i], i) < (number[k], k).
It is simple to show that mutual exclusion is observed. Consider Pi is in its critical section
and Pk is trying to enter its critical section. When process Pk executes the second while
statement for j = i, it finds that
number[i] != 0
(number[i], i) < (number[k], k).
Thus, it continues looping in the while statement until Pi leaves the Pi critical section.
We can prove that the remaining requirements hold by allowing the process to enter the
critical section on first come first served basis.

SYNCHRONIZATION HARDWARE

Some simple hardware instructions that are available on many systems that can be
used effectively in solving the critical section problem.
In a uniprocessor environment we can solve the critical section problem by
forbidding the interrupts to occur while a shared variable is being modified. This will
make sure that the current sequence of instructions would be allowed to execute in order
without preemption.

This is not possible in a multiprocessor environment for disabling interrupt in a


multiprocessor can be time consuming as the message is passed to all processors. This
message passing delays entry into each critical section and system efficiency decreases.

To rectify this problem many machine provide some special hardware instructions
that allow us to either test and modify the content of a word or to swap the contents of
two words, atomically –i.e. as one uninterruptible unit. These instructions can be used to
solve the control section problem in a simple manner.

There are two common types of instructions


1). TestAndSet instructions.
2). Swap instructions.
Let us see the main concept behind these types of instructions.

1. TestAndSet instructions:

This instructions can be defined as

boolean TestAndSet(boolean &target)


{
boolean n=target;
target=true;
return n;
}

The important characteristic is that this instruction is executed aromatically. Thus


if two TestAndSet instructions are executed simultaneousl6y, each on a different CPU,
they will be executed sequentially in some arbitrary order.

If the machine supports these instructions, thus we can implement mutual


exclusion by declaring a Boolean variable lock, initialized to false.

The structure of process pi is

do
{
while (TestAndSet (lock));
Critical section
lock =false;
R.S
} while (1);
2. Swap instructions:

This instructions is defined as


void Swap (boolean &a, boolean &b)
{
boolean temp=a;
a =b;
b =temp;
}
The Swap instruction operates on the contents of two words. It is also executed
atomically. If the machine supports Swap instruction, then mutual exclusion can be
provided as follows.
 A global boolean variable lock is declared and is initialized to false.
 In addition, each process also has a boolean variable key.
The structure of process pi is
do {
key=true;
while (key==true)
Swap (lock, key);
C.S.

lock=false;

R.S.
} while (1);

These algorithms do not satisfy the bounded waiting requirements. Hence another
algorithm that uses the TestAndSet instructions can be seen. This algorithm satisfies all
the C.S. requirements .The common data structures are:
boolean waiting [n];
boolean lock;
Then data structures are initialized to false.

The structure of process Pi is


do {
waiting [i]=true;
key=true;
while (waiting [i] && key)
key=TestAndSet[lock];
waiting [i]=false;

C.S.

j=(i+1)%n;
while ((j! = i) &&! waiting [j])
j=(j+1)%n;
if (j==I) lock = false;
else waiting [j]=false;

R.S.

} While (1);

To prove that the mutual exclusion requirement is must we noe that process Pi can
enter its C.S. only if either waiting [i]=false or key=false. The value of day can become
false only if the TestAndSet if executed. The first process to execute the test and set will
find key=false; all other must wait. The variable waiting[i] can become false only if
another process leaves its C.S. only one waiting[i] is set to false, maintaining the mutual
exclusion requirement..
To prove the process requirement is met

 The arguments presented for mutual exclusion also apply here.


 A process existing the C.S. either set lock to false or set waiting j] to false.
Both allow a process that is waiting to enter its C.S to proceed.
To prove that bounded waiting requirement is met.
 When a process leaves its C.S. it scans the array waiting in the cyclic
ordering(i+1, i+2,…….,n-1,0,………i-1).
 It designates the first process in this ordering i.e; in the entry section
(waiting[j]== true) as the next one to enter the C.S.
 Any process waiting to enter its C.S. will thus do so within n-1 turns.

SEMAPHORES

The solutions to the critical section problems are not easy to generalize to more complex
problems. To overcome this difficulty, a synchronization tool used called a Semaphore. A semaphore S
us an integer variable that, apart from initialization, is accessed only through two standard atomic
operations: wait and signal. These operations were originally termed
P for wait ie ; to test (Dutch proberen)
V for signal ie ; to increment (from verhogen)

The classical definition of wait in pseudocode is

wait(s)
{
while(S<=0)
;// no - op
S--;
}

The classical definition of signal in pseudocode is

signal(S)
{
S++;
}

Modifications to the integer value of the semaphore in the wait and signal operations must be
executed indivisibly. ie; when one process modifies the semaphore value, no other process can
simultaneously modify that same semaphore value. In addition, in the case of the wait(S), the testing of the
integer value of S(S<=0), and its possible modification (S--), must also be executed without interruption.

USAGE

Semaphores can be used to deal with the n-process critical section problem. The n processes
share a semaphore, mutex (standing for mutual exclusion), initialized to 1. Each process Pi is organized as
shown below.

do
{
wait (mutex);
critical section
signal(mutex);
remainder section
}while(1);

Semaphores can also be used to solve various synchronization problems.


Eg:- Consider two concurrently running processes:
P1 with a statement S1 and
P2 with a statement S2.

Suppose we require that S2 be executed only after S1 has completed. This can be readily
implemented by letting P1 and P2 share a common semaphore synch, initialized to 0, and by inserting the
statements

S1;
signal(synch);
in process P1, and the statements
wait(synch);
S2;
in process P2.

Since synch is initialized to 0, P2 will execute S2 only after P1 has invoked signal(synch)
which is after S1.
Implementation
The main disadvantage of the mutual - exclusion solutions seen in the two –process solutions
and of the semaphore definition given above, is that they all require busy waiting. While a process is in its
critical sections, any other process that tries to enter its critical section must loop continuously in the entry
code. This continual looping is clearly a problem in real multiprogramming systems, where a single CPU is
shared among many process. Busy waiting wastes CPU cycles that some other process might be able to use
productivity. This type of semaphore is also called a spin lock (because the process spins while waiting for
the lock).

Spinlocks are useful in multiprocessor systems. The advantage of spin lock is that no context
switch is required when a process must wait on a lock, and a context switch may take considerable time.
Thus, when locks are expected to be held for short times, spin locks are useful.

To overcome the need of busy waiting, the definition of wait and signal semaphore operations
can be modified. When a process executes the wait operation can be modified. When a process executes the
wait operation and finds that the semaphore value is not positive, it must wait. However, rather than busy
waiting the process can block itself. The block operation places a process into a waiting queue associated
with the semaphore, and the state of process is switched to the waiting state. Then, control is transferred to
the CPU scheduler, which selects another process to execute.

A process that is blocked, waiting on a semaphore S, should be started when some other process
executes a signal operation. The process is restarted by a wakeup operation, which changes the process from
the waiting state to the ready state. The process is then placed in the ready queue.
To implement semaphores under this definition, we define a semaphores as a "C" struct.

typedef struct
{
int value;
struct process * L;
}semaphore;

Each semaphores has an integer value and a list of processes. when a process must wait on a
semaphores ,it is added to the list of process. A single operation removes one process from the list of
waiting process and awakens that process.

The wait semaphore operation can now be defined as


void wait(semaphore S)
{
S.value--;
if(S.value<0)
{
add this process to S.L;
block();
}
}

The signal semaphore operation can now be defined as


void signal(semaphore S)
{
S.value++;
if(S.value<=0)
{
remove a process P from S.L;
wakeup(p);
}
}

The block operation suspends the process that invokes it. the wakeup(P) operation resumes the
execution of a blocked process P. These two operation are provided by the operating system as basic
system calls.

The critical aspect of semaphores is that they are executed automatically. It should be guaranteed that
no two process can execute wait and signal operation on the same semaphore at the same time. This
situation is a critical section problem.

In a uniprocessor environment, interrupts can be inhibited during the time the wait and signal
operations are executing. This scheme works in a uniprocessor environment because, once interrupts are
inhibited, instructions from different processes cannot be interleaved.

Only the currently running process executes, until interrupts are re-enabled and the scheduler
can regain control. In a multiprocessor environment, inhibiting interrupts does not work. Instructions from
different processes may be interleaved in some arbitrary way. If the H/W does not provide any special
instructions, then any of the early dealt software solutions for the critical section consists of the wait and
signal procedures.

Deadlock And Starvation


The implementation of a semaphore with a waiting queue may result in a situation
where two or more processes are waiting indefinitely for an event that can be caused only
by one of the waiting processes. The event is the execution of a single operation. When
such a state is reached these processes are said to be in deadlock.

To illustrate this, consider a system consisting of two processes P0 and P1 each


accessing to semaphores S and Q1 set to value 1.

P0 P1
Wait (S); wait (Q);
Wait (Q); wait(S);
: :
: :
signal(S) signal(Q)
signal(Q) signal(S)

Suppose that P0 execute wait(S), and then P1 execute wait(Q). when P0 executes
wait(Q) it must wait until P1 executes signal(Q). Similarly, when P1 executes wait(S) it
must wait until P0 executes signal(S). Since these signal operations cannot be executed, P0
and P1 are deadlocked.

We say that a set of processes is in a deadlock state when every process in the set
is waiting for an event that can be caused only by another process in the set.

Another problem related to deadlock is indefinite blocking or starvation, a


situation where processes wait indefinitely within a semaphore. Indefinite blocking may
occur if we add and remove processes from the list associated with a semaphore in LIFO
order.

Binary Semphore

The semaphore construct we saw in the previous sections commonly known as


counting semaphore, since its integer value can range over an unrestricted domain.
A binary semaphore is a semaphore with an integer that can range only between 0
and 1. A binary semaphore can be simpler to implement than a counting semaphore
depending on the hardware architecture.
We shall see how a counting semaphore can be implemented using binary
semaphore.
Let S be a counting semaphore. To implement it in term of binary semaphores we
need the following data structures:
Binay_semaphore S1,S2;
int c;
Initially S1=1, S2=0, and the value of integer c set to the initial value of the
counting semaphore S.
The waiting operation on the counting semaphore S can be implemented as :
wait(S1);
c--;
if(c<0){
signal(S1);
wait(S2);
}
signal(S1);
The signal operation on the counting semaphore S can be implemented as:
wait(S1);
c++;
if(c<=0)
Signal(S2);
else
Signal(S1);

CLASSICAL PROBLEMS OF SYNCHRONIZATION

We can see different types of synchronization problems.


1.The Bounded Buffer Problem

Its commonly used to illustrate the power of synchronization primitives.


Assume that the pool consists n buffers, each capable of holding one item.
The mutex semaphore provides mutual exclusion for access to the buffer pool and is
initialized to the value 1. The empty and full semaphores count the number of empty and
full buffer respectively. The semaphore empty is initialized to the value n: the semaphore
full is initialized to the value 0.
The code of the producers producing full buffer for the consumer process is

do
{
...
produce an item in next p
...
wait(empty);
wait(mutex);
...
add next p to buffer
...
signal(mutex);
signal(full);
}while(1);
the code of the consumer producing empty buffer for the producers process is
do
{
wait(full);
wait(mutex);
...
remove an item from buffer to next c
...
signal(mutex);
signal(empty);
...
consume the item in next c
...
}while(1);

2.The Reader Writer Problem

The data file or record object is to be shared among several concurrent processes.
Some of these processes may want only to read the content of the shared object, whereas
other may want to update (read & write) the shared object. Those processes that are
interested only in reading are called as readers others as writers.
If two readers access the shared data object simultaneously no problem will occur
but if a writer along with some other process simultaneously access the shared object then
chaos may occur.
To overcome this synchronization problem, known as readers-writers problem, it
is required that the writers have exclusive access to the shared object.
The readers-writers problem has several variations.

First readers-writers problem- requires that no reader will be kept waiting unless a
writer has already obtained permission to use the shared object. i.e., no reader should wait
for other readers to finish simply because a writer is waiting

Second reader-writer problem- requires that, once a writer is ready, that writer
perform its write as soon as possible i.e., of a writer is waiting to access the object, no
new readers may start reading.

A solution to the above two problems is starvation. In the first case the writers
may starve and in the second case reader may starve.
Solution to reader-writer problem

The reader processes share the following data structures:


Semaphore mutex,wrt;
int readcount;
The semaphore mutex and wrt are initialized to 1.
readcount is initialized to 0.
mutex: a semaphore that ensures mutual exclusion while readcount is updated.
wrt : a semaphore that ensures mutual exclusion for writer.
readcount : an integer variable that counts number of processes that are currently reading
the object.

The semaphore wrt is common to both reader and writer processes. The mutex
semaphore is used to ensure mutual exclusion when the variable readcount is updated.

The readcount variable keeps track of how many processes are currently reading
the object.
The semaphore wrt functions as a mutual exclusion semaphore for the writers it is
also used by the first and the last reader that enters or exists the C.S. It is not used by
readers who enter or exists while other readers are in their C.S.

Code for a writer process is


wait(wrt);
...
writing is performed

signal(wrt);
code for a reader process is
wait(mutex);
readcount++;
if(readcount = = 1)
wait(wrt);
signal(mutex);
....
reading is performed
...
wait(mutex);
readcount--;
if(readcount = = 0)
signal(wrt);
signal(mutex);
Note: -
1. If a writer is in the C.S and n readers are waiting, then one reader is
queued on wrt, and n-1 readers are queued on mutex.
2. When a writer executes signal(wrt), the execution of either the waiting
readers or a single writer may be resumed. The selection made by the scheduler.

3.The Dining-Philosophers Problem

Consider five philosophers who spend their lives thinking and eating. The
philosophers share a common circular table surrounded by five chairs, each belonging to
one philosopher. In the center of the table is a bowl of rice, and the table is laid with five
single chopsticks.

Rice

When a philosopher thinks, she does not interact with her colleagues. From time
to time, a philosopher gets hungry and tries to pickup the two chopsticks that are closest
to her (the chopsticks that are between her and her left and right neighbors). A
philosopher may pick up only one chopstick at a time. Obviously, she cannot pick up a
chopstick that is already in the hands of a neighbor. When a hungry philosopher has both
her chopsticks at the same time, she eats without releasing her chopsticks. When she
finishes eating, she puts down both of her chopsticks and starts thinking again.
The dining-philosophers problem is considered a classic synchronization problem,
as it is an example of a large class of concurrency-control problems. It is a simple
representation of the need to allocate serial resources among several processes in a
deadlock and starvation free manner.

One Simple Solution:


Represent each chopstick by a semaphore. A philosopher tries to grab the
chopsticks by executing a wait operation on that semaphore; she releases her chopsticks
by executing the signal operation on the appropriate semaphores thus, the shared data are
Semaphore chopsticks[5];
where all the elements of chopsticks are initialized to 1.
Structure of philosopher i
do{
wait(chopstick[i]);
wait(chopsticks[(i+1)%5]);
...
eat
...
signal(chopsticks[i]);
signal(chopsticks[(i+1)%5]);
...
think;
...
}while(1);

Although this solution guarantees that no two neighbors are eating


simultaneously, it nevertheless must be rejected because it has the possibility of creating
a deadlock. Suppose that all five philosophers become hungry simultaneously and each
grabs her left chopstick, then all the elements of chopsticks will now be equal to 0. When
each philosopher tries to grab her right chopsticks, she will be delayed forever.

Several possible remedies to the deadlock problem


 Allow at most four philosophers to be sitting simultaneously at the table.
 Allow a philosopher to pick up her chopsticks only if both are available (to do this
she must pick them up in a C.S).
 Use an asymmetric solution, i.e., an odd philosopher picks up first her left
chopsticks and then her right chopstick, whereas an even philosopher picks up her
right chopstick and then left chopstick.

Finally, any satisfactory solution to the dining-philosopher problem must guard


against the possibility that one of the philosophers will starve to death. A
deadlock free solution does not necessarily eliminate the possibility of starvation.

DEADLOCKS

In a multiprogramming environment, several processes may compete for a finite


number of resources. A process requests resources; if the resources are not available at
that time, the process enters a wait state. Waiting processes may never again change state,
because the resources they have requested are by other waiting processes. This situation
is called a deadlock.
A process must request a resource before using it, and must release the resource
after using it. A process may request as many resources as it requires to carry out its task.
The number of resources, requested may not exceed the total number of resources
available in the system. A process cannot request three printers if the system has only
two.
Under the normal mode of operation, a process may utilize a resource in only the
following sequence:

1. Request: If the request cannot be granted immediately, (if the resource is being
used by another process), then the requesting process must wait until it can
acquire the resource.
2. Use: The process can operate on the resource, (for example if the resource is a
printer, the process can print on the printer).
3. Release: The process releases the resources.
A set of processes is in a deadlock state when every process in the set is waiting for
an event that can be caused only be another process in the set.
DEADLOCK CHARACTERIZATION

I. Necessary Conditions:

A deadlock situation can arise if the following four conditions hold simultaneously in a
system:
1. Mutual exclusion: At least one resource must be held, that is only one process at
a time can use the resource. If another process requests that resource, the
requesting process must be delayed until the resource has been released.
2. Hold and wait: A process must be holding at least one resource and waiting to
acquire additional resources that are currently being held by other processes.
3. No preemption: Resources cannot be preempted, that is, a resource can be
released only after the process ahs completed its task.
4. Circular wait: A set {P0, P1,P2,……,Pn} of waiting processes must exist such
that P0 is waiting for a resource that is held by P1,
P1 is waiting for a resource that is held by P2,
P2 is waiting for a resource that is held by p3,
Pn-1 is waiting for a resource that is held by Pn,
Pn is waiting for a resource that is held by P0.

II. Resource Allocation Graph:

Deadlocks can be described in terms of directed graph called a system resource


allocation graph. This graph consists of set of vertices V and set of edges E. The set of
vertices V is partitioned into two different types of nodes
 P= {P1, P2,…,Pn}, set consisting of active processes in the system
 R={R1, R2,….,R3}, set consisting of all resource types in the system.
A directed edge from process Pi to resource type Rj is denoted by Pi Rj it signifies
that process Pi requested an instance of resource type Rj and is currently waiting for that
resource. This edge is called a request edge.
A directed edge from resource type Rj to resource type Pi is denoted by Rj Pi it
signifies that an instance of resource type Rj has been allocated to process Pi. This edge
is called a assignment edge.
Pictorially each process Pi is represented by a circle, and each resource type Rj as
a square. Since resource type Rj may have more than one instance, we represent each
such instance as a dot within the square.
The resource allocation graph shown in figure depicts the following situation:
Resource allocation graph

R1 R3

P
P P 3
1 2

R2

R4
 The sets P, R and E:
o P={P1,P2,P3}
o R={R1,R2,R3}
o E={P1 R1, P2 R3, R1 P2, R2 P2, R2 P1, R3 P3}
 Resource instances:
o One instance of resource type R1
o Two instances of resource type R2
o One instance of resource type R3
o Three instances of resource type R4

 Process states:
o Process P1 is holding an instance of resource type R2, and is waiting for
an instance of resource type R1.
o Process P2 is holding an instance of R1 and R2, and is waiting for an
instance of resource type R3.
o Process P3 is holding an instance of R3.
 Given the definition of a resource allocation graph, if the graph contain no cycle,
then no process in the system is deadlocked.
 If the graph contains a cycle, then a deadlock may exist.
 If each resource type is exactly having one instance, then a cycle implies that a
deadlock has occurred.
 If each resource type has several instances, then a cycle does not have a deadock
occurred.
Fig. Resource allocation graph with a deadlock

R1 R3

P
P P 3
1 2

R2

R4

Two minimal cycles exist in the system:


P1 R1 P2 R3 P3 R2 P1

P2 R3 P3 R2 P2
Processes P1, P2 and P3 are deadlocked. Process P2 is waiting for the resource R3,
which is held by process P3. Process P3, on the other hand, is waiting for either
process P1 or process P2 to release resource R2. In addition, process P1 is now
waiting for process P2 to release resource R1.

Methods for Handling Deadlocks

Deadlock problem can be dealt in one of the three ways:


 We can use a protocol to prevent or avoid deadlocks, ensuring that the system will
never enter a deadlock state.
 We can allow the system to enter a deadlock state, detect it, and recover.
 We can ignore the problem altogether, and pretend that deadlocks never occur in
the system.

To ensure that deadlocks never occur, the system can use either a deadlock
prevention or a deadlock avoidance scheme.
Deadlock prevention: this is a set of methods for ensuring that at least one of the
necessary condition cannot hold.
Deadlock avoidance: requires the OS be given in advance additional information
concerning which resources a process will request and use during its lifetime. With this
additional knowledge, we can decide for each request can be satisfied or must be delayed,
the system must consider the resources currently available, the resources currently
allocated to each process and the future requests and releases of each process.

DEADLOCK PREVENTION

Deadlocks can be prevented by ensuring that each of the four conditions cannot hold. The
conditions are:
 Mutual Exclusion
 Hold and Wait
 No Preemption
 Circular Wait

1. Mutual Exclusion:

The mutual exclusion condition must hold for non sharable resources. For example, a
printer cannot be simultaneously shared by several processes. Sharable resources on the
other hand, do not require mutually exclusive access, and thus cannot be involved in a
deadlock. Read only files are a good example for sharable resources. If several processes
attempt to open a read-only file at the same time, they can be granted simultaneous access
to the file. A process never needs to wait for a sharable resource.

2. Hold and Wait:

To ensure that the hold and wait condition never occurs in the system, we must guarantee
that, whenever a process requests a resource, it does not hold any other resources.
One protocol that can be used requires each process to request and be allocated all its
resources before it begins execution.
Another protocol allows a process to request resources only when the process has none.
A process may request some resources and use them. Before it can request any additional
resources, it must release all the resources that it is currently allocated.
Examples to illustrate the two protocols:
Consider a process that copies data from a tape drive to a disk file, sorts the disk file and
then prints the results to a printer.

Protocol one - If all resources must be requested at the beginning of the process, then the
process must initially request the tape drive, disk file and printer. It will hold the printer
for its entire execution, even though it needs the printer only at the end.

Protocol two – the second method allows the process to request initially only the tape
drive and disk file. It copies from the tape drive to the disk, then releases both the tape
drive and the disk file. The process must then again request the disk file and the printer.
After copying the disk file to the printer, it releases these two resources and terminates.
Disadvantages of two protocols:
1. Resource utilization may be low, since many of the resources may be allocated
but unused for a long period.
2. Starvation is possible. A process that needs several popular resources may have to
wait indefinitely, because at least one of the resources that it needs is always
allocated to some other process.

3. No Preemption

The third necessary condition is that there be no preemption of resources that have
already been allocated. To ensure this condition does not happen, the following protocol
is used.
If the process is holding some resources and requests another resource that cannot
be immediately allocated to it, then all resources for which the process is waiting. The
process will be restarted only when it can regain its old resources, as well as the new ones
that it is requesting.
If process requests some resources, we first check whether they are available. If
they are, we allocate them. If they are not available, we check whether they are allocated
to some other process that is waiting for additional resources. If so preempt the desired
resources from the waiting process and allocate them to the requesting process. If the
resources are not either available or held by a waiting process, the requesting process
must wait. While it is waiting, some of its resources may be preempted, but only if
another process requests them. A process can be restarted only when it is allocated the
new resources it is requesting and recovers any resources that were preempted while it is
waiting.

4. Circular Wait

The fourth condition for deadlocks is circular-wait. One way to ensure this conditions is
to impose a total ordering of all resource types, and to require that each process requests
resources in the increasing order of enumeration.
Let R = {R1, R2,…. Rm} be set of resource types. We assign to each resource
type an unique number, which compares the resources and to determine whether one
precedes another in our ordering.

Example: F(tape drive) = 1,


F(disk drive) = 5
F(printer) = 12.

Now consider the protocol to prevent deadlocks. Each process is requested in an


increasing order. If a process request resource type Ri, after that the process can request
instances of resource type Rj if and only if F(Rj) > F(Ri). Example, using the function
defined above, a process that wants to use a tape drive and printer at the same time must
first request the tape drive and then request the printer.
Alternatively, we can require that, whenever a process requests an instance of
resource type Rj, it has released any resources Ri such that the F(Ri)>= F(Rj). If these
two protocol are used, then the circular wait cannot hold.

DEADLOCK RECOVERY

There are two approaches to solve the deadlock problem. They are

 Suspend/Resume a Process
 Kill a Process

1. Suspend/Resume a Process:

In this method a process is selected based on a variety of criteria example low priority
and it is suspended for a long time. The resources are reclaimed form that process and
then allocated to other processes that are waiting for them. When one of the waiting
process gets over the original suspended process is resumed.
This process strategy cannot be used in any on-line or real time systems, because
the response time of some processes then become unpredictable.
Suspend/Resume operations are not easy to manage example that a tape is read
half way through and then a process holding the tape drive is suspended. The operator
will have to dismount that tape, mount the new tape for the new process to which the tape
drive is now to be allocated. After this new process is over, when the old process is
resumed, the tape for the original process will have to be mounted again, and more
importantly, it will exactly positioned.

2. Kill a Process:

The operating system decides to kill a process and reclaim all its resources after ensuring
that such action will solve the deadlock. This solution is simple but involves loss of at
least one process.
Choosing a process to be killed, again, depends on the scheduling policy and the process
priority. It is safest to kill a lowest priority process which has just begin, so the loss is not
very heavy.

DEADLOCK AVOIDANCE

Deadlock avoidance is concerned with starting with an environment, where a deadlock is


possible, but by some algorithm in the operating system, it is ensured before allocating
any resource that after allocating it, deadlock can be avoided. If that cannot be avoided,
the operating system does not grant the request of the process for a resource.
Dijkstra was the first person to propose an algorithm in 1965 for deadlock
avoidance. This is known as “Banker algorithm” due to its similarity in solving a
problem of a banker waiting to disburse loans to various customers within limited
resources.
This algorithm in the OS is such that it can know in advance before a resource is
allocated to a process, whether it can lead to a deadlock “unsafe state” or it can manage
to avoid it “safe state”.
Banker’s algorithm maintains two matrices.
Matrix A – consists of the resources allocated to different processes at a given time.
Matrix B – maintains the resources still needed by different processes at the same time.

Process Tape Printers Plotters


drives
P0 2 0 0
P1 0 1 0
P2 1 2 1
P3 1 0 1
Process Tape Printers Plotters
drives
P0 1 0 0
P1 1 1 0
P2 2 1 1
P3 1 1 1

Matrix A Matrix B
Resources assigned Resources still required

Vectors

Total Resources (T) = 543


Held Resources (H) = 432
Free Resources (F) = 111

Matrix A shows that process P0 is holding 2 tape drives. At the same time
P1 is holding 1 printer and so on. The total held resources by various processes are : 4
tape drives, 3 printers and 2 plotters.

This says that at a given moment, total resources held by various processes are: 4
tape drivers, 3printers and 2 plotters. This should not be confused with the decimal
number 432. That is why it is called a vector. By the same logic, the figure shows that the
vector for the Total Resources (T) is 543. This means that in the whole system, there are
physically 5 tape drivers, 4printers and 3 plotters. These resources are made known to the
operating system at the time of system generation. By subtraction of (H) from (T) column
wise, we get a vector (F) of free resources which is 111. This means that the resources
available to the operating system for further allocation are: 1 tape drive, 1 printer and 1
plotter at that juncture.

Matrix B gives process wise additional resources that are expected to be required
in the course during the execution of these processes. For instance, process P2 will
require 2 tape drives, 1 printer and 1 plotter, in addition to the resources already held by
it. It means that process P2 requires in all 1+2=3 tape drivers, 2+1=3 printers and 1+1=2
plotters. If the vector of all the resources required by all the processes (vector addition of
Matrix A and Matrix B) is less then the vector T for each of the resources, there will be
no contention and therefore, no deadlock. However, if that is not so, a deadlock has to be
avoided.

Having maintained these two matrices, the algorithm for the deadlock avoidance
works as follows:

(i) Each process declares the total required resources tot the operating system
at the beginning. The operating system puts this figure in Matrix B
(resources required for completion) against each process. For a newly
created process, the row in Matrix A is fully zeros to begin with, because
no resources are yet assigned for that process. For instance, at the
beginning of process P2, the figures for the row P2 in Matrix A will be all
0’s; and those in Matrix B will be 3, 3 and 2 respectively.

(ii) When a process requests the operating system for a resources, the
operating system finds out whether the resource is free and whether it can
be allocated by using the vector F. If it can be allocated, the operating
system does so, and updates Matrix A by adding 1 to the appropriate slot.
It simultaneously subtracts 1 from the corresponding slot of Matrix B. For
instance, starting from the beginning, if the operating system allocates a
tape drive to P2, the row for P2 in Matrix will become 1, 0 and 0. The row
for P2 in Matrix B will correspondingly become 2, 3 and 2. At any time,
the total vector of these two, i.e. addition of the corresponding numbers in
the two rows, is always constant and is equivalent to the total resources
needed by P2, which in this case will be 3, 3 and 2.

(iii) However, before making the actual allocation, whenever, a process makes
a request to the operating system for any resource, the operating system
goes through the Banker’s algorithm to ensure that after the imaginary
allocation, there need not be a deadlock, i.e. after the allocation, the
system will still be in a ‘safe state’. The operating system actually
allocates the resource only after ensuring this. If it finds that there can be a
deadlock after the imaginary allocation at some point in time, it postpones
the decision to allocate that resource. It calls this state of the system that
would result after the possible allocation as ‘unsafe’. Remember: the
unsafe state is not actually a deadlock. It is a situation of a potential
deadlock.

The point is: How does the operating system conclude about the safe or unsafe? It uses an
interesting method. It looks at vector F and each row of Matrix B. It compares them on a
vector to vector basis i.e. within the vector, it compares each digit separately to conclude
whether all the resources that a process is going to need to complete are available at that
juncture or not. For instance, the figure shows F = 111. It means that at that juncture, the
system has 1 tape drive, 1 printer and 1 plotter free and allocable. (The first row in Matrix
B for P0 to 100.) This means that if the operating system decides to allocate all needed
resources to P0, P0 can go to completion, because 111 > 100 on a vector basis. Similarly
row for P1 in Matrix B is 110. Therefore, if the operating system decides to allocate
resources to P1 instead of to P0, P1 can complete. The row for P2 is 211. Therefore, P2
cannot complete unless there is one more tape drive available. This is because 211 is
greater than 111 on a vector basis.
The vector comparison should not be confused with the arithmetic comparison.
For instance, if were 411 and a row in Matrix B was 322, it might appear that 411 > 322
and therefore, the process can go to completion. But that is not true. As 4 > 3, the tape
drives would be allocable. But as 1 < 2, the printer as well as the plotter would both fall
short.
The operating system now does the following to ensure the safe state:

(a) After the process requests for a resources, the operating system allocates it on a
‘trial’ basis.
(b) After this trial allocation, it updates all the matrices and vectors, i.e. it arrives at
the new values of F and Matrix B, as if the allocation was actually done.
Obviously, this updation will have to be done by the operating system in a
separate work area in the memory.
(c) It then compares vector F with each row of Matrix B on a vector basis.
(d) If F is smaller than each of the rows in Matrix B on a vector basis, i.e. even if all
F was made available to any of the processes in Matrix B, none would be
guaranteed to complete, the operating system concludes that it is an ‘unsafe state’.
Again, it does not mean that a deadlock has resulted. However, it means that it
can takes place if the operating system actually goes ahead with the allocation.
(e) If F is greater than any row for a process in Matrix B, the operating system
proceeds as follows:

 It allocates all the needed resources for that process on a trial basis.
 It assumes that after this trial allocation, that process will eventually get
completed, and, in fact, release all the resources on completion. These
resources now will be added to the free pool (F). Its now calculates all the
matrices and F after this trial allocation and the imaginary completion of
this process. It removes the row for the completed process from both the
matrices.
 It repeats the procedures from step © above. If in the process, all the rows
in the matrices get eliminated, i.e. all the process can go tot completion,
yit concludes that it is a ‘safe state’ i.e. even after the allocation, a
deadlock can be avoided. Otherwise, it concludes that it is an ‘unsafe
state’.
(f) For each request for any resources by a process, the operating system goes
through all these trial or imaginary allocations and updations, and if finds that
after the trial allocation, the state of the system would be ‘safe’, it actually goes
ahead and makes an allocation after which it updates various matrices and table in
real sense. The operating system may need to maintain two sets of matrices for
this purpose. Any time, before any allocation, it could copy the first set of
matrices (the real one) into the other, carry out all trial allocations and updations
in the other, and of the safe state results, update the former set with the
allocations.

Banker’s Algorithm

The resource-allocation graph algorithm is not applicable to a resource-allocation system


with multiple instances of each resource type. The allocation system with multiple
instances of each resource type. The deadlock-avoidance algorithm that we describe next
is applicable to such a system, but is less efficient than the resource-allocation graph
scheme. This algorithm is commonly known as the banker’s algorithm. The name was
chosen because this algorithm could used in a banking system to ensure that the bank
never allocates its available its available cash such that it can longer satisfy the needs of
all its customers.

When a new process enters the system, it must declare the maximum number of
instances of each resources type that it may need. This number may not exceed the total
number of resources in the system. When a user requests a set of resources, the system
must determine whether the allocation of these resources will leave the system in a safe
state. If it will, the resources are allocated; otherwise, the process must wait until some
other releases enough resources.

Safety Algorithm

The algorithm for finding out the whether or not a system is in a safe state can be
described as follows:

1. Let work and Finish be vectors of length m and n, respectively. Initialize


Work: =Available and Finish[i]:=false for i=1,2,3,….., n.
2. Find an I such that both

a. Finish[i]=false
b. Need i Work.

If no such I exists, go to step 4.

3. Work: =Work + Allocation;


Finish[i]: =true
Go to step 2.
4. If Finish[i] = true for all I, then the system is in a safe state.

This algorithm may require an order of m x n operations to decide whether a state is safe.

Resource-Request Algorithm

Let Request i be the request vector for process Pi. If requesti[j] = k, then process pi wants
k instances of resource type Rj. When a request for resources is made by process Pi, the
following actions are taken:
1. If Requesti < Needi, go to step 2. Otherwise , raise an error condition, since the
process has exceeded its maximum claim.
2. If Requesti< Available, go to step 3. Otherwise, Pi must wait, since the resources
are not available.
3. Have the system pretend to have allocated the requested resources to process Pi
by modifying the state as follows:

Available: = Available – Requesti;


Allocationi :=Allocationi + Requesti;
Needi := Needi – Requesti;

If the resulting resource-allocation state is safe, the transaction is completed and process
Pi is allocated its resources. However, if the new state is unsafe, then Pi must wait for
Request and the old resource-allocation state is restored.

DEADLOCK DETECTION

The graphs (DRAG) provide good help in doing this, as we have seen. However,
normally, a realistic DRAG is not as straight forward as a DRAG between two processes
(P1, P2) and two resources (R1 and R2) as depicted. In reality, there could be a number
of resource types such as printers, plotters, tapes, and so on. For instance, the system
could have two identical printers, and the operating system must be told about it at the
time of printers when requested. The complexity arises due to the fact that allocation to a
process is made of a specific resource by the operating system, depending upon the
availability, but the process normally makes the request to the operating system for only a
resource type. A very large number of processes can make this DRAG look more
complex and the deadlock detection more time consuming.

We will denote multiple instances of the same resource type by means of multiple
symbols within the square. For example, consider the DRAG shown in figure.
Figure: multiple resource for a type
P
1
R1 ( )R10 ( )R20 R2
( )R11

P
2
R1 is a resource type – say, a tape drive of a certain kind, and let us assume that there are
two tape drivers R10 and R11 of the same kind known to the system. R2 may be a of a
certain type there may be only one of that type available in the system – say, R20. In fig,
R10 is allocated to P1. P1 is waiting for R20. R20 is allocated to P2. Now comes the
question of last in the diagram. Let us assume that R11 is free and P2 wants it. In this
case, P2 can actually grab R11. And if it does so, an arrow will be actually drawn from
R11 to P2 as shown in Fig. If you traverse from a node, following the arrows, you would
not arrive at the starting node. This violates the rules for a circular wait. Therefore, even
if it gives an impression of a deadlock at first sight, it is NOT a deadlock situation.

P
1
R1 ( )R10 ( )R20 R2
( )R11

P
2
Figure: no deadlock situation.

Therefore, P2 in this case need not wait for R11. It can go to completion. The point is that
the visual illusion of the cycle should not deceive us. It is not a circular wait condition. If
R11 however, is also not free and is already grabbed by, say P1, it can lead to a deadlock
if P2 requests for R1.
The operating system, in the case, could do the following to detect a deadlock:

(i) Number all process as P0, P1 ….PN.

(i) Number each resource separately – using a meaningful coding scheme. For
instance, the first character could always be “R” denoting a resource. The
second character could denote the resource type (0=tape, 1=printer, etc.) and the
third character could denote the resource number or an instance within the type,
e.g. R00, R01, R02,…..could be different tape drivers of the same type; R10,
R11, R12, …….
(ii) Maintain two tables as shown in fig. One is a resource wise table giving , for
each resource, its type, allocation status, the process to which it is allocated and
the processes that are waiting for it.
Another table is a process wise table, giving for each process, the
resources held by it and the resources it is waiting for. This is normally held along
with PCB. Logically, it is a part of PCB, but an operating system could choose to
maintain it in a separate table linked to the PCB for that process. The operating
system could use this information to detect any deadlock, as we shall see later.

(iii) Whenever a process requests the operating system for a resource, the
request is obviously for a resource belonging to resource type. The user would
not really care which one is exactly allocated (if he did, a new resource type
should have been created). The operating system then goes through the resource
wise table to see if there is any free resource of that type. This also will
necessitate updating of both tables.
When a process releases a resource, again both tables will be updated
accordingly.
(iv) At any time, the operating system can use these tables to detect a circular wait
or a deadlock. Typically, whenever a resource is demanded by a process, before
actually allocating it, the operating system could use this algorithm to see
whether the allocation can potentially lead to a deadlock or not.

The working is as follows:

(a) Go through the resource wise table entries one by one (e.g. R00, R01 …etc.),
each time storing the resource numbers processed. This is useful in detecting
a circular wait, i.e. in finding out whether we have reached the same node
again or not.
(b) Ignore entries for free resources.
(c) For each of the entries, access the process to which the resource is allocated.
In the case, store the numbers R01 and P1 in separate lists called resource list
and process list respectively.
(d) Access the entry in the process wise table for that process obtained in step (c)
(P1 in this case).
(e) Access one by one the resources this process (P1) is waiting for. For example,
P1 is waiting for resource R20. Check if this is the same as the one already
encountered i.e. if R20 Is the same as R01 stored in step (c). In short, check
if circular wait is already encountered. If yes, the deadlock is detected. If no,
store this resource (e.g R20) in the resource list. This list will now contain
R01 and R20. The process list still contains only P1. Whether there is any
other resource apart from R20 that process P1 is waiting for. If there is any,
this procedure will have to be repeated. In this example, there is no such
resource. Therefore, the operating system goes to the next step (f).
(f) Access the next entry in the resource list maintained in step (e). This entry is
R20. Now access the resource wise table for R20 to find that R20 is allocated
to P5.
(g) Check if this process (i.e. P5) is the one already encountered in the process
list maintained in step (e). If it is the same, a deadlock is confirmed. In this
case, P5 is not the same as P1. So only store P5 after P1 in the process list and
proceed. The process list now contains P1 and P5. The resource list is still
R01, R20 as in step (e). After this, the operating system will have to choose
R10 and R23, as they are the resources process P5 is waiting for. It finds that
R10 is allocated to P1 already existed in the process list. Hence, a deadlock
(P1←R20←P5←R10←P1) has been detected.
Therefore, the operating system will have to maintain two lists – one list
of resources already encountered and a separate list of all the waiting
processes already encountered. Any time the operating system list while going
through the algorithm, the deadlock is confirmed.
(h) If a deadlock is not confirmed, continue this procedure for all the
permutations and combinations e.g for all the resources that a process is
waiting for and then for each of these resources, the processes to which they
are allocated. This procedure has to be repeated until both the lists are
exhausted one by one. If all the paths lead to resources which are free and
allocable, there is no deadlock. If all the paths make the operating system
repeatedly got through same process or resource, it is a deadlock situation.
Having finished one row, go to the next one and repeat this procedure for all the rows
where the status is NOT=free.

You might also like