OS Unit-3 Process Synchronization & Deadlock
OS Unit-3 Process Synchronization & Deadlock
VAMSHI KRISHNA
UNIT-III
INTER-PROCESS COMMUNICATION
Processes executing concurrently in the operating system may be either independent
processes or cooperating processes.
1. Independent Process: Any process that does not share data with any other process. An
Independent process does not affect or be affected by the other processes executing in the
system.
2. Cooperating Process: Any process that shares data with other processes. A cooperating
process can affect or be affected by the other processes executing in the system.
Cooperating processes require an Inter-Process Communication (IPC) mechanism that
will allow them to exchange data and information.
Reasons for providing cooperative process environment:
Information Sharing: Several users may be interested in the same piece of information
(i.e. a shared file), we must provide an environment to allow concurrent access to such
information.
Computation Speedup: If we want a particular task to run faster, we must break it into
subtasks. Each task will be executing in parallel with the other tasks.
Modularity: Dividing the system functions into separate processes or threads.
Convenience: Even an individual user may work on many tasks at the same time. For
example a user may be editing, listening to music and compiling in parallel.
There are two models of IPC: Message passing and Shared memory.
Message-Passing Systems
In the message-passing model, communication takes place by means of messages exchanged
between the cooperating processes.
1
KMIT/II-II/R18/OS P.VAMSHI KRISHNA
P and Q are two processes wants to communicate with each other then they send and receive
messages to each other through a communication link such as Hardware bus or Network.
Methods for implementing a logical communication links are:
1. Naming
2. Synchronization
3. Buffering
Naming
Processes that want to communicate use either Direct or Indirect communication.
In Direct communication, each process that wants to communicate must explicitly name the
recipient or sender of the communication.
send(P, message) — Send a message to process P.
receive(Q, message) — Receive a message from process Q.
A communication link in direct communication scheme has the following properties:
A link is established automatically between every pair of processes that want to
communicate. The processes need to know only each other’s identity to communicate.
A link is associated with exactly two processes. (i.e.) between each pair of processes,
there exists exactly one link.
In Indirect communication, the messages are sent to and received from mailboxes or ports.
A mailbox can be viewed abstractly as an object into which messages can be placed by
processes and from which messages can be removed.
Each mailbox has a unique integer identification value.
A process can communicate with another process via a number of different mailboxes, but
two processes can communicate only if they have a shared mailbox.
send (A, message) — Send a message to mailbox A.
receive (A, message) — Receive a message from mailbox A.
In this scheme, a communication link has the following properties:
A link is established between a pair of processes only if both members of the pair have a
shared mailbox.
A link may be associated with more than two processes.
Between each pair of communicating processes, a number of different links may exist,
with each link corresponding to one mailbox.
Synchronization
Message passing done in two ways:
1. Synchronous or Blocking
2. Asynchronous or Non-Blocking
Blocking send: The sending process is blocked until the message is received by the
receiving process or by the mailbox.
Non-blocking send: The sending process sends the message and resumes operation.
Blocking receive: The receiver blocks until a message is available.
Non-blocking receive: The receiver retrieves either a valid message or a null.
2
KMIT/II-II/R18/OS P.VAMSHI KRISHNA
Buffering
Messages exchanged by communicating processes reside in a temporary queue. Those queues
can be implemented in three ways:
1. Zero Capacity: Zero-capacity is called as a message system with no buffering. The
sender must block until the recipient receives the message.
2. Bounded Capacity: The queue has finite length n. Hence at most n messages can reside
in it. If the queue is not full when a new message is sent, the message is placed in the
queue and the sender can continue execution without waiting. If the link is full, the sender
must block until space is available in the queue.
3. Unbounded Capacity: The queue’s length is potentially infinite. Hence any number of
messages can wait in it. The sender never blocks.
IPC - Pipes in UNIX
Pipes were one of the first IPC mechanisms in early UNIX systems.
The pipes in UNIX are categorized into two types: Ordinary Pipes and Named Pipes
Ordinary Pipes
Ordinary pipes allow two processes to communicate in standard producer–consumer
fashion.
The producer writes to one end of the pipe (write-end) and the consumer reads from the
other end (read-end).
Ordinary pipes are unidirectional which allows only one-way communication. If two-way
communication is required, two pipes must be used. Each pipe transfer data in a different
direction.
On UNIX systems ordinary pipes are constructed using the function:
pipe(int fd[ ])
This function creates a pipe that is accessed through the int fd[ ] file descriptors: fd[0] is
the read-end of the pipe and fd[1] is the write-end.
UNIX treats a pipe as a special type of file. Thus, pipes can be accessed using ordinary
read( ) and write( ) system calls.
An ordinary pipe cannot be accessed from outside the process that created it.
A parent process creates a pipe and uses it to communicate with a child process.
A child process inherits open files from its parent. Since a pipe is a special type of file,
the child inherits the pipe from its parent process.
If a parent writes to the pipe then the child reads from pipe.
3
KMIT/II-II/R18/OS P.VAMSHI KRISHNA
Named Pipes
Named pipe provides the bidirectional communication and no parent–child relationship is
required.
Once a named pipe is established, several processes can use it for communication. A
named pipe has several writers.
Named pipes continue to exist after communicating processes have finished.
Both UNIX and Windows systems support named pipes.
Named pipes are referred to as FIFOs in UNIX systems.
Once Named pipes are created, they appear as typical files in the file system.
FIFO is created with the mkfifo( ) system call and manipulated with the ordinary open( ),
read( ), write( ) and close( ) system calls.
It will continue to exist until it is explicitly deleted from the file system.
Although FIFOs allow bidirectional communication, only one-way transmission is
permitted. If data must travel in both directions, two FIFOs are used. The communicating
processes must reside on the same machine.
If inter-machine communication is required, sockets must be used.
Shared Memory Systems
In the shared-memory model, a region of memory will be shared by cooperating processes.
Processes can exchange information by reading and writing data to the shared region.
A shared-memory region (segment) resides in the address space of the process.
Other processes that wish to communicate using this shared-memory segment must attach
it to their address space.
Normally, the operating system tries to prevent one process from accessing another
process’s memory.
Shared memory requires that two or more processes agree to remove this restriction. They
can then exchange information by reading and writing data in the shared areas.
The form of the data and the location are determined by these processes and are not under
the operating system’s control.
The processes are also responsible for ensuring that they are not writing to the same
location simultaneously.
4
KMIT/II-II/R18/OS P.VAMSHI KRISHNA
The shared buffer is implemented as a circular array with two logical pointers: in and out.
The variable ―in‖ points to the next free position in the buffer and ―out‖ points to the first
full position in the buffer.
An integer variable counter is initialized to 0. Counter is incremented every time we add a
new item to the buffer and is decremented every time we remove one item from the
buffer.
The buffer is empty when counter== 0 and the buffer is full when counter== Buffer_size.
The producer process has a local variable next_produced in which the new item to be
produced is stored.
The consumer process has a local variable next_consumed in which the item to be
consumed is stored.
Note: Although the producer and consumer routines shown above are correct separately, they
may not function correctly when executed concurrently.
Example: Let us consider the counter =5. Producer and consumer processes concurrently
execute the statements ―counter++‖ and ―counter--‖.
Let R1 and R2 are two registers. the statement ―counter++‖ may be implemented in machine
language as follows:
T0: R1 = counter
T1: R1 = R1 + 1
T2: counter = R1
where register1 is one of the local CPU registers. Similarly, the statement ―counter--‖ is
implemented as follows:
T3: R2 = counter
T4: R2 = R2 – 1
T5: counter = R2
We execute the statements in the order T0, T1, T2, T3, T4, T5,T6 then we get the accurate
counter value=5.
Now if these statements are executed concurrently by interleaving as follows:
T0: R1 = counter {R1 = 5, counter=5}
T1: R1 = R1 + 1 {R1 = 6, counter=5}
T2: R2 = counter {R2 = 5, counter=5}
T3: R2 = R2 − 1 {R2 = 4, counter=5}
T4: counter = R1 {counter = 6, R1=6}
T5: counter = R2 {counter = 4, R2=4}
Note that we have arrived at the incorrect state ―counter == 4‖, indicating that four buffer
locations are full but actually five buffer locations are full.
If we reversed the order of the statements at T4 and T5, we would arrive at the incorrect state
―counter == 6‖.
Race condition
Several processes access and manipulate the same data concurrently and the outcome of
the execution depends on the particular order in which the access takes place is called a
Race Condition.
6
KMIT/II-II/R18/OS P.VAMSHI KRISHNA
To guard against the race condition, we need to ensure that only one process at a time can
be manipulating the variable counter. To make such a guarantee, we require that the
processes be synchronized.
THE CRITICAL-SECTION PROBLEM
Consider a system consisting of n processes {P0, P1, ..., Pn−1}.
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.
When one process is executing in its critical section, no other process is allowed to
execute in its critical section.
do {
Entry section
Critical Section
Exit section
Remainder section
} while (true);
Each process must request permission to enter its critical section. The section of code
implementing entering request is the Entry section.
The critical section may be followed by an Exit section.
The remaining code is the Remainder section.
The entry section and exit section are enclosed in boxes to highlight these important
segments of code.
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
processes can be executing in their critical sections.
2. Progress. If no process is executing in its critical section and some processes wish to
enter their critical sections, then only those processes that are not executing in their
remainder sections can participate in deciding which will enter its critical section next and
this selection cannot be postponed indefinitely.
3. Bounded waiting. After a process has made a request to enter its critical section and
before that request is granted, there exists a limit on the number of times that other
processes are allowed to enter their critical sections..
Two general approaches are used to handle critical sections in operating systems:
1. Preemptive Kernel allows a process to be preempted while it is running in kernel mode.
2. Non-preemptive Kernel does not allow a process running in kernel mode to be
preempted.
PETERSON’S SOLUTION
Peterson’s solution is a classic Software-Based Solution to the critical-section problem.
Peterson’s solution is restricted to two processes that alternate execution between their
critical sections and remainder sections.
7
KMIT/II-II/R18/OS P.VAMSHI KRISHNA
The processes are numbered P0 and P1. Let Pi represents one process and Pj represents
other processes (i.e. j = i-1)
do
{
flag[i] = true;
turn = j;
while (flag[j] && turn == j);
Critical Section
flag[i] = false;
Remainder Section
} while (true);
Peterson’s solution requires the two processes to share two data items:
int turn;
boolean flag[2];
The variable turn indicates whose turn it is to enter its critical section. At any point of time
the turn value will be either 0 or 1 but not both.
if turn == i, then process Pi is allowed to execute in its critical section.
if turn == j, then process Pj is allowed to execute in its critical section.
The flag array is used to indicate if a process is ready to enter its critical section.
Example: if flag[i] is true, this value indicates that Pi is ready to enter its critical section.
To enter the critical section, process Pi first sets flag[i]=true and then sets turn=j,
thereby Pi checks 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 the same
time. Only one of these assignments will be taken. The other will occur but will be
overwritten immediately.
The eventual value of turn determines which of the two processes is allowed to enter its
critical section first.
The above code must satisfy the following requirements:
1. Mutual exclusion
2. The progress
3. The bounded-waiting
Check for Mutual Exclusion
Each Pi enters its critical section only if either flag[j] == false or turn == i.
If both processes can be executing in their critical sections at the same time, then
flag[0] == flag[1] == true. But the value of turn can be either 0 or 1 but cannot be both.
Hence P0 and P1 could not have successfully executed their while statements at about the
same time.
If Pi executed ―turn == j‖ and the process Pj executed flag[j]=true then Pj will have
successfully executed the while statement. Now Pj will enter into its Critical section.
8
KMIT/II-II/R18/OS P.VAMSHI KRISHNA
At this time, flag[j] == true and turn == j and this condition will persist as long as Pj is
in its critical section. As a result, mutual exclusion is preserved.
Check for Progress and Bounded-waiting
The while loop is the only possible way that a process Pi can be prevented from entering
the critical section only if it is stuck in the while loop with the condition flag[j] == true
and turn == j.
If Pj is not ready to enter the critical section, then flag[j] == false and Pi can enter its
critical section.
If Pj has set flag[j] == true and is also executing in its while statement, then either 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 its
critical section.
If Pj resets flag[j] to true, it must also set turn == i. Thus, since Pi does not change the
value of the variable turn while executing the while statement, Pi will enter the critical
section (Progress) after at most one entry by Pj (Bounded Waiting).
Problem with Peterson Solution
There are no guarantees that Peterson’s solution will work correctly on modern computer
architectures perform basic machine-language instructions such as load and store.
The critical-section problem could be solved simply in a single-processor environment if
we could prevent interrupts from occurring while a shared variable was being modified.
This is an Non-preemptive kernel approach.
We could be sure that the current sequence of instructions would be allowed to execute in
order without preemption.
No other instructions would be run, so no unexpected modifications could be made to the
shared variable.
This solution is not as feasible in a multiprocessor environment.
Disabling interrupts on a multiprocessor can be time consuming, since the message is
passed to all the processors.
This message passing delays entry into each critical section and system efficiency
decreases and if the clock is kept updated by interrupts there will be an effect on a
system’s clock.
SYNCHRONIZATION HARDWARE
Modern computer systems provide special hardware instructions that allow us either to test
and modify the content of a word or to swap the contents of two words atomically (i.e.) as
one uninterruptible unit.
There are two approaches in hardware synchronization:
1. test_and_set function
2. compare_and_swap function
9
KMIT/II-II/R18/OS P.VAMSHI KRISHNA
test_and_set function
The test_and_set instruction is executed atomically (i.e.) if two test_and_set( ) instructions
are executed simultaneously each on a different CPU, they will be executed sequentially in
some arbitrary order.
If the machine supports the test_and_set( ) instruction, then we can implement mutual
exclusion by declaring a boolean variable lock. The lock is initialized to false.
The definition of test_and_set instruction for process Pi is given as:
boolean test_and_set(boolean *target)
{
boolean rv = *target;
*target = true;
return rv;
}
Below algorithm satisfies all the requirements of Critical Section problem for the process Pi
that uses two Boolean data structures: waiting[ ], lock.
boolean waiting[i] = false;
boolean lock = false;
do
{
waiting[i] = true;
key = true;
while (waiting[i] && key)
key = test and set(&lock);
waiting[i] = false;
/* critical section */
j = (i + 1) % n;
while ((j != i) && !waiting[j])
j = (j + 1) % n;
if (j == i)
lock = false;
else
waiting[j] = false;
/* remainder section */
} while (true);
Mutual Exclusion
Process Pi can enter its critical section only if either waiting[i] == false or key == false.
The value of key can become false only if the test_and_set( ) is executed.
The first process to execute the test_and_set( ) will find key == false and all other
processes must wait.
The variable waiting[i] can become false only if another process leaves its critical
section.
Only one waiting[i] is set to false, maintaining the mutual-exclusion requirement.
10
KMIT/II-II/R18/OS P.VAMSHI KRISHNA
Progress
A process exiting the critical section either sets lock==false or sets waiting[j]==false.
Both allow a process that is waiting to enter its critical section to proceed.
This requirement ensures progress property.
Bounded Waiting
When a process leaves its critical section, 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 that is in the entry section (waiting[j] ==
true) as the next one to enter the critical section.
Any process waiting to enter its critical section will thus do so within n−1 turns.
This requirement ensures the bounded waiting property.
compare_and_swap function
compare_and_swap( ) is also executed atomically. The compare_and_swap( ) instruction
operates on three operands. The definition code as given below:
int compare_and_swap(int *value, int expected, int new_value)
{
int temp = *value;
if (*value == expected)
*value = new_value;
return temp;
}
Mutual-exclusion implementation with the compare and swap( ) instruction:
do
{
while (compare_and_swap(&lock, 0, 1) != 0); /* do nothing */
/* critical section */
lock = 0;
/* remainder section */
} while (true);
The operand value is set to new value only if the expression (*value == exected) is true.
Regardless, compare_and_swap( ) always returns the original value of the variable value.
Mutual Exclusion with compare_and_swap( )
A global variable lock is declared and is initialized to 0 (i.e. lock=0).
The first process that invokes compare_and_swap( ) will set lock=1. It will then enter its
critical section, because the original value of lock was equal to the expected value of 0.
Subsequent calls to compare_and_swap( ) will not succeed, because lock now is not equal
to the expected value of 0. (lock==1).
When a process exits its critical section, it sets lock back to 0 (lock ==0), which allows
another process to enter its critical section.
Problem with Hardware Solution:
The hardware-based solutions to the critical-section problem are complicated and they are
inaccessible to application programmers.
11
KMIT/II-II/R18/OS P.VAMSHI KRISHNA
MUTEX LOCKS
Mutex Locks are short for Mutual Exclusive Locks.
Mutex locks are software solution to the critical section problem.
Mutex lock are used to protect critical regions and thus prevent race conditions.
In Mutex locking, a process must acquire the lock before entering a critical section and it
releases the lock when it exits the critical section.
The acquire( )function acquires the lock and the release( ) function releases the lock.
Critical section solution through Mutex locks:
do
{
acquire( )
{
while (!available); /* busy wait */
available = false;
}
Critical Section
release( )
{
available = true;
}
remainder section
} while (true);
Explanation of Algorithm:
A mutex lock has a boolean variable available whose value indicates if the lock is
available or not.
If the lock is available, a call to acquire( ) succeeds and the lock is then set unavailable to
other processes.
A process that attempts to acquire an unavailable lock is blocked until the lock is
released.
Calls to either acquire( ) or release( ) must be performed atomically.
Disadvantage with Mutex Locks: Busy Waiting
Mutex locks implementation leads to Busy Waiting.
While a process is in its critical section, any other process that tries to enter its critical
section must loop continuously in the call to acquire( ).
This type of mutex lock is also called a Spinlock because the process ―spins‖ while
waiting for the lock to become available.
This continual looping is clearly a problem in a real multiprogramming system, where a
single CPU is shared among many processes. Busy waiting wastes a lot of CPU cycles.
12
KMIT/II-II/R18/OS P.VAMSHI KRISHNA
SEMAPHORES
Semaphores provides solution to the critical section problem.
A semaphore S is an integer variable that is accessed only through two standard atomic
operations: wait( ) and signal( ).
The definition of wait( ) and signal( ) are as follows:
wait(S)
{
while (S <= 0); // busy wait
S--;
}
signal(S)
{
S++;
}
All modifications to the integer value of the semaphore in the wait( ) and signal( ) operations
must be executed all at once.
When one process modifies the semaphore value, no other process can simultaneously
modify that same semaphore value.
In wait(S) function, the statements (S ≤ 0) and its possible modification (S--) must be
executed without interruption.
Semaphore Usage
Operating system provides two types of semaphores: Binary and Counting Semaphore.
Binary Semaphore
The value of a binary semaphore can range only between 0 and 1.
Binary semaphores behave similarly to mutex locks.
Counting Semaphore
The value of a counting semaphore can range over an unrestricted domain.
Counting semaphores can be used to control access to a given resource consisting of a
finite number of instances.
The semaphore is initialized to the number of resources available.
Each process that wishes to use a resource performs a wait( ) operation on the semaphore
and thereby decrementing the count value (S--).
When a process releases a resource, it performs a signal( ) operation and incrementing the
count value (S++).
When the count for the semaphore goes to 0 (S<=0), all resources are being used.
After that, processes that wish to use a resource will block until the count becomes greater
than 0.
Semaphores provides solution for Synchronization problem
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.
We can implement this scheme by letting P1 and P2 share a common semaphore synch,
initialized to 0 (i.e. synch==0).
13
KMIT/II-II/R18/OS P.VAMSHI KRISHNA
14
KMIT/II-II/R18/OS P.VAMSHI KRISHNA
signal(semaphore *S)
{
S->value++;
if (S->value <= 0)
{
remove a process P from S->list;
wakeup(P);
}
}
It is critical that semaphore operations wait( ) and signal( ) be executed atomically. This ensures
mutual exclusion property of Critical Section problem.
The block( ) operation suspends the process that invokes it.
The wakeup(P) operation resumes the execution of a blocked process P.
The list of waiting processes can be easily implemented by a link field in each process
control block (PCB).
Each semaphore contains an integer value and a pointer to a list of PCBs.
A FIFO queue is used to add and remove a process from the list. It ensures the Bounded
Waiting property.
The semaphore contains both head and tail pointers to the FIFO queue.
Note: The above algorithm does not eliminate the busy waiting problem completely instead
it limits the busy_waiting problem to very short amount of time.
Deadlocks and Starvation
A set of processes is in a deadlocked state when every process in the set is waiting for an
event that can be caused only by another process in the set. The events may be either resource
acquisition or resource release.
Note: 1. Deadlocks leads to a problem of indefinite blocking and starvation.
2. The implementation of a semaphore with a waiting queue may result in Deadlock.
Example: Consider a system consisting of two processes P0 and P1. Both are accessing two
semaphores S and Q. and S==Q==1.
P0 P1
wait(S); wait(Q);
wait(Q); wait(S);
. .
. .
. .
signal(S); signal(Q);
signal(Q); signal(S);
Suppose that P0 executes wait(S) and then P1 executes 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.
15
KMIT/II-II/R18/OS P.VAMSHI KRISHNA
Priority Inversion
It is a scheduling problem. It occurs only in systems with more than two priorities.
Consider there are three processes L, M, H whose order of priorities are given as L< M < H
and all are wanted to access the resource R.
When a higher-priority process (H) needs to read or modify kernel data resource R that
are currently being accessed by a lower-priority process (L).
Since kernel data are protected with a lock, the higher-priority process (H) will have to
wait for a lower-priority (L) to finish with the resource.
The situation becomes more complicated if the lower-priority process (L) is preempted in
favor of another process (M) with a higher priority.
Now the higher priority process (H) must have to wait until the process M has to release
the lock and then L has to release the lock then only the process H can access the kernel
data resource R.
This problem is called Priority Inversion.
One solution for priority inversion is that the system will have only two priorities.
These systems solve the priority inversion problem by implementing a Priority
Inheritance Protocol.
In this protocol, all processes that are accessing resources needed by a higher-priority
process inherit the higher priority until they are finished with the resources in question.
When they are finished, their priorities revert to their original values.
Example:
A priority-inheritance protocol would allow process L to temporarily inherit the priority
of process H, thereby preventing process M from preempting process L’s execution.
When process L had finished using resource R, it would relinquish its inherited priority
from H and assume its original priority.
Because resource R would now be available, process H will get the resource instead of
process M.
Note: This solution is inefficient for systems with more than one priority given for processes.
CLASSIC PROBLEMS OF SYNCHRONIZATION
There are three classic problem that are related synchronization when processes are executing
concurrently.
1. The Bounded-Buffer Problem
2. The Readers–Writers Problem
3. The Dining-Philosophers Problem
The Bounded-Buffer Problem
The major problem in Producer-consumer process is The Bounded-Buffer problem.
Let the Buffer pool consists of n locations, each location stores one item.
The Solution for Producer-Consumer Process can be given as:
int n;
semaphore mutex = 1;
semaphore empty = n;
semaphore full = 0
16
KMIT/II-II/R18/OS P.VAMSHI KRISHNA
Producer process: do {
/* produce an item in next produced */
wait(empty); /* If any one location on buffer is empty */
wait(mutex);
/* add next produced to the buffer */
signal(mutex);
signal(full);
} while (true);
Consumer process: do {
wait(full); /* If any one location in buffer is full */
wait(mutex); /*remove an item from buffer to next consumed */
………….
signal(mutex);
signal(empty); /* consume the item in next consumed */
} while (true);
Explanation of above algorithm
There is one integer variable and three semaphore variables are declared in the definition.
Integer variable n represents the size of the buffer.
The mutex semaphore provides mutual exclusion for accesses to the buffer pool and is
initialized to the value 1. Mutex variable allows only either producer process or consumer
process allows to access the buffer at a time.
The empty semaphore counts the no. of free location in buffer, it is initialized to n.
The full semaphore counts the number of full locations buffers, is initialized to 0.
empty=n and full=0 represents that initially all locations in the buffer are empty.
Producer process
wait(empty): Any location in the buffer is free, then wait(empty) operation is successful
and producer process will put an item into buffer. If wait(empty) is false then the
producer process will be blocked.
wait(mutex): Here it allows the producer to use the buffer and produce an item into
buffer.
signal(mutex): Buffer will be released by producer process.
signal(full): It indicates one or more item added to the buffer if signal(full) is successful.
Consumer process
wait(full): If wait(full) < 0 then the buffer is empty and there are no item in the buffer to
consume. Hence the consumer process will be blocked. Otherwise the buffer is having
some items the consumer process can consume an item.
wait(mutex): It allows consumer to use buffer.
signal(mutex): Buffer is released by consumer.
signal(empty): Consumer consumed an item. Hence one more location in buffer is free.
17
KMIT/II-II/R18/OS P.VAMSHI KRISHNA
18
KMIT/II-II/R18/OS P.VAMSHI KRISHNA
19
KMIT/II-II/R18/OS P.VAMSHI KRISHNA
In this situation, several processes may be executing in their critical sections simultaneously,
violating the mutual-exclusion requirement. This error may be discovered only if several
processes are simultaneously active in their critical sections.
Example:2
Suppose that a process replaces signal(mutex) with wait(mutex). That is, it executes
wait(mutex);
...
critical section
...
wait(mutex);
In this case, a deadlock will occur.
Example:3
Suppose that a process omits the wait(mutex) or the signal(mutex), or both. In this case,
either mutual exclusion is violated or a deadlock will occur.
MONITORS
Monitors are developed to deal with semaphore errors. Monitors are high-level language
synchronization constructs.
A monitor type is an ADT that includes a set of programmer defined operations that are
provided with mutual exclusion within the monitor.
The monitor type also declares the variables whose values define the state of an instance
of that type, along with the bodies of functions that operate on those variables.
Monitor Syntax:
monitor monitor_ name
{
/* shared variable declarations */
function P1 ( . . . )
{
...
}
function P2 ( . . . )
{
...
}
...
function Pn ( . . . )
{
...
}
initialization code ( . . . )
{
...
}
}
21
KMIT/II-II/R18/OS P.VAMSHI KRISHNA
The representation of a monitor type cannot be used directly by the various processes.
A function 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 functions.
The monitor construct ensures that only one process at a time is active within the monitor.
the programmer does not need to code this synchronization constraint explicitly
To solve the problem of synchronization problem along with monitors a construct called
Condition has been implemented. Condition construct defines one or more variables of type
“condition”. The syntax of condition is:
condition x,y;
The only operations that can be invoked on a condition variable are wait( ) and signal( ).
x.wait( ): Process invokes x.wait( ) is suspended until another process invokes x.signal( ).
x.signal( ): It resumes exactly one suspended process.
22
KMIT/II-II/R18/OS P.VAMSHI KRISHNA
if (state[i] != EATING)
self[i].wait( );
}
void putdown(int i)
{
state[i] = THINKING;
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;
}
}
The distribution of the chopsticks is controlled by the monitor Dining_Philosophers.
Each philosopher before starting to eat, must invoke the operation pickup( ). This act may
result in the suspension of the philosopher process.
After the successful completion of the pickup( ) operation, the philosopher may eat.
After pickup( ) operation the philosopher invokes the putdown( ) operation.
Thus, philosopher i must invoke pickup( ) & putdown( ) operations in following sequence:
DiningPhilosophers.pickup(i);
Eat
DiningPhilosophers.putdown(i);
This solution ensures that no two neighbors are eating simultaneously and that no deadlocks
will occur.
Implementing a Monitor Using Semaphores
Monitor uses three variables: semaphore mutex=1; semaphore next=0; int next_count;
For each monitor, a semaphore mutex is provided. A process must execute wait(mutex)
before entering the monitor and must execute signal(mutex) after leaving the monitor.
The signaling processes can use next variable to suspend themselves, because signaling
process must wait until the resumed process exit or leave.
next count is an integer variable used to count the number of processes suspended on
next.
23
KMIT/II-II/R18/OS P.VAMSHI KRISHNA
24
KMIT/II-II/R18/OS P.VAMSHI KRISHNA
Consider the below code the Resource_Allocator monitor that controls the allocation of a
single resource among competing processes.
monitor Resource_Allocator
{
boolean busy;
condition x;
void acquire(int time)
{
if (busy)
x.wait(time);
busy = true;
}
void release( )
{
busy = false;
x.signal( );
}
initialization_code( )
{
busy = false;
}
}
Each process, when requesting an allocation of this resource, specifies the maximum time
it plans to use the resource.
Monitor allocates the resource to the process that has the shortest time-allocation request.
The process that needs to access the resource must observe the following sequence:
R.acquire(t); /* R is an instance of type ResourceAllocator */
...
access the resource;
...
R.release( );
Problems with monitor
1. A process might access a resource without first gaining access permission to the resource.
2. A process might never release a resource once it has been granted access to the resource.
3. A process might attempt to release a resource that it never requested.
4. A process might request the same resource twice (without first releasing the resource).
25
KMIT/CSE/II-II/OS-3 P.VAMSHI KRISHNA
DEADLOCKS
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. The events with which we are mainly
concerned here are resource acquisition and resource release.
SYSTEM MODEL
A system consists of a finite number of resources to be distributed among a number of
competing processes.
Resources are categorized into two types: Physical resources and Logical resources
Physical resources: Printers, Tape drives, DVD drives, memory space and CPU cycles
Logical resources: Semaphores, Mutex locks and files.
Each resource type consists of some number of identical instances. (i.e.) If a system has two
CPU’s then the resource type CPU has two instances.
A process may utilize a resource in the following sequence under normal mode of operation:
1. Request: The process requests the resource. If the resource is being used by another
process then the request cannot be granted immediately then the requesting process must
wait until it can acquire the resource.
2. Use: The process can operate on the resource.
Example: If the resource is a printer, the process can print on the printer.
3. Release: The process releases the resource.
System calls for requesting and releasing resources:
Device System calls: request( ) and release( )
Semaphore System calls: wait( ), signal( )
Mutex locks: acquire( ), release( ).
Memory System Calls: allocate( ) and free( )
File System calls: open( ), close( ).
A System Table maintains the status of each resource whether the resource is free or
allocated. For each resource that is allocated, the table also records the process to which it is
allocated. If a process requests a resource that is currently allocated to another process, it can
be added to a queue of processes waiting for this resource.
FOUR NECESSARY CONDITIONS OF DEADLOCK
A deadlock situation can arise if the following 4 conditions hold simultaneously in a system:
1. Mutual exclusion. Only one process at a time can use the resource. If other 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. If a process holding a resource and the resource cannot be preempted
until the process has completed its task.
4. Circular wait. A set {P0, P1, ..., Pn} of waiting processes must exist such that P0 is
waiting for a resource held by P1, P1 is waiting for a resource held by P2, ..., Pn−1 is
waiting for a resource held by Pn and Pn is waiting for a resource held by P0.
26
KMIT/CSE/II-II/OS-3 P.VAMSHI KRISHNA
RESOURCE-ALLOCATION GRAPH
The resource allocation graph is used for identification of deadlocks in the system.
A System Resource-Allocation Graph G={V,E} is a directed graph that consists of a set of
vertices V and a set of edges E.
The set of vertices V is partitioned into two types of nodes: Processes and Resources.
1. Process set P= {P1, P2, ..., Pn} consisting of all the active processes in the system.
2. Resource set R= {R1, R2, ..., Rm} consisting of all resource types in the system.
The set of Edges E is divided into types: Request Edge and Assignment Edge.
1. Request Edge (Pi → Rj): It signifies that process Pi has requested an instance of
resource type Rj and Pi is currently waiting for the resource Rj.
2. Assignment edge (Rj → Pi): It signifies that an instance of resource type Rj has been
allocated to process Pi.
Processes can be represented in Circles and Resources can be represented in Rectangles.
Instance of resource can be represented by a Dot.
Note:
1. When process Pi requests an instance of resource type Rj, a request edge is inserted in the
Resource-allocation graph.
2. When this request can be fulfilled, the request edge is transformed to an assignment edge.
3. When the process no longer needs access to the resource, it releases the resource and the
assignment edge is deleted.
Resource allocation graph shows three situations:
1. Graph with No deadlock
2. Graph with a cycle and deadlock
3. Graph with a cycle and no deadlock
Resource Allocation Graph without Deadlock
The below graph consists of three sets: Process P, Resources R and Edges E.
27
KMIT/CSE/II-II/OS-3 P.VAMSHI KRISHNA
Consider the above graph, with processes and Resources and have some edges:
P1 → R1 → P2 → R3 → P3 → R2 → P1
P2 → R3 → P3 → R2 → P2
Process P2 is waiting for the resource R3, which is held by process P3.
Process P3 is waiting for either process P1 or process P2 to release resource R2.
Process P1 is waiting for process P2 to release resource R1.
Hence the Processes P1, P2 and P3 are deadlocked.
Resource Allocation Graph with a Cycle and No Deadlock
29
KMIT/CSE/II-II/OS-3 P.VAMSHI KRISHNA
No Preemption
To ensure that No preemption condition does not hold, we can use the following protocol:
If a process is holding some resources and requests another resource that cannot be
immediately allocated to it, then all resources the process is currently holding are
preempted (i.e.) resources are implicitly released.
The preempted resources are added to the list of 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
resources that it is requesting.
Note: This protocol is often applied to resources whose state can be easily saved and restored
later such as CPU registers and memory space. It cannot be applied to resources such as
mutex locks and semaphores.
Circular Wait
One way to ensure that circular wait condition never holds is to impose a total ordering of all
resource types and to require that each process requests resources in an increasing order of
enumeration.
Consider the set of resource types R={R1, R2, ..., Rm} and N be the set of natural numbers.
we define a one-to-one function F: R → N.
The function assigns each resource type to a unique integer number, which allows us to
compare two resources and to determine whether one resource precedes another resource
in our ordering.
Example: If the set of resource types R includes tape drives, disk drives and printers, then the
function F: R → N might be defined as follows:
F (Tape drive) = 1 (F: Tape drive → 1)
F (Disk drive) = 5 (F: Disk drive → 1)
F (Printer) = 12 (F: Printer → 1)
We can now consider the following protocol to prevent deadlocks:
Each process can request resources only in an increasing order of enumeration. That is, a
process can initially request any number of instances of a resource type Ri.
After that, the process can request instances of resource type Rj iff F(Rj) > F(Ri)
Example: A process that wants to use the tape drive and printer at the same time must
first request the tape drive and then request the printer.
Alternatively, we can require that a process requesting an instance of resource type Rj
must have released any resources Ri such that F(Ri) ≥ F(Rj).
Note: If several instances of the same resource type are needed, a single request for all of
them must be issued.
Disadvantage of Deadlock Prevention
Deadlock-prevention algorithms leads to low resource utilization and the system throughput
will be reduced.
30
KMIT/CSE/II-II/OS-3 P.VAMSHI KRISHNA
DEADLOCK AVOIDANCE
In Deadlock avoidance the processes first informs the operating system about their maximum
allocation of resources to be requested and used during its life time.
With this information, the operating system can decide for each request whether the
resource will be granted immediately or the process should wait for resources.
To take this decision about decision about the resource allocation, the operating system
must consider the resources currently available, the resources currently allocated to each
process and the future requests and releases of each process.
Algorithms for Deadlock Avoidance
A deadlock-avoidance algorithm dynamically examines the Resource-Allocation State to
ensure that a circular-wait condition can never exist.
The Resource Allocation State is defined by the number of available resources and allocated
resources and the maximum demands of the processes.
There are three algorithms are designed for deadlock avoidance:
1. Safe State
2. Resource Allocation Graph Algorithm
3. Bankers Algorithm
Safe State Algorithm
If the system can allocate resources to each process up to its maximum in some order and still
avoid a deadlock then the state is called Safe state.
A system is in a safe state only if there exists a Safe sequence.
A sequence of processes <P1, P2, ..., Pn> is a safe sequence for the current allocation
state, if for each process Pi, the resource requests that Pi can still make can be satisfied by
the currently available resources plus the resources held by all Pj, with j < i.
In this situation, if the resources that Pi needs are not immediately available, then Pi can
wait until all Pj have finished.
When Pj have finished its task, Pi can obtain all of its needed resources and after
completing its designated task Pi can return its allocated resources and terminate.
When Pi terminates, Pi+1 can obtain its needed resources and so on.
If no such sequence exists, then the system state is said to be unsafe.
Note:
1. A safe state is not a deadlocked state and a deadlocked state is an unsafe state.
2. An unsafe state may lead to a deadlock but not all unsafe states are deadlocks.
3. As long as the state is safe, the operating system can avoid unsafe and deadlocked states.
4. In an unsafe state, operating system cannot prevent processes from requesting resources
in such a way that a deadlock occurs. Behavior of the processes controls unsafe states.
31
KMIT/CSE/II-II/OS-3 P.VAMSHI KRISHNA
Example: Consider a system with 12 magnetic tape drives and 3 processes: P0, P1 and P2.
Process Maximum Needs Current Needs
P0 10 5
P1 4 2
P2 9 2
The above table describes as follows:
Process P0 requires 10 tape drives, P1 needs 4 tape drives and P2 need 9 tape drives.
At time t0, process P0 is holding 5 tape drives, P1 and P2 is holding 2 tape drives each.
Now there are 3 free tape drives.
At time t0, the system is in a safe state. <P1, P0, P2> sequence satisfies the safety condition.
Process P1 can immediately be allocated all its tape drives and then return all 4 resources.
(i.e.) P1 currently holding 2 tape drives and out of 3 free tape drives 2 tape drives will be
given to P1. Now P1 is having all 4 resources. Hence P1 will use all of its resources and
after completing its task P1 releases all 4 resources and then returns to the system. Now
the system is having 5 available tape drives.
Now process P0 needs 5 tape drives and the system has 5 available tape drives. Hence P0
can get all its tape drives and it reaches its maximum 10 tape drives. After completing its
task P0 returns the resources to the system. Now system has 10 available tape drives.
Now the process P2 needs 7 additional resources and system have 10 resources available.
Hence process P2 can get all its tape drives and return them. Now the system will have
all 12 tape drives available.
Problem: Low Resource utilization
If a process requests a resource that is currently available, it may still have to wait. Hence
there exist a low resource utilization is possible.
Resource-Allocation-Graph Algorithm
In this algorithm we use three edges: request edge, assignment edge and a claim edge.
Claim edge Pi → Rj indicates that process Pi may request resource Rj at some time in
the future.
Claim edge resembles a request edge in direction but is represented by dashed line.
When process Pi requests resource Rj, the claim edge Pi → Rj is converted to a request
edge.
When a resource Rj is released by Pi, the assignment edge Rj → Pi is reconverted to a
claim edge Pi → Rj.
The resources must be claimed a priori in the system. That is, before process Pi starts
executing, all its claim edges must already appear in the resource-allocation graph.
32
KMIT/CSE/II-II/OS-3 P.VAMSHI KRISHNA
33
KMIT/CSE/II-II/OS-3 P.VAMSHI KRISHNA
34
KMIT/CSE/II-II/OS-3 P.VAMSHI KRISHNA
The Available vector can be calculated by subtractring total no of resources from the sum of
resources allocated to each process.
Available resources of A= Total resources of A – Sum of resources allocated to Process P1 to P4
By using the banker’s algorithm we can decide whether the state is safe or not.
After solving the above problem by using bankers algorithm we will get to a safe state with
safe sequence <P1,P3,P4,P0,P2>.
Now we get a safe state, the resources will be granted immediately for requested process P1.
DEADLOCK DETECTION ALGORITHM
If a system does not employ either a Deadlock-Prevention or a Deadlock-Avoidance
algorithm then a deadlock situation may occur. In this environment, the system may provide:
An algorithm that examines the state of the system to determine whether a deadlock has
occurred
An algorithm to recover from the deadlock.
Deadlock Detection in Single Instance of Each Resource Type
If all resources have only a single instance then we can define a Deadlock-Detection
algorithm that uses a variant of the resource-allocation graph called a wait-for graph.
We obtain wait-for graph from the resource-allocation graph by removing the resource nodes
and collapsing the appropriate edges.
An edge from Pi to Pj in a wait-for graph implies that process Pi is waiting for process Pj
to release a resource that Pi needs.
An edge Pi → Pj exists in a wait-for graph if and only if the corresponding resource
allocation graph contains two edges Pi → Rq and Rq → Pj for some resource Rq .
35
KMIT/CSE/II-II/OS-3 P.VAMSHI KRISHNA
36
KMIT/CSE/II-II/OS-3 P.VAMSHI KRISHNA
Initially the system is not in Deadlock State. If we apply the Deadlock Detection algorithm
we will find the sequence < P0, P2, P3, P1, P4 > results in Finish[i] == true for all i.
The system is in safe state hence there is no deadlock.
RECOVERY FROM DEADLOCK
There are two options for breaking a deadlock.
1. Process termination
2. Resource Preemption
Process Termination
To eliminate deadlocks by aborting a process, we use one of two methods. In both methods,
the system reclaims all resources allocated to the terminated processes.
Abort all Deadlocked processes: This method clearly will break the deadlock cycle, but
at great expense. The deadlocked processes may have computed for a long time and the
results of these partial computations must be discarded and probably will have to be
recomputed later.
Abort one process at a time until the Deadlock cycle is eliminated: This method incurs
considerable overhead, since after each process is aborted, a deadlock-detection algorithm
must be invoked to determine whether any processes are still deadlocked.
Many factors may affect which process is chosen for preempting includes:
1. Priority of the process.
2. How long the process has computed and how much longer the process will compute
before completing its designated task.
3. How many and what types of resources the process has used.
4. How many more resources the process needs in order to complete.
5. How many processes will need to be terminated?
6. Whether the process is Interactive or Batch.
Resource Preemption
To eliminate deadlocks using resource preemption, we successively preempt some resources
from processes and give these resources to other processes until the deadlock cycle is broken.
There are 3 issues related to Resource Preemption:
37
KMIT/CSE/II-II/OS-3 P.VAMSHI KRISHNA
38