Learning outcomes
- Describe deadlock conditions and compare deadlock prevention, avoidance, detection and recovery techniques.
Understand how operating systems avoid deadlocks using safe-state checking, how they detect deadlocks after they occur, and how they recover using termination or resource preemption.
Once deadlock has been characterized and its necessary conditions are understood, the next question is practical: what should the operating system do about it? There are three major approaches:
This lecture focuses on the last two: avoidance and detection with recovery.
Deadlock avoidance is less rigid than prevention. It does not stop resource sharing completely. Instead, it checks each resource request carefully and grants it only if the system will remain in a safe state.
A system is in a safe state if there exists at least one safe sequence of all processes such that each process can obtain its remaining resources, finish, and release its resources for the next process.
So the key idea is: avoidance refuses requests that would move the system from safe to unsafe.
If each resource type has only one instance, the Resource Allocation Graph can be extended using a claim edge. A claim edge shows that a process may request that resource in the future.
When a request is made, the operating system checks whether granting the request would create a cycle. If granting it creates a cycle, the request is delayed.
For systems with multiple instances of each resource type, the classical avoidance method is the Banker’s Algorithm. The OS behaves like a cautious banker: it grants a request only if the resulting state remains safe.
| Structure | Meaning |
|---|---|
Available[m] |
Number of available instances of each resource type |
Max[n][m] |
Maximum demand of each process |
Allocation[n][m] |
Resources of each type currently allocated to each process |
Need[n][m] |
Remaining resource need of each process |
The basic formula is:
Need = Max - Allocation
The safety algorithm checks whether the current state is safe.
Work = AvailableFinish[i] = false for all processesPi such that:
Finish[i] == falseNeed[i] <= WorkWork = Work + Allocation[i]
Finish[i] = true
If all Finish[i] values become true, the system is safe.
Otherwise, the state is unsafe.
When process Pi requests Request[i], the OS performs these checks:
Request[i] <= Need[i], continue. Otherwise, the process has exceeded its maximum claim.Request[i] <= Available, continue. Otherwise, the process must wait.Available = Available - Request[i]
Allocation[i] = Allocation[i] + Request[i]
Need[i] = Need[i] - Request[i]
In deadlock detection, the operating system does not try to prevent or avoid deadlocks during normal execution. It simply allows the system to operate, then checks periodically whether a deadlock has occurred.
This approach is useful when:
For single-instance resources, a Wait-For Graph is used.
Pi → Pj means Pi is waiting for a resource held by Pj.Rule: If the wait-for graph contains a cycle, then a deadlock exists.
For multiple instances, the OS uses a matrix-based detection algorithm. The main data structures are:
AvailableAllocationRequestDetection steps:
Work = AvailableAllocation[i] == 0, set Finish[i] = true, else falsePi such that:
Finish[i] == falseRequest[i] <= WorkWork = Work + Allocation[i]
Finish[i] = true
Finish[i] values remain false, those processes are deadlocked.Detection may be run:
Frequent detection finds deadlocks earlier but adds overhead. Infrequent detection reduces overhead but may allow the deadlock to affect more processes.
Once deadlock is detected, the system must recover. The two standard recovery strategies are:
There are two main approaches:
When aborting one process at a time, the OS must choose a victim carefully. Victim-selection factors include:
Instead of killing processes, the OS may take resources away from one process and give them to another. This sounds elegant, but it is not easy in practice.
Three main issues arise:
Resource preemption is difficult because many resources are not safely preemptable. Taking away a printer or an in-progress I/O device is not the same as taking away CPU time.
| Method | Main Idea | Deadlock Possible? | Overhead | Resource Utilization |
|---|---|---|---|---|
| Prevention | Break one necessary condition | No | Medium | Often poor |
| Avoidance | Grant requests only if safe | No | High | Better than prevention |
| Detection | Allow deadlocks, then find them | Yes, then recover | Variable | Usually higher |
| Ignore | Do nothing special | Yes | Very low | High, but risky |
Consider a system with five processes P0 to P4 and three resource types A, B, C. Let the currently available resources be:
Available = [3, 3, 2]
Let the current allocation and maximum need be:
| Process | Allocation (A B C) | Max (A B C) |
|---|---|---|
| P0 | 0 1 0 | 7 5 3 |
| P1 | 2 0 0 | 3 2 2 |
| P2 | 3 0 2 | 9 0 2 |
| P3 | 2 1 1 | 2 2 2 |
| P4 | 0 0 2 | 4 3 3 |
First compute:
Need = Max - Allocation
| Process | Need (A B C) |
|---|---|
| P0 | 7 4 3 |
| P1 | 1 2 2 |
| P2 | 6 0 0 |
| P3 | 0 1 1 |
| P4 | 4 3 1 |
Work = [3, 3, 2]
So pretend P1 finishes and releases its allocation:
Work = Work + Allocation[P1]
Work = [3,3,2] + [2,0,0] = [5,3,2]
Work = [5,3,2] + [2,1,1] = [7,4,3]
Work = [7,4,3] + [0,0,2] = [7,4,5]
Work = [7,4,5] + [0,1,0] = [7,5,5]
Work = [7,5,5] + [3,0,2] = [10,5,7]
<P1, P3, P4, P0, P2>
Conclusion: Because all processes can finish in some order, the system is in a safe state. Therefore, the current allocation is acceptable.