FreeRTOS(V8.0.1)系统之Heap_2

来源:互联网 发布:淘宝安全证书过期 编辑:程序博客网 时间:2024/06/17 01:09
#include <stdlib.h>#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE#include "FreeRTOS.h"#include "task.h"#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE#define configADJUSTED_HEAP_SIZE( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT )// 在使用之前初始化堆结构体static void prvHeapInit( void );//为堆分配空间 ucHeap[17 * 1024] static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];//定义一个连接链表结构,按照其空闲块的大小连接。typedef struct A_BLOCK_LINK{struct A_BLOCK_LINK *pxNextFreeBlock;//链表中下一个空闲块指针size_t xBlockSize;//空闲块的大小} BlockLink_t;static const uint16_t heapSTRUCT_SIZE= ( ( sizeof ( BlockLink_t ) + ( portBYTE_ALIGNMENT - 1 ) ) & ~portBYTE_ALIGNMENT_MASK );#define heapMINIMUM_BLOCK_SIZE( ( size_t ) ( heapSTRUCT_SIZE * 2 ) )static BlockLink_t xStart, xEnd;//创建xStart和xEnd链表连接体,用来指示开始链表和结束链表static size_t xFreeBytesRemaining = configADJUSTED_HEAP_SIZE;//跟踪剩余空闲字节数目,但是对于存储碎片没有提示/* STATIC FUNCTIONS ARE DEFINED AS MACROS TO MINIMIZE THE FUNCTION CALL DEPTH. */ //插入一个空闲块到空闲块链表--其遵守大小排序。小的空闲块在链表开始,大的空闲块在链表末尾。#define prvInsertBlockIntoFreeList( pxBlockToInsert )\{\BlockLink_t *pxIterator;\size_t xBlockSize;\/*获取要插入空闲块的大小*/\xBlockSize = pxBlockToInsert->xBlockSize;\\\/*  遍历链表寻找一块比我们要插入的区域还要大的区域*/\for( pxIterator = &xStart; pxIterator->pxNextFreeBlock->xBlockSize < xBlockSize; pxIterator = pxIterator->pxNextFreeBlock )\{\/*循环内部不做任何事情,只是在不断遍历整个链表*/\}\\/* 更新链表,将要插入的区域插入到链表中正确位置*/\\pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;\pxIterator->pxNextFreeBlock = pxBlockToInsert;\}/*-----------------------------------------------------------*/void *pvPortMalloc( size_t xWantedSize ){BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;static BaseType_t xHeapHasBeenInitialised = pdFALSE;//标志是否首次使用pvPortMallocvoid *pvReturn = NULL;vTaskSuspendAll();//将任务调度器挂起{//若是初次使用malloc,那么需要初始化block链表if( xHeapHasBeenInitialised == pdFALSE ){prvHeapInit();xHeapHasBeenInitialised = pdTRUE;}//第1次调用pvPortMalloc(),那么需要初始化一些关键量,主要是在xStart和xEnd之间将xHeap.ucHeap插进去,下面是一个简单的单向链接图示//xStart->xHeap.ucHeap->xEnd->NULL//对heapSTRUCT_SIZE的定义是这样的: //static const uint16_t heapSTRUCT_SIZE= ( ( sizeof ( BlockLink_t ) + ( portBYTE_ALIGNMENT - 1 ) ) & ~portBYTE_ALIGNMENT_MASK );//我觉得这句话讲不出什么道理来,可能和linux中page之间的hole空间类似,是为了防止越界之类在空间上做的额外申请,//因为xBlockLink数据区作为管理本段内存所使用的关键域,如果因为数据操作越界而被修改,那是相当可怕的尤其在动态内存申请、释放比较频繁的时候//最后计算效果是这样的:如果sizeof( xBlockLink )=7[注:当然编译器不会让它是这个值,怎么着也是2的倍数,这里只是做个极端的例子(gliethttp)],//那么heapSTRUCT_SIZE = 7+3=10,不过实际在at91sam7s64上sizeof( xBlockLink )=8,所以heapSTRUCT_SIZE=8/*要求数量是增强的所有它除了要求的字节数外还包含一个BlockLink_t结构体*/if( xWantedSize > 0 ){xWantedSize += heapSTRUCT_SIZE;/*确保这个块总是对齐到要求的字节数上 。*/if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0 ){//和方法1中一样,将xWantedSize调整为4的整倍数。字节对齐,at91sam7s64默认使用4字节对齐所以下面折行程序,将xWantedSize扩展成4字节的倍数,//比如:xWantedSize=13,那么xWantedSize=13+(4-13%4)=13+3=16(gliethttp)/* Byte alignment required. */xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );}}if( ( xWantedSize > 0 ) && ( xWantedSize < configADJUSTED_HEAP_SIZE ) )//若想要的空闲块的大小在0~configTOTAL_HEAP_SIZE{//xWantedSize是一个合法的数值,那么下面将检测自己是否还有这么大的空闲块,因为在内存释放时不提供小内存块合并整理机制,//所以小内存块会随着时间和不同大小内存块。动态申请、释放的频繁发生最终变的越来越多,直到没有大内存可以申请到,全部都是小内存为止。//可以将相邻的小块内存合并成大块内存,很好的解决了内存合并生成大内存块的问题。//当然这种方法,可能远远不如linux中的Buddy伙伴算法灵活,但是在嵌入式系统中应该能够满足用户对内存的基本需求了。//[其实可以采用以前一篇文章《一种轻巧的“内存动态分配管理机制”》中提到的动态内存分配和申请方式,//文章地址http://blog.chinaunix.net/u1/38994/showart_351550.html]pxPreviousBlock = &xStart;//从空闲内存链表头开始找pxBlock = xStart.pxNextFreeBlock;while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )//空闲块的大小小于需要的大小并且存在下一个空闲块{pxPreviousBlock = pxBlock;//将每次寻找到的空闲块都保存到pxPreviousBlock中,以备后面调用pxBlock = pxBlock->pxNextFreeBlock;//获取下一个空闲块用以检测}if( pxBlock != &xEnd ) //若我们没有到整个内存的最后就找到我们需要的空闲块{pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + heapSTRUCT_SIZE );//pxPreviousBlock->pxNextFreeBlock~pxPreviousBlock->pxNextFreeBlock+heapSTRUCT_SIZE之间的空间存放管理数据pxNextFreeBlock和xBlockSizepxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;//把本pxBlock从空闲链表上摘下//#define heapMINIMUM_BLOCK_SIZE    ( ( size_t ) ( heapSTRUCT_SIZE * 2 ) )//如果本pxBlock剩余的字节数大于heapMINIMUM_BLOCK_SIZE,即:还可以用来申请heapSTRUCT_SIZE个字节数据        //那么切割本pxBlock内存块(注意:xWantedSize是已经包含heapSTRUCT_SIZE的了[gliethttp]),//pxNewBlockLink为切割出来的新的空闲内存块首地址,已经4字节对齐if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE ){pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );//将新的生成的空闲块的地址存起来pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;//计算新空闲内存块包括heapSTRUCT_SIZE控制域和数据域的总大小pxBlock->xBlockSize = xWantedSize;   //将申请到的内存块的大小,填入内存块控制域//把切割出来的内存块添加到空闲内存链表xStart~xEnd之间(注:xStart~xEnd是按空闲内存大小,以从小到达的顺序链起来的单向链表[gliethttp])          //也就是这样虽然看上去,FreeRTOS可以实现各种内存块的申请,但是因为切割出去的小内存块,在内存释放回收的时候,并不能自动进行内存合并整理,          //也就是空闲内存块不能变大,最后只能导致小内存空闲块越来越多,大内存空闲块越来越少,直到不能申请到大内存块prvInsertBlockIntoFreeList( ( pxNewBlockLink ) ); //将新生成的空闲块重新插入到链表中[链表内部按空闲块的大小排序]}xFreeBytesRemaining -= pxBlock->xBlockSize;//剩余空闲块比特数更新}}traceMALLOC( pvReturn, xWantedSize );}(void)xTaskResumeAll();//在运行上面临界区的程序时,可能有任务需要调度,但因为调度器的挂起而没有被调度,只是给出了登记,而这个xTaskResumeAll函数就是要把放进//xPendingReadyList链表中的任务节点转移到真正的就绪链表pxReadyTasksLists里面,如果任务是因为tick缺失或者因为在恢复实际走过的滴答数时有//任务需要抢占CPU,则 xAlreadyYielded 都为真,从而导致下面不会运行,如果没有被抢占也就是说当前还是处于最高级任务,但是上面的延时已经使//其阻塞,从而在下面发生抢占#if( configUSE_MALLOC_FAILED_HOOK == 1 ){if( pvReturn == NULL ){extern void vApplicationMallocFailedHook( void );vApplicationMallocFailedHook();}}#endifreturn pvReturn;//返回申请到的内存数据区地址,pvReturn-heapSTRUCT_SIZE处存放了管理该pvReturn内存的控制数据}/*-----------------------------------------------------------*/void vPortFree( void *pv ){uint8_t *puc = ( uint8_t * ) pv;BlockLink_t *pxLink;if( pv != NULL ){puc -= heapSTRUCT_SIZE;//内存数据区的前heapSTRUCT_SIZE空间存放了管理本段内存数据区的控制数据pxLink = ( void * ) puc;vTaskSuspendAll();//锁住调度器{//把释放出来的内存块添加到空闲内存链表xStart~xEnd之间//(注:xStart~xEnd是按空闲内存大小,以从小到达的顺序链起来的单向链表[gliethttp])//也就是这样虽然看上去,FreeRTOS可以实现各种内存块的申请,但是因为切割出去的小内存块//在内存释放回收的时候,并不能自动进行内存合并整理,也就是空闲内存块不能变大,//最后只能导致小内存空闲块越来越多,大内存空闲块越来越少,直到不能申请到大内存块prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );//将新的空闲块重新插入到链表中[链表内部按空闲块的大小排序]xFreeBytesRemaining += pxLink->xBlockSize;traceFREE( pv, pxLink->xBlockSize );}( void ) xTaskResumeAll();}}/*-----------------------------------------------------------*/size_t xPortGetFreeHeapSize( void ){return xFreeBytesRemaining;}/*-----------------------------------------------------------*/void vPortInitialiseBlocks( void ){/* This just exists to keep the linker quiet. */}/*-----------------------------------------------------------*/static void prvHeapInit( void ){BlockLink_t *pxFirstFreeBlock;uint8_t *pucAlignedHeap;//确保堆栈是从一个正确的对齐边界开始的。pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ( portPOINTER_SIZE_TYPE ) ~portBYTE_ALIGNMENT_MASK ) );//*start存放的空block链表模块的第一个项的指针,主要用来预防编译警告的。xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;xStart.xBlockSize = ( size_t ) 0;//*xEnd被用来标记block空链表中最后一个项的。xEnd.xBlockSize = configADJUSTED_HEAP_SIZE;xEnd.pxNextFreeBlock = NULL;//首先将整个空间设成一个空闲块pxFirstFreeBlock = ( void * ) pucAlignedHeap;pxFirstFreeBlock->xBlockSize = configADJUSTED_HEAP_SIZE;pxFirstFreeBlock->pxNextFreeBlock = &xEnd;}/*-----------------------------------------------------------*/

0 0