>
section 9 of 185 min read

9. Real-Time Operating Systems

When does a project deserve an RTOS? Roughly: more than ~5 concurrent activities with independent timing, more than ~32 KB of code, or any need for priority preemption. Below that, super-loop or foreground/background is simpler.

9.1 What an RTOS gives you

  • Tasks (or threads): independent units of execution, each with stack, priority, state.
  • Scheduler that decides who runs.
  • Synchronization primitives: semaphores, mutexes, queues, event flags.
  • Time services: software timers, sleep, wait-for-event-with-timeout.
  • Memory pools (sometimes), software watchdogs, tickless idle.

It does not give you process isolation (no MMU on Cortex-M baseline; with MPU you can do partial isolation). All tasks share the address space, so a bad pointer in one task corrupts another's stack.

9.2 Restaurant kitchen analogy

Picture a restaurant kitchen at peak hour. Tasks are dishes being prepared; cooks are CPU cores; the head chef is the scheduler. A high-priority dish (a VIP order) preempts the medium one. Cooks share resources: one fryer, one grill. Mutexes are "I have the fryer right now." Semaphores are "the dishwasher signaled that clean plates are ready, you can plate up." Queues are the conveyor between prep and grill. Priority inversion is when a sous-chef holds the only chef's knife while the head chef waits, and a low-priority side-dish prep keeps blocking the sous-chef from finishing.

That picture covers RTOS scheduling, mutexes, queues, ISR-as-doorbell-from-the-front-of-house, and priority inversion in one image.

9.3 Scheduling policies

  • Preemptive priority-based. Highest-priority ready task runs. The default in FreeRTOS, Zephyr, ChibiOS. Common pitfall: starvation if a high-priority task never blocks. Discipline: every task must vTaskDelay, wait on a queue, or otherwise release the CPU.
  • Round-robin. Tasks of equal priority share the CPU in time slices. Default tick is 1-10 ms.
  • Cooperative. A task runs until it explicitly yields. Simple, deterministic, but a buggy task can starve everyone.
  • Rate-monotonic (RM). Static priorities assigned by period: shortest period gets highest priority. Schedulable iff CPU utilization n(21/n1)\le n(2^{1/n} - 1), ~69 % asymptotically.
  • Earliest deadline first (EDF). Dynamic priorities by deadline. Optimal (100 % utilizable) but rare in real systems because it is hard to verify and most kernels do not implement it.

9.4 Context switching

The scheduler runs (often on the SysTick or a hardware timer ISR). It saves the current task's registers and stack pointer to its TCB (Task Control Block), picks the next task, restores its registers, returns from interrupt into the new task. On Cortex-M, the trick uses PendSV (a low-priority software interrupt) so the switch happens atomically after any pending higher-priority work.

Context switch cost is typically 1-3 microseconds on a Cortex-M4 at 168 MHz. Multiplied by tick rate, this caps total tasks; a 1 ms tick with 0.5 us switch costs 0.05 % CPU.

9.5 Inter-task communication

  • Queues. A FIFO of fixed-size messages. Thread-safe, ISR-safe (with *FromISR variants). The standard pattern: ISR pushes, task pops.
  • Semaphores. Counting (resource pool) or binary (signal/wait). Often used as ISR-to-task notification: ISR give, task take.
  • Mutexes. Like binary semaphores but with priority inheritance: if a low-priority task holds a mutex and a high-priority task wants it, the holder is temporarily boosted to the waiter's priority. Prevents priority inversion. Use mutex (not binary semaphore) for protecting shared data.
  • Event flags / event groups. Wait for any/all of a bitmap. "Wake when (sensor_ready AND comm_ready) OR error."
  • Direct task notifications. A FreeRTOS-specific 32-bit value attached to each TCB; faster than queues for one-to-one signaling.
c
// Producer task
void sensor_task(void *pv) {
    while (1) {
        sample_t s = read_sensor();
        xQueueSend(q_samples, &s, portMAX_DELAY);
        vTaskDelay(pdMS_TO_TICKS(10));
    }
}
 
// Consumer task
void log_task(void *pv) {
    sample_t s;
    while (1) {
        xQueueReceive(q_samples, &s, portMAX_DELAY);
        log_sample(&s);
    }
}

9.6 Priority inversion, in slow motion

Three tasks: H (high), M (medium), L (low). H and L share a resource via mutex M_lock.

  1. L runs, grabs M_lock.
  2. H wakes (high priority), preempts L. Tries to take M_lock. Blocks because L has it.
  3. M wakes. L is ready but lower priority than M. M runs and runs, never blocks.
  4. H is waiting on L, but L cannot run because M is hogging CPU.
  5. H misses its deadline.

Mars Pathfinder famously hit this in 1997. Fix: the mutex implements priority inheritance: when H tries to take M_lock and finds L holds it, L is temporarily boosted to H's priority. M cannot starve L; L finishes and releases M_lock; H gets it; everyone returns to normal.

The take-away: use mutexes (with PI), not binary semaphores, when protecting shared data between mixed-priority tasks.

9.7 The big-name RTOSes

  • FreeRTOS. Tiny, free (MIT), Amazon-shepherded. Runs on everything from PIC to Cortex-A. The most-deployed RTOS in the world by unit volume. Great learning curve.
  • Zephyr. Linux Foundation's modern RTOS. Device-tree-based config, network stack, USB, BLE, Wi-Fi. Cortex-M, Cortex-R, RISC-V, x86. Heavier than FreeRTOS but featureful. Used by Nordic, Intel, Linaro.
  • ChibiOS. Italian, very small and fast. HAL with a clean API. Popular in motor control and audio.
  • RTEMS. Veteran (1988), used heavily in space (NASA, ESA). MIT license. Real-time POSIX-like.
  • ThreadX. Express Logic, now Microsoft Azure RTOS. Industrial, medical, automotive. Tiny footprint, fast.
  • VxWorks. Wind River's flagship. Used in the Mars rovers, F-35, BMW iDrive. POSIX, certified to DO-178, IEC 61508.
  • QNX. Microkernel-based. Cars (BlackBerry QNX is in millions of vehicles), medical, industrial.
  • Embedded Linux. Yocto, Buildroot. Not real-time by default; PREEMPT_RT patch makes it soft real-time. Used when complexity demands it (multi-gig RAM, GPU, network stack).
rendering diagram...