Date: marzo 4, 2025
Author: Guillermo Garcia
Categories: RTOS Tags: FreeRTOS
Understanding how FreeRTOS handles heap memory through static and dynamic allocation can improve performance and system stability. This article will explore these two allocation methods, their advantages and disadvantages.
Table of Contents
FreeRTOS provides multiple memory management schemes to allocate heap memory dynamically. These schemes are defined in heap_1.c
to heap_5.c
, each offering different features and trade-offs:
pvPortMalloc()
but lacks vPortFree()
(no deallocation).malloc()
and free()
.Choosing the right management scheme depends on system requirements and available RAM.
When the kernel requires RAM, instead of calling malloc() directly it calls pvPortMalloc().
When RAM is being freed, instead of calling free() directly, the kernel calls vPortFree().
pvPortMalloc() has the same prototype as malloc(), and vPortFree() has the same prototype
as free().
When we create a task, the FreeRTOS kernel creates a series of resources in memory reserved exclusively for this task. The resources that a task needs are the following:
Task Control Block (TCB)
Task Stack
The way these resources are managed for each task depends on the memory management algorithm chosen in the FreeRTOS kernel. In the case of heap_2.c
, the management would be as shown in the following image.
A shows the array after three tasks have been created. A large free block remains at
the top of the array.
/* This is in the FreeRTOSConfig.h file */ #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 75 * 1024 ) )
Heap_2.c
also uses a simple array dimensioned by configTOTAL_HEAP_SIZE. It uses a best
fit algorithm to allocate memory and, unlike heap_1
, it does allow memory to be freed. Again,
the array is declared statically, so will make the application appear to consume a lot of RAM,
even before any of the array has been assigned.
We now know that when we create tasks, exclusive memory resources will be created for each task. These resources are managed according to the algorithm chosen in the heap_2.c
kernel for our case.
It is possible to manage memory resources in two ways in FreeRTOS: statically, that is, we assign the size of that memory, or dynamically we let the kernel manage and assign the resources.
Static allocation is performed at compile time and does not require heap memory. Instead, memory is assigned to variables in the .bss
or .data
section of RAM.
Advantages:
Disadvantages:
Example of Static Allocation in FreeRTOS:
static StackType_t taskStack[configMINIMAL_STACK_SIZE]; static StaticTask_t taskControlBlock; void vTaskFunction(void *pvParameters) { for(;;) { // Task Code } } void createTask() { xTaskCreateStatic( vTaskFunction, "TaskName", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, taskStack, &taskControlBlock ); }
It is possible to enable Static Allocation support by placing the definition in the FreeRTOSConfig.h file.
/* This is in the FreeRTOSConfig.h file */ #define configSUPPORT_STATIC_ALLOCATION 1
FreeRTOS API functions are available which all are using static memory:
When configSUPPORT_STATIC_ALLOCATION 1
is enabled it is necessary to define callback functions that must be provided by the application to supply the RAM used by the idle and timer service tasks. We define the functions with the following prototype.
/* configSUPPORT_STATIC_ALLOCATION is set to 1, so the application must provide an implementation of vApplicationGetIdleTaskMemory() to provide the memory that is used by the Idle task. */ void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ) { /* If the buffers to be provided to the Idle task are declared inside this function then they must be declared static - otherwise they will be allocated on the stack and so not exists after this function exits. */ static StaticTask_t xIdleTaskTCB; static StackType_t uxIdleTaskStack[ configMINIMAL_STACK_SIZE ]; /* Pass out a pointer to the StaticTask_t structure in which the Idle task's state will be stored. */ *ppxIdleTaskTCBBuffer = &xIdleTaskTCB; /* Pass out the array that will be used as the Idle task's stack. */ *ppxIdleTaskStackBuffer = uxIdleTaskStack; /* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer. Note that, as the array is necessarily of type StackType_t, configMINIMAL_STACK_SIZE is specified in words, not bytes. */ *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; } /*-----------------------------------------------------------*/ /* configSUPPORT_STATIC_ALLOCATION and configUSE_TIMERS are both set to 1, so the application must provide an implementation of vApplicationGetTimerTaskMemory() to provide the memory that is used by the Timer service task. */ void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize ) { /* If the buffers to be provided to the Timer task are declared inside this function then they must be declared static - otherwise they will be allocated on the stack and so not exists after this function exits. */ static StaticTask_t xTimerTaskTCB; static StackType_t uxTimerTaskStack[ configTIMER_TASK_STACK_DEPTH ]; /* Pass out a pointer to the StaticTask_t structure in which the Timer task's state will be stored. */ *ppxTimerTaskTCBBuffer = &xTimerTaskTCB; /* Pass out the array that will be used as the Timer task's stack. */ *ppxTimerTaskStackBuffer = uxTimerTaskStack; /* Pass out the size of the array pointed to by *ppxTimerTaskStackBuffer. Note that, as the array is necessarily of type StackType_t, configTIMER_TASK_STACK_DEPTH is specified in words, not bytes. */ *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH; }
Dynamic memory allocation occurs at runtime using pvPortMalloc()
and vPortFree()
. This provides greater flexibility but comes with risks such as fragmentation and potential memory leaks.
Advantages:
Disadvantages:
Example of Dynamic Allocation in FreeRTOS:
void vTaskFunction(void *pvParameters) { for(;;) { // Task Code } } void createTask() { xTaskCreate( vTaskFunction, "TaskName", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); }
It is possible to enable Dynamic Allocation support by placing the definition in the FreeRTOSConfig.h file.
/* This is in the FreeRTOSConfig.h file */ #define configSUPPORT_DYNAMIC_ALLOCATION 1
Choosing between static and dynamic allocation in FreeRTOS depends on the system’s memory constraints and performance requirements. Static allocation offers stability and predictability, while dynamic allocation provides flexibility at the cost of potential fragmentation.
Deja una respuesta