RTOS Concepts
What a real-time operating system actually adds over a bare-metal loop: tasks, schedulers, and the deterministic timing guarantees that come with them.
Polling, interrupts, and DMA handle one timing demand at a time well. Real products usually have several at once — blink an LED every second, sample a sensor every 10 ms, respond to a button within 50 ms — and a single bare-metal loop handling all of them starts to get fragile. That's the problem an RTOS solves.
Bare-metal loops vs. tasks
A bare-metal "super loop" runs one function after another, forever, in a fixed sequence. Every function has to finish quickly and cooperate, because nothing else runs until it returns. An RTOS instead lets you write each responsibility as an independent task — its own function, with its own stack — and the RTOS decides which task runs when.
void sensor_task(void *params) {
for (;;) {
read_sensor();
vTaskDelay(pdMS_TO_TICKS(10)); // yields the CPU for 10 ms
}
}
void blink_task(void *params) {
for (;;) {
led_toggle();
vTaskDelay(pdMS_TO_TICKS(1000));
}
}Each task behaves as if it has the CPU to itself; the RTOS is responsible for making that illusion hold up.
The scheduler
The scheduler decides which ready task actually runs at any given moment. Two broad strategies:
- Cooperative: a task runs until it voluntarily yields. Simple, but one badly behaved task can starve everything else.
- Preemptive: the scheduler can interrupt a running task — typically the moment a higher-priority task becomes ready — and switch to it immediately. This is what makes real-time guarantees possible: a critical task doesn't have to wait for a lower-priority one to finish.
Most embedded RTOSes (FreeRTOS, Zephyr, ThreadX) use preemptive, priority-based scheduling by default.
Context switching
Switching from one task to another means saving the running task's registers and stack pointer somewhere, then restoring the next task's. This context switch happens on every timer tick (for time-sliced tasks of equal priority) and on every event that makes a higher-priority task ready. It's fast — typically microseconds — but not free, which is part of why an RTOS adds some overhead compared to a bare-metal loop.
When an RTOS earns its overhead
An RTOS is worth the added complexity once a product has multiple independent timing domains that are awkward to interleave by hand, needs to react to events with bounded latency regardless of what else is happening, or benefits from isolating subsystems (networking, sensor fusion, UI) into separable tasks. A single-purpose blinking LED firmware almost never needs one; a sensor-fusion flight controller juggling IMU sampling, telemetry, and control loops almost always does.
The next sub-lesson, Task Scheduling & Priorities, covers how priority is actually assigned and what can go wrong when it's assigned carelessly.