Assignment 05: Deadlock Management
I. Objective & Theoretical Framework
In a multiprogramming environment, several processes may compete for a finite number of resources. If a process requests resources that are currently unavailable, it enters a waiting state. A deadlock occurs when a waiting process can never again change state because the resources it has requested are held by other waiting processes.
This laboratory focuses on Deadlock Avoidance using the Banker’s Algorithm. This approach requires the operating system to be given advanced information concerning which resources a process will request and use during its lifetime. The system uses this knowledge to evaluate whether granting a request will leave the system in a “Safe State.”
The Banker’s Algorithm is specifically applicable to a system with multiple instances of each resource type.
II. Prerequisite Knowledge & Resources
- Vector and Matrix Arithmetic: The Banker’s Algorithm relies heavily on 1D arrays (Available resources) and 2D arrays/structures (Maximum, Allocation, and Need matrices).
- System State Definitions:
- Safe State: An execution sequence exists where all processes can finish executing without deadlocking.
- Unsafe State: A state that may lead to a deadlock.
- Formulas: You must dynamically calculate the Need matrix using the relation: $Need_{i,j} = Max_{i,j} - Allocation_{i,j}$
III. Starter Code & Partial Implementations
To manage the matrices cleanly, it is highly recommended to use an array of structures. The following starter code provides the data structure and the initial logic to compute the need matrix.
#include <stdio.h>
// Structure to hold resource data for a single process
struct process_node {
int all[10]; // Allocation
int max[10]; // Maximum demand
int need[10]; // Remaining need
int flag; // Visited/Finished status
};
int main() {
struct process_node p[10];
int avail[10], seq[10];
int n, r, i, j;
printf("Enter number of processes: ");
scanf("%d", &n);
printf("Enter number of resource types: ");
scanf("%d", &r);
// [Omitted: Code to scan Allocation and Max matrices from the user]
// Calculate the Need Matrix
for(i = 0; i < n; i++) {
for(j = 0; j < r; j++) {
p[i].need[j] = p[i].max[j] - p[i].all[j];
// Safety catch for invalid inputs
if(p[i].need[j] < 0) {
p[i].need[j] = 0;
}
}
p[i].flag = 0; // Initialize as unvisited
}
// [Insert Safety Algorithm Logic Here]
return 0;
}
IV. Step-by-Step Task List
Matrix Initialization: Complete the starter code by writing the
forloops to accept user input for the Allocation and Maximum matrices, as well as the initial Available resources array.The Safety Algorithm: Implement the core Banker’s logic. Iterate through all unvisited processes (
flag == 0). For each process, check if itsneedfor every resource type is $\le$ theavailresources.Simulate Execution: If a process can be executed, mark it as visited (
flag = 1), add it to your Safe Sequence array (seq), and release its allocated resources back into theavailpool ($Available = Available + Allocation$).State Output: Loop this process until all processes are visited (System is in a Safe State) or until you complete a full loop without being able to execute any process (System is in an Unsafe State). Print the Safe Sequence if one exists.
V. Common Pitfalls & Debugging Strategies
Infinite Loops: When searching for a safe process, if the system is in an unsafe state, your
whileloop might run forever. You must include a counter or a boolean trigger (e.g.,int g = 0;that turns to1if a process executes). If a full pass finishes and $g == 0$, you mustbreakthe loop and declare a deadlock.Element-wise Comparison: Do not try to compare arrays directly in C. You must iterate through every resource type $j$ for process $i$ to verify that $Need_{i,j} \le Available_j$ for all $j$. A single requested resource exceeding availability means the process must wait.
VI. Real-World Case Study
The principles of deadlock avoidance are foundational to distributed systems. In a cluster computing environment or a distributed database, multiple nodes often request locks on shared data tables concurrently. If these locks are not mathematically vetted for safety before being granted, a circular wait occurs, freezing the database. While modern operating systems rarely use the Banker’s algorithm for general processes due to the overhead of knowing “Max Demand” in advance, specialized distributed transaction managers and embedded avionics systems still rely heavily on graph-based avoidance algorithms derived directly from these concepts.
VII. Advanced Variant Tasks
Dynamic Resource Request Algorithm: Extend your program to handle dynamic requests. Prompt the user: “Enter New Request Details” (including PID and the requested resource vector).
First, check if the request exceeds the process’s declared Maximum.
Second, check if the request exceeds current Availability.
If both checks pass, pretend to allocate the resources, run the Safety Algorithm, and if the resulting state is safe, commit the allocation. If unsafe, rollback the allocation and deny the request.
Distributed Deadlock Detection Engine: Extend your Banker’s Algorithm simulation to act as a cluster monitor. Instead of static input, configure your program to accept a continuous stream of lock requests and releases from simulated remote nodes. Build a Resource Allocation Graph (RAG) in the background and write a cycle-detection algorithm to output a real-time alert:
DEADLOCK DETECTED: Nodes [X, Y, Z] involved in circular wait.
VIII. Resources & Further Reading
- OSTEP - Concurrency Bugs: Read Chapter 32: Common Concurrency Problems (PDF) for a breakdown of the conditions required for a deadlock (Circular Wait, Hold-and-Wait, No Preemption, Mutual Exclusion).
- Edsger Dijkstra’s Original Paper: For a historical deep dive, search for Dijkstra’s original 1965 manuscript (EWD108) introducing the Banker’s Algorithm. It remains a masterclass in algorithmic design for distributed environments.