Embedded & Bare-Metal

an RTOS task (thread)

If a real-time operating system is a stage manager directing several performers, each performer is a task: an independent activity with its own job, like 'read the sensor' or 'drive the display'. You write a task as an ordinary-looking function with its own loop, and the RTOS makes it look as if your task has the whole processor to itself, even though several tasks share one core by taking turns.

Under the hood, each task gets its OWN stack (a chunk of RAM for its local variables and call frames) and a saved set of register values (its context). At any instant only one task actually runs on the core; the rest are suspended with their contexts frozen. When the scheduler decides to switch, it performs a context switch: it saves the running task's registers into its task control block, loads another task's saved registers, and resumes that task exactly where it left off. A task is always in one of a few states: running (on the core now), ready (could run, waiting its turn), or blocked (waiting for something — a delay to expire, a message to arrive, a mutex to free). When a task blocks (for example by calling a delay or waiting on a queue), it voluntarily gives up the core so other tasks can run; this is what makes cooperative coexistence possible. Each task carries a priority that the scheduler uses to decide who runs.

It matters because tasks let you decompose a tangled 'do everything in one loop' program into clear, independent units — each task reasons only about its own job and communicates with others through queues and semaphores. The honest cautions: tasks in an RTOS typically SHARE one address space (there is usually no memory protection between them on a small MCU), so a bug in one task can corrupt another's memory — unlike full OS processes, which are isolated. Each task's stack is a FIXED size you choose up front, and overflowing it (deep recursion, big local arrays) silently corrupts adjacent memory, a notorious embedded crash. And because tasks run concurrently and can be preempted at any point, any data shared between tasks needs proper synchronization (a mutex or queue), exactly as with threads.

// A task is a function with its own loop; it blocks to yield the core. void uartTask(void *params) { char msg[32]; for (;;) { // blocks here until a message arrives — other tasks run meanwhile xQueueReceive(rxQueue, msg, portMAX_DELAY); process(msg); } } // Stack size (e.g. 256 words) is fixed when you create the task.

A task blocks on a queue to give up the core; its own stack and saved registers let the scheduler resume it exactly where it paused.

Each task's stack is a fixed size you pick in advance; overflowing it silently corrupts neighbouring memory and is a classic, hard-to-find crash. And small-MCU tasks share one address space, so they are NOT isolated like OS processes.

Also called
taskRTOS threadthread of execution任務執行緒