FreeRtos内存管理

来源:互联网 发布:stm32 压缩算法 编辑:程序博客网 时间:2024/06/14 02:29

FreeRtos内存管理

标签(空格分隔): FreeRtos内存


FreeRtos的内存管理接口,都遵循POSIX接口标准.可移植操作系统接口(英语:Portable Operating System Interface,缩写为POSIX),是IEEE为要在各种UNIX操作系统上运行软件,而定义API的一系列互相关联的标准的总称,其正式称呼为IEEE Std 1003,而国际标准名称为ISO/IEC 9945。该标准要求malloc分配的内存地址必须是对齐的地址.

FreeRtos 内存管理共4种方式,分别对应源码文件中的heap_1.c,heap_2.c,heap_3.c,heap_4.c.

FreeRtos内存管理方式 优点 缺点 备注 heap_1.c 只能malloc,不能free heap_2.c 1.malloc速度快
1.容易出现内存碎片,如果不停的malloc小内存块,大内存块会被拆分为小的内存块,但是free的时候,并不会将小内存块合并,就会出现malloc大内存,无法分配成功的问题
2.free时,无法对该节点检查.free其他内存,或者free两次,无法检查 Free-list链表的节点是按照节点内存块大小排序的 heap_3.c 采用系统默认的malloc和free,只是添加了线程保护 heap_4.c 1.内存合并功能,不会出现内存碎片
2.添加节点保护,malloc时会在内存快上设定标志位,避免多次free和free其他内存引起的crash 1.malloc的时候有大小限制,因为malloc标志位是放在size的最高bit
2.malloc的时间比heap_2.c要慢 Free-list链表的节点是按照节点地址排序的,目的是方便内存合并

1 heap_1.c源码分析

这种方式比较简单,只支持用户malloc内存,不支持用户free.heap_1的内存池是用户自定义了一块数组,为了保证地址对齐,将该数组放在了结构体的第2个元素.

#全局数组ucHeap作为heap的内存词,这里为了保证heap起始地址是4字节对齐的,将ucHeap放在结构体里面保证地址对齐.extern struct xRTOS_HEAP{    unsigned long ulDummy;    unsigned char ucHeap[ configTOTAL_HEAP_SIZE ];} xHeap;
void *pvPortMalloc( size_t xWantedSize ){void *pvReturn = 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. */            #保证malloc的长度是4字节对齐的,那下一次malloc的地址还是4字节对齐的            xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );        }    #endif    vTaskSuspendAll();    {        /* Check there is enough room left for the allocation. */        if( ( ( xNextFreeByte + xWantedSize ) < configTOTAL_HEAP_SIZE ) &&            ( ( xNextFreeByte + xWantedSize ) > xNextFreeByte ) )/* Check for overflow. */        {            /* Return the next free byte then increment the index past this            block. */            pvReturn = &( xHeap.ucHeap[ xNextFreeByte ] );            xNextFreeByte += xWantedSize;                   }       }    xTaskResumeAll();    return pvReturn;}

2 heap_2.c源码分析

heap_2的内存池也是一块用户自定义的静态数组,与heap_1的区别是,这块数组的初始化地址可能是非4字节对齐的.因此初始化的时候会取4字节对齐的地址开始作为整个内存池使用.
heap_2的核心思想是将空闲的内存,放到Free-List链表里面管理.Free-List链表里面的节点是按照该节点内存块大小排序存储的.每次malloc的时候,会从Free-List链表里面取出来可以满足malloc大小的节点使用,同时在malloc的时候,会对较大的节点进行拆分,将空闲区域按照区域大小排序插入到Free-List的列表里面.用户调用Free的时候,也是将某个节点插入到Free-List链表里面.

static void prvHeapInit( void ){BlockLink_t *pxFirstFreeBlock;uint8_t *pucAlignedHeap;    /* Ensure the heap starts on a correctly aligned boundary. */    pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );    /* xStart is used to hold a pointer to the first item in the list of free    blocks.  The void cast is used to prevent compiler warnings. */    xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;    xStart.xBlockSize = ( size_t ) 0;    /* xEnd is used to mark the end of the list of free blocks. */    xEnd.xBlockSize = configADJUSTED_HEAP_SIZE;    xEnd.pxNextFreeBlock = NULL;    /* To start with there is a single free block that is sized to take up the    entire heap space. */    pxFirstFreeBlock = ( void * ) pucAlignedHeap;    pxFirstFreeBlock->xBlockSize = configADJUSTED_HEAP_SIZE;    pxFirstFreeBlock->pxNextFreeBlock = &xEnd;}

初始化完成之后Free链表如下.可以看出此时Free链表里面只有一个节点,并且大小就是用户配置之后的对齐的大小.

image_1bnl3gsorlh0l1c1hpqmbl1geqp.png-23.3kB
用户连续两次malloc内存,第一次申请100个字节,第二次申请200个字节.从图中可以看到每次申请的时候都会从Free-list链表里面分配相应的内存空间,并在每块空间上前添加pnext和blocksize,目的就是为了将来Free的时候将该区域放到Free-list链表里面.
image_1bnl80cv2dbfuea1dtkd3m4dr1j.png-55.8kB
用户连续调用两次free之后,使用的节点被重新插入到Free-List的列表里面.从这里可以看出,free的时候,会存在内存碎片.和初始化相比,原来是只有一个空闲节点,现在是变成了3个.
image_1bnljrmoj10s01d6vt02ig62j19.png-44.4kB
free的时候 ,就是通过这个宏将原来的节点插入到Free-list链表里面.

/* * Insert a block into the list of free blocks - which is ordered by size of * the block.  Small blocks at the start of the list and large blocks at the end * of the list. */#define prvInsertBlockIntoFreeList( pxBlockToInsert )                               \{                                                                                   \BlockLink_t *pxIterator;                                                            \size_t xBlockSize;                                                                  \                                                                                    \    xBlockSize = pxBlockToInsert->xBlockSize;                                       \                                                                                    \    /* Iterate through the list until a block is found that has a larger size */    \    /* than the block we are inserting. */                                          \    for( pxIterator = &xStart; pxIterator->pxNextFreeBlock->xBlockSize < xBlockSize; pxIterator = pxIterator->pxNextFreeBlock ) \    {                                                                               \        /* There is nothing to do here - just iterate to the correct position. */   \    }                                                                               \                                                                                    \    /* Update the list to include the block being inserted in the correct */        \    /* position. */                                                                 \    pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;                 \    pxIterator->pxNextFreeBlock = pxBlockToInsert;                                  \}

3.heap_3.c 源码分析

heap_3.c只是在系统的malloc和free加了线程保护.

4.heap_4.c 源码分析

heap_4的内存池也是一块用户自定义的静态数组,与heap_1的区别是,这块数组的初始化地址可能是非4字节对齐的.因此初始化的时候会取4字节对齐的地址开始作为整个内存池使用.
heap_4主要是对heap_2的缺陷进行解决:1.内存分片 2.free时会检查参数的合法性.
heap_4的核心思想是将空闲的内存,放到Free-List链表里面管理,这和heap_2是相同的.区别在于heap_2的链表节点是按照节点内存块大小排序的,heap_4的链表节点是按照节点的地址排序的.

static void prvHeapInit( void ){BlockLink_t *pxFirstFreeBlock;uint8_t *pucAlignedHeap;size_t uxAddress;size_t xTotalHeapSize = configTOTAL_HEAP_SIZE;    /* Ensure the heap starts on a correctly aligned boundary. */    uxAddress = ( size_t ) ucHeap;    if( ( uxAddress & portBYTE_ALIGNMENT_MASK ) != 0 )    {        uxAddress += ( portBYTE_ALIGNMENT - 1 );        uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK );        xTotalHeapSize -= uxAddress - ( size_t ) ucHeap;    }    pucAlignedHeap = ( uint8_t * ) uxAddress;    /* xStart is used to hold a pointer to the first item in the list of free    blocks.  The void cast is used to prevent compiler warnings. */    xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;    xStart.xBlockSize = ( size_t ) 0;    /* pxEnd is used to mark the end of the list of free blocks and is inserted    at the end of the heap space. */    uxAddress = ( ( size_t ) pucAlignedHeap ) + xTotalHeapSize;    uxAddress -= xHeapStructSize;    uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK );    pxEnd = ( void * ) uxAddress;    pxEnd->xBlockSize = 0;    pxEnd->pxNextFreeBlock = NULL;    /* To start with there is a single free block that is sized to take up the    entire heap space, minus the space taken by pxEnd. */    pxFirstFreeBlock = ( void * ) pucAlignedHeap;    pxFirstFreeBlock->xBlockSize = uxAddress - ( size_t ) pxFirstFreeBlock;    pxFirstFreeBlock->pxNextFreeBlock = pxEnd;    /* Only one block exists - and it covers the entire usable heap space. */    xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;    xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;    /* Work out the position of the top bit in a size_t variable. */    #这是个标志位,uint32最高bit被置为1,之后malloc会拿该标志位进行mask运算,表示该内存区域已经malloc完成    xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );}

初始化完成之后Free-list链表如下,从图中可以看出与heap_2的区别是将X_end节点独立开发,放到了ucHeap里.
image_1bnnfsnd8d6e14a94iib1eh169.png-24kB
用户连续两次malloc内存,第一次申请100个字节,第二次申请200个字节.从图中可以看到每次申请的时候都会从Free-list链表里面分配相应的内存空间,并在每块空间上前添加pnext和blocksize,目的就是为了将来Free的时候将该区域放到Free-list链表里面. 从图中可以看到,每个节点xBlockSize=xBlockSize|xBlockAllcatedBit;使用xBlockSize的最高bit表示该内存块已经malloc,malloc的size最大范围变为 原来的1/2.

image_1bnnh3qlu1n3e1gattkg1rl31hrm.png-43.6kB

用户连续调用两次free之后,使用的节点被重新插入到Free-List的列表里面.从这里可以看出,free的时候,会合并内存碎片.
image_1bnnhfuc0p0d4r2dn7q3v1ic1g.png-23.5kB
free源码

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 -= xHeapStructSize;        /* This casting is to keep the compiler from issuing warnings. */        pxLink = ( void * ) puc;        /* Check the block is actually allocated. */        configASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 );        configASSERT( pxLink->pxNextFreeBlock == NULL );#检测这个内存块是否合法,malloc的时候,会将该bit置上        if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 )        {#这个也是在malloc的时候置为了NULL            if( pxLink->pxNextFreeBlock == NULL )            {                /* The block is being returned to the heap - it is no longer                allocated. */#清空该bit                pxLink->xBlockSize &= ~xBlockAllocatedBit;                vTaskSuspendAll();                {                    /* Add this block to the list of free blocks. */                    xFreeBytesRemaining += pxLink->xBlockSize;                    traceFREE( pv, pxLink->xBlockSize );                    prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );                }                ( void ) xTaskResumeAll();            }            else            {                mtCOVERAGE_TEST_MARKER();            }        }        else        {            mtCOVERAGE_TEST_MARKER();        }    }}

插入free-list链表操作

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 address    than 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 after    make 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 before    make 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 block    before and the block after, then it's pxNextFreeBlock pointer will have    already been set, and should not be set here as that would make it point    to itself. */    if( pxIterator != pxBlockToInsert )    {        pxIterator->pxNextFreeBlock = pxBlockToInsert;    }    else    {        mtCOVERAGE_TEST_MARKER();    }}
原创粉丝点击