Task Scheduling & Priorities
How priority-based preemptive scheduling decides which task runs next, what priority inversion is, and how rate-monotonic analysis tells you whether a task set can meet its deadlines.
RTOS concepts introduced preemptive, priority-based scheduling: the highest-priority ready task always runs. The two things that go wrong with this in practice are priority inversion and simply assigning more work than the deadlines allow.
Priority inversion
Priority inversion happens when a high-priority task is blocked waiting on a resource held by a low-priority task — and a medium-priority task, unrelated to either, keeps preempting the low-priority task, indirectly blocking the high-priority one indefinitely.
Low-priority task: locks mutex M, then gets preempted
Medium-priority task: runs (doesn't need M), keeps preempting the low task
High-priority task: blocked waiting for M — but M's owner never gets to runThe fix most RTOSes implement is priority inheritance: while a low-priority task holds a mutex a higher-priority task is waiting on, the low-priority task is temporarily boosted to that higher priority — just long enough to finish and release the mutex, restoring forward progress for everyone waiting.
Rate-monotonic scheduling
For periodic tasks (sample a sensor every 10 ms, run a control loop every 5 ms), a common and analyzable policy is rate-monotonic scheduling: assign higher priority to tasks with shorter periods. It's provably optimal among fixed-priority policies — if any fixed-priority assignment can meet all deadlines, rate-monotonic can too.
Whether a given task set is schedulable at all can be checked with a utilization bound:
U = Σ (C_i / T_i) for each task i, where C_i = worst-case execution time, T_i = period
U ≤ n × (2^(1/n) − 1) sufficient condition for n tasksWorked example: two tasks, one needing 2 ms of CPU time every 10 ms and another needing 3 ms every 20 ms:
U = (2/10) + (3/20) = 0.2 + 0.15 = 0.35
bound for n=2: 2 × (2^0.5 − 1) ≈ 0.83
0.35 ≤ 0.83 → schedulableIf utilization exceeds the bound, the task set might still happen to work, but it isn't guaranteed — exactly the kind of risk you don't want to discover in the field.
Why this matters in practice
Priority assignment isn't a tuning knob to fix later — get it wrong and a task can miss every deadline even though the CPU is mostly idle, simply because of how priorities interact. Treating scheduling as something to reason about up front, rather than something to debug under pressure, is the difference between firmware that's reliable and firmware that's "mostly fine."
The next sub-lesson, Interrupt-Safe Design, covers the other half of real-time correctness: making sure data shared between interrupt handlers and tasks stays consistent.