UNIT-3
UNIT-3
System Model
For the purposes of deadlock discussion, a system can be modeled as a
collection of limited resources, which can be partitioned into different
categories, to be allocated to a number of processes, each having different
needs.
Resource categories may include memory, printers, CPUs, open files, tape
drives, CD-ROMS, etc.
By definition, all the resources within a category are equivalent, and a
request of this category can be equally satisfied by any one of the
resources in that category. If this is not the case ( i.e. if there is some
difference between the resources within a category ), then that category
needs to be further divided into separate categories. For example,
"printers" may need to be separated into "laser printers" and "color inkjet
printers".
Some categories may have a single resource.
In normal operation a process must request a resource before using it, and
release it when it is done, in the following sequence:
1. Request - If the request cannot be immediately granted, then the
process must wait until the resource(s) it needs become available.
For example the system calls open( ), malloc( ), new( ), and
request( ).
2. Use - The process uses the resource, e.g. prints to the printer or
reads from the file.
3. Release - The process relinquishes the resource. so that it becomes
available for other processes. For example, close( ), free( ), delete( ),
and release( ).
For all kernel-managed resources, the kernel keeps track of what
resources are free and which are allocated, to which process they are
allocated, and a queue of processes waiting for this resource to become
available. Application-managed resources can be controlled using
mutexes or wait( ) and signal( ) calls, ( i.e. binary or counting semaphores.
)
A set of processes is deadlocked when every process in the set is waiting
for a resource that is currently allocated to another process in the set (and
which can only be released when that other waiting process makes
progress).
INTRODUCTION TO DEADLOCK
Every process needs some resources to complete its execution. However, the
resource is granted in a sequential order.
Let us assume that there are three processes P1, P2 and P3. There are three
different resources R1, R2 and R3. R1 is assigned to P1, R2 is assigned to P2
and R3 is assigned to P3.
After some time, P1 demands for R2 which is being used by P2. P1 halts its
execution since it can't complete without R2. P2 also demands for R3 which is
being used by P3. P2 also stops its execution because it can't continue without
R3. P3 also demands for R1 which is being used by P1 therefore P3 also stops
its execution.
In this scenario, a cycle is being formed among the three processes. None of the
process is progressing and they are all waiting. The computer becomes
unresponsive since all the processes got blocked.
Example of Deadlock
Necessary Conditions
The deadlock situation can only arise if all the following four conditions hold
simultaneously:
1.Mutual Exclusion
3. NO preemption
Resources cannot be taken from the process because resources can be released
only voluntarily by the process holding them.
4. Circular wait
In this condition, the set of processes are waiting for each other in the circular
form.
The above four conditions are not completely independent as the circular wait
condition implies the hold and wait condition. We emphasize on the fact that all
four conditions must hold for a deadlock.
Deadlock conditions can be avoided with the help of a number of methods. Let
us take a look at some of the methods.
Mutual Exclusion
There should be a resource that can only be held by one process at a time.
In the diagram below, there is a single instance of Resource 1 and it is
held by Process 1 only.
Preventing Hold and Wait
A process can hold multiple resources and still request more resources
from other processes which are holding them. In the diagram given below,
Process 2 holds Resource 2 and Resource 3 and is requesting the
Resource 1 which is held by Process 1.
A state is safe if the system can allocate resources to each process (up to its
maximum requirement) in some order and still avoid a deadlock. Formally, a
system is in a safe state only, if there exists a safe sequence. So a safe state is
not a deadlocked state and conversely a deadlocked state is an unsafe state.
The above Figure shows the Safe, unsafe, and deadlocked state spaces
Let us consider a system having 12 magnetic tapes and three processes P1, P2,
P3. Process P1 requires 10 magnetic tapes, process P2 may need as many as 4
tapes, process P3 may need up to 9 tapes. Suppose at a time to, process P1 is
holding 5 tapes, process P2 is holding 2 tapes and process P3 is holding 2 tapes.
(There are 3 free magnetic tapes)
Processes Maximum Needs Current Needs
So at
P1 10 5
P2 4 2
P3 9 2
time t0, the system is in a safe state. The sequence is <P2, P1, P3> satisfies the
safety condition. Process P2 can immediately be allocated all its tape drives and
then return them. After the return the system will have 5 available tapes, then
process P1 can get all its tapes and return them (the system will then have 10
tapes); finally, process P3 can get all its tapes and return them (The system will
then have 12 available tapes).
A system can go from a safe state to an unsafe state. Suppose at time t1, process
P3 requests and is allocated one more tape. The system is no longer in a safe
state. At this point, only process P2 can be allocated all its tapes. When it
returns them the system will then have only 4 available tapes. Since P1 is
allocated five tapes but has a maximum of ten so it may request 5 more tapes. If
it does so, it will have to wait because they are unavailable. Similarly, process
P3 may request its additional 6 tapes and have to wait which then results in a
deadlock.
The mistake was granting the request from P3 for one more tape. If we made P3
wait until either of the other processes had finished and released its resources,
then we could have avoided the deadlock.
Note: In a case, if the system is unable to fulfill the request of all processes
then the state of the system is called unsafe.
The main key of the deadlock avoidance method is whenever the request is
made for resources then the request must only be approved only in the case if
the resulting state is a safe state.
DEADLOCK DETECTION
If a system does not employ either a deadlock-prevention or deadlock-
avoidance algorithm, then there are chances of occurrence of a deadlock.
Thus order to get rid of deadlocks the operating system periodically checks the
system for any deadlock. After Finding the deadlock the operating system will
recover from it using recovery techniques.
Now, the main task of the operating system is to detect the deadlocks and this is
done with the help of Resource Allocation Graph.
The RAG is a directed graph consisting of vertices and directed edges. The
vertex set is partitioned into two types, a subset representing processes and
another subset representing resources. Pictorially, the resources are represented
by rectangles with dots within, each dot representing an instance of the resource
and circles represent processes.
P3 is holding R3.
If a RAG has no cycle (a closed loop in the direction of the edges), then the
system is not in a state of deadlock. If on the other hand, there are cycles, then a
deadlock may exist. If there are only single instances of each resource type,
then a cycle in a RAG is a necessary and sufficient condition for existence of a
deadlock.
In the system, there may exist a number of instances and according to them,
there are two types of resource vertices and these are single instances and
multiple instances.
Single Instance
In a single instance resource type, there is a single dot inside the box. The
single dot mainly indicates that there is one instance of the resource.
Multiple Instance
In multiple instance resource types, there are multiple dots inside the box, and
these Multiple dots indicate that there are multiple instances of the resources.
Processes P1, P2 and P3 are deadlock and are in a circular wait. P2 is waiting
for R3 held by P3. P3 is waiting for P1 or P2 to release R2. So also P1 is
waiting for P2 to release R1.
P1->R1->P2->R3->P3->R2->P1
P2->R3->P3->R2->P2
If there are multiple instances of resources types, then a cycle does not
necessarily imply a deadlock. Here a cycle is a necessary condition but not a
sufficient condition for the existence of a deadlock. Here also there is a cycle.
P1->R1->P3->R2->P1
Suppose there are four processes P1, P2, P3, P4 and there are two instances of
resource R1 and two instances of resource R2:
1. Process Termination
In order to eliminate deadlock by aborting the process, we will use one of two
methods given below. In both methods, the system reclaims all resources that
are allocated to the terminated processes.
2. Resource Preemption
(a)Selecting a victim:
We must determine which resources and which processes are to be preempted
and also the order to minimize the cost.
(b)Rollback:
We must determine what should be done with the process from which
resources are preempted. One simple idea is total rollback. That means abort
the process and restart it.
(c)Starvation:
In a system, it may happen that same process is always picked as a victim. As
a result, that process will never complete its designated task. This situation is
called Starvation and must be avoided. One solution is that a process must be
picked as a victim only a finite number of times.
Prevention, avoidance and detection are the three basic approaches to handle
deadlocks. But they do not encompass all the problems encountered. Thus a
combined approach of all the three basic approaches is used.
Process Management and Synchronization
The Critical Section Problem
Process synchronization refers to the idea that multiple processes are to join
up or handshake at a certain point, in order to reach an agreement or commit to
a certain sequence of action. Coordination of simultaneous processes to
complete a task is known as process synchronization.
The critical section problem
Consider a system, assume that it consisting of n processes. Each process
having a segment of code. This segment of code is said to be critical section.
E.G: Railway Reservation System.
Two persons from different stations want to reserve their tickets, the train
number, destination is common, the two persons try to get the reservation at the
same time. unfortunately, the available berths are only one, both are trying for
that berth.
It is also called the critical section problem. solution is when one process is
executing in its critical section, no other process is to be allowed to execute in
its critical section.
The critical section problem is to design a protocol that the processes can use to
cooperate. 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.
A solution to the critical section problem must satisfy the following 3
requirements:
1.mutual exclusion:
Only one process can execute their critical section at any time.
2.Progress:
When no process is executing a critical section for a data, one of the
processes wishing to enter a critical section for data will be granted entry.
3.Bounded wait:
No process should wait for a resource for infinite amount of time.
Critical section:
The portion in any program that acceses a shared resource is called as critical
section (or) critical region.
Peterson’s solution:
Peterson solution is one of the solutions to critical section problem involving
two processes. This solution states that when one process is executing its
critical section then the other process executes the rest of the code and vice
versa.
Peterson solution requires two shared data items:
turn: indicates whose turn it is to enter into the critical section. If turn == i,
then process i is allowed into their critical section.
flag: indicates when a process wants to enter into critical section. when
process i wants to enter their critical section, it sets flag[i] to true.
do {
flag[i]= TRUE; turn = j;
while (flag[j] && turn == j);
critical section
flag[i] = FALSE;
remainder section
} while (TRUE);
Synchronization hardware
Definition:
boolean TestAndSet(boolean&lock)
{
boolean temp=lock;
Lock=true;
return temp;
}
Algorithm for TestAndSet
do
{
while testandset(&lock)
//do nothing
//critical section
lock=false
remainder section
}while(TRUE);
Swap instruction can also be used for mutual exclusion Definition
Void swap(boolean &a, boolean &b)
{
boolean temp=a;
a=b;
b=temp;
}
Algorithm
Do
{
key=true;
while(key=true)
swap(lock,key);
critical section
lock=false;
remainder section
}while(1);
lock is global variable initialized to false. each process has a local variable key.
A process wants to enter critical section, since the value of lock is false and key
is true.
lock=false
key=true
after swap instruction,
lock=true key=false
now key=false becomes true, process exits repeat-until, and enter into critical
section.
When process is in critical section (lock=true), so other processes wanting to
enter critical section will have
lock=true key=true
Hence they will do busy waiting in repeat-until loop until the process exits
critical section and sets the value of lock to false.
Semaphores
Problems:
1. Deadlock
Deadlock occurs when multiple processes are blocked. each waiting for a
resource that can only be freed by one of the other blocked processes.
2. Starvation
one or more processes gets blocked forever and never get a chance to take their
turn in the critical section.
3. Priority inversion
If low priority process is running, medium priority processes are waiting for
low priority process, high priority processes are waiting for medium priority
processes. this is called Priority inversion.
The two most common kinds of semaphores are counting semaphores and
binary semaphores. Counting semaphores represent multiple resources, while
binary semaphores, as the name implies, represents two possible states
(generally 0 or 1; locked or unlocked).
1. Bounded-buffer problem
Two processes share a common, fixed –size buffer.
Producer puts information into the buffer, consumer takes it out.
The problem arise when the producer wants to put a new item in the buffer, but
it is already full. The solution is for the producer has to wait until the consumer
has consumed at least one buffer. similarly if the consumer wants to remove an
item from the buffer and sees that the buffer is empty, it goes to sleep until the
producer puts something in the buffer and wakes it up.
The structure of the producer process
do {
// produce an item in nextp wait
(empty);
wait (mutex);
// add the item to the buffer signal
(mutex);
signal (full);
} while (TRUE);
The structure of the consumer process
do
{
wait (full);
wait (mutex);
// remove an item from buffer to nextc signal
(mutex);
signal (empty);
// consume the item in nextc
} while (TRUE);
MONITORS
Monitor provides condition variables along with two operations on them i.e.
wait and signal.
wait(condition variable) signal(condition variable)
Every condition variable has an associated queue. A process calling wait on a
particular condition variable is placed into the queue associated with that
condition variable. A process calling signal on a particular condition variable
causes a process waiting on that condition variable to be removed from the
queue associated with it.
Solution to Producer consumer problem using monitors:
monitor producer consumer
condition full, empty;
int count;
procedure insert(item)
{
if(count==MAX)
wait(full);
insert_item(item);
count=count+1;
if(count==1)
signal(empty);
}
procedure remove()
{
if(count==0)
wait(empty);
remove_item(item);
count=count-1;
if(count==MAX-1) signal(full);
}
procedure producer()
{
producerconsumer.insert(item);
}
procedure consumer()
{
producerconsumer.remove();
}
Solution to dining philosophers problem using monitors
A philosopher may pickup his forks only if both of them are available. A
philosopher can eat only if his two neighbours are not eating. some other
philosopher can delay himself when he is hungry.
Dining philosophers.Take_forks( ) : acquires forks ,which may block the
process.
Eat noodles ( )
Dining philosophers.put_ forks( ): releases the forks.
Resuming processes within a monitor
If several processes are suspended on condition x and x.signal( ) is executed
by some process. Then
how do we determine which of the suspended processes should be resumed
next?
solution is FCFS (process that has been waiting the longest is resumed first).
In many circumstances, such simple technique is not adequate. alternate
solution is to assign priorities and wake up the process with the highest
priority.
Resource allocation using monitor
boolean
inuse=false;
conditionavailable;
//conditionvariable
monitorentry void get resource()
{
if(inuse) //is resource inuse
{
wait(available); wait until available issignaled
}
inuse=true; //indicate resource is now inuse
}
monitor entry void return resource()
{
inuse=false; //indicate resource is not in use
signal(available); //signal a waiting process to proceed
}
IPC between processes on a single computer system and different
systems- using pipes, FIFOs, message queues, shared memory.
Inter process communication (IPC) is used for exchanging data between
multiple threads in one or more processes or programs. The Processes may
be running on single or multiple computers connected by a network.
Pipes
Files
Shared memory
Message passing
Sockets
1. Shared Memory
2. Message passing
There are two processes: Producer and Consumer. The producer produces
some items and the Consumer consumes that item. The two processes share
a common space or memory location known as a buffer where the item
produced by the Producer is stored and from which the Consumer consumes
the item if needed. There are two versions of this problem: the first one is
known as the unbounded buffer problem in which the Producer can keep on
producing items and there is no limit on the size of the buffer, the second
one is known as the bounded buffer problem in which the Producer can
produce up to a certain number of items before it starts waiting for
Consumer to consume it. We will discuss the bounded buffer problem.
First, the Producer and the Consumer will share some common memory,
then the producer will start producing items. If the total produced item is
equal to the size of the buffer, the producer will wait to get it consumed by
the Consumer. Similarly, the consumer will first check for the availability
of the item. If no item is available, the Consumer will wait for the Producer
to produce it. If there are items available, Consumer will consume them.
The pseudo-code to demonstrate is provided below:
C
#define buff_max 25
#define mod %
struct item{
// or consumed data
---------
int free_index = 0;
int full_index = 0;
C
item nextProduced;
while(1){
// for production.
// if so keep waiting.
shared_buff[free_index] = nextProduced;
C
item nextConsumed;
while(1){
while((free_index == full_index);
nextConsumed = shared_buff[full_index];
In the above code, the Producer will start producing again when the
(free_index+1) mod buff max will be free because if it it not free, this
implies that there are still items that can be consumed by the Consumer so
there is no need to produce more. Similarly, if free index and full index
point to the same index, this implies that there are no items to consume.
A link has some capacity that determines the number of messages that can
reside in it temporarily for which every link has a queue associated with it
which can be of zero capacity, bounded capacity, or unbounded capacity. In
zero capacity, the sender waits until the receiver informs the sender that it
has received the message. In non-zero capacity cases, a process does not
know whether a message has been received or not after the send operation.
For this, the sender must communicate with the receiver explicitly.
Implementation of the link depends on the situation, it can be either a direct
communication link or an in-directed communication link.
Direct Communication links are implemented when the processes use a
specific process identifier for the communication, but it is hard to identify
the sender ahead of time.
For example the print server.
In-direct Communication is done via a shared mailbox (port), which
consists of a queue of messages. The sender keeps the message in mailbox
and the receiver picks them up.
Message Passing through Exchanging the Messages.
Synchronous and Asynchronous Message Passing:
A process that is blocked is one that is waiting for some event, such as a
resource becoming available or the completion of an I/O operation. IPC is
possible between the processes on same computer as well as on the
processes running on different computer i.e. in networked/distributed
system. In both cases, the process may or may not be blocked while sending
a message or attempting to receive a message so message passing may be
blocking or non-blocking. Blocking is
considered synchronous and blocking send means the sender will be
blocked until the message is received by receiver. Similarly, blocking
receive has the receiver block until a message is available. Non-blocking is
considered asynchronous and Non-blocking send has the sender sends the
message and continue. Similarly, Non-blocking receive has the receiver
receive a valid message or null. After a careful analysis, we can come to a
conclusion that for a sender it is more natural to be non-blocking after
message passing as there may be a need to send the message to different
processes. However, the sender expects acknowledgment from the receiver
in case the send fails. Similarly, it is more natural for a receiver to be
blocking after issuing the receive as the information from the received
message may be used for further execution. At the same time, if the
message send keep on failing, the receiver will have to wait indefinitely.
That is why we also consider the other possibility of message passing.
There are basically three preferred combinations:
C
void Producer(void){
int item;
Message m;
while(1){
receive(Consumer, &m);
item = produce();
build_message(&m , item ) ;
send(Consumer, &m);
Consumer Code
C
void Consumer(void){
int item;
Message m;
while(1){
receive(Producer, &m);
item = extracted_item();
send(Producer, &m);
consume_item(item);
Pipe
Socket
Remote Procedural calls (RPCs)
Pipes
Pipe is widely used for communication between two related processes. This
is a half-duplex method, so the first process communicates with the second
process. However, in order to achieve a full-duplex, another pipe is needed.
FIFO:
Socket:
The socket is the endpoint for sending or receiving data in a network. This is
true for data sent between processes on the same computer or data sent
between different computers on the same network. Most of the operating
systems use sockets for inter process communication.
Working of RPC
The following steps take place during a RPC:
4. On the server, the transport layer passes the message to a server stub,
which demarshalls(unpack) the parameters and calls the desired server
routine using the regular procedure call mechanism.
5. When the server procedure completes, it returns to the server stub (e.g.,
via a normal procedure call return), which marshalls the return values
into a message. The server stub then hands the message to the transport
layer.
6. The transport layer sends the result message back to the client transport
layer, which hands the message back to the client stub.
7. The client stub demarshalls the return parameters and execution returns
to the caller.
RPC ISSUES
On the client side, the stub handles the interface between the client’s local
procedure call and the run-time system, marshaling and unmarshaling data,
invoking the RPC run-time protocol, and if requested, carrying out some of
the binding steps.
On the server side, the stub provides a similar interface between the run-
time system and the local manager procedures that are executed by the
server.
3. Binding: How does the client know who to call, and where the service
resides?
The most flexible solution is to use dynamic binding and find the server at
run time when the RPC is first made. The first time the client stub is
invoked, it contacts a name server to determine the transport address at
which the server resides.
Binding consists of two parts:
Naming:
Remote procedures are named through interfaces. An interface
uniquely identifies a particular service, describing the types
and numbers of its arguments. It is similar in purpose to a type
definition in programming languauges.
Locating:
Finding the transport address at which the server actually resides.
Once we have the transport address of the service, we can send
messages directly to the server.
A Server having a service to offer exports an interface for it. Exporting an
interface registers it with the system so that clients can use it.