5.5.9 · D5Embedded Systems & Real-Time Software

Question bank — RTOS concepts — task, scheduler, preemption, context switch

2,048 words9 min readBack to topic

Picture 1 — what the CPU saves on a switch

Figure — RTOS concepts — task, scheduler, preemption, context switch
Recall What each acronym means

r0–r3, r12 ::: general-purpose scratch registers holding intermediate values of the current computation. LR (Link Register) ::: the return address — where execution jumps back to when the current function finishes. PC (Program Counter) ::: the address of the next instruction to run — "where am I in the code." xPSR (Program Status Register) ::: the condition flags (zero, carry, negative) that record the outcome of recent operations. r4–r11 ::: more general registers the compiler uses for longer-lived locals; hardware does not auto-save these, so PendSV must. SP (Stack Pointer) ::: points to the top of this task's private stack; stored in the TCB as the handle to the whole frame.


Picture 2 — preemption on a timeline

Figure — RTOS concepts — task, scheduler, preemption, context switch

Picture 3 — priority inversion and its fix

Figure — RTOS concepts — task, scheduler, preemption, context switch

True or false — justify

More tasks means more parallel speed on a single-core MCU
False. One core runs exactly one instruction stream; extra tasks add switching overhead and stack RAM. They buy responsiveness and structure, not throughput. See Bare-metal super-loop vs RTOS.
A higher tick frequency makes a system "more real-time"
False. Event-driven preemption already fires instantly via interrupts; the tick only paces time-based delays. Raising it just burns CPU as .
A context switch only needs to save the program counter
False. The PC alone is useless without the general registers, status flags, and stack pointer — those hold the task's live data (see Picture 1). Lose them and the task resumes on garbage.
Two equal-priority tasks can preempt each other
False. Preemption is strictly priority-driven. Equal-priority tasks share the CPU by round-robin time-slicing at tick boundaries, not by preempting.
A Blocked task still consumes CPU cycles while it waits
False. Blocked tasks are ineligible to run, so the scheduler simply skips them — that is exactly why blocking (not busy-waiting) frees the CPU for others.
In fixed-priority preemptive scheduling, the highest-priority Ready task always owns the CPU
True. That is the defining rule. The only caveats are while an ISR runs or during the brief context-switch window itself.
Preemption requires the running task to cooperate or yield
False. That describes cooperative scheduling. Preemption forcibly pauses the running task the instant a higher-priority task becomes Ready — no cooperation needed (see Picture 2).
On Cortex-M, hardware auto-saves the entire register set on exception entry
False. Hardware auto-pushes only r0–r3, r12, LR, PC, xPSR (8 registers); the software PendSV handler must still push r4–r11 to capture the full state — exactly the split shown in Picture 1.
The scheduler runs only on the periodic tick interrupt
False. It also runs after any event that may unblock a higher-priority task (e.g. a semaphore give from an ISR), so preemption is immediate, not tick-delayed. See Semaphores and Queues.

Spot the error

while(!data_ready); inside a task, waiting 5 ms for UART data
The task stays Running and hogs the CPU for 5 ms, starving lower-priority work. Fix: use a blocking call like xQueueReceive so the task enters Blocked and the scheduler runs others.
"To meet a 1 ms deadline, I'll raise the tick to 100 kHz."
Confuses time-slicing with event response. The deadline is met by priority + preemption via interrupts, not tick rate. A 100 kHz tick with a 2 µs switch wastes 20% CPU for nothing.
"Give the WiFi stack and the motor loop the same high priority so both are fast."
Equal priority means they round-robin and can delay each other by a full time slice. The hard-real-time motor loop must be strictly higher so it always preempts the WiFi work.
"Saving only the PC to the TCB is enough for a context switch."
The TCB stores the saved stack pointer, which is the handle to the whole register frame on that task's stack — not just the PC. See Stack memory and TCB.
"PendSV should have the highest interrupt priority so switches happen fast."
Backwards. PendSV is set to the lowest priority so the switch happens only after all real ISRs finish, avoiding nested-switch chaos. See Interrupts and ISR latency.
"A low-priority task can safely block a high-priority task if they never share resources."
True only if they truly share nothing. The moment they share a mutex, a low task holding it can block a high task — that is priority inversion (Picture 3). See Priority Inversion and Mutexes.
"I use a plain mutex, so a medium task can never delay my high task."
Wrong without priority inheritance: a medium task can run ahead of the low lock-holder, indefinitely stalling the high task. A priority-inheritance or priority-ceiling mutex prevents this. See Priority Inversion and Mutexes.

Why questions

Why must every task have its own separate stack?
Each task saves its return addresses, local variables, and register frame on its stack; sharing one stack would let tasks overwrite each other's live state on every switch.
Why does the scheduler pick by priority and readiness, not just priority?
The highest-priority task might be Blocked (waiting on an event). Running it would be impossible, so the scheduler picks the highest-priority task that is actually Ready.
Why is busy-waiting called "stealing" the CPU while blocking is "releasing" it?
Busy-waiting keeps the task Running, so the scheduler can't hand the CPU to anyone; blocking flips the task to Blocked, freeing the CPU for other Ready tasks immediately.
Why does context-switch overhead scale with tick frequency?
In one second there are ticks; if each worst-case tick forces a switch costing , the wasted fraction is — linear in tick rate.
Why can the highest-priority task's response time be bounded by just ?
Because nothing of higher priority exists to delay it — only finishing the triggering ISR () and one context switch () stand between the event and its first instruction. Lower tasks add blocking terms (see Rate Monotonic Scheduling).
Why does preemption help meet deadlines that cooperative scheduling would miss?
Cooperative scheduling waits for the running task to yield, which may be far past the deadline; preemption pauses it the instant the urgent task is Ready, so latency doesn't depend on the other task's code.
Why is "one core runs one instruction stream" the key fact behind most RTOS misconceptions?
It kills the illusion that tasks run truly in parallel — concurrency is faked by fast switching, so extra tasks add overhead rather than speed. True parallelism needs multiple cores.
Why does priority inheritance solve inversion but a plain mutex does not?
Inheritance temporarily raises the low lock-holder to the waiting high task's priority, so no medium task can jump ahead; a plain mutex leaves the holder at its low priority, exposed to medium-task interference. See Priority Inversion and Mutexes.

Edge cases

What happens if every task is Blocked at once?
No task is Ready, so the scheduler runs the idle task — a lowest-priority do-nothing task that keeps the CPU alive (often sleeping for power) until an event unblocks someone.
What is "tickless idle" and how does it change the no-task case?
When all tasks are Blocked, the kernel stops the periodic tick entirely and sleeps the MCU deep, waking on the next timed event instead of every millisecond. This slashes idle power and removes needless tick-driven switches, so effectively drops to zero while idle.
Does tickless idle change how context-switch overhead is counted?
Yes — the "one switch per tick" worst case only applies while tasks are runnable. During tickless sleep no ticks fire, so no switches occur; overhead is paid only when real work resumes.
What happens if two tasks of equal, highest priority are both Ready?
Neither can preempt the other; they share the CPU by round-robin time-slicing, each running for one tick before the scheduler rotates to the next.
Is a context switch even needed when the same task keeps running after a tick?
No. If the highest-priority Ready task is still the current one, the scheduler re-selects it and skips the register save/restore — no switch cost is paid.
What if a high-priority task becomes Ready while an ISR is still running?
The switch is deferred until the ISR (and any pending higher-priority ISRs) finish — that is why PendSV sits at the lowest interrupt priority. The task then preempts as soon as the CPU returns to thread mode.
If shrinks toward zero, does raising the tick become free?
In the limit , so switch cost vanishes — but the tick ISR itself still costs cycles and cache disruption, so it is never truly free.
A task voluntarily yields but is the only Ready task — what runs next?
Itself. With no other Ready task of equal-or-higher priority, the scheduler re-selects the same task, so a yield with no contender is effectively a no-op.
With priority inheritance active, what happens the moment the low task releases the mutex?
Its temporary priority boost is dropped back to its original level and the waiting high task immediately acquires the lock and preempts — the inversion window closes. See Priority Inversion and Mutexes.