php做网站怎么布局,什么是域名解析,长沙关键词优化新行情报价,做网站一定要自己搭建服务器吗目录
heap_1.c内存管理算法
heap_2.c内存管理算法
heap_3.c内存管理算法 heap_4.c内存管理算法
heap_5.c内存管理算法 内存管理对应用程序和操作系统来说非常重要#xff0c;而内存对于嵌入式系统来说是寸土寸金的资源#xff0c;FreeRTOS操作系统将内核与内存管理分开实…目录
heap_1.c内存管理算法
heap_2.c内存管理算法
heap_3.c内存管理算法 heap_4.c内存管理算法
heap_5.c内存管理算法 内存管理对应用程序和操作系统来说非常重要而内存对于嵌入式系统来说是寸土寸金的资源FreeRTOS操作系统将内核与内存管理分开实现操作系统内核仅规定了必要的内存管理函数原型而不关心这些函数的具体实现。这样做大有好处可以增加系统的灵活性不同的应用场合可以使用不同的内存分配实现选择对自己更有利的内存管理算法。比如对于安全型的嵌入式系统通常不允许动态分配内存那么可以采用非常简单的内存管理策略一经申请的内存甚至不允许被释放。 FreeRTOS内核规定的几个内存管理的函数原型为
1. void *pvPortMalloc( size_t xSize ) 内存申请函数
2. void vPortFree( void *pv ) 内存释放函数
3. void vPortInitialiseBlocks( void ) 初始化内存堆函数
4. size_t xPortGetFreeHeapSize( void ) 获取当前未分配的内存堆大小
5. size_t xPortGetMinimumEverFreeHeapSize( void )获取未分配的内存堆历史最小值FreeRTOS中内存管理的接口函数为pvPortMalloc、vPortFree对应于C库的malloc和free函数。
源码中对应的提供了5个文件对应内存管理的5种方法。
文件 优点缺点heap_1.c分配简单时间确定只分配不回收heap_2.c动态分配最佳匹配碎片时间不定heap_3.c调用标准库函数速度慢时间不定heap_4.c相邻空闲内存可以合并可解决碎片问题时间不定heap_5.c在heap_4.c基础上支持分隔的内存块可解决碎片问题时间不定
对于heap_1.c和heap_2.c和heap_4.c这三种内存管理算法内存堆实际上是一个很大的静态数组定义为
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
其中宏configTOTAL_HEAP_SIZE用来定义内存堆的大小这个宏在FreeRTOSConfig.h中设置。 对于heap_3.c这种策略只是简单的包装了标准库中的malloc()和free()函数包装后的malloc()和free()函数具备线程保护。因此内存堆需要通过编译器或者启动文件设置堆空间。 heap_5.c比较有趣它允许程序设置多个非连续内存堆比如需要快速访问的内存堆设置在片内RAM稍微慢速访问的内存堆设置在外部RAM。每个内存堆的起始地址和大小由应用程序设计者定义。需要手动自己定义一个数组手动调用函数初始化内存
heap_1.c内存管理算法 通过阅读heap_1.c的源码分析可以知道它只实现了pvPortMalloc没有实现vPortFree.
如果你的程序不需要删除内核对象那么可以使用heap_1.c它只分配不回收内存
实现最简单没有碎片问题一些要求非常严格的系统里不允许使用动态内存就可以使用heap_1
对于这种内存分配我们可以把内存看成一个一根完整的长面包每次申请内存就从一端切下适当长度的面包返回给申请者知道面包被分配完毕就这么简单。
它的实现原理很简单首先定义一个大数组
/* 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 ];
#elsestatic uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#endif /* configAPPLICATION_ALLOCATED_HEAP */然后对于 pvPortMalloc 调用时从这个数组中分配空间。 FreeRTOS在创建任务时需要2个内核对象task control block(TCB)、stack。 使用heap_1时内存分配过程如下图所示 A创建任务之前整个数组都是空闲的B创建第1个任务之后蓝色区域被分配出去了C创建3个任务之后的数组使用情况 内存申请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 ) );}}#endifvTaskSuspendAll();{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 thisblock. */pvReturn pucAlignedHeap xNextFreeByte;xNextFreeByte xWantedSize;}traceMALLOC( pvReturn, xWantedSize );}( void ) xTaskResumeAll();#if( configUSE_MALLOC_FAILED_HOOK 1 ){if( pvReturn NULL ){extern void vApplicationMallocFailedHook( void );vApplicationMallocFailedHook();}}#endifreturn pvReturn;
}如果是第一次执行这个函数需要将变量pucAlignedHeap指向内存堆区域第一个地址对齐处。我们上面说内存堆其实是一个大数组编译器为这个数组分配的起始地址是随机的可能不符合我们的对齐需要这时候要进行调整。比如数组地址是0x20000001这时候一些芯片是对齐才能工作所以代码一开始设置的内存初始状态如下图所示 在申请内存过程中同样会对要申请的内存大小做一个字节对齐的操作向上对齐 比如对于8字节对齐的系统来说 你申请11个字节大小的内存这时候向上对齐就是16字节接下来就是挂起调度器防止多个任务同时申请内存因为内存是不可重入的使用了静态变量。
分配内存的过程如下图 下面是一个更加清晰的整体的一个分配内存的图 内存分配完成后不管有没有分配成功都恢复之前挂起的调度器。 如果内存分配不成功这里最可能是内存堆空间不够用了会调用一个钩子函数vApplicationMallocFailedHook()。这个钩子函数由应用程序提供通常我们可以打印内存分配设备信息或者点亮也故障指示灯。
heap_2.c内存管理算法
Heap_2之所以还保留只是为了兼容以前的代码。新设计中不再推荐使用Heap_2。建议使用Heap_4来替代Heap_2更加高效。
Heap_2也是在数组上分配内存跟Heap_1不一样的地方在于
Heap_2使用最佳匹配算法(best fit)来分配内存它支持vPortFree
最佳匹配算法
假设heap有3块空闲内存5字节、25字节、100字节pvPortMalloc想申请20字节找出最小的、能满足pvPortMalloc的内存25字节把它划分为20字节、5字节 返回这20字节的地址剩下的5字节仍然是空闲状态留给后续的pvPortMalloc使用
与Heap_4相比Heap_2不会合并相邻的空闲内存所以Heap_2会导致严重的碎片化问题。
但是如果申请、分配内存时大小总是相同的这类场景下Heap_2没有碎片化的问题。所以它适合这种场景频繁地创建、删除任务但是任务的栈大小都是相同的(创建任务时需要分配TCB和栈TCB总是一样的)。
虽然不再推荐使用heap_2但是它的效率还是远高于malloc、free。
Tips: 其实heap_2.c算法就是在heap_1.c的基础上进行的改进其中可以思考为什么heap_1.c没办法实现free函数吗因为free函数释放内存只传入你要释放内存的地址对于要释放多少呢heap_1.c算法是没有记录的我记得我在学习C语言的时候也想过这个问题free函数只传入要释放内存的地址它怎么知道要释放多大呢在操作系统的学习中了解过这部分知识其实申请的时候会有一个头部在申请内存的前面这个添加头部的方法其实很常见。 局部静态变量pucAlignedHeap指向对齐后的内存堆起始位置。地址对齐的原因在第一种内存管理策略中已经说明。假如内存堆数组ucHeap从RAM地址0x10002003处开始系统按照8字节对齐则对齐后的内存堆与第一个内存管理策略一样如图所示 与第一种内存管理策略不同第二种内存管理策略使用一个链表结构来跟踪记录空闲内存块将空闲块组成一个链表。头部结构体定义为
typedef struct A_BLOCK_LINK
{struct A_BLOCK_LINK *pxNextFreeBlock; /* The next free block in the list. */size_t xBlockSize; /* The size of the free block. */
} BlockLink_t;static BlockLink_t xStart, xEnd;
两个BlockLink_t类型的全局静态变量xStart和xEnd用来标识空闲内存块的起始和结束。刚开始时整个内存堆有效空间就是一个空闲块 当申请N字节内存时实际上不仅需要分配N字节内存还要分配一个BlockLink_t类型结构体空间用于描述这个内存块结构体空间位于空闲内存块的最开始处。当然和第一种内存管理策略一样申请的内存大小加上BlockLink_t类型结构体大小之后也要向上扩大到对齐字节数的整数倍 。
我们看一下内存申请过程首先计算实际要分配的内存大小判断申请的内存是否合法。如果合法则从链表头xStart开始查找如果某个空闲块的xBlockSize字段大小能容得下要申请的内存则从这块内存取出合适的部分返回给申请者剩下的内存块组成一个新的空闲块按照空闲块的大小顺序插入到空闲块链表中小块在前大块在后。
/*-----------------------------------------------------------*/void *pvPortMalloc( size_t xWantedSize )
{
BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
static BaseType_t xHeapHasBeenInitialised pdFALSE;
void *pvReturn NULL;vTaskSuspendAll();{/* If this is the first call to malloc then the heap will requireinitialisation to setup the list of free blocks. */if( xHeapHasBeenInitialised pdFALSE ){prvHeapInit();xHeapHasBeenInitialised pdTRUE;}/* The wanted size is increased so it can contain a BlockLink_tstructure in addition to the requested amount of bytes. */if( xWantedSize 0 ){xWantedSize heapSTRUCT_SIZE;/* Ensure that blocks are always aligned to the required number of bytes. */if( ( xWantedSize portBYTE_ALIGNMENT_MASK ) ! 0 ){/* Byte alignment required. */xWantedSize ( portBYTE_ALIGNMENT - ( xWantedSize portBYTE_ALIGNMENT_MASK ) );}}if( ( xWantedSize 0 ) ( xWantedSize configADJUSTED_HEAP_SIZE ) ){/* Blocks are stored in byte order - traverse the list from the start(smallest) block until one of adequate size is found. */pxPreviousBlock xStart;pxBlock xStart.pxNextFreeBlock;while( ( pxBlock-xBlockSize xWantedSize ) ( pxBlock-pxNextFreeBlock ! NULL ) ){pxPreviousBlock pxBlock;pxBlock pxBlock-pxNextFreeBlock;}/* If we found the end marker then a block of adequate size was not found. */if( pxBlock ! xEnd ){/* Return the memory space - jumping over the BlockLink_t structureat its start. */pvReturn ( void * ) ( ( ( uint8_t * ) pxPreviousBlock-pxNextFreeBlock ) heapSTRUCT_SIZE );/* This block is being returned for use so must be taken out of thelist of free blocks. */pxPreviousBlock-pxNextFreeBlock pxBlock-pxNextFreeBlock;/* If the block is larger than required it can be split into two. */if( ( pxBlock-xBlockSize - xWantedSize ) heapMINIMUM_BLOCK_SIZE ){/* This block is to be split into two. Create a new blockfollowing the number of bytes requested. The void cast isused to prevent byte alignment warnings from the compiler. */pxNewBlockLink ( void * ) ( ( ( uint8_t * ) pxBlock ) xWantedSize );/* Calculate the sizes of two blocks split from the singleblock. */pxNewBlockLink-xBlockSize pxBlock-xBlockSize - xWantedSize;pxBlock-xBlockSize xWantedSize;/* Insert the new block into the list of free blocks. */prvInsertBlockIntoFreeList( ( pxNewBlockLink ) );}xFreeBytesRemaining - pxBlock-xBlockSize;}}traceMALLOC( pvReturn, xWantedSize );}( void ) xTaskResumeAll();#if( configUSE_MALLOC_FAILED_HOOK 1 ){if( pvReturn NULL ){extern void vApplicationMallocFailedHook( void );vApplicationMallocFailedHook();}}#endifreturn pvReturn;
}阅读源码的个人理解
第一次调用malloc申请内存时需要进行heap的初始化操作而它的初始化操作就是将heap加上一个头部以及初始化xStart和xEndheap_2.c的内存管理是通过块Block进行管理的。
在申请内存块的时候使用的是最佳匹配算法在空闲块的链表中找到一个刚好合适又最小的块如果申请之后还剩下比较大的内存则会进行拆分把剩余的内存块添加回空闲内存块管理的链表中按照内存大小进行插入。正是因为上面空闲内存的插入是按照大小进行插入这一个特点注定了空闲内存块之间是不能进行合并的所以会出现内存碎片的情况.
所以我们可以知道heap_2.c适合对于那些频繁申请释放固定大小内存的场合这样不会造成内存碎片。
/*-----------------------------------------------------------*/void vPortFree( void *pv )
{
uint8_t *puc ( uint8_t * ) pv;
BlockLink_t *pxLink;if( pv ! NULL ){/* The memory being freed will have an BlockLink_t structure immediatelybefore it. */puc - heapSTRUCT_SIZE;/* This unexpected casting is to keep some compilers from issuingbyte alignment warnings. */pxLink ( void * ) puc;vTaskSuspendAll();{/* Add this block to the list of free blocks. */prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );xFreeBytesRemaining pxLink-xBlockSize;traceFREE( pv, pxLink-xBlockSize );}( void ) xTaskResumeAll();}
}阅读代码释放内存的原理也很简单无非就是通过要释放内存的地址往前推得到内存块结构体就可以知道要释放的内存大小了。
heap_3.c内存管理算法
第三种内存管理策略简单的封装了标准库中的malloc()和free()函数采用的封装方式是操作内存前挂起调度器、完成后再恢复调度器。封装后的malloc()和free()函数具备线程保护。 第一种和第二种内存管理策略都是通过定义一个大数组作为内存堆数组的大小由宏configTOTAL_HEAP_SIZE指定。第三种内存管理策略与前两种不同它不再需要通过数组定义内存堆而是需要使用编译器设置内存堆空间一般在启动代码中设置。因此宏configTOTAL_HEAP_SIZE对这种内存管理策略是无效的。 heap_4.c内存管理算法
跟Heap_1、Heap_2一样Heap_4也是使用大数组来分配内存。
Heap_4使用首次适应算法(first fit)来分配内存。它还会把相邻的空闲内存合并为一个更大的空闲内存这有助于较少内存的碎片问题。
首次适应算法
假设堆中有3块空闲内存5字节、200字节、100字节pvPortMalloc想申请20字节找出第1个能满足pvPortMalloc的内存200字节把它划分为20字节、180字节 返回这20字节的地址剩下的180字节仍然是空闲状态留给后续的pvPortMalloc使用
Heap_4会把相邻空闲内存合并为一个大的空闲内存可以较少内存的碎片化问题。适用于这种场景频繁地分配、释放不同大小的内存。 Tips其实个人感觉第四种内存管理算法就是在第二种内存管理算法heap_2.c的基础上再次进行改进而已 由于heap_2.c算法存在内存碎片的问题所以heap_4.c可以将相邻的内存块进行合并这就意味着空闲内存块之间是按照地址进行存放的不是像heap_2.c一样按照大小进行排序管理的。
和第二种内存管理策略一样它也使用一个链表结构来跟踪记录空闲内存块。头部结构体定义是一样的。
与第二种内存管理策略一样空闲内存块也是以单链表的形式组织起来的BlockLink_t类型的全局静态变量xStart表示链表头但第四种内存管理策略的链表尾保存在内存堆空间最后位置并使用BlockLink_t指针类型局部静态变量pxEnd指向这个区域第二种内存管理策略使用静态变量xEnd表示链表尾这里我想吐槽一下感觉完全没必要啊完全可以使用和heap_2.c一样的管理可能这里是为了节省内存能省就省就和迷你列表项一样 重点第四种内存管理算法和第二种内存管理算法之间最大的不同就是第四种内存管理算法的空闲内存块链表不是以内存块大小为存储顺序而是以内存块起始地址大小为存储顺序的地址小的在前地址大的在后其实这好像是必须的因为你要合并相邻的内存块肯定地址要按顺序来才能合并嘛这是为了适应合并算法而作的改变。 /*-----------------------------------------------------------*/void *pvPortMalloc( size_t xWantedSize )
{
BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
void *pvReturn NULL;vTaskSuspendAll();{/* If this is the first call to malloc then the heap will requireinitialisation to setup the list of free blocks. */if( pxEnd NULL ){prvHeapInit();}else{mtCOVERAGE_TEST_MARKER();}/* Check the requested block size is not so large that the top bit isset. The top bit of the block size member of the BlockLink_t structureis used to determine who owns the block - the application or thekernel, so it must be free. */if( ( xWantedSize xBlockAllocatedBit ) 0 ){/* The wanted size is increased so it can contain a BlockLink_tstructure in addition to the requested amount of bytes. */if( xWantedSize 0 ){xWantedSize xHeapStructSize;/* Ensure that blocks are always aligned to the required numberof bytes. */if( ( xWantedSize portBYTE_ALIGNMENT_MASK ) ! 0x00 ){/* Byte alignment required. */xWantedSize ( portBYTE_ALIGNMENT - ( xWantedSize portBYTE_ALIGNMENT_MASK ) );configASSERT( ( xWantedSize portBYTE_ALIGNMENT_MASK ) 0 );}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}if( ( xWantedSize 0 ) ( xWantedSize xFreeBytesRemaining ) ){/* Traverse the list from the start (lowest address) block untilone of adequate size is found. */pxPreviousBlock xStart;pxBlock xStart.pxNextFreeBlock;while( ( pxBlock-xBlockSize xWantedSize ) ( pxBlock-pxNextFreeBlock ! NULL ) ){pxPreviousBlock pxBlock;pxBlock pxBlock-pxNextFreeBlock;}/* If the end marker was reached then a block of adequate sizewas not found. */if( pxBlock ! pxEnd ){/* Return the memory space pointed to - jumping over theBlockLink_t structure at its start. */pvReturn ( void * ) ( ( ( uint8_t * ) pxPreviousBlock-pxNextFreeBlock ) xHeapStructSize );/* This block is being returned for use so must be taken outof the list of free blocks. */pxPreviousBlock-pxNextFreeBlock pxBlock-pxNextFreeBlock;/* If the block is larger than required it can be split intotwo. */if( ( pxBlock-xBlockSize - xWantedSize ) heapMINIMUM_BLOCK_SIZE ){/* This block is to be split into two. Create a newblock following the number of bytes requested. The voidcast is used to prevent byte alignment warnings from thecompiler. */pxNewBlockLink ( void * ) ( ( ( uint8_t * ) pxBlock ) xWantedSize );configASSERT( ( ( ( size_t ) pxNewBlockLink ) portBYTE_ALIGNMENT_MASK ) 0 );/* Calculate the sizes of two blocks split from thesingle block. */pxNewBlockLink-xBlockSize pxBlock-xBlockSize - xWantedSize;pxBlock-xBlockSize xWantedSize;/* Insert the new block into the list of free blocks. */prvInsertBlockIntoFreeList( pxNewBlockLink );}else{mtCOVERAGE_TEST_MARKER();}xFreeBytesRemaining - pxBlock-xBlockSize;if( xFreeBytesRemaining xMinimumEverFreeBytesRemaining ){xMinimumEverFreeBytesRemaining xFreeBytesRemaining;}else{mtCOVERAGE_TEST_MARKER();}/* The block is being returned - it is allocated and ownedby the application and has no next block. */pxBlock-xBlockSize | xBlockAllocatedBit;pxBlock-pxNextFreeBlock NULL;xNumberOfSuccessfulAllocations;}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}traceMALLOC( pvReturn, xWantedSize );}( void ) xTaskResumeAll();#if( configUSE_MALLOC_FAILED_HOOK 1 ){if( pvReturn NULL ){extern void vApplicationMallocFailedHook( void );vApplicationMallocFailedHook();}else{mtCOVERAGE_TEST_MARKER();}}#endifconfigASSERT( ( ( ( size_t ) pvReturn ) ( size_t ) portBYTE_ALIGNMENT_MASK ) 0 );return pvReturn;
}申请内存的代码和heap_2.c相比无非就是heap的初始化对于xEnd有点不一样一个是指针一个是变量然后就是空闲内存块的插入算法是不一样一个按照地址来一个按照大小插入。
/*-----------------------------------------------------------*/static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert )
{
BlockLink_t *pxIterator;
uint8_t *puc;/* Iterate through the list until a block is found that has a higher addressthan the block being inserted. */for( pxIterator xStart; pxIterator-pxNextFreeBlock pxBlockToInsert; pxIterator pxIterator-pxNextFreeBlock ){/* Nothing to do here, just iterate to the right position. */}/* Do the block being inserted, and the block it is being inserted aftermake a contiguous block of memory? */puc ( uint8_t * ) pxIterator;if( ( puc pxIterator-xBlockSize ) ( uint8_t * ) pxBlockToInsert ){pxIterator-xBlockSize pxBlockToInsert-xBlockSize;pxBlockToInsert pxIterator;}else{mtCOVERAGE_TEST_MARKER();}/* Do the block being inserted, and the block it is being inserted beforemake a contiguous block of memory? */puc ( uint8_t * ) pxBlockToInsert;if( ( puc pxBlockToInsert-xBlockSize ) ( uint8_t * ) pxIterator-pxNextFreeBlock ){if( pxIterator-pxNextFreeBlock ! pxEnd ){/* Form one big block from the two blocks. */pxBlockToInsert-xBlockSize pxIterator-pxNextFreeBlock-xBlockSize;pxBlockToInsert-pxNextFreeBlock pxIterator-pxNextFreeBlock-pxNextFreeBlock;}else{pxBlockToInsert-pxNextFreeBlock pxEnd;}}else{pxBlockToInsert-pxNextFreeBlock pxIterator-pxNextFreeBlock;}/* If the block being inserted plugged a gab, so was merged with the blockbefore and the block after, then its pxNextFreeBlock pointer will havealready been set, and should not be set here as that would make it pointto itself. */if( pxIterator ! pxBlockToInsert ){pxIterator-pxNextFreeBlock pxBlockToInsert;}else{mtCOVERAGE_TEST_MARKER();}
}这个链表插入函数在内存释放函数中也会进行调用它也是内存释放的关键代码 它里面最关键的就是要进行前后相邻内存块是否能进行合并的判断和合并操作先和前面的内存块判断是否进行合并然后再和后面的内存块进行同样的操作。总的来说其实不是特别难而且heap_4.c是一种比较均衡的内存管理算法克服了前面heap_1.c和heap_2.c的问题所以大多数还是采用这个内存管理算法的以前不懂得时候就感觉它是万能的选这个准没错。
heap_5.c内存管理算法
Heap_5分配内存、释放内存的算法跟Heap_4是一样的。
相比于Heap_4Heap_5并不局限于管理一个大数组它可以管理多块、分隔开的内存。
在嵌入式系统中内存的地址可能并不连续我们heap_4.c是一块连续的内存声明的一个大数组嘛而有些时候有的系统中内存地址可能不连续这种场景下可以使用Heap_5。
既然内存是分隔开的那么就需要进行初始化确定这些内存块在哪、多大
在使用pvPortMalloc之前必须先指定内存块的信息需要用户自己指定 使用vPortDefineHeapRegions来指定这些信息
怎么指定一块内存使用如下结构体
typedef struct HeapRegion
{uint8_t * pucStartAddress; // 起始地址size_t xSizeInBytes; // 大小
} HeapRegion_t;
怎么指定多块内存使用一个HeapRegion_t数组在这个数组中低地址在前、高地址在后必须这样做。
比如
HeapRegion_t xHeapRegions[]
{{ ( uint8_t * ) 0x80000000UL, 0x10000 }, // 起始地址0x80000000大小0x10000{ ( uint8_t * ) 0x90000000UL, 0xa0000 }, // 起始地址0x90000000大小0xa0000{ NULL, 0 } // 表示数组结束};
因为heap_5.c的malloc函数没有像前面几种内存管理算法一样第一次调用的时候进行了内存heap的初始化所以在使用任何内存分配和释放操作前调用vPortDefineHeapRegions()函数初始化这些内存堆用户自己手动调用。 第一、第二和第四种内存管理策略都是利用一个大数组作为内存堆使用并且只需要应用程序指定数组的大小通过宏configTOTAL_HEAP_SIZE定义数组定义由内存管理策略实现。第五种内存管理策略有些不同首先它允许跨内存区定义多个内存堆比如在片内RAM中定义一个内存堆还可以在片外RAM再定义内存堆其次用户需要指定每个内存堆区域的起始地址和内存堆大小、将它们放在一个HeapRegion_t结构体类型数组中并需要在使用任何内存分配和释放操作前调用vPortDefineHeapRegions()函数初始化这些内存堆。
/*-----------------------------------------------------------*/void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions )
{
BlockLink_t *pxFirstFreeBlockInRegion NULL, *pxPreviousFreeBlock;
size_t xAlignedHeap;
size_t xTotalRegionSize, xTotalHeapSize 0;
BaseType_t xDefinedRegions 0;
size_t xAddress;
const HeapRegion_t *pxHeapRegion;/* Can only call once! */configASSERT( pxEnd NULL );pxHeapRegion ( pxHeapRegions[ xDefinedRegions ] );while( pxHeapRegion-xSizeInBytes 0 ){xTotalRegionSize pxHeapRegion-xSizeInBytes;/* Ensure the heap region starts on a correctly aligned boundary. */xAddress ( size_t ) pxHeapRegion-pucStartAddress;if( ( xAddress portBYTE_ALIGNMENT_MASK ) ! 0 ){xAddress ( portBYTE_ALIGNMENT - 1 );xAddress ~portBYTE_ALIGNMENT_MASK;/* Adjust the size for the bytes lost to alignment. */xTotalRegionSize - xAddress - ( size_t ) pxHeapRegion-pucStartAddress;}xAlignedHeap xAddress;/* Set xStart if it has not already been set. */if( xDefinedRegions 0 ){/* xStart is used to hold a pointer to the first item in the list offree blocks. The void cast is used to prevent compiler warnings. */xStart.pxNextFreeBlock ( BlockLink_t * ) xAlignedHeap;xStart.xBlockSize ( size_t ) 0;}else{/* Should only get here if one region has already been added to theheap. */configASSERT( pxEnd ! NULL );/* Check blocks are passed in with increasing start addresses. */configASSERT( xAddress ( size_t ) pxEnd );}/* Remember the location of the end marker in the previous region, ifany. */pxPreviousFreeBlock pxEnd;/* pxEnd is used to mark the end of the list of free blocks and isinserted at the end of the region space. */xAddress xAlignedHeap xTotalRegionSize;xAddress - xHeapStructSize;xAddress ~portBYTE_ALIGNMENT_MASK;pxEnd ( BlockLink_t * ) xAddress;pxEnd-xBlockSize 0;pxEnd-pxNextFreeBlock NULL;/* To start with there is a single free block in this region that issized to take up the entire heap region minus the space taken by thefree block structure. */pxFirstFreeBlockInRegion ( BlockLink_t * ) xAlignedHeap;pxFirstFreeBlockInRegion-xBlockSize xAddress - ( size_t ) pxFirstFreeBlockInRegion;pxFirstFreeBlockInRegion-pxNextFreeBlock pxEnd;/* If this is not the first region that makes up the entire heap spacethen link the previous region to this region. */if( pxPreviousFreeBlock ! NULL ){pxPreviousFreeBlock-pxNextFreeBlock pxFirstFreeBlockInRegion;}xTotalHeapSize pxFirstFreeBlockInRegion-xBlockSize;/* Move onto the next HeapRegion_t structure. */xDefinedRegions;pxHeapRegion ( pxHeapRegions[ xDefinedRegions ] );}xMinimumEverFreeBytesRemaining xTotalHeapSize;xFreeBytesRemaining xTotalHeapSize;/* Check something was actually defined before it is accessed. */configASSERT( xTotalHeapSize );/* Work out the position of the top bit in a size_t variable. */xBlockAllocatedBit ( ( size_t ) 1 ) ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );
}这个函数可以看到代码量还是很大的但其实原理就是以链表的形式管理这些分隔的内存块。 一旦内存堆初始化之后内存申请和释放都和第四种内存管理策略相同不再单独分析。 第五种内存管理策略允许内存堆跨越多个非连续的内存区并且需要显示的初始化内存堆除此之外其它操作都和第四种内存管理策略十分相似。