Resource Management in FreeRTOS

Scan to FollowLearn Embedded Together, learn and grow together

Resource Management in FreeRTOS

The FreeRTOS introductory series aims to help beginners quickly get started and master the basic principles and usage of FreeRTOS while organizing knowledge.

FreeRTOS Quick Start – Exploring the System

FreeRTOS Official Chinese Website is Now Live

FreeRTOS Coding Standards and Data Types

FreeRTOS Quick Start – Task Management

FreeRTOS Learning – Detailed Explanation of Message Queues

FreeRTOS Learning – Counting Semaphores

FreeRTOS Learning – Mutex Semaphores

FreeRTOS Learning – Event Groups

FreeRTOS Learning – Task Notifications

FreeRTOS Learning – Software Timers

FreeRTOS Learning – Memory Management

This article introduces the resource management related content of FreeRTOS.

As a popular Real-Time Operating System (RTOS), FreeRTOS’s resource management mechanism is crucial for system stability and reliability.

Resource management mainly involves access control to shared resources between tasks, including the allocation and usage of memory, peripherals, and data.

In a multitasking environment, when multiple tasks access shared resources simultaneously without control, it may lead to data inconsistency, system crashes, and other issues. FreeRTOS provides various resource management mechanisms to avoid these problems:

  1. Critical Section Protection
  2. Mutex
  3. Binary Semaphore
  4. Counting Semaphore
  5. Recursive Mutex
  6. Reader-Writer Lock (partial implementation)

Critical Section Protection

2.1 Basic Concepts

A critical section refers to a code segment that accesses shared resources, which must ensure exclusive access during its execution.

FreeRTOS provides two methods for critical section protection:

// Enter critical section (disable all interrupts)
taskENTER_CRITICAL();

// Code accessing shared resources

// Exit critical section (restore interrupts)
taskEXIT_CRITICAL();

// Nested critical section version
taskENTER_CRITICAL_FROM_ISR();
taskEXIT_CRITICAL_FROM_ISR();

Features and Considerations

  • The strictest protection mechanism, disables interrupts
  • Critical section code should be kept as short as possible to avoid affecting system real-time performance
  • Do not call APIs that may block tasks within a critical section
  • Nested calls are safe

Mutex

Basics of Mutex

A mutex is a special type of binary semaphore used to implement mutual access to resources, featuring a priority inheritance mechanism.

// Create mutex
SemaphoreHandle_t xMutex = xSemaphoreCreateMutex();

// Take mutex
if(xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) 
{
    // Access shared resources
    
    // Release mutex
    xSemaphoreGive(xMutex);
}

Priority Inheritance Mechanism

When a high-priority task is blocked waiting for a mutex held by a low-priority task, the low-priority task temporarily inherits the high-priority task’s priority to reduce the impact of priority inversion issues.

Usage Scenarios

  • Protect shared resources that require long operations
  • Resource access may involve task blocking
  • Scenarios that require automatic resolution of priority inversion

Binary Semaphore

Basic Usage

A binary semaphore can be used for task synchronization or simple mutual exclusion control, but it does not have the priority inheritance feature.

// Create binary semaphore
SemaphoreHandle_t xBinarySemaphore = xSemaphoreCreateBinary();

// Give semaphore
xSemaphoreGive(xBinarySemaphore);

// Take semaphore
if(xSemaphoreTake(xBinarySemaphore, portMAX_DELAY) == pdTRUE) {
    // Execute protected code
}

Differences from Mutex

  1. Binary semaphore does not record the holder, while mutex does
  2. Binary semaphore lacks a priority inheritance mechanism
  3. Binary semaphore is more suitable for event notifications, while mutex is better for resource protection

Counting Semaphore

Basic Concepts

A counting semaphore is used to manage a limited number of resources, with its value representing the number of available resources.

// Create counting semaphore, initial value is the maximum number of resources
SemaphoreHandle_t xCountingSemaphore = xSemaphoreCreateCounting(MAX_RESOURCES, MAX_RESOURCES);

// Take resource
if(xSemaphoreTake(xCountingSemaphore, portMAX_DELAY) == pdTRUE) 
{
    // Use resource
    
    // Release resource
    xSemaphoreGive(xCountingSemaphore);
}

Typical Applications

  • Manage a limited number of hardware resources (e.g., DMA channels)
  • Buffer management in producer-consumer problems
  • Limit the number of concurrent accesses

Recursive Mutex

6.1 Concept of Recursive Locks

Allows the same task to acquire the same mutex multiple times without causing deadlock.

// Create recursive mutex
SemaphoreHandle_t xRecursiveMutex = xSemaphoreCreateRecursiveMutex();

// Recursive take
xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY);
xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY);

// Recursive give
xSemaphoreGiveRecursive(xRecursiveMutex);
xSemaphoreGiveRecursive(xRecursiveMutex);

Usage Scenarios

  • Functions that may be called recursively need to access shared resources
  • A task needs to enter the same critical section multiple times

Reader-Writer Lock (Partial Implementation)

Some FreeRTOS port versions or extensions provide a reader-writer lock mechanism, allowing multiple readers or a single writer to access resources.

// Create read-write lock
SemaphoreHandle_t xReadWriteLock = xSemaphoreCreateReadWrite();

// Take read lock
xSemaphoreTakeRead(xReadWriteLock, portMAX_DELAY);
// Multiple readers can hold the read lock simultaneously
xSemaphoreGiveRead(xReadWriteLock);

// Take write lock
xSemaphoreTakeWrite(xReadWriteLock, portMAX_DELAY);
// Only one writer is allowed
xSemaphoreGiveWrite(xReadWriteLock);

Usage of Resource Management

  1. Minimize Critical Sections: Keep critical sections as short as possible
  2. Avoid Nesting: Minimize the levels of nested locks
  3. Fixed Order: Acquire multiple locks in a fixed order to avoid deadlocks
  4. Timeout Settings: Set reasonable timeouts for lock operations to avoid permanent blocking
  5. Priority Design: Design task priorities reasonably to reduce priority inversion
  6. Resource Layering: Layer resource management to reduce complexity

Common Problems and Solutions

Priority Inversion Problem

Phenomenon: Medium-priority tasks preempt low-priority tasks, causing high-priority tasks to be blocked

Solution:

  • Use mutex instead of binary semaphore
  • Design task priorities reasonably
  • Consider using priority ceiling protocol (supported by some implementations)

Deadlock Problem

Phenomenon: Multiple tasks wait for each other to hold resources

Solution:

  • Acquire multiple locks in a fixed order
  • Use lock timeout mechanisms
  • Design lock-free algorithms (e.g., RCU)

Resource Exhaustion Problem

Phenomenon: Semaphore count is exhausted, tasks cannot acquire resources

Solution:

  • Set initial resource quantity reasonably
  • Implement resource recovery mechanisms
  • Add resource monitoring and alerts

FreeRTOS Resource Management API Reference

API Function Description
<span>vPortEnterCritical()</span> Enter critical section
<span>vPortExitCritical()</span> Exit critical section
<span>xSemaphoreCreateMutex()</span> Create mutex
<span>xSemaphoreCreateBinary()</span> Create binary semaphore
<span>xSemaphoreCreateCounting()</span> Create counting semaphore
<span>xSemaphoreCreateRecursiveMutex()</span> Create recursive mutex
<span>xSemaphoreTake()</span> Take semaphore
<span>xSemaphoreGive()</span> Give semaphore
<span>xSemaphoreTakeRecursive()</span> Recursive take
<span>xSemaphoreGiveRecursive()</span> Recursive give
<span>uxSemaphoreGetCount()</span> Get semaphore count value

Application Examples

Multitasking Access to Shared Peripherals

// Protect shared UART resource
SemaphoreHandle_t xUartMutex;

void UART_Init() 
{
    xUartMutex = xSemaphoreCreateMutex();
}

void Task1(void *pvParameters) 
{
    while(1) 
    {
        if(xSemaphoreTake(xUartMutex, pdMS_TO_TICKS(100)) == pdTRUE) 
        {
            printf("Task1 using UART\n");
            xSemaphoreGive(xUartMutex);
        }
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

void Task2(void *pvParameters) 
{
    while(1) 
    {
        if(xSemaphoreTake(xUartMutex, pdMS_TO_TICKS(100)) == pdTRUE) 
        {
            printf("Task2 using UART\n");
            xSemaphoreGive(xUartMutex);
        }
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

Producer-Consumer Model

// Use counting semaphore to manage buffer
SemaphoreHandle_t xEmptySlots;
SemaphoreHandle_t xFilledSlots;
QueueHandle_t xBufferQueue;

void ProducerTask(void *pvParameters) 
{
    int data = 0;
    while(1) 
    {
        // Wait for empty slots
        xSemaphoreTake(xEmptySlots, portMAX_DELAY);
        
        // Produce data and put it in the queue
        xQueueSend(xBufferQueue, &data, 0);
        data++;
        
        // Notify consumer of new data
        xSemaphoreGive(xFilledSlots);
    }
}

void ConsumerTask(void *pvParameters) 
{
    int receivedData;
    while(1) 
    {
        // Wait for data
        xSemaphoreTake(xFilledSlots, portMAX_DELAY);
        
        // Get data from the queue
        xQueueReceive(xBufferQueue, &receivedData, 0);
        printf("Consumed: %d\n", receivedData);
        
        // Notify producer of empty slot
        xSemaphoreGive(xEmptySlots);
    }
}

Conclusion

FreeRTOS provides a rich set of resource management mechanisms, and developers should choose the appropriate methods based on specific application scenarios:

  1. Simple Shared Variables: Critical section protection
  2. Short-term Resource Access: Mutex
  3. Event Notification: Binary semaphore
  4. Multiple Similar Resources: Counting semaphore
  5. Recursive Access: Recursive mutex
  6. Read-Mostly Scenarios: Reader-writer lock (if supported)

Proper use of these mechanisms can build stable and efficient embedded real-time systems, avoiding resource conflicts, deadlocks, priority inversions, and ensuring reliable system operation.

Resource Management in FreeRTOS

Follow 【Learn Embedded Together】 to become better together

If you find this article useful, click “Share”, “Like”, “Recommend”!

Leave a Comment