Skip to content
Home
RTOS Concepts: Real-Time Operating Systems Explained

RTOS Concepts: Real-Time Operating Systems Explained

Embedded Systems Embedded Systems 8 min read 1495 words Beginner ExcellentWiki Editorial Team

Why Use an RTOS?

A Real-Time Operating System (RTOS) manages the execution of multiple tasks on embedded systems with predictable timing guarantees. Unlike general-purpose operating systems such as Linux that optimize for average throughput and fairness among processes, an RTOS guarantees that critical tasks meet their deadlines under all load conditions. The FreeRTOS kernel, the most widely deployed RTOS with over 10 billion instances across consumer, industrial, and automotive products according to AWS, proves that a full-featured RTOS can fit in as little as 5 KB of Flash memory on a modest microcontroller. When an application must handle multiple concurrent activities — reading sensors, processing data, driving displays, responding to user input, and communicating over networks — an RTOS provides structured concurrency that a simple super-loop cannot achieve without becoming brittle and difficult to maintain.

RTOS vs GPOS

RTOS implementations provide fixed-priority preemptive scheduling where the highest-priority ready task runs immediately, bounded interrupt latency typically under 100 CPU cycles as measured from interrupt assertion to the first instruction of the handler, a kernel footprint of 1–50 KB leaving most Flash for the application, and context switching overhead measured in microseconds. General-purpose OSes use fairness-based scheduling policies such as the Completely Fair Scheduler (CFS) in Linux, have megabyte-sized kernels with drivers and subsystems that consume memory, and cannot guarantee deterministic timing because drivers may disable interrupts for extended periods. The choice between an RTOS and a GPOS is determined by the application’s timing requirements — any system with hard real-time deadlines should use an RTOS.

Core Concepts

Tasks and Threads

A task is an independent execution unit with its own stack, priority level, and execution state. Tasks run in an infinite loop, waiting for events through blocking API calls, processing those events, and then suspending until the next event. The RTOS scheduler determines which task runs at any given time based on priority and state. Each task’s stack must be sized to handle its worst-case call depth plus interrupt context. The ARM Cortex-M process stack pointer (PSP) separates task stacks from the main stack used for interrupt handling, preventing task stack overflow from corrupting interrupt handlers.

void vTaskFunction(void *pvParameters) {
    for (;;) {
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
---
xTaskCreate(vTaskFunction, "Task1", 256, NULL, 1, NULL);

Task States

In FreeRTOS and most RTOS kernels, tasks transition through well-defined states managed by the scheduler. A task in the Ready state is eligible to run but waiting for CPU time because a higher-priority task is executing. The Running state means the task currently occupies the CPU. A task enters the Blocked state when it waits for an event such as a semaphore, queue data, or a time delay — blocked tasks consume zero CPU cycles, which is the primary power-saving mechanism in RTOS-based systems. The Suspended state removes a task from scheduling entirely until it is explicitly resumed. Tasks in the Deleted state have had their resources reclaimed. Understanding these states is essential for debugging scheduling issues.

Scheduling Algorithms

The most common RTOS scheduling algorithm is fixed-priority preemptive scheduling: the highest-priority ready task runs, and a task is preempted when a higher-priority task becomes ready. Tasks at the same priority level share CPU time using round-robin or time-sliced scheduling with a configurable time quantum. Rate Monotonic Scheduling (RMS) assigns higher priority to tasks with shorter periods and is provably optimal among fixed-priority schemes for periodic tasks with independent deadlines equal to periods, as proven by Liu and Layland in their 1973 IEEE Transactions on Computers paper. Earliest Deadline First (EDF) uses dynamic priorities and can theoretically schedule any task set with utilization under 100%, but is harder to implement and degrades unpredictably under overload.

Synchronization Primitives

RTOS kernels provide several synchronization primitives that enable safe communication between tasks and between ISRs and tasks. Binary semaphores are used for signaling — a producer task or ISR gives the semaphore, and a consumer task takes it, blocking if it is not available. Counting semaphores manage access to a pool of identical resources such as DMA channels or hardware timers. Mutexes provide mutual exclusion with priority inheritance, which prevents unbounded priority inversion by temporarily boosting the priority of the mutex holder to that of the highest-priority waiting task. Queues pass data between tasks with FIFO ordering, configurable element size, and deterministic copy times. Stream buffers and message buffers provide variable-length data transfer without copying the data twice.

SemaphoreHandle_t xSemaphore;
xSemaphoreGive(xSemaphore);       // Signal
xSemaphoreTake(xSemaphore, portMAX_DELAY);  // Wait (block until available)

Priority Inversion

Priority inversion is a classic RTOS hazard where a high-priority task is blocked waiting for a resource held by a low-priority task. The situation becomes unbounded when a medium-priority task preempts the low-priority task, extending the high-priority task’s blocking time indefinitely. The Mars Pathfinder mission famously experienced this bug in 1997, as documented in the JPL report by Mike Jones. Priority inheritance solves this by temporarily raising the low-priority task’s priority to match the blocked high-priority task, allowing it to complete the critical section and release the mutex. Always use mutexes (not binary semaphores) for mutual exclusion in RTOS applications to ensure priority inheritance is active.

Inter-Task Communication

Tasks communicate through well-defined mechanisms provided by the RTOS kernel. Queues are the most common method — one task sends data to a queue, and another task receives it. The queue handles synchronization automatically: the receiver blocks if the queue is empty, and the sender blocks if the queue is full (with configurable timeout). Message queues support variable-length messages through pointer passing, reducing data copying for large structures. Event groups allow a task to wait for any combination of multiple events, with each bit representing a different event. Task notifications are a lightweight alternative to semaphores and queues, providing direct signaling between tasks with lower overhead than dedicated synchronization objects.

Memory Management

RTOS kernels must handle memory efficiently without fragmentation, which is unacceptable in long-running embedded systems that may operate for years without a reboot. Fixed-size memory pools (also called memory partitions) allocate and deallocate blocks of predetermined sizes with O(1) deterministic timing and zero fragmentation. FreeRTOS provides three heap implementations: heap_1 (simple, no deallocation, deterministic), heap_2 (best-fit with deallocation but possible fragmentation), and heap_4 (first-fit with coalescing, recommended for most applications). Many safety-critical systems governed by MISRA-C or DO-178C prohibit dynamic memory allocation entirely, preferring static allocation at compile time.

Choosing an RTOS

FreeRTOS is the most popular RTOS with MIT licensing, zero royalty fees, a kernel footprint of 5–10 KB, broad architecture support including ARM Cortex-M, RISC-V, AVR, PIC, and Xtensa, and commercial support and integration with AWS IoT services. Zephyr is a Linux Foundation project with extensive in-tree driver support for over 500 boards, built-in Bluetooth 5.x and Wi-Fi stacks, a Devicetree-based hardware abstraction layer, and a strong security focus with TF-M integration. RT-Thread offers POSIX compatibility, a component-rich ecosystem with GUI, networking, and filesystem packages, and strong popularity in Asian markets. Azure RTOS ThreadX provides pre-certification for safety standards including IEC 61508 SIL 4, ISO 26262 ASIL D, and IEC 62304 Class C, reducing certification effort for safety-critical products.

Tickless Idle Mode

To conserve power, the RTOS enters a tickless idle state when all tasks are blocked waiting for events or time delays. The system tick timer is reprogrammed to wake at the earliest pending task deadline rather than firing at every tick interval. Between the programmed wake events, the microcontroller enters a deep sleep mode with power consumption as low as 2 μA on low-power MCUs. FreeRTOS supports tickless idle through a configuration option and provides a vendor hook for the MCU-specific sleep mode implementation. This feature is essential for battery-powered devices that spend the majority of their lifetime waiting for infrequent events.

Frequently Asked Questions

What is the difference between a mutex and a semaphore? A mutex is a binary semaphore with priority inheritance and recursion support for mutual exclusion. A binary semaphore is used for signaling between tasks or ISRs. Counting semaphores manage access to multiple identical resources.

How much Flash memory does FreeRTOS require? FreeRTOS requires approximately 5–10 KB of Flash for the kernel core, plus additional memory for each task’s stack, queues, and semaphores. A minimal two-task application fits in 32 KB Flash MCU.

Can I use an RTOS on an 8-bit microcontroller? Yes, FreeRTOS supports AVR and PIC architectures, but the overhead of context switching may be disproportionate for small MCUs. For 8-bit systems with under 4 KB RAM, consider a cooperative scheduler or super-loop with a simple state machine.

What is priority inheritance and when is it needed? Priority inheritance prevents unbounded priority inversion by temporarily boosting the priority of a task holding a mutex to that of the highest-priority waiting task. It is needed whenever tasks of different priorities share a protected resource.

How do I debug RTOS issues? Use the RTOS-aware debugging features in your IDE (STM32CubeIDE, IAR, Keil) that show task states, stack usage, and queue contents. FreeRTOS provides vTaskList() and vTaskGetRunTimeStats() for console-based debugging.

Related: Embedded C Programming | Real-Time Systems

Section: Embedded Systems 1495 words 8 min read Beginner 756 articles in section Report inaccuracy Back to top