Priority Scheduling — Mars Pathfinder Inversion
Mars Pathfinder's priority inversion caused random resets and data loss.
- Priority scheduling selects the highest-priority ready process to run.
- Starvation occurs when low-priority processes never get CPU – solved by aging.
- Aging gradually increases waiting processes' priority – guarantees eventual execution.
- Linux nice values range -20 (highest) to +19 (lowest) – lower numeric means higher priority.
- Production insight: priority inversion can cause real-time tasks to miss deadlines if a high-priority task waits on a low-priority lock.
- Biggest mistake: confusing numeric priority direction – always verify with
ps -eo pid,pri,nice.
Priority scheduling is like a hospital triage system — critical patients are seen first regardless of arrival order. The danger: low-priority patients (processes) might never be seen if high-priority ones keep arriving. Aging solves this by gradually increasing the priority of waiting processes — the longer you wait, the more important you become.
Priority scheduling is what your OS uses right now. Linux's nice values (-20 to +19), Windows' priority classes (0-31), Java's Thread.setPriority() — all are implementations of priority scheduling. The starvation problem is real: in early Unix systems before aging was implemented, background processes at low priority could wait hours or days before getting CPU time if the system was heavily loaded.
The solution — aging — is an example of a general algorithm design principle: make greedy choices less greedy over time by adjusting inputs. A low-priority process that has waited long enough becomes a high-priority process. The invariant being maintained is that no process waits forever, even if it regularly loses to higher-priority work.
Priority Scheduling Implementation
The non-preemptive priority scheduler implemented here picks the highest-priority ready process (lower numeric value = higher priority). It uses a min-heap to manage ready processes efficiently. Processes are sorted by arrival time first; then at each tick, all processes that have arrived are pushed onto the heap. The scheduler always runs the process with smallest priority number. This is a classic batch-oriented approach – suitable when tasks are short and infrequent, but dangerous in interactive systems where unpredictability can cause missed deadlines.
Starvation and Aging
Starvation occurs when low-priority processes wait indefinitely because high-priority ones keep arriving.
Aging solution: increase the priority of a process by 1 every T time units it waits. Eventually every process becomes highest priority and executes. The choice of T is critical – too large and starvation persists, too small and the priority system loses its intended differentiation. A common starting point: T = 5 * average burst time of all processes.
Multilevel Queue Scheduling
Modern OSes use multilevel queues – separate queues for different process types (real-time, interactive, batch), each with its own scheduling algorithm. Processes are permanently assigned to a queue based on their type. Multilevel Feedback Queue (MLFQ) adds mobility: processes move between queues based on behaviour – CPU-bound processes drift to lower priority, I/O-bound stick to higher priority. This is what Linux and Windows implement underneath (with dynamic priority boosts for interactive tasks).
MLFQ prevents starvation by periodically boosting all processes to the top queue (every ~200ms in Linux). This ensures every process gets a time slice eventually.
ps -eo pid,pri,nice,cls to identify unexpected demotions.nice and priority levels to confirm correct scheduling class.Preemptive vs Non-Preemptive Priority Scheduling
In non-preemptive priority scheduling, once a process starts running, it continues until it blocks or finishes – even if a higher-priority process arrives in the meantime. This can cause high-priority tasks to miss deadlines. In preemptive priority scheduling, the running process is immediately interrupted when a higher-priority process becomes ready. Preemptive ensures that the highest-priority ready process always runs, but it adds context-switch overhead and requires careful synchronization for shared resources.
Real-time systems (e.g., VxWorks, RT-Linux) use preemptive priority scheduling. General-purpose OSes use a hybrid: preemptive with time quanta, where priority determines the next process but CPU-bound tasks eventually get demoted.
Priority Inversion – The Mars Pathfinder Bug
In 1997, the Mars Pathfinder rover, running VxWorks, experienced repeated system resets. The root cause was priority inversion: a high-priority data bus task blocked waiting for a mutex held by a low-priority meteorological task. A medium-priority communications task ran continuously, preventing the low-priority task from releasing the mutex. The high-priority task starved, causing a hardware watchdog to reset the system.
The fix was to enable priority inheritance on the mutex: when a high-priority task blocks on a lock, the lock-holding low-priority task temporarily inherits the higher priority, allowing it to run and release the lock quickly. This is now standard practice in real-time operating systems.
- Without inheritance: low-priority holder stays low, gets preempted by medium tasks → high-priority starves.
- With inheritance: low-priority holder temporarily runs at high priority → releases lock fast → returns to original priority.
- This is a simple, local fix that prevents cascading failures in real-time systems.
Mars Pathfinder – Priority Inversion That Crashed a Spacecraft
- Priority inheritance should be enabled on all mutexes shared between tasks of different priorities.
- Always test with worst-case priority scenarios, even if they seem unlikely.
- Use aging or priority donation to prevent indefinite blocking in priority-based systems.
ps -eo pid,pri,nice,pcpu,cmd --sort=pri and look for processes with low nice values (high numbers) consuming no CPU.strace -p <pid> to see blocked syscalls./proc/lock_stat or enable priority inheritance in the scheduler (requires kernel support).renice -n <value> -p <pid> or add aging mechanism in scheduler.Key takeaways
Common mistakes to avoid
3 patternsAssuming higher numeric priority means higher scheduling priority
ps -eo pid,pri,nice.Not implementing aging in priority scheduling
Confusing preemptive and non-preemptive priority scheduling
Interview Questions on This Topic
What is starvation in priority scheduling and how does aging solve it?
Frequently Asked Questions
That's Scheduling. Mark it forged?
3 min read · try the examples if you haven't