heap1是FreeRTOS中内存管理最简单的一个,它简单到只能申请内存。是的,跟你想的一样,一旦申请成功后,这块内存再也无法释放。对于大多数嵌入式系统,特别是对安全要求高的嵌入式系统,这种内存管理策略很有用,因为对系统软件来说,逻辑越简单,越安全。实际上,大多数的嵌入式系统并不需要动态删除任务、信号量和队列等。而是在初始化的时候一次性创建,便一直存在,永不删除。
总结heap1特性如下:
1、适用于那些一旦创建好任务、信号量和队列就再也不删除的的应用。
2、具有可确定性(执行所花费的时间大多数一样),而且不会导致内存碎片。
3、代码实现和内存分配过程都非常简单,内存是从一个静态数组中获取,也就是适用于那些不需要动态内存分配的应用。
我们可以将第一种内存管理看作切面包:初始化的静态数组就像一个完整的长棍面包,每次申请内存,就从一端切下适当长度的面包返还给申请者,直到面包被分配完毕。
heap1的内存管理策略使用两个静态变量来跟踪内存的使用,定义如下:
static size_t xNextFreeByte = ( size_t ) 0; void *pvPortMalloc( size_t xWantedSize ) { static uint8_t *pucAlignedHeap = NULL; ... }
其中 xNextFreeByte 用来保存 pucAlignedHeap 到内存堆剩余内存首地址之间的偏移值,如下图所示:
变量 xNextFreeByte 一开始初始化为0,每次成功申请内存后,都会增加申请内存的大小(对齐会导致实际申请的内存更多)。 变量 pucAlignedHeap 指向每次对齐后的内存堆首地址,内存对齐的好处是硬件访问数据只要单次读取,而且某些类型的内核不支持非对齐访问。STM32F0x系列的单片机要求16位对齐,非对齐读取会导致单片机进入HardFault.
内存申请函数:pvPortMalloc
void *pvPortMalloc( size_t xWantedSize ) { void *pvReturn = NULL; static uint8_t *pucAlignedHeap = NULL; /* Ensure that blocks are always aligned to the required number of bytes. */ #if(portBYTE_ALIGNMENT != 1) { if(xWantedSize & portBYTE_ALIGNMENT_MASK) { /* Byte alignment required. */ xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); } } #endif vTaskSuspendAll(); { if(pucAlignedHeap == NULL) { /* Ensure the heap starts on a correctly aligned boundary. */ pucAlignedHeap = (uint8_t *)(((portPOINTER_SIZE_TYPE)&ucHeap[portBYTE_ALIGNMENT]) & (~((portPOINTER_SIZE_TYPE) portBYTE_ALIGNMENT_MASK))); } /* Check there is enough room left for the allocation. */ if( ( ( xNextFreeByte + xWantedSize ) < configADJUSTED_HEAP_SIZE ) && ( ( xNextFreeByte + xWantedSize ) > xNextFreeByte ) )/* Check for overflow. */ { /* Return the next free byte then increment the index past this block. */ pvReturn = pucAlignedHeap + xNextFreeByte; xNextFreeByte += xWantedSize; } traceMALLOC( pvReturn, xWantedSize ); } ( void ) xTaskResumeAll(); #if( configUSE_MALLOC_FAILED_HOOK == 1 ) { if( pvReturn == NULL ) { extern void vApplicationMallocFailedHook( void ); vApplicationMallocFailedHook(); } } #endif return pvReturn; }
函数逻辑步骤:
1、xWantedSizev 根据宏 portBYTE_ALIGNMENT (实验环境默认为4) 判断是否要字节对齐,调整 xWantedSize 为对齐字节数的倍数。 调整方法是,找到比 xWantedSize 大, 且是最靠近 xWantedSize 的对齐数。假设 xWantedSize 是11,经过调整后,结果为12。
2、注意!内存申请的过程中禁止调度,不能被其他任务打断。FreeRTOS 使用 vTaskSuspendAll 和 xTaskResumeAll 禁止任务调度。
3、不仅申请的内存块大小要求4字节对齐,内存堆的起始地址也必须是4字节对齐。pucAlignedHeap 为NULL的时候说明是第一次申请内存,需要初始化相关参数。ucHeap 此时的起始地址显然不是4字节的倍数,pucAlignedHeap 不能以ucHeap 的首地址作为内存堆的首地址, pucAlignedHeap 经过调整后变为 0x0041b14c, 以3字节的损失换来了起始地址的对齐。ucHeap 和 pucAlignedHeap 第一次初始化的状态如下图所示:
4、进行边界检查,查看内存堆是否足够本次分配,xNextFreeByte 与 xWantedSize的和不能超出可供使用的内存边界。如果内存够分配且不会越界,那么即将申请到的内存首地址赋值给pReturn 并更新全局变量 xNextFreeByte。特别说明的是实际可用的内存是 configADJUSTED_HEAP_SIZE, 而不是configTOTAL_HEAP_SIZE。从代码中我们可以看出,FreeRTOS提前保留一部分字节供内存对齐使用。
/* A few bytes might be lost to byte aligning the heap start address. */ #define configADJUSTED_HEAP_SIZE ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT )
5、宏 configUSE_MALLOC_FAILED_HOOK 为1说明使能了内存申请失败钩子函数,在申请失败的情况下,会调用钩子函数 vApplicationMallocFailedHook ,这个钩子函数由用户应用程序提供,一般用来打印故障信息。
参考文章:
原文地址:https://www.cnblogs.com/soga238/p/10280006.html