十九、内存管理--FreeRTOS
内存管理的基本概念什么是内存在计算机系统中变量中间数据一般是存储在系统空间的只有在实际使用才将这个数据从存储空间加载到中央处理器内部来进行运算。通常存储空间分为两种一种内部存储空间一种是外部存储空间内部存储空间访问比较快它能够按照变量的地址来随机地进行访问就是RAM叫做随机存储器而外部存储空间保存的内容相对来说比较固定的即使掉电也不会丢失可以把它理解为硬盘。本节讲解的是内部存储空间--RAM随机存储器当然SDRAM配套板子上有但这里不讲FreeRTOS的内存管理是怎样的其实FreeRTOS将内存和内核分开内核包括了一些IPC通信机制但是对于内存管理FreeRTOS没有太多的要求他只规定了内存管理函数的接口就是内存给内核提供了统一的接口具体是怎么实现的内核并不关心比如它的内核是IPC但是内存有很多种方案无论你用了多少种方案你只要给我统一的一个接口就可以了那么在嵌入式设计中内存分配是应该根据我们所设计的一个系统来决定选择哪种内存分配算法的对于可靠性要求非常高的系统应该选择一些静态的分配比如说只需要申请不需要释放假如我们又有申请又有释放的话就需要提供一种动态分配的内存我申请了内存在我不需要的时候我把它释放掉。为什么不用C库的内存管理其实在电脑中我们用malloc和free这些是C库提供的是没有问题的但是在嵌入式系统中它有很大的弊端比如说它分配的时间是不稳定的并且代码的实现也占据了很大的内存空间在电脑中我们又很大的内存比如说8G或者说FLASH很大但是在嵌入式中内存是很小的用不起C库的消耗同时他们是不安全的比如说一个线程来分配一个内存另一个线程也来分配吗不可能的所以FreeRTOS提供的内存管理是当前任务在分配其它任务就不能来打扰我也就是说这个任务来分配的时候另一个任务来打断你你就会分配失败同时他可能会产生内存碎片。而且这两个函数会使得链接器的配置非常复杂如果C库申请内存允许你申请的方向是往栈的方向生长他会覆盖一些栈的内容这是一个非常重要的缺陷。什么是内存碎片在嵌入式系统设计的时候由于有实时性的要求很少去使用虚拟内存像Windows中会使用该机制所有的内存都是由用户参与配置的就是用户必须知道内存是多少分配是多少直接操作的是物理内存所有分配的内存不可以超过系统所有物理内存的总和而系统的所有东西都是用户参与管理的所以内存很重要。同时在嵌入式系统中对内存的分配时间也很严格分配时间如果过长将影响实时性一般的内存管理算法是根据存储的数据长度来一块块去寻找与它相接近的内存块然后将数据存储在里面而寻找这样的内存块需要的时间是不确定的这对于实时操作系统来说是不可接受的因此需要一个更好的内存管理算法让它可以在可预测的时间内完成在嵌入式系统中内存是非常珍贵的嵌入式中64K的内存相对于电脑中8GB的内存是小巫见大巫用了一块就少一块随着内存的申请和释放就会产生一些内存碎片。由于连续的分配和释放导致内存出现不连续的情况而某个时刻需要分配很大的内存空间但此时没有足够大的连续的内存空间所以就会分配失败因此这就是一个内存碎片。内存碎片的危害是非常大的假如此时有一个碎片化的内存很小且不连续有时候分配2K也找不到连续的内存即使当前内存还剩60K或者30K但是找不到2K的空间也会申请失败。因此不同的嵌入式系统对不同的配置有不同的要求FreeRTOS提供了5种内存分配管理算法heap_1.c~heap_5.c根据不一样的应用选择不一样的分配算法heap_1.c只能申请内存不能释放内存看一下代码怎么实现的/* FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. All rights reserved VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. This file is part of the FreeRTOS distribution. FreeRTOS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License (version 2) as published by the Free Software Foundation AND MODIFIED BY the FreeRTOS exception. *************************************************************************** ! NOTE: The modification to the GPL is included to allow you to ! ! distribute a combined work that includes FreeRTOS without being ! ! obliged to provide the source code for proprietary components ! ! outside of the FreeRTOS kernel. ! *************************************************************************** FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Full license text is available on the following link: http://www.freertos.org/a00114.html *************************************************************************** * * * FreeRTOS provides completely free yet professionally developed, * * robust, strictly quality controlled, supported, and cross * * platform software that is more than just the market leader, it * * is the industrys de facto standard. * * * * Help yourself get started quickly while simultaneously helping * * to support the FreeRTOS project by purchasing a FreeRTOS * * tutorial book, reference manual, or both: * * http://www.FreeRTOS.org/Documentation * * * *************************************************************************** http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading the FAQ page My application does not run, what could be wrong?. Have you defined configASSERT()? http://www.FreeRTOS.org/support - In return for receiving this top quality embedded software for free we request you assist our global community by participating in the support forum. http://www.FreeRTOS.org/training - Investing in training allows your team to be as productive as possible as early as possible. Now you can receive FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers Ltd, and the worlds leading authority on the worlds leading RTOS. http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, including FreeRTOSTrace - an indispensable productivity tool, a DOS compatible FAT file system, and our tiny thread aware UDP/IP stack. http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. Come and try FreeRTOSTCP, our new open source TCP/IP stack for FreeRTOS. http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS licenses offer ticketed support, indemnification and commercial middleware. http://www.SafeRTOS.com - High Integrity Systems also provide a safety engineered and independently SIL3 certified version for use in safety and mission critical applications that require provable dependability. 1 tab 4 spaces! */ /* * The simplest possible implementation of pvPortMalloc(). Note that this * implementation does NOT allow allocated memory to be freed again. * * See heap_2.c, heap_3.c and heap_4.c for alternative implementations, and the * memory management pages of http://www.FreeRTOS.org for more information. */ #include stdlib.h /* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining all the API functions to use the MPU wrappers. That should only be done when task.h is included from an application file. */ #define MPU_WRAPPERS_INCLUDED_FROM_API_FILE #include FreeRTOS.h #include task.h #undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE #if( configSUPPORT_DYNAMIC_ALLOCATION 0 ) #error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0 #endif /* A few bytes might be lost to byte aligning the heap start address. */ #define configADJUSTED_HEAP_SIZE ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT ) /* Allocate the memory for the heap. */ /* Allocate the memory for the heap. */ #if( configAPPLICATION_ALLOCATED_HEAP 1 ) /* The application writer has already defined the array used for the RTOS heap - probably so it can be placed in a special segment or address. */ extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; #else static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; #endif /* configAPPLICATION_ALLOCATED_HEAP */ static size_t xNextFreeByte ( size_t ) 0; /*-----------------------------------------------------------*/ 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; } /*-----------------------------------------------------------*/ void vPortFree( void *pv ) { /* Memory cannot be freed using this scheme. See heap_2.c, heap_3.c and heap_4.c for alternative implementations, and the memory management pages of http://www.FreeRTOS.org for more information. */ ( void ) pv; /* Force an assert as it is invalid to call this function. */ configASSERT( pv NULL ); } /*-----------------------------------------------------------*/ void vPortInitialiseBlocks( void ) { /* Only required when static memory is not cleared. */ xNextFreeByte ( size_t ) 0; } /*-----------------------------------------------------------*/ size_t xPortGetFreeHeapSize( void ) { return ( configADJUSTED_HEAP_SIZE - xNextFreeByte ); }首先系统会分配一个数组来做内存堆内存堆的大小在FreeRTOSConfig.h中配置configTOTAL_HEAP_SIZE的分配了36K的RAM分配的过程下图辅助理解heap_2.c最佳匹配算法与heap_4.c很相像但是它不能将两个小内存合并成一个大的内存块。特点1.可以用在那些反复的删除任务、队列、信号量等内核对象且不担心内存碎片的应用程序2.具有不确定性3.最好不用于那些内存分配和释放是随机大小的应用程序。heap_3.c封装了标准C库的malloc()和free()heap_4.c主要使用这个采用最佳匹配算法以及合并算法空闲内存块以单链表的形式连接起来的特点1.可用于重复删除任务、队列、信号量、互斥量等的应用程序2.可用于分配和释放随机字节内存的应用程序xFreeBytesRemaining:表示当前系统中未分配的内存堆大小xMinimumEverFreeBytesRemaining:表示未分配内存堆空间历史最小的内存值priHeapInit()分配和释放函数最好看书heap_5.c与heap_4.c一样不过它支持两块内存允许内存堆跨越多个非连续的内存区调用vPortDefineHeapRegions()函数来实现系统管理的内存初始化HeapRegion_t结构体调用vPortDefineHeapRegions()函数初始化内存堆/* 在内存中为内存堆分配两个内存块 第一个内存块大小为0x10000字节起始地址为0x80000000, 第一个内存块大小为0xa0000字节起始地址为0x90000000。 起始地址为0x80000000的内存块起始地址更低因此放到了数组的第一个位置 */ const HeapRegion_t xHeapRegions[] { { ( uint8_t * ) 0x80000000UL, 0x10000 }, { ( uint8_t * ) 0x90000000UL, 0xa0000 }, { NULL, 0 } //数组结尾 }; /* 向函数vPortDefineHeapRegions() 传递形参 */ vPortDefineHeapRegions(xHeapRegions);vPortDefineHeapRegions()实验