freeRTOS 内存管理

标准 C 库中的 malloc()和 free()也可以实现动态内存管理,但是如下原因限制了其使用:

● 在小型的嵌入式系统中效率不高。
● 会占用很多的代码空间。
● 它们不是线程安全的。
● 具有不确定性,每次执行所用的时间不同。
● 会导致内存碎片。
● 使链接器的配置变得复杂。

五种内存分配方案: FreeRTOS->Source->portable->MemMang

FreeRTOS 中的内存堆为 ucHeap[],大小为configTOTAL_HEAP_SIZE。

#if( configAPPLICATION_ALLOCATED_HEAP == 1 )
  extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; //需要用户自行定义内存堆
#else
  static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; //编译器决定
#endif
当宏 configAPPLICATION_ALLOCATED_HEAP 为 1 的时候需要用户自行定义内存堆,
否则的话由编译器来决定,默认都是由编译器来决定的。
如果自己定义的话就可以将内存堆定义到外部 SRAM 或者 SDRAM 中。

1、2、4、5可以通过函数 xPortGetFreeHeapSize()来获取剩余的内存大小。

 

 

 

一、heap_1

#define portBYTE_ALIGNMENT 8                                portmacro.h
#define portBYTE_ALIGNMENT_MASK (portBYTE_ALIGNMENT - 1) portable.h

heap_1.c
/* A few bytes might be lost to byte aligning the heap start address. */
#define configADJUSTED_HEAP_SIZE ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT )
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. */ 申请的时候,WantedSize向align_mask对齐(取余),WantedSize减去这个余数零头,然后加上一个byte_align整数。比如想要9,向7取余是1,就是9-1+8=16,最后取了16个字节的空间。(8的倍数) xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); } } #endif vTaskSuspendAll(); { if( pucAlignedHeap == NULL ) { /* Ensure the heap starts on a correctly aligned boundary. */ 确保取得地址是向 byte_align = 8字节对齐。
前边的byte_align个字节就不要了,所以是从ucHeap[byte_align]之后的地址开始申请空间。地址最小的变动是bit3,bit012始终是0. 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 ) /*adjusted空间是去掉byte_align个字节之后的heap空间,申请的空间大小要小于总的这个adjusted空间*/ && ( ( 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; }

 

 

 

二、heap_2

heap_2提供了内存释放函数。

heap_2不会把释放的内存块合并成一个大块,这样随着你不断的申请内存,内存堆就会被分为很多个大小不一的内存(块),导致内存碎片!

 

读代码时,先看一下下面我写的加粗的几句话,好理解。

其实做的这个链表,它只管理空闲的内存块,

但分配出去的内存块也要带着链表管理用的这个结构体,方便别人释放之后再回到链表中管理。

 

而且可以看出,一旦内存被分配了大小,其大小是不能再更改的,所以就会导致内存碎片问题,导致程序崩溃。

  1 内存块:分配出去每块内存,和空闲的内存,都叫内存块。
  2 但是这些内存块大小不一,所以引入链表。
  3 typedef struct A_BLOCK_LINK
  4 {
  5     struct A_BLOCK_LINK *pxNextFreeBlock;    /*链表中下一个空闲块 */
  6     size_t xBlockSize;                       /*内存块的大小+8(要加上这两个变量的空间)*/
  7 } BlockLink_t;
  8 
  9 static BlockLink_t xStart, xEnd;  记录链表的头和尾
 10 
 11 static const uint16_t heapSTRUCT_SIZE    = ( ( sizeof ( BlockLink_t ) + ( portBYTE_ALIGNMENT - 1 ) ) & ~portBYTE_ALIGNMENT_MASK );
 12 
 13 #define heapMINIMUM_BLOCK_SIZE    ( ( size_t ) ( heapSTRUCT_SIZE * 2 ) )
 14 
 15 /* A few bytes might be lost to byte aligning the heap start address. */
 16 #define configADJUSTED_HEAP_SIZE    ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT )
 17 
 18 /* Keeps track of the number of free bytes remaining, but says nothing about
 19 fragmentation. */
 20 static size_t xFreeBytesRemaining = configADJUSTED_HEAP_SIZE;
 21 
 22 
 23 /*-----------------------------------------------------------*/
 24 static void prvHeapInit( void )
 25 {
 26 BlockLink_t *pxFirstFreeBlock;
 27 uint8_t *pucAlignedHeap;
 28 
 29     /* Ensure the heap starts on a correctly aligned boundary. */
 30     pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
 31 
 32     /* xStart is used to hold a pointer to the first item in the list of free
 33     blocks.  The void cast(void*强转) is used to prevent compiler warnings. */
 34     xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;
 35     xStart.xBlockSize = ( size_t ) 0;
 36 
 37     /* xEnd is used to mark the end of the list of free blocks. */
 38     xEnd.xBlockSize = configADJUSTED_HEAP_SIZE;
 39     xEnd.pxNextFreeBlock = NULL;
 40 
 41     /* To start with there is a single free block that is sized to take up the
 42     entire heap space. */  初始化第一个空闲内存块
 43     pxFirstFreeBlock = ( void * ) pucAlignedHeap;
 44     pxFirstFreeBlock->xBlockSize = configADJUSTED_HEAP_SIZE;
 45     pxFirstFreeBlock->pxNextFreeBlock = &xEnd;
 46 }
 47 
 48 
 49 
 50     1. 内存块是按大小排序的!!!链表的排序,并不是内存地址上是连续的!!!
2. 大小为12/14的两个内存块之间,插入大小为13内存块,并不用移动内存拷贝内存,只是简单的用链表把他们三个 按顺序串起来管理而已。
3. 以上说的三个内存块都是空闲内存块,可能是被使用过后扔回来的,大小是固定的。
4. 内存管理是不对已分配的空间进行管理的(废话!因为已经分配的空间,人家正在被用着呢,有人管理,不用这里操心!!!)

51
* Insert a block into the list of free blocks - which is ordered by size of 52 * the block. Small blocks at the start of the list and large blocks at the end 53 * of the list. 54 *-----------------------------------------------------------*/ 55 #define prvInsertBlockIntoFreeList( pxBlockToInsert ) \ 56 { \ 57 BlockLink_t *pxIterator;迭代 \ 58 size_t xBlockSize; \ 59 \ 60 xBlockSize = pxBlockToInsert->xBlockSize; \ 61 \ 62 /* Iterate through the list until a block is found that has a larger size */ \ 63 /* than the block we are inserting. */ \ 64 for( pxIterator = &xStart; pxIterator->pxNextFreeBlock->xBlockSize < xBlockSize; pxIterator = pxIterator->pxNextFreeBlock ) \ 65 { \ 66 /* There is nothing to do here - just iterate to the correct position. */ \ 67 } \ 68 \ 69 /* Update the list to include the block being inserted in the correct */ \ 70 /* position. */ \ 71 pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock; \ 72 pxIterator->pxNextFreeBlock = pxBlockToInsert; \ 73 } 74 75 76 /*-----------------------------------------------------------*/ 77 void *pvPortMalloc( size_t xWantedSize ) 78 { 79 BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink; 80 static BaseType_t xHeapHasBeenInitialised = pdFALSE; 81 void *pvReturn = NULL; 82 83 vTaskSuspendAll(); 84 { 85 /* If this is the first call to malloc then the heap will require 86 initialisation to setup the list of free blocks. */ 87 if( xHeapHasBeenInitialised == pdFALSE ) 88 { 89 prvHeapInit(); 90 xHeapHasBeenInitialised = pdTRUE; 91 } 92 93 /* The wanted size is increased so it can contain a BlockLink_t 94 structure in addition to the requested amount of bytes. */ 95 if( xWantedSize > 0 ) 96 { 97 xWantedSize += heapSTRUCT_SIZE; 加上个heapStruct对byte_align取整 98 99 /* Ensure that blocks are always aligned to the required number of bytes. */ 100 if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0 ) 101 { 102 /* Byte alignment required. */ 103 xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); 加到一起,再取一次整。 104 } 105 } 106 107 if( ( xWantedSize > 0 ) && ( xWantedSize < configADJUSTED_HEAP_SIZE ) ) 108 { 109 /* Blocks are stored in byte order - traverse the list from the start 110 (smallest) block until one of adequate size is found. */ 内存块是按大小排序的!!! 111 pxPreviousBlock = &xStart; 从最小的start内存块开始。 112 pxBlock = xStart.pxNextFreeBlock; 113 while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) ) 114 { 115 pxPreviousBlock = pxBlock; 116 pxBlock = pxBlock->pxNextFreeBlock; 117 } 118 119 /* If we found the end marker then a block of adequate size was not found. */ 120 if( pxBlock != &xEnd ) 上边的遍历,到最后都没找到大小合适的,就失败了。 121 { 122 /* Return the memory space - jumping over the BlockLink_t structure 123 at its start. */ 124 pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + heapSTRUCT_SIZE );(为什么不写成pxBlock + heapStruct_size) 125 126 /* This block is being returned for use so must be taken out of the 127 list of free blocks. */ 128 pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock; pxBlock指向的空间被踢掉了。 129 130 /* If the block is larger than required it can be split into two. */ 131 if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE ) 满足WantedSize剩下的空间,比MiniBlockSize还大,就分割一下。 132 { 133 /* This block is to be split into two. Create a new block 134 following the number of bytes requested. The void cast is 135 used to prevent byte alignment warnings from the compiler. */ 136 pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize ); pxBlock + WantedSize之后的那个空间,割出来 137 138 /* Calculate the sizes of two blocks split from the single 139 block. */ 140 pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize; 剩余空间大小 141 pxBlock->xBlockSize = xWantedSize; 分配出去的WantedSize空间大小。都已经分配出去了,还要带着链表结构!为的是变成空闲空间,回来的时候,由链表管理!!! 142 143 /* Insert the new block into the list of free blocks. */ 144 prvInsertBlockIntoFreeList( ( pxNewBlockLink ) ); 把分割出来的空余空间,再扔回到链表中。 145 } 146 147 xFreeBytesRemaining -= pxBlock->xBlockSize; 全局变量改动一下。 148 } 149 } 150 151 traceMALLOC( pvReturn, xWantedSize ); 152 } 153 ( void ) xTaskResumeAll(); 154 155 #if( configUSE_MALLOC_FAILED_HOOK == 1 ) 156 { 157 if( pvReturn == NULL ) 158 { 159 extern void vApplicationMallocFailedHook( void ); 160 vApplicationMallocFailedHook(); 161 } 162 } 163 #endif 164 165 return pvReturn; 166 } 167 168 169 void vPortFree( void *pv ) 170 { 171 uint8_t *puc = ( uint8_t * ) pv; 172 BlockLink_t *pxLink; 173 174 if( pv != NULL ) 175 { 176 /* The memory being freed will have an BlockLink_t structure immediately 177 before it. */ 178 puc -= heapSTRUCT_SIZE; 一开始指向的是要释放的空间地址,减去一个heapStruct之后,指向heapStruct这个“空闲空间链表”用来管理的结构体。 179 180 /* This unexpected casting is to keep some compilers from issuing 181 byte alignment warnings. */ 182 pxLink = ( void * ) puc; 183 184 vTaskSuspendAll(); 185 { 186 /* Add this block to the list of free blocks. */ 187 prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) ); 扔回到空闲空间链表 188 xFreeBytesRemaining += pxLink->xBlockSize; 189 traceFREE( pv, pxLink->xBlockSize ); 190 } 191 ( void ) xTaskResumeAll(); 192 } 193 } 194 /*-----------------------------------------------------------*/ 195 196 size_t xPortGetFreeHeapSize( void ) 197 { 198 return xFreeBytesRemaining; 199 }

 

 

 

三、heap_3

这个分配方法是对标准 C 中的函数 malloc()和 free()的简单封装, FreeRTOS 对这两个函数做了线程保护

#include <stdlib.h>

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();
    }
}

 

1、需要编译器提供一个内存堆,STM32 可以修改启动文件中的 Heap_Size。编译器库要提供 malloc()和 free()函数。
2、具有不确定性
3、可能会增加代码量。
注意,在 heap_3 中 configTOTAL_HEAP_SIZE 是没用的!

 

 

 

四、heap_4

最优的匹配算法?在哪儿?

内存块合并算法,倒是看到了。

Insert函数:内存块的排序不再使用blockSize的大小进行排序,而是使用内存块的地址排序(方便内存碎片合并!)

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;  //不同于heap2,这里的End节点放到了空闲空间的最后
    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. */
    //size_t最高位置1,BlockLink_t.BlockSize本来是描述内存块大小的
    //这里将其最高位用来描述这个内存块是否被使用了,所以heap4中的内存块最大只能是0x7FFF_FFFF
    xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );
}

 

 

 

处理碎片的秘诀:

 1 static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert )
 2 {
 3 BlockLink_t *pxIterator;
 4 uint8_t *puc;
 5 
 6     /* Iterate through the list until a block is found that has a higher address
 7     than the block being inserted. */
 8     //这里的排序,比较的是地址,不再是blockSize
 9     for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock )
10     {
11         /* Nothing to do here, just iterate to the right position. */
12     }
13 
14     /* Do the block being inserted, and the block it is being inserted after
15     make a contiguous block of memory? */
16     //要插回来的内存块B的起始地址,刚好是"被B插到边的A"的结束地址,所以这两块可以合并!!!
17     //因为链表在内存上是不连续的,A的地址比B的地址小,并不一定他们两个就是地址上连续的。
18     puc = ( uint8_t * ) pxIterator;
19     if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert )
20     {
21         pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;
22         //AB合并,BlockToInsert指向A的起始地址
23         pxBlockToInsert = pxIterator;
24     }
25     else
26     {
27         mtCOVERAGE_TEST_MARKER();
28     }
29 
30     /* Do the block being inserted, and the block it is being inserted before
31     make a contiguous block of memory? */
32     //注意区分这里和上边的,before/after
33     //要插回来的B的结束地址,刚好是"被B插到边的C”的起始地址,可以合并!!!
34     puc = ( uint8_t * ) pxBlockToInsert;
35     if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock )
36     {
37         if( pxIterator->pxNextFreeBlock != pxEnd )  //不是End节点
38         {
39             /* Form one big block from the two blocks. */  //合并
40             pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize;
41             pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock;
42         }
43         else
44         {
45             pxBlockToInsert->pxNextFreeBlock = pxEnd;  //C是End节点,那不能合并了
46         }
47     }
48     else
49     {
50         pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;  //不合并的处理
51     }
52 
53     /* If the block being inserted plugged a gab, so was merged with the block
54     before and the block after, then it's pxNextFreeBlock pointer will have
55     already been set, and should not be set here as that would make it point
56     to itself. */
57     if( pxIterator != pxBlockToInsert )  //两次合并都没进行
58     {
59         pxIterator->pxNextFreeBlock = pxBlockToInsert;
60     }
61     else
62     {
63         mtCOVERAGE_TEST_MARKER();
64     }
65 }

 

 

  1 void *pvPortMalloc( size_t xWantedSize )
  2 {
  3 BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
  4 void *pvReturn = NULL;
  5 
  6     vTaskSuspendAll();
  7     {
  8         /* If this is the first call to malloc then the heap will require
  9         initialisation to setup the list of free blocks. */
 10         if( pxEnd == NULL )
 11         {
 12             prvHeapInit();
 13         }
 14         else
 15         {
 16             mtCOVERAGE_TEST_MARKER();
 17         }
 18 
 19         /* Check the requested block size is not so large that the top bit is
 20         set.  The top bit of the block size member of the BlockLink_t structure
 21         is used to determine who owns the block - the application or the
 22         kernel, so it must be free. */
 23         //内存块大小最大是0x7FFF_FFFF
 24         if( ( xWantedSize & xBlockAllocatedBit ) == 0 )
 25         {
 26             /* The wanted size is increased so it can contain a BlockLink_t
 27             structure in addition to the requested amount of bytes. */
 28             if( xWantedSize > 0 )
 29             {
 30                 xWantedSize += xHeapStructSize;  //加上个heapStruct对byte_align取整
 31                 /* Ensure that blocks are always aligned to the required number
 32                 of bytes. */
 33                 if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 )
 34                 {
 35                     /* Byte alignment required. */
 36                     xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );  //加到一起,再一次取整  !!!见后!!!
 37                     configASSERT( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) == 0 );  //如果没有向byte_align个字节对齐,就报错了。
 38                 }
 39                 else
 40                 {
 41                     mtCOVERAGE_TEST_MARKER();
 42                 }
 43             }
 44             else
 45             {
 46                 mtCOVERAGE_TEST_MARKER();
 47             }
 48 
 49             if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) )
 50             {
 51                 /* Traverse the list from the start    (lowest address) block until
 52                 one    of adequate size is found. */
 53                 //虽然链表已经不按大小排序了,但是按大小找个合适的内存块,也说得过去
 54                 pxPreviousBlock = &xStart;
 55                 pxBlock = xStart.pxNextFreeBlock;
 56                 while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
 57                 {
 58                     pxPreviousBlock = pxBlock;
 59                     pxBlock = pxBlock->pxNextFreeBlock;
 60                 }
 61 
 62                 /* If the end marker was reached then a block of adequate size
 63                 was    not found. */
 64                 if( pxBlock != pxEnd )
 65                 {
 66                     /* Return the memory space pointed to - jumping over the
 67                     BlockLink_t structure at its start. */
 68                     pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize );
 69 
 70                     /* This block is being returned for use so must be taken out
 71                     of the list of free blocks. */
 72                     pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;  //从链表踢掉
 73 
 74                     /* If the block is larger than required it can be split into
 75                     two. */
 76                     if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )  //可以分割
 77                     {
 78                         /* This block is to be split into two.  Create a new
 79                         block following the number of bytes requested. The void
 80                         cast is used to prevent byte alignment warnings from the
 81                         compiler. */
 82                         pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
 83                         configASSERT( ( ( ( size_t ) pxNewBlockLink ) & portBYTE_ALIGNMENT_MASK ) == 0 );  //没有向byte_align对齐,就报错
 84 
 85                         /* Calculate the sizes of two blocks split from the
 86                         single block. */
 87                         pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
 88                         pxBlock->xBlockSize = xWantedSize;
 89 
 90                         /* Insert the new block into the list of free blocks. */
 91                         prvInsertBlockIntoFreeList( pxNewBlockLink );  //分割出来的扔回链表
 92                     }
 93                     else
 94                     {
 95                         mtCOVERAGE_TEST_MARKER();
 96                     }
 97 
 98                     xFreeBytesRemaining -= pxBlock->xBlockSize;  //更新剩余空间变量
 99 
100                     if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining )
101                     {
102                         xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;  //更新最小内存块的大小
103                     }
104                     else
105                     {
106                         mtCOVERAGE_TEST_MARKER();
107                     }
108 
109                     /* The block is being returned - it is allocated and owned
110                     by the application and has no "next" block. */
111                     pxBlock->xBlockSize |= xBlockAllocatedBit;  //置1表示找个内存块被占用了
112                     pxBlock->pxNextFreeBlock = NULL;
113                 }
114                 else
115                 {
116                     mtCOVERAGE_TEST_MARKER();
117                 }
118             }
119             else
120             {
121                 mtCOVERAGE_TEST_MARKER();
122             }
123         }
124         else
125         {
126             mtCOVERAGE_TEST_MARKER();
127         }
128 
129         traceMALLOC( pvReturn, xWantedSize );
130     }
131     ( void ) xTaskResumeAll();
132 
133     #if( configUSE_MALLOC_FAILED_HOOK == 1 )
134     {
135         if( pvReturn == NULL )
136         {
137             extern void vApplicationMallocFailedHook( void );
138             vApplicationMallocFailedHook();
139         }
140         else
141         {
142             mtCOVERAGE_TEST_MARKER();
143         }
144     }
145     #endif
146 
147     configASSERT( ( ( ( size_t ) pvReturn ) & ( size_t ) portBYTE_ALIGNMENT_MASK ) == 0 );  //没有向byte_align对齐,就报错。
148     return pvReturn;
149 }

!!!处【原子资料分析】

xWantedSize为0x7FFFFFFF,那么 xWantedSize 加上结构体BlockLink_t 的大小就是0x7FFFFFFF + 8 = 0x80000007,

再做一次 8 字节对齐 xWantedSize 就是 0x80000008,其最高位为1。

前面已经说了, BlockLink_t 中的变量 xBlockSize 的最高位是用来标记内存块是否被使用的,这里明显冲突了,但是 FreeRTOS 对此并没有做处理。

(那么能分配的最大空间值,应该是0x7FFF_FFFF - 8 = 0x7FFFFFF7了?我猜的。。。)

 

 

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 );  //最高位是1
        configASSERT( pxLink->pxNextFreeBlock == NULL );  //是被使用的内存块(分配的时候NextFreeBlock被设置为NULL)

        if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 )
        {
            if( pxLink->pxNextFreeBlock == NULL )
            {
                /* The block is being returned to the heap - it is no longer
                allocated. */
                pxLink->xBlockSize &= ~xBlockAllocatedBit;  //清零标志,0x8000_0008变成0x0000_0008

                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();
        }
    }
}

 

 

 

 

五、heap5

heap_5 允许内存堆跨越多个不连续的内存段。

STM32 可以外接 SRAM 甚至大容量的 SDRAM,如果使用 heap_4 的话你就只能在内部 RAM 和外部SRAM 或 SDRAM 之间二选一了,

使用 heap_5 的话就不存在这个问题,两个都可以一起作为内存堆来用。


如果使用 heap_5 的话,在调用 API 函数之前需要先调用函数 vPortDefineHeapRegions ()来对内存堆做初始化处理。

函数 vPortDefineHeapRegions()有一个参数,参数是一个 HeapRegion_t 类型的数组,

HeapRegion 为一个结构体,此结构体在portable.h 中有定义,

typedef struct HeapRegion
{
  uint8_t *pucStartAddress;  //内存块的起始地址
  size_t xSizeInBytes;       //内存段大小
} HeapRegion_t;

现在有三个内存段: CCM、内部 SRAM、外部 SDRAM,

起始分别为: 0X10000000、 0X20000000、 0XC0000000,

大小分别为: 64KB、192KB、 32MB,那么数组就如下:

HeapRegion_t xHeapRegions[] =
{
  { ( uint8_t * ) 0X10000000UL, 0x10000 }, //CCM 内存,起始地址 0X10000000,大小 64KB
  { ( uint8_t * ) 0X20000000UL, 0x30000 },//内部 SRAM 内存,起始地址 0X20000000,大小为 192KB
  { ( uint8_t * ) 0XC0000000UL, 0x2000000},//外部 SDRAM 内存,起始地址 0XC0000000,大小为 32MB
  { NULL, 0 } //数组结尾
};

注意,数组中成员顺序按照地址从低到高的顺序排列,而且最后一个成员必须使用 NULL。


heap_5 允许内存堆不连续,说白了就是允许有多个内存堆。

在 heap_2 和 heap_4 中只有一个内存堆,初始化的时候只也只需要处理一个内存堆。

heap_5 有多个内存堆,这些内存堆会被连接在一起,和空闲内存块链表类似,这个处理过程由函数 vPortDefineHeapRegions()完成。

 1 void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions )
 2 {
 3 BlockLink_t *pxFirstFreeBlockInRegion = NULL, *pxPreviousFreeBlock;
 4 size_t xAlignedHeap;
 5 size_t xTotalRegionSize, xTotalHeapSize = 0;
 6 BaseType_t xDefinedRegions = 0;
 7 size_t xAddress;
 8 const HeapRegion_t *pxHeapRegion;
 9 
10     /* Can only call once! */
11     configASSERT( pxEnd == NULL );
12 
13     pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] );
14 
15     while( pxHeapRegion->xSizeInBytes > 0 )
16     {
17         xTotalRegionSize = pxHeapRegion->xSizeInBytes;
18 
19         /* Ensure the heap region starts on a correctly aligned boundary. */
20         xAddress = ( size_t ) pxHeapRegion->pucStartAddress;
21         if( ( xAddress & portBYTE_ALIGNMENT_MASK ) != 0 )
22         {
23             xAddress += ( portBYTE_ALIGNMENT - 1 );
24             xAddress &= ~portBYTE_ALIGNMENT_MASK;
25 
26             /* Adjust the size for the bytes lost to alignment. */
27             xTotalRegionSize -= xAddress - ( size_t ) pxHeapRegion->pucStartAddress;
28         }
29 
30         xAlignedHeap = xAddress;
31 
32         /* Set xStart if it has not already been set. */
33         if( xDefinedRegions == 0 )
34         {
35             /* xStart is used to hold a pointer to the first item in the list of
36             free blocks.  The void cast is used to prevent compiler warnings. */
37             xStart.pxNextFreeBlock = ( BlockLink_t * ) xAlignedHeap;
38             xStart.xBlockSize = ( size_t ) 0;
39         }
40         else
41         {
42             /* Should only get here if one region has already been added to the
43             heap. */
44             configASSERT( pxEnd != NULL );
45 
46             /* Check blocks are passed in with increasing start addresses. */
47             configASSERT( xAddress > ( size_t ) pxEnd );
48         }
49 
50         /* Remember the location of the end marker in the previous region, if
51         any. */
52         pxPreviousFreeBlock = pxEnd;
53 
54         /* pxEnd is used to mark the end of the list of free blocks and is
55         inserted at the end of the region space. */
56         xAddress = xAlignedHeap + xTotalRegionSize;
57         xAddress -= xHeapStructSize;
58         xAddress &= ~portBYTE_ALIGNMENT_MASK;
59         pxEnd = ( BlockLink_t * ) xAddress;
60         pxEnd->xBlockSize = 0;
61         pxEnd->pxNextFreeBlock = NULL;
62 
63         /* To start with there is a single free block in this region that is
64         sized to take up the entire heap region minus the space taken by the
65         free block structure. */
66         pxFirstFreeBlockInRegion = ( BlockLink_t * ) xAlignedHeap;
67         pxFirstFreeBlockInRegion->xBlockSize = xAddress - ( size_t ) pxFirstFreeBlockInRegion;
68         pxFirstFreeBlockInRegion->pxNextFreeBlock = pxEnd;
69 
70         /* If this is not the first region that makes up the entire heap space
71         then link the previous region to this region. */
72         if( pxPreviousFreeBlock != NULL )
73         {
74             pxPreviousFreeBlock->pxNextFreeBlock = pxFirstFreeBlockInRegion;
75         }
76 
77         xTotalHeapSize += pxFirstFreeBlockInRegion->xBlockSize;
78 
79         /* Move onto the next HeapRegion_t structure. */
80         xDefinedRegions++;
81         pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] );
82     }
83 
84     xMinimumEverFreeBytesRemaining = xTotalHeapSize;
85     xFreeBytesRemaining = xTotalHeapSize;
86 
87     /* Check something was actually defined before it is accessed. */
88     configASSERT( xTotalHeapSize );
89 
90     /* Work out the position of the top bit in a size_t variable. */
91     xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );
92 }

 

 

 

 

 

 

 

 

 

 

 

 

留白

posted @ 2017-11-29 02:18  为民除害  阅读(2314)  评论(0编辑  收藏  举报