Loading learning content...
In priority scheduling systems, a fundamental fairness problem emerges: starvation. A process starves when it remains indefinitely in the ready queue, perpetually denied CPU access because higher-priority processes continuously arrive. Unlike deadlock—where processes wait for each other—starvation involves waiting for a resource (CPU time) that is continuously given to others.
Starvation represents a failure of the scheduling system to provide basic fairness guarantees. While prioritization is necessary for system functionality, unchecked priority differences create pathological conditions where legitimate work never completes.
Understand the causes and manifestations of starvation, distinguish starvation from related problems like deadlock and livelock, analyze systems for starvation vulnerability, and prepare for the aging solution covered in the next page.
Starvation (also called indefinite blocking or indefinite postponement) occurs when a process is perpetually denied necessary resources to proceed. In CPU scheduling:
A process suffers starvation when it waits indefinitely in the ready queue while the scheduler continuously selects other processes for execution.
A scheduling algorithm exhibits starvation if there exists a process P such that:
lim(t→∞) [waiting_time(P, t)] = ∞
while P remains runnable (not blocked, not terminated).
| Problem | Definition | Detection | Resolution |
|---|---|---|---|
| Starvation | Process waits indefinitely for resource given to others | Waiting time analysis, priority monitoring | Aging, fair scheduling, priority ceiling |
| Deadlock | Circular wait where processes block each other | Wait-for graph cycle detection | Prevention, avoidance, detection+recovery |
| Livelock | Processes actively change state but make no progress | Progress monitoring, state repetition | Randomization, backoff strategies |
| Priority Inversion | High-priority waits for low-priority holding resource | Lock ownership analysis | Priority inheritance, priority ceiling |
In starvation, the system makes progress—other processes run and complete. Only specific processes are denied resources. In deadlock, no involved process can proceed. Starvation is a fairness failure; deadlock is a progress failure.
Starvation arises from the interaction between scheduling policy and workload characteristics. Understanding these causes is essential for prevention:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
"""Demonstration of starvation in priority scheduling.Low-priority process never runs due to continuous high-priority arrivals."""import random class Scheduler: def __init__(self): self.time = 0 self.ready_queue = [] # (priority, arrival, name, remaining) self.waiting_times = {} def arrive(self, name, priority, burst): self.ready_queue.append((priority, self.time, name, burst)) if name not in self.waiting_times: self.waiting_times[name] = 0 def tick(self): if not self.ready_queue: self.time += 1 return None # Sort by priority (highest first), then arrival self.ready_queue.sort(key=lambda x: (-x[0], x[1])) # Run highest priority for 1 unit pri, arr, name, remaining = self.ready_queue.pop(0) # Update waiting times for all OTHER processes for i, (p, a, n, r) in enumerate(self.ready_queue): self.waiting_times[n] = self.waiting_times.get(n, 0) + 1 self.time += 1 if remaining > 1: self.ready_queue.append((pri, arr, name, remaining - 1)) return name # Simulate: low-priority process vs continuous high-priority arrivalssched = Scheduler() # Low priority process arrives at t=0sched.arrive("LowPriority", priority=1, burst=5) # High priority processes arrive every 2 time unitsfor t in range(100): running = sched.tick() # New high-priority arrival every 2 ticks if t % 2 == 0 and t > 0: sched.arrive(f"High_{t}", priority=10, burst=2) if t % 20 == 0: print(f"t={t}: Running={running}, " f"LowPriority wait={sched.waiting_times.get('LowPriority', 0)}") print(f"\nFinal: LowPriority waiting time = {sched.waiting_times.get('LowPriority', 0)}")print("LowPriority is STARVING - it may never complete!")Starvation manifests in various scenarios across modern computing environments:
| Scenario | Victim | Cause | Symptom |
|---|---|---|---|
| Busy web server | Background log rotation | Continuous request handling at higher priority | Logs grow unbounded, disk fills |
| Desktop system | System updates | User interactive apps always prioritized | Security patches never install |
| Database server | Index maintenance | Query processing prioritized over maintenance | Query performance degrades over time |
| Real-time system | Diagnostic tasks | Control loops consume all high-priority slots | System health unknown |
| Cloud scheduler | Low-bid batch jobs | Premium customers always served first | Batch jobs exceed SLA |
The 1997 Mars Pathfinder mission experienced repeated system resets due to priority inversion—a related problem where a high-priority task waited for a low-priority task holding a mutex. While not pure starvation, it illustrates how priority-based scheduling failures can have severe real-world consequences.
Unlike deadlock, starvation has no definitive detection algorithm—a process waiting a long time might run eventually. Detection relies on heuristics and monitoring:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
/* Simple starvation detection monitor */#include <stdio.h>#include <time.h> #define MAX_PROCESSES 100#define STARVATION_THRESHOLD_MS 30000 /* 30 seconds */ typedef struct { int pid; int priority; time_t last_scheduled; unsigned long wait_time_ms; int is_runnable;} ProcessInfo; ProcessInfo processes[MAX_PROCESSES];int process_count = 0; void check_starvation(void) { time_t now = time(NULL); for (int i = 0; i < process_count; i++) { if (!processes[i].is_runnable) continue; unsigned long wait = (now - processes[i].last_scheduled) * 1000; if (wait > STARVATION_THRESHOLD_MS) { printf("WARNING: PID %d (priority %d) potential starvation!\n" " Wait time: %lu ms (threshold: %d ms)\n", processes[i].pid, processes[i].priority, wait, STARVATION_THRESHOLD_MS); /* Could trigger: priority boost, alert, or logging */ } }} void on_process_scheduled(int pid) { for (int i = 0; i < process_count; i++) { if (processes[i].pid == pid) { processes[i].last_scheduled = time(NULL); break; } }}Starvation's effects extend beyond the starving process itself, creating cascading failures:
Starvation often goes unnoticed until catastrophic failure. A batch job that 'can wait' accumulates for days until disk fills. A maintenance task deferred 'just this once' compounds until database corruption. Design systems assuming starvation WILL occur without explicit prevention.
Before examining specific solutions (aging, in the next page), consider the general principles for starvation prevention:
The most widely implemented solution is aging—gradually increasing the priority of waiting processes. The next page explores aging mechanisms in detail.
The next page examines aging—the primary mechanism for preventing starvation by gradually elevating waiting process priorities, ensuring bounded waiting times.