FreeRTOS —— Memory Management

Memory Management

名词解释:heap segmentationRAM 中没有一块完整的内存空间以满足内存申请需求,但是总的剩余空间大于需求的情况。这就表现了分配算法的重要性。

Stack & Heap

stack(栈),有时也称为 frame(帧)。一帧里存放了一个子过程(subroutine,其实也就是函数) 的信息。这些信息包括函数的返回地址和传入参数。当函数中再调用函数时,这些信息会放入堆栈中,而函数返回时,这些信息出栈并恢复到寄存器中。

函数中每定义一个变量,就会将其存储到栈中。函数返回时,释放整个栈空间。栈的优点在于,分配的整个栈空间都可供函数使用,无需再动态分配或者释放空间。

总结:

  • the stack grows and shrinks as functions push and pop local variables
  • there is no need to manage the memory yourself, variables are allocated and freed automatically
  • the stack has size limits
  • stack variables only exist while the function that created them, is running

Heap 是动态分配的空间,由操作系统管理*(不再跟栈一样单独为函数管理)*。堆中的空间在程序执行时可以分配,释放,改变大小。这些操作通过 mallocfree 实现。

Stack:

  • very fast access
  • don’t have to explicitly de-allocate variables
  • space is managed efficiently by CPU, memory will not become fragmented
  • local variables only
  • limit on stack size (OS-dependent)
  • variables cannot be resized

Heap:

  • variables can be accessed globally
  • no limit on memory size
  • (relatively) slower access
  • no guaranteed efficient use of space, memory may become fragmented over time as blocks of memory are allocated, then freed
  • you must manage memory (you’re in charge of allocating and freeing variables)
  • variables can be resized using realloc()

Dynamic Memory Allocation

FreeRTOS 将内存分配实现成 protable layer,可以自定义内存分配算法以实现不同的场景要求。需要申请内存时,调用 pvPortMalloc()vPortFree()。两者对应于 C 中的 malloc()free()

FreeRTOS 实现了5种分配方式,分别对应 heap_1.cheap_2.cheap_3.cheap_4.cheap_5.c

Heap_1

针对的场景是系统初始化,分配的内存会一直持续到程序结束。Heap1 仅实现了 pvPortMalloc() 函数,未实现 vPortFree() 函数。(根本就没考虑释放)Heap_1 堆的大小由 FreeRTOSConfig.hconfigTOTAL_HEAP_SIZE 决定。图示如下:

在这里插入图片描述

大的矩形框是堆,configTOTAL_HEAP_SIZE 决定大小。每一个任务由 TCBStack (栈)组成。创建一个任务就在堆中为其分配一块空间。

这里一个任务应该就是一个进程(函数),所以是用栈。

Heap_2

heap_2heap_1 相同,也是将大的空间切分为小的块供程序使用。不同之处在于:

  1. heap_2 允许空间释放
  2. heap_2 实现了分配算法,而不是直接分配。

heap_2 不会将分配后剩余的空间整合起来,对于 fragmentation 更敏感。其分配过程如下:

在这里插入图片描述

Heap_3

直接使用了 C 的 mallocfree。此时 configTOTAL_HEAP_SIZE 失效,不再决定堆的大小。heap_3 的优势在于 thread-safescheduler suspension

Heap_4

heap_1heap_2 原理相同:将整块的堆划分为小块供调度使用。heap_4 会把剩余的小块整合起来,形成大的内存块。用于处理内存请求大小不同的情况。图示:

在这里插入图片描述

Heap_5

Heap_4 基础上,可以定义多个 RAM 块大小。因此,在调用 pvPortMalloc() 分配内存之前,需要初始化——vPortDefineHeapRegions() ()。 Example:

// declaration of vPortDefineHeapRegions
void vPortDefineHeapRegions(const HeapRegion_t* const pxHeapRegions);

// declaration of HeapRegion_t
typedef struct HeapRegion
{
	// start address of a block of memory that will be part of the heap
	uint_8* pucStartAddress;

	// size of the block 
	size_t xSizeInBytes;
}

/* Define the start address and size of the three RAM regions. */ 
#define RAM1_SIZE             ( 65 * 1024 ) 
static uint_8 ucHeap[RAM1_SIZE];
#define RAM2_START_ADDRESS    ( ( uint8_t * ) 0x00020000 ) 
#define RAM2_SIZE             ( 32 * 1024 )
#define RAM3_START_ADDRESS    ( ( uint8_t * ) 0x00030000 ) 
#define RAM3_SIZE             ( 32 * 1024 ) 

/* Create an array of HeapRegion_t definitions, with an index for each of the three RAM regions, 
and terminating the array with a NULL address.  The HeapRegion_t structures must appear in start address 
order,with the structure that contains the lowest start address appearing first. */ 
const HeapRegion_t xHeapRegions[] = 
{ 
	{ ucHeap, RAM1_SIZE }, 
	{ RAM2_START_ADDRESS, RAM2_SIZE },
	 { RAM3_START_ADDRESS, RAM3_SIZE }, 
	 { NULL,               0         }  /* Marks the end of the array. */ 
}; 
int main( void ) 
{ 
	/* Initialize heap_5. */ 
	vPortDefineHeapRegions( xHeapRegions ); 
	/* Add application code here. */ 
} 

这里在 RAM1 中使用数组声明一块空间是将最后绝对地址确定交给连接器。因为在 RAM1 中还有其余变量,这会占据一部分空间,所以不能直接用 RAM1 的首地址。

比如 STM32 从 0x2000 0000 开始是内部 RAM,0x6000 0000 开始是外部 RAM。在 link script连接文件 )可以只声明内部 RAM 大小和空间。然后用 heap_5 声明外部 RAM 空间,供操作系统分配使用。

总结

  • heap_1 :只分配,不释放,适用于始终存在的情况
  • heap_2 :可以释放,但不会整合剩余的内存块,适用于申请内存大小相同,相近的情况
  • heap_3 :实现了 C 的 mallocfree 函数
  • heap_4 :可以释放,整合剩余的内存块,适用于申请内存大小不同的情况
  • heap_5 :自定义 RAM 块大小和位置,用于分配。

使用函数总结

  • pvPortMalloc()
  • vPortFree()
  • vPortDefineHeapRegions()
  • xPortGetFreeHeapSize()
  • xPortGetMinimumEverFreeHeapSize()
  • void vApplicationMallocFailedHook( void )

这是分配内存失败时的回调函数,需要将configUSE_MALLOC_FAILED_HOOK 设置为1。

参考文章

  1. Mastering the FreeRTOS ™Real Time Kernel

猜你喜欢

转载自blog.csdn.net/lib0000/article/details/112425821