ToB企服应用市场:ToB评测及商务社交产业平台

标题: #FREERTOS的和heap_4内存分配算法 [打印本页]

作者: 万有斥力    时间: 2023-4-5 22:25
标题: #FREERTOS的和heap_4内存分配算法
FreeRTOS的heap_4内存管理算法具有内存碎片合并的功能,可以有效防止内存碎片产生,使用First fit算法,在实现上与C标准库的malloc类似,但是效率更高且能进行碎片合并回收。以下是个人对源码的解析,有空再补充详细。
一、初始化
  1. static void prvHeapInit( void )
  2. {
  3.     BlockLink_t *pxFirstFreeBlock;
  4.     uint8_t *pucAlignedHeap;
  5.     size_t uxAddress;
  6.     size_t xTotalHeapSize = configTOTAL_HEAP_SIZE;
  7. /*====================================== 1 ===========================================*/
  8.     /* 字节对齐,4字节 */
  9.     uxAddress = ( size_t ) ucHeap;
  10.     /*字节对齐,一般是8字节*/
  11.     if( ( uxAddress & portBYTE_ALIGNMENT_MASK ) != 0 )
  12.     {
  13.         /* 对齐处理 */
  14.         uxAddress += ( portBYTE_ALIGNMENT - 1 );
  15.         uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
  16.         xTotalHeapSize -= uxAddress - ( size_t ) ucHeap;
  17.     }
  18.     /*取对齐后的地址*/
  19.     pucAlignedHeap = ( uint8_t * ) uxAddress;
  20. /*====================================== 2 ===========================================*/
  21.     /* 把xStart的next指针指向对齐后的头地址,长度设置为0.xStart只是链表头不参与内存分配*/
  22.     xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;
  23.     xStart.xBlockSize = ( size_t ) 0;
  24. /*====================================== 3 ===========================================*/
  25.     /* 计算尾部指针地址 */
  26.     uxAddress = ( ( size_t ) pucAlignedHeap ) + xTotalHeapSize;
  27.     /* 减去end所占用的8个字节 */
  28.     uxAddress -= xHeapStructSize;
  29.     /* pxend字节对齐,也就是尾部会空出8-15字节用于放pxend */
  30.     uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
  31.     /* pxend初始化 */
  32.     pxEnd = ( void * ) uxAddress;
  33.     pxEnd->xBlockSize = 0;
  34.     pxEnd->pxNextFreeBlock = NULL;
  35. /*====================================== 4 ===========================================*/
  36.     /* 初始化头结构,也就是xstart一开始指向的那个地址 */
  37.     pxFirstFreeBlock = ( void * ) pucAlignedHeap;
  38.     pxFirstFreeBlock->xBlockSize = uxAddress - ( size_t ) pxFirstFreeBlock;
  39.     pxFirstFreeBlock->pxNextFreeBlock = pxEnd;
  40.     /* 初始化内存最大使用量和剩余空间这两个变量的值 */
  41.     xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
  42.     xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
  43.     /* 定义xBlockSize最高bit,因为xBlockSize的最高bit用于判断是否使用 */
  44.     xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );
  45. }
复制代码
三、释放内存
  1. void *pvPortMalloc( size_t xWantedSize )
  2. {
  3.     BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
  4.     void *pvReturn = NULL;
  5.     {
  6.         /* 如果还没初始化的话,就先初始化. */
  7.         if( pxEnd == NULL )
  8.         {
  9.             prvHeapInit();
  10.         }
  11.         
  12.         /* 检查要分配的大小是否超过了最大值,因为最高位用来标志空闲块是否已经使用,
  13.             所以能分配的空间最大值为0x7FFF FFFF 也就是2G*/
  14.         if( ( xWantedSize & xBlockAllocatedBit ) == 0 )
  15.         {
  16.             /* 检查分配空间是否为0 */
  17.             if( xWantedSize > 0 )
  18.             {
  19.                 /* 加上链表结构的大小 */
  20.                 xWantedSize += xHeapStructSize;
  21.                 /* 日常字节对齐 */
  22.                 if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 )
  23.                 {
  24.                     /* 补齐. */
  25.                     xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
  26.                 }
  27.             }
  28.             /* 这里也判断xWantedSize>0,可以跟上面的代码合并啊,判断空闲的空间还够不够 */
  29.             if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) )
  30.             {
  31.                 /* 从头开始查找大小够分配的空闲块,直到找到pxend. */
  32.                 pxPreviousBlock = &xStart;
  33.                 pxBlock = xStart.pxNextFreeBlock;
  34.                 while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
  35.                 {
  36.                     pxPreviousBlock = pxBlock;
  37.                     pxBlock = pxBlock->pxNextFreeBlock;
  38.                 }
  39.                 /* 如果是pxEnd就是说没有能够分配的空闲块了,分配失败 */
  40.                 if( pxBlock != pxEnd )
  41.                 {
  42.                     /* 分配的地址是空闲块管理结构地址+结构大小,如图
  43.                                 分配了的空间     新的空闲块
  44.                         |____|_______________|________________|
  45.                           ☝  ↑分配的内存地址
  46.                     有足够空间的结构, */
  47.                     pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize );
  48.                     /* 跳过刚刚被使用的空闲块,指向下一块 */
  49.                     pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;
  50.                     /* 如果当前空闲块分配完之后剩余的大小还>=16字节,就分成两块 */
  51.                     if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
  52.                     {
  53.                         /* 创建一个新的空闲块,计算偏移地址 */
  54.                         pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
  55.                         /* 初始化新空闲块的大小,next需要做插入处理 */
  56.                         pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
  57.                         /* 旧块重新定义大小 */
  58.                         pxBlock->xBlockSize = xWantedSize;
  59.                         /* Insert the new block into the list of free blocks.看英语解释 */
  60.                         prvInsertBlockIntoFreeList( pxNewBlockLink );
  61.                     }
  62.                     /* 扣除剩余的空间统计 */
  63.                     xFreeBytesRemaining -= pxBlock->xBlockSize;
  64.                     /* 记录当前使用空间的最大值,也就是记录系统运行中最多用了多少空间 */
  65.                     if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining )
  66.                     {
  67.                         xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
  68.                     }
  69.                     /* 最高位置为1,清楚next指针,标记已经用掉了 */
  70.                     pxBlock->xBlockSize |= xBlockAllocatedBit;
  71.                     pxBlock->pxNextFreeBlock = NULL;
  72.                 }
  73.             }
  74.         }
  75.     }
  76.     {
  77.         if( pvReturn == NULL )
  78.         {
  79.             printf("malloc fail \r\n");   
  80.         }
  81.         
  82.     }
  83.     return pvReturn;
  84. }
复制代码
四、碎片整理

把新的空闲列表项插入链表中,同时进行空闲块合并。
  1. oid vPortFree( void *pv )
  2. {
  3. uint8_t *puc = ( uint8_t * ) pv;
  4. BlockLink_t *pxLink;
  5.     if( pv != NULL )
  6.     {
  7.         /* 找到结构体的地址
  8.                    ↓puc地址
  9.             |______|___________________|
  10.             ↑BlockLink_t地址*/
  11.         puc -= xHeapStructSize;
  12.         /* 防一手编译器警告 */
  13.         pxLink = ( void * ) puc;
  14.         /* 通过最高位判断是否已经使用 */
  15.         if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 )
  16.         {
  17.             /* 已经使用的next被复制为null,可以看malloc */
  18.             if( pxLink->pxNextFreeBlock == NULL )
  19.             {
  20.                 /*清掉标志位 */
  21.                 pxLink->xBlockSize &= ~xBlockAllocatedBit;
  22.                 {
  23.                     /* 统计空闲内内存大小,插入链表中. */
  24.                     xFreeBytesRemaining += pxLink->xBlockSize;
  25.                     prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
  26.                 }
  27.             }
  28.             
  29.         }
  30.         
  31.     }
  32. }
  33. /*-----------------------------------------------------------*/
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4