II_Unit_OS
II_Unit_OS
Communication between processes takes place through calls to send() and receive()
primitives.
Message passing may be either blocking or nonblocking— also known as synchronous and
asynchronous
Blocking send. The sending process is blocked until the message is received by the receiving
process or by the mailbox
Nonblocking send. The sending process sends the message and resumes Operation
Blocking receive. The receiver blocks until a message is available.
Nonblocking receive. The receiver retrieves either a valid message or a null.
When both send() and receive() are blocking, we have a rendezvous between the sender and
the receiver.
When both send() and receive() are blocking, we have a rendezvous(meeting) between the
sender and the receiver
Automatic or explicit buffering:
Messages exchanged by communicating processes reside in a temporary queue. Basically,
such queues can be implemented in three ways:
o Zero capacity.
o Bounded capacity
o unbounded capacity
Zero capacity. The queue has a maximum length of zero (the link cannot have any messages
waiting in it). In this case, the sender must block until the recipient receives the message.
Bounded capacity. The queue has finite length n; thus, at most n messages can reside in it.
Unbounded capacity. The queue’s length is potentially infinite; thus, any number of
messages can wait in it. The sender never blocks.
The zero-capacity case is sometimes referred to as a message system with no buffering. The
other cases are referred to as systems with automatic buffering.
Scheduling criteria
The criteria include the following:
• CPU utilization. The CPU Should be kept as busy as possible for effective CPU utilization.
• Throughput: The total number of process completed per unit time is called as throughput.
• Turnaround time: The interval from the time of submission of a process to the time of completion
is the
turnaround time.
Turnaround time = Waiting time + Burst time
• Waiting time: Waiting time is the sum of the periods spent waiting in the ready queue.
Waiting = time of completion –burst time
• Response time: The time from the submission of a request until the first response is produced.
Scheduling algorithms
CPU scheduling deals with the problem of deciding which of the processes in the ready
queue is to be allocated the CPU. There are many different CPU-scheduling algorithms.
First-Come, First-Served Scheduling
Shortest-Job-First Scheduling
Priority Scheduling
Round-Robin Scheduling
Multilevel Queue Scheduling
Multilevel Feedback Queue Scheduling
If the processes arrive in the order P1, P2, P3, and are served in FCFS order, we get the result
shown in the following Gantt chart.
The waiting time is 0 milliseconds for process P1, 24 milliseconds for process P2, and 27
milliseconds for process P3.
Thus, the average waiting time is (0 + 24 + 27)/3 = 17 milliseconds.
If the processes arrive in the order P2, P3, P1, however, the results will be as shown in the
following Gantt chart:
The average waiting time is now (6 + 0 + 3)/3 = 3 milliseconds. Thus, the average waiting
time under an FCFS policy is generally not minimal.
Assume that we have one CPU-bound process and many I/O-bound processes. The CPU-
bound process will get and hold the CPU.
During this time, all the other processes will finish their I/O and will move into the ready
queue, waiting for the CPU.
While the processes wait in the ready queue, the I/O devices are idle.
Now the CPU-bound process finishes its CPU burst and moves to an I/O device. All the I/O-
bound processes, which have short CPU bursts, execute quickly and move back to the I/O
queues. Now the CPU sits idle. This is called as CONVOY EFFECT which results in lower
CPU and device utilization.
The FCFS scheduling algorithm is non preemptive. Once the CPU has been allocated to a
process, that process keeps the CPU until it releases the CPU, either by terminating or by
requesting I/O.
ADVANTAGES:
Better for long processes
Simple method (i.e., minimum overhead on processor)
No starvation
DISADVANTAGES:
Waiting time can be large if short requests wait behind the long ones.
It is not suitable for time sharing systems where it is important that each user
should get the CPU for an equal amount of time interval.
A proper mix of jobs is needed to achieve good results from FCFS scheduling.
SHORTEST-JOB-FIRST SCHEDULING:
With this algorithm the process that comes with the shortest job will be allocated the CPU
first.
This algorithm includes with each process the length of the process’s next CPU burst. When
the CPU is available, it is assigned to the process that has the smallest next CPU burst.
If the next CPU bursts of two processes are the same, FCFS scheduling is used to break the
tie.
This is also called as shortest next CPU burst algorithm.
EXAMPLE: consider the following set of processes, with the length of the CPU burst given
in milliseconds:
Using SJF scheduling, we would schedule these processes according to the following Gantt
chart:
The waiting time is 3 milliseconds for process P1, 16 milliseconds for process P2, 9
milliseconds for process P3, and 0 milliseconds for process P4.
Thus, the average waiting time is (3 + 16 + 9 + 0)/4 = 7 milliseconds.
The SJF algorithm can be either preemptive or nonpreemptive.
When a new process arrives at the ready queue while a previous process is still executing and
the next CPU burst of the newly arrived process is shorter than what is left of the currently
executing process, then a preemptive or non preemptive approach can be chosen.
A preemptive SJF algorithm will preempt the currently executing process.
A nonpreemptive SJF algorithm will allow the currently running process to finish its CPU
burst. Preemptive SJF scheduling is sometimes called shortest-remaining-time-first
scheduling.
EXAMPLE: consider the following four processes, with the length of the CPU burst given in
milliseconds:
Using priority scheduling, we would schedule these processes according to the following
Gantt chart:.
EXAMPLE: Consider the following set of processes that arrive at time 0, with the length of
the CPU burst given in milliseconds:
If we use a time quantum of 4 milliseconds, then process P1 gets the first 4 milliseconds.
Since it requires another 20 milliseconds, it is preempted after the first time quantum, and the
CPU is given to the next process in the queue, process P2
Process P2 does not need 4 milliseconds, so it quits before its time quantum expires.
The CPU is then given to the next process, process P3. Once each process has received 1
time quantum, the CPU is returned to process P1 for an additional time quantum.
P1 waits for 6 milliseconds (10 - 4), P2 waits for 4 milliseconds, and P3 waits for 7
milliseconds.
Thus, the average waiting time is 17/3 = 5.66 milliseconds.
The performance of the RR algorithm depends heavily on the size of the time quantum.
If the time quantum is extremely large, the RR policy is the same as the FCFS policy.
If the time quantum is extremely small the RR approach can result in a large number of
context switches.
ADVANTAGES:
Does not suffer by starvation.
DISADVANTAGE:
The average waiting time under the RR policy is often long.
There is Low throughput.
There are Context Switches.
MULTI LEVEL QUEUE SCHEDULING:
The processes are divided into foreground process and background process.
These two types of processes have different response-time requirements and have different
scheduling needs.
The foreground processes may have priority over background processes.
A multilevel queue scheduling algorithm partitions the ready queue into several separate
queues.
The processes are permanently assigned to one queue, generally based on some property of
the process, such as memory size, process priority, or process type.
Example of a multilevel queue scheduling algorithm with five queues, in order of priority:
1. System processes
2. Interactive processes
3. Interactive editing processes
4. Batch processes
5. Student processes
Each queue has its own scheduling algorithm. For example, separate queues might be used
for foreground and background processes.
The foreground queue might be scheduled by an RR algorithm, while the background queue
is scheduled by an FCFS algorithm.
There must be scheduling among the queues, which is commonly implemented as fixed-
priority preemptive scheduling.
No process in the batch queue, for example, could run unless the queues for system
processes,
interactive processes, and interactive editing processes were all empty.
If an interactive editing process entered the ready queue while a batch process was running,
the batch process would be preempted.
MULTILEVEL FEEDBACK QUEUE SCHEDULING:
The multilevel feedback queue scheduling algorithm, allows a process to move between
queues.
The idea is to separate processes according to the characteristics of their CPU bursts.
If a process uses too much CPU time, it will be moved to a lower-priority queue.
A process that waits too long in a lower-priority queue may be moved to a higher-priority
queue. This form of aging prevents starvation.
EXAMPLE: For example, consider a multilevel feedback queue scheduler with three queues,
numbered from 0 to 2
The scheduler first executes all processes in queue 0. Only when queue 0 is empty will it
execute processes in queue 1. Similarly, processes in queue 2 will be executed only if queues
0 and 1 are empty.
A process that arrives for queue 1 will preempt a process in queue 2. A process in queue 1
will in turn be preempted by a process arriving for queue 0.
A process entering the ready queue is put in queue 0. A process in queue 0 is given a time
quantum of 8 milliseconds.
If it does not finish within this time, it is moved to the tail of queue 1. If queue 0 is empty,
the process at the head of queue 1 is given a quantum of 16 milliseconds.
If it does not complete, it is preempted and is put into queue 2. Processes in queue 2 are run
on an FCFS basis but are run only when queues 0 and 1 are empty. a multilevel feedback
queue
2.2.3 Multiple-processor scheduling
Load Balancing
If SMP, need to keep all CPUs loaded for efficiency
Load balancing attempts to keep workload evenly distributed
Two general approaches to Load Balancing
Push migration – periodic task checks load on each processor, and if found pushes task
from overloaded CPU to other CPUs
Pull migration – idle processors pulls waiting task from busy processor
Multicore Processors
Recent trend to place multiple processor cores on same physical chip
Faster and consumes less power
Multiple threads per core also growing
o Takes advantage of memory stall to make progress on another thread while memory
retrieve happens
When a processor accesses memory, it spends a significant amount of time waiting for the
data to become available. This situation is known as a memory stall.
There are two ways to multithread a processing core: coarse-grained and fine-grained
multithreading.
With coarse-grained multithreading, a thread executes on a processor until a long-latency
event such as a memory stall occurs.
Fine-grained (or interleaved) multithreading switches between threads at a much finer
level of granularity—typically at the boundary of an instruction cycle
Real time scheduling
Soft real-time systems – no guarantee as to when critical real-time process will be scheduled
Hard real-time systems – task must be serviced by its deadline.
Event latency is the amount of time that elapses from when an event occurs to when it is
serviced
Priority-based Scheduling
For real-time scheduling, scheduler must support preemptive, priority-based scheduling
But only guarantees soft real-time
For hard real-time must also provide ability to meet deadlines
Processes have new characteristics: periodic ones require CPU at constant intervals
o Has processing time t, deadline d, period p
o 0≤t≤d≤p
o Rate of periodic task is 1/p
What is unusual about this form of scheduling is that a process may have to announce its
deadline requirements to the scheduler.
Then, using a technique known as an admission-control algorithm, the scheduler does one
of two things.
o It either admits the process, guaranteeing that the process will complete on time,
o rejects the request as impossible if it cannot guarantee that the task will be serviced
by its deadline
Virtualization and Scheduling
Virtualization software schedules multiple guests onto CPU(s)
Each guest doing its own scheduling
o Not knowing it doesn’t own the CPUs
o Can result in poor response time
o Can effect time-of-day clocks in guests
Can undo good scheduling algorithm efforts of guests
2.3 Threads
2.3.1 Overview
A thread is a basic unit of CPU utilization. It is a smallest unit of execution of a program that
determines the flow of control of execution.
It comprises a thread ID, a program counter, a register set, and a stack.
It shares with other threads belonging to the same process its code section, data section, and
other operating-system resources, such as open files.
Within a process, there may be one or more threads, each with the following:
2.2 Process Synchronization
A cooperating process is one that can affect or be affected by other processes executing in
the system.
Cooperating processes can either directly share a logical address space (that is, both code
and data) or be allowed to share data only through files or messages.
EXAMPLE:
Consider the producer consumer process that contains a variable called counter.
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 code for the producer process is
while (true) {
/* produce an item in next produced */
while (counter == BUFFER SIZE)
; /* do nothing */
buffer[in] = next produced;
in = (in + 1) % BUFFER SIZE;
counter++;
}
The code for the consumer process is
while (true) {
while (counter == 0)
; /* do nothing */
next consumed = buffer[out];
out = (out + 1) % BUFFER SIZE;
counter--;
/* consume the item in next consumed */
}
Suppose that the value of the variable counter is currently 5 and that the producer and
consumer processes concurrently execute the statements “counter++” and “counter--”.
Following the execution of these two statements, the value of the variable counter may be 4,
5, or 6!
The only correct result, though, is counter == 5, which is generated correctly if the producer
and consumer execute separately.
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.
To guard against the race condition, only one process at a time should be allowed for
manipulating which can be achieved using Synchronization.
The critical-section problem
The critical-section problem is to design a protocol that the processes can use to cooperate.
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. That is, no two processes are executing in their critical sections at the
same time.
The structure of critical section problem is
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 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 are enclosed in boxes to highlight these important
segments of code
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.
3. Bounded waiting. There exists a bound, or limit, on the number of times that other
processes are allowed to enter their critical sections after a process has made a request to
enter its critical section and before that request is granted.
Two general approaches are used to handle critical sections in operating systems: preemptive
kernels and non preemptive kernels. .A preemptive kernel allows a process to be preempted
while it is running in kernel mode. A non preemptive kernel does not allow a process running in
kernel mode to be preempted; a kernel-mode process will run until it exits kernel mode, blocks,or
voluntarily yields control of the CPU.
SOLUTIONS TO CRITICAL SECTION PROBLEM: PETERSON’S SOLUTION:
Peterson’s solution is restricted to two processes that alternate execution between their
critical sections and remainder sections.
The processes are numbered P0 and P1. For convenience, when presenting Pi, we use Pj to
denote the other process.
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. That is, if turn == i,
then process Pi 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. For
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] to be true and then sets turn to the
value j, thereby checking that if the other process wishes to enter the critical section, it can do
so.
Similarly to enter the critical section, process Pj first sets flag[j] to be true and then sets turn
to the value i, thereby checking that if the other process wishes to enter the critical section.
The solution is correct and thus provides the following.
1. Mutual exclusion is preserved.
2. The progress requirement is satisfied.
3. The bounded-waiting requirement is met.
Synchronization hardware
The software-based solutions such as Peterson’s are not guaranteed to work on modern computer
architectures. There are several more solutions to the critical-section problem using techniques
ranging from hardware to software-based APIs available to both kernel developers and
application programmers. All these solutions are based on the premise of locking —that is,
protecting critical regions through the use of locks.
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
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
The test and set() instruction can be defined as shown in Figure 5.3.
The important characteristic of this instruction is that it is executed atomically.
Thus, 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, initialized to false.
The structure of process P is shown in Figure 5.4.
The compare_and_ swap() instruction, in contrast to the test_and_set() instruction, operates
on three operands; it is defined in Figure 5.5.
The operand value is set to new_value only if the expression (*value == expected) is true.
Regardless, compare_and_ swap() always returns the original value of the variable value.
Like the test_and_set() instruction, compare_and_swap() is executed atomically.
Mutual exclusion can be provided as follows: a global variable (lock) is declared and is
initialized to 0. The first process that invokes compare_and_swap() will set lock to 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.
When a process exits its critical section, it sets lock back to 0, which allows another process to
enter its critical section.
The structure of process Pis shown in Figure 5.6
Although these algorithms satisfy the mutual-exclusion requirement, they do not satisfy the
bounded-waiting requirement. In Figure 5.7, we present another algorithm using the
test_and_set() instruction that satisfies all the critical-section requirements. The common data
structures are
boolean waiting[n];
boolean lock;
These data structures are initialized to false. To prove that the mutual exclusion requirement is
met, we note that 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; all others 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.
To prove that the bounded-waiting requirement is met, 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 – 1turns.
Mutex locks
Hardware-based solutions to the critical-section problem are complicated as well as
inaccessible to application programmers.
OS designers build software tools to solve the critical section problem. Example:MUTEX
LOCK.
Mutex locks are used to protect critical regions and thus prevent race conditions
A process must acquire the lock before entering a critical section; it releases the lock when it
exits the critical section.
The acquire()function acquires the lock, and the release() function releases the lock.
A mutex lock has a boolean variable availablewhose value indicates if the lock is available
or not.
If the lock is available, a call to acquire() succeeds, and the lock is then considered
unavailable.
A process that attempts to acquire an unavailable lock is blocked until the lock is released.
The definition of acquire() is as follows:
acquire() {
while (!available)
; /* busy wait */
available = false;;
}
The definition of release() is as follows:
release()
{
available = true;
}
The main disadvantage of the implementation given here is that it requires 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.
Busy waiting wastes CPU cycles that some other process might be able to use productively.
Spinlocks do have an advantage, however, in that no context switch is required when a
process must wait on a lock, and a context switch may take considerable time
Semaphores
A semaphore S is an integer variable that, is accessed only through two standard atomic
operations: wait() and signal().
The wait() operation was originally termed P and the meaning is to test, the signal() was
originally called V and the meaning is to increment.
The definition of wait() is as follows:
wait(S) {
while (S <= 0)
; // busy wait
S--;
}
The definition of signal() is as follows:
signal(S) {
S++;
}
Operating systems often distinguish between counting and binary semaphores.
The value of a counting semaphore can range over an unrestricted domain.
The value of a binary semaphore can range only between 0 and 1. Thus, binary semaphores
behave similarly to mutex locks.
Counting semaphores can be used to control access to a given resource consisting of a finite
number of instances.
In this case 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
When a process releases a resource, it performs a signal() operation.
When the count for the semaphore goes to 0, all resources are being used. After that,
processes that wish to use a resource will block until the count becomes greater than 0.
SEMAPHORE IMPLEMENTATION:
The main disadvantage of semaphore is that it requires busy waiting. When one process is in
its critical section any other process that tries to enter the critical section must loop
continuously in the entry code.
The mutual exclusion implementation with semaphores is given by
do
{
wait(mutex);
//critical section
signal(mutex);
//remainder section
}while(TRUE);
To overcome the need for busy waiting, we can modify the wait() and signal() operations.
When a process executes the wait() operation and finds that the semaphore value is not
positive, it must wait. However, rather than engaging in busy waiting, the process can block
itself.
The block operation places a process into a waiting queue associated with the semaphore.
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 restarted 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.
To implement semaphores under this definition, we define a semaphore as follows:
typedef struct {
int value;
struct process *list;
} semaphore
Each semaphore has an integer value and a list of processes list. When a process must wait
on a semaphore, it is added to the list of processes.
A signal() operation removes one process from the list of waiting processes and awakens that
process.
The wait() semaphore operation can be defined as
wait(semaphore *S) {
S->value--;
if (S->value < 0) {
add this process to S->list;
block();
}
}
The signal() semaphore operation can be defined as
signal(semaphore *S) {
S->value++;
if (S->value <= 0) {
remove a process P from S->list;
wakeup(P);
}
}
The Block() and wakeup() operations are provided by the operating system as system calls.
When a hungry philosopher has both her chopsticks at the same time, she eats without
releasing the chopsticks. When she is finished eating, she puts down both chopsticks.
One simple solution is to represent each chopstick with a semaphore.
A philosopher tries to grab a chopstick by executing a wait() operation on that semaphore.
She releases her chopsticks by executing the signal () operation on the appropriate
semaphores.
The shared data is semaphore chopstick [5]; where all the elements of the chopsticks are
initialized to 1.
Sometimes a process have to access shared memory or files, or doing other critical things that
can lead to races.
That part of the program which accesses the shared memory or file is called the critical
region or critical section.
Mutual exclusion is needed, where if one process is using a shared variable or file, the other
processes will be excluded from doing the same thing.
Achieving mutual exclusion is a major design issue in any operating system
We need four conditions to hold to have a good solution race conditions:
1. Any two processes can not be simultaneously inside their critical regions.
2. No assumptions may be made about speeds or the number of CPUs.
3. Any process running outside its critical region can not block other processes.
4. Any process should not have to wait forever to enter its critical region
Example:
At time T1 process A enters its critical region
At T2 process B attempts to enter its critical region but fails. Until T3 B is temporarily
suspended.
At time T3, B enters its critical region
At T4, B leaves its critical region
Monitors
Although semaphores provide a convenient and effective mechanism for process
synchronization, using them incorrectly can result in timing errors that are difficult to detect.
EXAMPLE: Suppose that a process interchanges the order in which the wait() and signal()
operations on the semaphore mutex are executed, resulting in the following execution:
signal(mutex);
...
critical section
...
wait(mutex);
In this situation, several processes may be executing in their critical sections simultaneously,
violating the mutual-exclusion requirement.
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. To deal with such errors one fundamental high-level
synchronization constructs called the monitor type is used.
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.
Thus, 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 monitors also provide mechanisms of synchronization by the condition construct. A
programmer who needs to write a tailor-made synchronization scheme can define one or
more variables of type condition:
Condition x, y;
The only operations that can be invoked on a condition variable are wait() and signal().
The operation x.wait();means that the process invoking this operation is suspended until
another process invokes x.signal();
The x.signal() operation resumes exactly one suspended process
Now suppose that, when the x.signal() operation is invoked by a process P, there exists a
suspended process associated with condition x.
Clearly, if the suspended process Q is allowed to resume its execution, the signaling process
P must wait. Otherwise, both P and Q would be active simultaneously within the monitor.
Note, however, that conceptually both processes can continue with their execution. Two
possibilities exist:
1. Signal and wait. P either waits until Q leaves the monitor or waits for another condition.
2. Signal and continue. Q either waits until P leaves the monitor or waits for another
condition.
2.3 Deadlock
System model
A process requests resources; if the resources are not available at that time, the process enters
a waiting state. Sometimes, a waiting process is never again able to change state, because the
resources it has requested are held by other waiting processes. This situation is called a
deadlock.
The resources of a computer system may be partitioned into several types such as CPU
cycles, files, and I/O devices (such as printers and DVD drives)
A process must request a resource before using it and must release the resource after using it.
A process may utilize a resource in only the following sequence.
o Request. The process requests the resource. If the request cannot be granted
immediately then the requesting process must wait until it can get the resource.
o Use. The process can operate on the resource
o Release. The process releases the resource.
Deadlock characterization
In a deadlock, processes never finish executing, and system resources are tied up, preventing
other jobs from starting.
NECESSARY CONDITIONS FOR DEADLOCKS:
A deadlock situation can arise if the following four conditions hold simultaneously in a
system:
o Mutual exclusion. At least one resource must be held in a non-sharable mode; that is,
only one process at a time can use the resource.
o 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.
o No preemption. Resources cannot be preempted; that is, a resource can be released
only voluntarily by the process holding it, after that process has completed its task.
o 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.
DEADLOCK PREVENTION:
Deadlock prevention provides a set of methods to ensure that at least one of the four
necessary conditions cannot hold.
These methods prevent deadlocks by constraining how requests for resources can be made.
MUTUAL EXCLUSION:
The mutual exclusion condition must hold. That is, at least one resource must be non-
sharable.
Sharable resources, in contrast, do not require mutually exclusive access and thus cannot be
involved in a deadlock.
Read-only files are a good example of a sharable resource. If several processes attempt to
open a read-only file at the same time, they can be granted simultaneous access to the file.
We cannot prevent deadlocks by denying the mutual-exclusion condition for the non-
sharable resources
HOLD AND WAIT:
Whenever a process requests a resource, it does not hold any other resources.
An alternative protocol allows a process to request resources only when it 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.
To illustrate the difference between these two protocols, we consider a process that copies
data from a DVD drive to a file on disk, sorts the file, and then prints the results to a printer.
If all resources must be requested at the beginning of the process, then the process must
initially request the DVD drive,
disk file, and printer. It will hold the printer for its entire execution, even though it needs the
printer only at the end.
The second method allows the process to request initially only the DVD drive and disk file. It
copies from the DVD drive to the disk and then releases both the DVD drive and the disk
file. The process must then request the disk file and the printer. After copying the disk file to
the printer, it releases these two resources and terminates.
Both these protocols have two main disadvantages.
o First, resource utilization may be low, since resources may be allocated but unused
for a long period.
o Second, starvation is possible
NO PREEMPTION:
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.
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
ones that it is requesting.
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 generally be applied to such resources as mutex locks and semaphores.
CIRCULAR WAIT:
One way to ensure that this 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.
Let R = {R1, R2, ..., Rm} be the set of resource types.
Assign to each resource type a unique integer number, which allows us to compare two
resources and to determine whether one precedes another in our ordering.
If the set of resource types R includes tape drives, disk drives, and printers, then the function
F might be defined as follows:
F(tape drive) = 1
F(disk drive) = 5
F(printer) = 12
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 —say, Ri .
After that, the process can request instances of resource type Rj if and only if 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.
Deadlock avoidance
Possible side effects of preventing deadlocks are low device utilization and reduced system
throughput.
Deadlock avoidance requires that the operating system be given additional information in
advance concerning which resources a process will request and use during its lifetime.
The simplest and most useful model requires that each process declare the maximum number
of resources of each type that it may need.
Given this a priori information, it is possible to construct an algorithm that ensures that the
system will never enter a deadlocked state.
A deadlock-avoidance algorithm dynamically examines the resource-allocation state to
ensure that a circular-wait condition can never exist.
SAFE STATE:
A state is safe if the system can allocate resources to each process in some order and still
avoid a deadlock.
A system is in a safe state only if there exists a safe sequence.
A safe state is not a deadlocked state. Conversely, a deadlocked state is an unsafe state.
Not all unsafe states are deadlocks; however an unsafe state may lead to a deadlock.
A sequence of processes <P1, P2, ..., Pn>is a safe sequence for the current allocation state if,
the resource requests that Pi make can be satisfied by the currently available resources plus
the resources held by all Pj, with j < i.
Example: consider a system with twelve magnetic tape drives and three processes: P0, P1,
and P2
Process P0 requires ten tape drives, process P1 may need as many as four tape drives, and
process P2 may need up to nine tape drives.
Suppose that, at time t0, process P0 is holding five tape drives, process P1 is holding two
tape drives, and process P2 is holding two tape drives
At time t0, the system is in a safe state. The sequence <P1, P0, P2>satisfies the safety
condition.
Process P1 can immediately be allocated all its tape drives and then return them (the system
will then have five available tape drives);
Then process P0 can get all its tape drives and return them (the system will then have ten
available tape drives); and finally process P2 can get all its tape drives and return them (the
system will then have all twelve tape drives available).
RESOURCE ALLOCATION GRAPH:
A new type of edge, called a claim edge is used in resource allocation graph.
A claim edge Pi → Rj indicates that process Pi may request resource Rj at some time in the
future.
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 claim
edge Pi ->Rj
.
Now suppose that process Pi requests resource Rj. The request can be granted only if
converting the request edge Pi → Rj to an assignment edge Rj → Pi does not result in the
formation of a cycle in the resource-allocation graph.
If no cycle exists, then the allocation of the resource will leave the system in a safe state. If a
cycle is found, then the allocation will put the system in an unsafe state.
Example for Unsafe state: To illustrate this algorithm, we consider the resource-allocation
graph above. Suppose that P2 requests R2. Although R2 is currently free, we cannot allocate
it to P2, since this action will create a cycle in the graph as shown below. A cycle, as
mentioned, indicates that the system is in an unsafe state. If P1 requests R2, and P2 requests
R1, then a deadlock will occur.
BANKERS ALGORITHM:
When a new process enters the system, it must declare the maximum number of instances of
each resource 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
process releases enough resources.
The following data structures are needed to implement bankers algorithm, where n is the
number of
processes in the system and m is the number of resource types:
• Available: Length m indicates the number of available resources of each type.
• Max. An n × m matrix defines the maximum demand of each process.
• Allocation. An n × m matrix defines the number of resources of each type currently
allocated.
• Need. An n × m matrix indicates the remaining resource need of each process.
1. SAFETY ALGORITHM:
It is an algorithm for finding out whether or not a system is in a safe state
1) LetWork and Finish be vectors of length m and n, respectively. Initialize Work = Available
and Finish[i] = false for i = 0, 1, ..., n − 1.
2) Find an index i such that both
a. Finish[i] == false
b. Needi ≤Work
If no such i exists, go to step 4
3) Work =Work + Allocationi
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 × n2 operations to determine whether a state is safe.
2. RESOURCE REQUEST ALGORITHM:
This is an algorithm for determining whether requests can be safely granted.
Let Requesti be the request vector for process Pi.
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 Requesti , and the old resource-
allocation state is restored.
Example:
Deadlock detection
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:
o An algorithm that examines the state of the system to determine whether a deadlock
has occurred
o An algorithm to recover from the deadlock
A Deadlock detection algorithm examines the state of the system to determine whether a
deadlock has occurred
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.
This graph is obtained from the resource-allocation graph by removing the resource nodes
and collapsing the appropriate edges.
A deadlock exists in the system if and only if the wait-for graph contains a cycle.
To detect deadlocks, the system needs to maintain the wait for graph and periodically invoke
an algorithm that searches for a cycle in the graph.
SEVERAL INSTANCE OF RESOURCE TYPE:
The wait-for graph scheme is not applicable to a resource-allocation system with multiple
instances of each resource type
This algorithm uses the following data structure,
• Available. A vector of length m indicates the number of available resources of each type.
• Allocation. An n × m matrix defines the number of resources currently allocated to each
process.
• Request. An n × m matrix indicates the current request of each process.
1. Let Work and Finish be vectors of length m and n, respectively. Initialize Work =
Available.
For i = 0, 1, ..., n–1, if Allocationi _= 0, then Finish[i] = false. Otherwise, Finish[i] =
true.
2. Find an index i such that both
a. Finish[i] == false
b. Requesti ≤Work
If no such i exists, go to step 4.
3. Work =Work + Allocationi
Finish[i] = true
Go to step 2.
4. If Finish[i] ==false for some i, 0≤i<n, then the system is in a deadlocked state.
Moreover, if Finish[i] == false, then process Pi is deadlocked.