An Introduction to FreeRTOS Kernel Source Code: From Task Creation to Context Switching

1. Introduction

FreeRTOS has become the preferred choice for embedded real-time systems due to its small footprint, portability, and flexible customization capabilities. Before delving into the source code, let us review the core requirements of real-time operating systems (RTOS) in the embedded field:

  1. Determinism: Predictable task response delays.
  2. Scalability: Support for memory usage ranging from a few KB to several tens of KB.
  3. Low Overhead: Efficient context switching, scheduling, and synchronization.

This article will combine the implementation of FreeRTOS with comparisons to other common RTOS (such as ThreadX and Zephyr) and general-purpose operating systems (such as Linux-CFS), helping readers build a more complete understanding of scheduling and context switching.

2. Task Creation and Resource Initialization

2.1 FreeRTOS Task Control Block (TCB) Structure

The <span>tskTCB</span> in FreeRTOS is the core data structure for tasks:

typedef struct tskTaskControlBlock{    volatile StackType_t    *pxTopOfStack;   /* Stack top pointer */    ListItem_t              xStateListItem;  /* State list node */    UBaseType_t             uxPriority;      /* Static priority */    UBaseType_t             uxBasePriority;  /* For priority inheritance */    /* Other members: task name, event list, TLS, etc. */} tskTCB;

Comparison:

  • ThreadX uses a bitmap + ready list dual mechanism;
  • Zephyr uses a “ready queue group” plus a bitmap, allowing constant time to locate the highest priority.
  • Linux-CFS is not suitable for hard real-time scenarios, as it uses a red-black tree (<span>rb-tree</span>) to store ready processes, dynamically adjusting based on runtime share.

2.2 Stack Frame Initialization

FreeRTOS pre-loads the “exception stack frame” onto the task stack in <span>prvInitialiseTaskStack()</span>:

StackType_t *prvInitialiseTaskStack( StackType_t *pxTopOfStack,                                     TaskFunction_t pxCode,                                     void *pvParameters ){    /* xPSR, PC, LR, R12, R3, R2, R1, R0 */    *--pxTopOfStack = portINITIAL_XPSR;    *--pxTopOfStack = ( StackType_t ) pxCode;        /* PC */    *--pxTopOfStack = ( StackType_t ) prvTaskExitError;    /* ... */    *--pxTopOfStack = ( StackType_t ) pvParameters;  /* R0 */    /* Reserve R4–R11 */    pxTopOfStack -= 8;    return pxTopOfStack;}

Comparison:

  • µC/OS-II also constructs the exception stack frame in the stack;
  • Zephyr implements this through assembly macros, using architecture-specific interrupt entry points.

3. Scheduler and Ready List Management

3.1 Organization of Ready Tasks

FreeRTOS maintains a doubly linked list for each priority in <span>pxReadyTasksLists[]</span>, and uses a bitmap <span>uxReadyPriorities</span> to record non-empty list bits:

static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];static UBaseType_t uxReadyPriorities;
  • Insertion: <span>vListInsertEnd()</span> adds to the end of the corresponding priority list.
  • Selection: <span>taskRECORD_READY_PRIORITY()</span> and bit operations quickly update;
  • Finding the Highest Priority: <span>portGET_HIGHEST_PRIORITY()</span> uses CLZ/BSF instructions to obtain the highest set bit in O(1).

Comparison:

  • ThreadX directly uses a 32 or 64-bit ready bitmap, combined with <span>__CLZ()</span> to achieve O(1).
  • Linux-CFS uses a red-black tree (O(log n)) or completely fair queue, suitable for a large number of processes.

3.2 Priority Inheritance and Mutexes

FreeRTOS’s mutex <span>Mutex</span> supports priority inheritance:

  1. If a low-priority task holds the lock, a high-priority task is blocked, and the holding task is temporarily elevated to the blocked task’s priority.
  2. After releasing the lock, the original priority is restored.
/* In vTaskPriorityInherit(): */if( pxMutexHolder->uxPriority < uxMutexWaitingPriority ){    /* Temporarily elevate */    vTaskPrioritySet( pxMutexHolder, uxMutexWaitingPriority );}

Comparison:

  • Zephyr also supports priority inheritance and priority ceiling;
  • Linux RT-PREEMPT has added a mutex inheritance mechanism in the kernel, with higher complexity.

4. Clock Ticks and Triggering Context Switches

4.1 SysTick Driver

On the Cortex-M platform, FreeRTOS uses SysTick by default to generate fixed ticks, with the interrupt handling as follows:

void xPortSysTickHandler( void ){    if( xTaskIncrementTick() != pdFALSE )    {        /* If a higher priority task is ready, trigger PendSV */        portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;    }}
  • <span>xTaskIncrementTick()</span>: Updates the global tick <span>xTickCount</span>, handling delayed tasks and waking software timers.
  • Optional Tickless: In <span>configUSE_TICKLESS_IDLE=1</span> mode, SysTick can be dynamically disabled to further reduce power consumption.

Comparison:

  • FreeRTOS Tickless vs Zephyr Tickless: Both calculate the next wake-up time using RTC or low-power timers.
  • Linux has recently promoted fully dynamic ticks (<span>NO_HZ_FULL</span>), but due to high complexity, it is rarely used in embedded systems.

5. PendSV Interrupt: Core Context Switching

5.1 Assembly Implementation

PendSV is the lowest priority exception used for executing context switches:

PendsvHandler:    MRS   R0, PSP    STMDB R0!, {R4–R11}         ; Save caller registers    LDR   R1, =pxCurrentTCB    STR   R0, [R1]              ; Update current task stack top    BL    vTaskSwitchContext    ; Select the next TCB from the ready list    LDR   R1, =pxCurrentTCB    LDR   R0, [R1]              ; New task stack top    LDMIA R0!, {R4–R11}         ; Restore registers    MSR   PSP, R0    BX    LR

5.2 Switching Overhead and Measurement

  • Number of Registers: Cortex-M saves R4–R11, totaling 8 registers.
  • Context Overhead: Approximately 40–50 CPU cycles, depending on compiler optimization and memory access latency.
  • Comparison: General-purpose OS (like Linux) typically needs to save more registers, MMU context, page tables, TLB, etc., with overhead in the hundreds of cycles.

6. Comparison with Other Operating Systems

Feature FreeRTOS ThreadX Zephyr Linux-CFS
Ready Organization Priority list + bitmap O(1) Bitmap O(1) + list Ready queue group + bitmap O(1) rb-tree O(log n)
Context Switching PendSV O(1) SVC + PendSV IRQ + Switch Soft interrupt + Trap, high overhead
Priority Inheritance Supported Supported Supported Supported (RT-PREEMPT patch)
Tickless Optional support Optional Optional Partial support, complex
Memory Usage ≈ 8–15 KB ≈ 4–12 KB ≈ 18–40 KB >1 MB

7. Flowchart

flowchart LR  subgraph Task Creation    A[xTaskCreate] --> B[Allocate TCB & Stack]    B --> C[Initialize Stack Frame]    C --> D[Insert into Ready List]  end  subgraph Scheduling and Switching    E[SysTick Interrupt] --> F[xTaskIncrementTick]    F -->|Higher priority ready| G[Trigger PendSV]    G --> H[PendSV Handler]    H --> I[vTaskSwitchContext]    I --> J[Restore New Task Context]  end

8. In-Depth Thoughts

  1. Scalability: FreeRTOS allows developers to add custom statistics and tracking through hook functions and configuration macros.
  2. Multi-architecture Support: Supports not only Cortex-M but also ARM7/9, RISC-V, MIPS, PowerPC, etc. Porting requires only providing the corresponding ISR and context switching assembly.
  3. Real-time Performance Evaluation: Can measure context switching and interrupt latency using hardware timers to ensure worst-case response time (WCRT) is met.

9. Conclusion

This article not only analyzes the task creation, ready management, tick triggering, and context switching of FreeRTOS from the source code perspective but also helps readers understand the trade-offs in performance and functionality through comparisons with other RTOS and general-purpose operating systems. For further reading, you can delve into the following source files:

  • <span>tasks.c</span>, <span>list.c</span>, <span>queue.c</span>: Core task and queue management
  • <span>port.c</span>, <span>portasm.s</span>: Porting layer context switching details
  • <span>timers.c</span>: Software timer implementation

I hope this article helps you quickly get started with the FreeRTOS kernel and lays a solid foundation for real-time systems.

Leave a Comment