Thursday, April 7, 2016

Memory Management - Heap? Stack Overflow?

Memory Management

Every new task created will require some RAM memory to be allocated by the RTOS kernel (both for the task control and for the task's stack) in a heap (basically an area in the memory known by kernel where everything is stored). The same happens to every queue, mutex, semaphore, etc. When some of those are deleted, the memory should (must?) be freed. In a common OS, with no time restrictions, this is performed by using malloc() and free(). The problem is that these commands are usually non deterministic (the time spent can vary), may take a lot of unnecessary code space, are not thread safe and can just not be available in the system. To solve these problems, the memory management algorithm is allowed to be written by the user, which means this is located rather than in the core code of the FreeRTOS, but in the portable layer. Instead of calling malloc() and free(), the kernel will call pvPortMalloc() and pvPortFree().


Nevertheless, FreeRTOS includes 5 sample implementations of the memory management unit that can be used. These are called "heap_*" and each has its own source file located in Source/Portable/MemMang (the Full Demo only uses the heap_5.c). Other implementations may be added and used, but one of those samples must be included in your project (the RTOS kernel will use it, even though the application uses another one). The 5 sample implementations are describe below.
  • heap_1.c: this is the simplest one and doesn't permit memory to be freed. This is used a lot, as lots of applications creates and initializes all the tasks, queues, semaphores, etc, at system boot and never delete them. The implementation simply divides an array as RAM is requested (the size of this "array", which is actually the size of the heap, is defined in FreeRTOSConfig.h as configTOTAL_HEAP_SIZE). The API function xPortGetFreeHeapSize() may be used to optimize this value. [1]
  • heap_2.c: this is similar to heap_1, but allow freeing memory. To keep it simple, there is no de-fragmentation algorithm, which means that adjacent free blocks aren't combined in one larger block (this is done in heap_4, it's called coalescence algorithm). This implementation should be used if the amount of memory freed and "malloced" are always the same, i.e., the tasks's stacks are always the same size or the amount of queue storage is always the same, otherwise, the memory may become fragmented into small blocks and future allocations may fail. configTOTAL_HEAP_SIZE controls the size of the heap. [2]
  • heap_3.c: just implements a wrapper so that the compiler library's free() and malloc() implementation are used. It just makes those functions thread safe. Beware that this implementation is not deterministic and will probably increase the kernel code size. configTOTAL_HEAP_SIZE has no effect. [3]
  • heap_4.c: works similar to heap_2, but implements a coalescence algorithm (combines adjacent free memory blocks into one larger block). This is quite useful when the application needs to allocate memory by itself (by the use of pvPortMalloc() and pvPortFree()), instead of using an API for that. If necessary, the heap can be located in a specific location (address) by setting the option configAPPLICATION_ALLOCATED_HEAP in FreeRTOSConfig.h and explicitly declaring the ucHeap[ configTOTAL_HEAP_SIZE  ] array (more details on [4]).
  • heap_5.c: implements all that heap_4 does, plus allows heap to be formed by several different sized blocks in RAM, instead of one contiguous huge block (i.e., one block starts at address A, with size X, the next starts at address B, with size Y, and so on). The heap is initialized by calling vPortDefineHeapRegions(), which takes as parameter a structure that describes it. Nothing can use the heap before this functions is executed. As Full Demo uses the heap_5, it has to call the function (you can check under prvInitialiseHeap() in main.c). More details can be seen in the official webpage [5].
A few tricks can be used when talking about the heap management. In systems where there is an external, slower, RAM, using two implementations at once allows task stacks and other RTOS objects to be placed in the fast internal RAM, while application data can be placed in the external one.

Stack Usage - Stack Overflow

The task stack is managed by the task itself, which has to be kept within the size determined at creation time (when xTaskCreate() is called). Sometimes, the task tries to use more memory than is available, which causes a popular problem, a common reason for the system's instability: a Stack Overflow.
To ease the debugging of this problem, FreeRTOS offers two mechanisms to check for stack overflow and call a function (hook) if that happens. The configCHECK_FOR_STACK_OVERFLOW, in FreeRTOSConfig.h, chooses between the two methods and, if one of the methods is chosen, a hook function must be provided (vApplicationStackOverflowHook()).

  • Method 1 (configCHECK_FOR_STACK_OVERFLOW set to 1): the stack pointer is checked whenever the task is swept out of the running state. If the pointer is out of boundaries, the hook is called.
  • Method 1 and 2 (configCHECK_FOR_STACK_OVERFLOW set to 2): the task's stack is filled with known values when the task is first created. Whenever the task is swept out of the running state, the last 16 bytes are check and, if any of those are not as predicted, the hook is called. This method can only be used in conjunction to the method 1.


[1] http://www.freertos.org/a00111.html#heap_1
[2] http://www.freertos.org/a00111.html#heap_2
[3] http://www.freertos.org/a00111.html#heap_3
[4] http://www.freertos.org/a00111.html#heap_4
[5] http://www.freertos.org/a00111.html#heap_5

No comments:

Post a Comment