Preface
This article introduces the basic knowledge of memory management, and see the detailed source code analysis of the source code.FreeRTOS memory management example analysis》
FreeRTOS provides several memory heap management solutions, both complex and simple. The simplest management strategy can also meet the requirements of many applications, such as applications with high security requirements, which do not allow dynamic memory allocation at all.
FreeRTOS also allows you to implement memory heap management yourself, and even allows you to use two memory heap management solutions at the same time. The two memory heaps are implemented simultaneously to allow task stacks and other RTOS objects to be placed into fast internal RAM and application data to low-speed external RAM.
Whenever a task, queue, mutex, software timer, semaphore, or event group is created, the RTOS kernel allocates RAM to them. The malloc() and free() functions in the standard function library can sometimes be used to complete this task, but:
- In embedded systems, they are not always usable;
- They take up more valuable code space;
- They are not thread protection;
- They are not deterministic (the time each call may be executed differently);
Therefore, it is usually necessary to provide an alternative memory allocation scheme.
Embedded/real-time systems have hugely different RAM and time requirements, so a RAM memory allocation algorithm may belong to only a subset of an application.
To avoid this problem, FreeRTOS retains memory allocation API functions at the porting layer. The porting layer is outside the RTOS core code source file (not belonging to the core source code), which allows different applications to provide application implementations that suit them. When the RTOS kernel needs RAM, call the pvPortMallo() function instead of the malloc() function. When RAM is about to be released, call the vPortFree() function instead of the free() function.
5 simple memory allocation implementations are provided in the FreeRTOS download package, which will be described later in this article. Users can choose one of them appropriately or design their own memory allocation strategy.
The memory allocation schemes provided by FreeRTOS are located in different source files (heap_1.c, heap_2.c, heap_3.c, heap_4.c, heap_5.c), and the source files are located in the download package\FreeRTOS\Source\portable\MemMang folder. Other implementation methods can be added as needed. If you want to use the memory heap allocation scheme provided by FreeRTOS, the selected source file must be correctly included in the project file.
1.heap_1.c
This is the easiest of all implementations. Once memory is allocated, it does not even allow the allocated memory to be released. Despite this, heap_1.c is suitable for most embedded applications. This is because most deeplyembedded applications just create all tasks, queues, semaphores, etc. at the system startup and will use them until the end of the program, never need to be deleted.
When it is necessary to allocate RAM, this memory allocation scheme simply subdivides a large array into a subset. The capacity size of a large array is set by the configTOTAL_HEAP_SIZE macro in the file.
The API function xPortGetFreeHeapSize() returns the total size of unallocated stack space. You can use this function return value to reasonably set configTOTAL_HEAP_SIZE.
Function introduction:
- Used for applications that never delete tasks, queues, semaphores, mutexes, etc. (In fact, most applications using FreeRTOS meet this criteria)
- The execution time is determined and no memory fragmentation is generated
- The implementation and allocation process is very simple, and the memory required is allocated from a static array, meaning that this memory allocation is usually only suitable for applications that do not perform dynamic memory allocation.
2.heap_2.c
Unlike Scheme 1, this scheme uses an optimal matching algorithm that allows the previously allocated blocks of memory. It does not synthesize adjacent free blocks into a larger block (in other words, this causes memory fragmentation).
The valid stack space size is defined by the configTOTAL_HEAP_SIZE macro located in the file.
The API function xPortGetFreeHeapSize() returns the size of the remaining unallocated stack space (can be used to optimize the value of the configTOTAL_HEAP_SIZE macro), but cannot provide fragmented details of the unallocated memory.
Function introduction:
It can be used to repetitively allocate and delete tasks, queues, semaphores, mutexes, etc. with the same stack space, and does not consider memory fragmentation.
Cannot be used for apps that allocate and release random byte stack space
- If an application dynamically creates and deletes tasks, and the stack space allocated to tasks is always the same size, heap_2.c is available in most cases. However, if the stacks allocated to tasks are not always equal, the freed valid memory may be fragmented, forming many small memory blocks. In the end, memory allocation failure will occur because there is not enough continuous stack space. Heap_4.c is a good choice in this case.
- If an application dynamically creates and deletes queues, and in each case the queue storage area (queue storage area refers to the number of queue items multiplied by each queue length) is the same, heap_2.c can be used in most cases. However, if the queue storage areas are not always equal in each case, the freed valid memory may be fragmented, forming many small memory blocks. In the end, memory allocation failure will occur because there is not enough continuous stack space. Heap_4.c is a good choice in this case.
- The application directly calls the pvPortMalloc() and vPortFree() functions, not only indirectly through the FreeRTOS API.
If queues, tasks, semaphores, mutexes, etc. in your application are in an unpredictable order, it may cause memory fragmentation issues. Although this is a low-probability event, it must be kept in mind.
Not deterministic, but it is much more efficient than the malloc function in the standard library.
heap_2.c is suitable for most small real-time systems that require dynamically creating tasks.
3.heap_3.c
heap_3.c simply wraps the malloc() and free() functions in the standard library, and the wrapped malloc() and free() functions are thread protection.
Function introduction:
- The linker needs to set a stack, and the compiler library provides malloc() and free() functions.
- Not sure
- It may significantly increase the code size of the RTOS kernel
Note: When using heap_3, the configTOTAL_HEAP_SIZE macro definition in the file has no effect.
4.heap_4.c
This scheme uses an optimal matching algorithm, but not like Scheme 2. It merges adjacent free memory blocks into a larger block (including a merge algorithm).
The valid stack space size is defined by configTOTAL_HEAP_SIZE located in the file.
The API function xPortGetFreeHeapSize() returns the size of the remaining unallocated stack space (can be used to optimize the value of the configTOTAL_HEAP_SIZE macro), but cannot provide fragmented details of the unallocated memory.
Function introduction:
- Can be used for applications that repetitive allocation, delete tasks, queues, semaphores, mutexes, and more.
- Can be used to allocate and free random byte memory, and does not produce severe fragmentation like heap_2.c.
- Not deterministic, but it is much more efficient than the malloc function in the standard library.
heap_4.c is also particularly suitable for porting layer code. You can directly use the pvPortMalloc() and vPortFree() functions to allocate and free memory.
5.heap_5.c (Newly added to V8.1.0)
This solution also implements the merge algorithm in heap_4.c and allows the stack to span multiple non-contiguous memory areas.
Heap_5 is initialized by calling the vPortDefineHeapRegions() function. Memory allocation and release are not allowed before the function is executed. Creating an RTOS object (task, queue, semaphore, etc.) will implicitly call pvPortMalloc(), so it must be noted that before using heap_5 to create any object, you must first execute the vPortDefineHeapRegions() function.
The vPortDefineHeapRegions() function only requires a single parameter. This parameter is a HeapRegion_t structure type array. HeapRegion_t is defined in it as follows:
typedef struct HeapRegion { /* The start address of the memory block used for the memory heap*/ uint8_t *pucStartAddress; /* Memory block size */ size_t xSizeInBytes; } HeapRegion_t;
This array must end with a NULL pointer and 0 byte element, and the starting address must be arranged from small to large. The following code snippet provides an example. The MSVCWin32 emulator sample program uses heap_5, so it can be used as a reference routine.
/* Allocate two memory blocks in memory for the memory heap. The first memory block is 0x10000 bytes, with a starting address of 0x80000000, The second memory block is 0xa0000 bytes, the starting address is 0x900000000. The starting address is 0x8000000000 The starting address is lower, so it is placed in the first position of the array.*/ const HeapRegion_t xHeapRegions[] = { { ( uint8_t * ) 0x80000000UL, 0x10000 }, { ( uint8_t * ) 0x90000000UL, 0xa0000 }, { NULL, 0 } /* end of array. */ }; /* Pass array parameters to the function vPortDefineHeapRegions(). */ vPortDefineHeapRegions( xHeapRegions );
The above is the detailed content of the memory management analysis of FreeRTOS real-time operating system. For more information about FreeRTOS memory management, please pay attention to my other related articles!