freeRTOS内存管理

FreeRTOS(五)——heap文件解析
c
阅读约 18 分钟
FreeRTOS提供了5中内存分配的方式,分别在heap_1.c, heap_2.c, heap_3.c, heap_4.c, heap_5.c中。

对于传统的库函数malloc和free,有以下的缺陷:

线程不安全
耗时
有些嵌入式硬件没有实现
所以FreeRTOS提供了5中内存分配的方式,用户当然也可以自己实现。

下面分别来解析以下这5个heap文件
5个heap的位置在Source/portable/MemMang/

heap_1.c
从一个静态数组力分配
只分配内存,不释放
适用于不会删除任务 、队列、信号量、互斥量的应用程序

注释
The simplest possible implementation of pvPortMalloc(). Note that this implementation does NOT allow allocated memory to be freed again.
表示只分配,不释放

内存分配函数
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;

}
解析
首先是内存对齐判断
#if( portBYTE_ALIGNMENT != 1 )
{
if( xWantedSize & portBYTE_ALIGNMENT_MASK )
{
/* Byte alignment required. */
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
}
}
#endif
这里,如果内存对齐有效,则执行,如果要分配的内存不是对齐字节(通常是8)的整数倍,则补齐,所以xWantedSize要加上要补齐的长度

然后挂起调度器
vTaskSuspendAll();
防止分配内存过程中被打断。

静态数组对齐检查
pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
这里uheap就是静态数组,首先确保它对齐了,因为uheap的首地址可能不是8的倍数,所以,确保实际开始分配的地址是对齐的
比如基础地址低三位是3,+8之后是11,屏蔽低三位后是8,也就是8的倍数,对齐了,这样开头就有5个字节直接被抛弃了

溢出检测
if( ( ( xNextFreeByte + xWantedSize ) < configADJUSTED_HEAP_SIZE ) && ( ( xNextFreeByte + xWantedSize ) > xNextFreeByte ) )
/* Check for overflow. */
分配内存
pvReturn = pucAlignedHeap + xNextFreeByte;
xNextFreeByte += xWantedSize;
恢复调度器
( void ) xTaskResumeAll();
分配失败hook函数
#if( configUSE_MALLOC_FAILED_HOOK == 1 )
{
if( pvReturn == NULL )
{
extern void vApplicationMallocFailedHook( void );
vApplicationMallocFailedHook();
}
}
#endif
如果开启了分配失败hook函数,就调用该hook

释放
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 );

}
heap_1.c里不释放内存

heap_2.c
/*

  • A sample implementation of pvPortMalloc() and vPortFree() that permits
  • allocated blocks to be freed, but does not combine adjacent free blocks
  • into a single larger block (and so will fragment memory). See heap_4.c for
  • an equivalent that does combine adjacent blocks into single larger blocks.
    */
    分配和释放内存,但对内存碎片不做处理,也就是说,不会把内存碎片合并为一个大的碎片

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 require
    initialisation to setup the list of free blocks. */
    if( xHeapHasBeenInitialised == pdFALSE )
    {
        prvHeapInit();
        xHeapHasBeenInitialised = pdTRUE;
    }

    /* The wanted size is increased so it can contain a BlockLink_t
    structure 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 structure
            at its start. */
            pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + heapSTRUCT_SIZE );

            /* This block is being returned for use so must be taken out of the
            list 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 block
                following the number of bytes requested. The void cast is
                used to prevent byte alignment warnings from the compiler. */
                pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );

                /* Calculate the sizes of two blocks split from the single
                block. */
                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();
    }
}
#endif

return pvReturn;

}
/-----------------------------------------------------------/

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 immediately
    before it. */
    puc -= heapSTRUCT_SIZE;

    /* This unexpected casting is to keep some compilers from issuing
    byte 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_2.c里,不同内存间靠链表维系。
所以有这么一个结构体

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;
这个结构体主要充当表头的作用,用来串起每一块内存,方便分配和释放,分配就是一个插入链表的操作,释放就是删除链表的操作。

heap_3.c
这个文件里直接调用了malloc和free,依赖平台自己的实现。

void *pvPortMalloc( size_t xWantedSize )
{
void *pvReturn;

vTaskSuspendAll();
{
    pvReturn = malloc( 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 )
{
if( pv )
{
vTaskSuspendAll();
{
free( pv );
traceFREE( pv, 0 );
}
( void ) xTaskResumeAll();
}
}
heap_4.c
同heap_2.c的实现,不同的是,4会合并内存碎片,而且比malloc高效,非常常用,但是依然有碎片的风险。
它只是把空闲内存都串起来。
比如要分配一个大内存,但是内存里都是不连续的碎片空间,也会造成失败

heap_5.c
同4,不同的,可以使用不连续的内存空间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值