首页 > 系统相关 >#FREERTOS的和heap_4内存分配算法

#FREERTOS的和heap_4内存分配算法

时间:2023-04-05 22:13:53浏览次数:51  
标签:pxBlock FREERTOS pxNextFreeBlock xWantedSize pxBlockToInsert xBlockSize 内存 heap 

FreeRTOS的heap_4内存管理算法具有内存碎片合并的功能,可以有效防止内存碎片产生,使用First fit算法,在实现上与C标准库的malloc类似,但是效率更高且能进行碎片合并回收。以下是个人对源码的解析,有空再补充详细。

一、初始化

static void prvHeapInit( void )
{
    BlockLink_t *pxFirstFreeBlock;
    uint8_t *pucAlignedHeap;
    size_t uxAddress;
    size_t xTotalHeapSize = configTOTAL_HEAP_SIZE;

/*====================================== 1 ===========================================*/
    /* 字节对齐,4字节 */
    uxAddress = ( size_t ) ucHeap;
    /*字节对齐,一般是8字节*/
    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;

/*====================================== 2 ===========================================*/
    /* 把xStart的next指针指向对齐后的头地址,长度设置为0.xStart只是链表头不参与内存分配*/
    xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;
    xStart.xBlockSize = ( size_t ) 0;
/*====================================== 3 ===========================================*/
    /* 计算尾部指针地址 */
    uxAddress = ( ( size_t ) pucAlignedHeap ) + xTotalHeapSize;
    /* 减去end所占用的8个字节 */
    uxAddress -= xHeapStructSize;
    /* pxend字节对齐,也就是尾部会空出8-15字节用于放pxend */
    uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
    /* pxend初始化 */
    pxEnd = ( void * ) uxAddress;
    pxEnd->xBlockSize = 0;
    pxEnd->pxNextFreeBlock = NULL;

/*====================================== 4 ===========================================*/
    /* 初始化头结构,也就是xstart一开始指向的那个地址 */
    pxFirstFreeBlock = ( void * ) pucAlignedHeap;
    pxFirstFreeBlock->xBlockSize = uxAddress - ( size_t ) pxFirstFreeBlock;
    pxFirstFreeBlock->pxNextFreeBlock = pxEnd;
    /* 初始化内存最大使用量和剩余空间这两个变量的值 */
    xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
    xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
    /* 定义xBlockSize最高bit,因为xBlockSize的最高bit用于判断是否使用 */
    xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );
}

  1. 进行字节对齐,找到对齐后的首地址,在32位机中以8字节进行对齐。
  2. 初始化xStart的值。
  3. 计算对齐后的尾部地址,将pxEnd指向这一地址,同时初始化。
  4. 初始化xStart指向的头地址的值,因为还没分配,所以next指向pxend,size为整个空间大小。初始化用于记录剩余空间的变量值

二、申请内存

void *pvPortMalloc( size_t xWantedSize )
{
    BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
    void *pvReturn = NULL;

    {
        /* 如果还没初始化的话,就先初始化. */
        if( pxEnd == NULL )
        {
            prvHeapInit();
        }
        
        /* 检查要分配的大小是否超过了最大值,因为最高位用来标志空闲块是否已经使用,
            所以能分配的空间最大值为0x7FFF FFFF 也就是2G*/
        if( ( xWantedSize & xBlockAllocatedBit ) == 0 )
        {
            /* 检查分配空间是否为0 */
            if( xWantedSize > 0 )
            {
                /* 加上链表结构的大小 */
                xWantedSize += xHeapStructSize;
                /* 日常字节对齐 */
                if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 )
                {
                    /* 补齐. */
                    xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
                }
            }
            /* 这里也判断xWantedSize>0,可以跟上面的代码合并啊,判断空闲的空间还够不够 */
            if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) )
            {
                /* 从头开始查找大小够分配的空闲块,直到找到pxend. */
                pxPreviousBlock = &xStart;
                pxBlock = xStart.pxNextFreeBlock;
                while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
                {
                    pxPreviousBlock = pxBlock;
                    pxBlock = pxBlock->pxNextFreeBlock;
                }
                /* 如果是pxEnd就是说没有能够分配的空闲块了,分配失败 */
                if( pxBlock != pxEnd )
                {
                    /* 分配的地址是空闲块管理结构地址+结构大小,如图
                                分配了的空间     新的空闲块
                        |____|_______________|________________| 
                          ☝  ↑分配的内存地址
                    有足够空间的结构, */
                    pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize );
                    /* 跳过刚刚被使用的空闲块,指向下一块 */
                    pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;
                    /* 如果当前空闲块分配完之后剩余的大小还>=16字节,就分成两块 */
                    if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
                    {
                        /* 创建一个新的空闲块,计算偏移地址 */
                        pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
                        /* 初始化新空闲块的大小,next需要做插入处理 */
                        pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
                        /* 旧块重新定义大小 */
                        pxBlock->xBlockSize = xWantedSize;
                        /* Insert the new block into the list of free blocks.看英语解释 */
                        prvInsertBlockIntoFreeList( pxNewBlockLink );
                    }
                    /* 扣除剩余的空间统计 */
                    xFreeBytesRemaining -= pxBlock->xBlockSize;
                    /* 记录当前使用空间的最大值,也就是记录系统运行中最多用了多少空间 */
                    if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining )
                    {
                        xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
                    }
                    /* 最高位置为1,清楚next指针,标记已经用掉了 */
                    pxBlock->xBlockSize |= xBlockAllocatedBit;
                    pxBlock->pxNextFreeBlock = NULL;
                }
            }
        }
    }
    {
        if( pvReturn == NULL )
        {
            printf("malloc fail \r\n");    
        }
        
    }
    return pvReturn;
}

三、释放内存

oid vPortFree( void *pv )
{
uint8_t *puc = ( uint8_t * ) pv;
BlockLink_t *pxLink;

    if( pv != NULL )
    {
        /* 找到结构体的地址
                   ↓puc地址
            |______|___________________| 
            ↑BlockLink_t地址*/
        puc -= xHeapStructSize;
        /* 防一手编译器警告 */
        pxLink = ( void * ) puc;
        /* 通过最高位判断是否已经使用 */
        if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 )
        {
            /* 已经使用的next被复制为null,可以看malloc */
            if( pxLink->pxNextFreeBlock == NULL )
            {
                /*清掉标志位 */
                pxLink->xBlockSize &= ~xBlockAllocatedBit;
                {
                    /* 统计空闲内内存大小,插入链表中. */
                    xFreeBytesRemaining += pxLink->xBlockSize;
                    prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
                }
            }
            
        }
        
    }
}
/*-----------------------------------------------------------*/

四、碎片整理

把新的空闲列表项插入链表中,同时进行空闲块合并。

static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert )
{
    BlockLink_t *pxIterator;
    uint8_t *puc;    
    /* 遍历链表,找到newlist的前一个list地址,也就是插入的位置.        
    heap4对链表的地址管理都是从小到大,所以只要循环比对地址大小就行了 */    
    for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock )    
    {        
        /* Nothing to do here, just iterate to the right position. */    
    }    
    /* 插入前,检查前(已有的项)后(要插入的项)两个空闲块是否相邻,相邻的话直接合并 */    
    puc = ( uint8_t * ) pxIterator;    
    if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert )    
    {        
        /* 合并处理 */        
        pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;        
        pxBlockToInsert = pxIterator;    
    }    
    /* 插入前,检查前(要插入的项pxBlockToInsert)后(已有的项)两个空闲块是否相邻,相邻的话直接合并,        
    跟上面的流程相同,只是比对的是跟在新链表后面的那个 */    
    puc = ( uint8_t * ) pxBlockToInsert;    
    if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock )    
    {        
        if( pxIterator->pxNextFreeBlock != pxEnd )        
        {            
            /* 合成一块 */            
            pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize;            pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock;     
        }        
        else        
        {            
            /* 不合并的话给新链表项的next赋值 */            
            pxBlockToInsert->pxNextFreeBlock = pxEnd;        
        }    
    }    
    else    
    {        
        /* 不合并的话给新链表项的next赋值 */        
        pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;    
    }    
    /* 如果没进行过合并,插入新链表 */    
    if( pxIterator != pxBlockToInsert )    
    {        
        pxIterator->pxNextFreeBlock = pxBlockToInsert;    
    }    
}




标签:pxBlock,FREERTOS,pxNextFreeBlock,xWantedSize,pxBlockToInsert,xBlockSize,内存,heap,
From: https://www.cnblogs.com/chenpangzhi/p/17291060.html

相关文章

  • 内存泄漏的情况
    Java有垃圾收集器实现内存自动管理,虽然GC有效的处理了大部分内存,但并不能完全保证内存的不泄漏。可能导致内存泄漏的情况:①静态集合类,如HashMap、LinkedList等②未关闭的资源,如数据库连接、IO连接等③变量不合理的作用域④内部类持有外部类⑤改变哈希值⑥过期引用⑦缓存泄......
  • 动态内存管理——动态内存函数
    动态内存管理,也叫动态内存分配,顾名思义:动态的来分配内存。1.为什么存在动态内存分配我们已经知道的内存分配方式有:创建一个变量:整型,分配4个字节的空间;长整型;分配8个字节的空间,又或者创建一个数组,创建一个函数的形参........但是,这些开辟空间的方式都有两个特点:1.空间大小固定; 2.......
  • 深入理解 JVM---JVM 和 GC 日志 以及 内存分配回收日志
    虚拟机及垃圾收集器日志1、JDK9日志在JDK9以前,HotSpot并没有提供统一的日志处理框架,虚拟机各个功能模块的日志开关分布在不同的参数上,日志级别、循环日志大小、输出格式、重定向等设置在不同功能上都要单独解决。直到JDK9,这种混乱不堪的局面才终于消失,HotSpot所有功能的日......
  • 有关IOS内存读写冲突
    有关IOS内存读写冲突在写内存相关代码时,获取已使用内存代码中报错lethostPort:mach_port_t=mach_host_self()varhost_size=mach_msg_type_number_t(MemoryLayout<vm_statistics_data_t>.stride/MemoryLayout<integer_t>.stride)varpagesize:vm_siz......
  • linux 查看内存使用情况命令
    查看所有盘符的使用情况:df-h查看各个用户使用的存储空间大小:du-sh/home/*查看当前目录总共占的容量,而不单独列出各个子项占用的容量:du-sh查看当前目录下一级子文件和子目录所占用的磁盘容量:du-lh--max-depth=1统计当前文件夹|目录大小,并按文件大小排序:du-sh*|sort......
  • 细节拉满,80 张图带你一步一步推演 slab 内存池的设计与实现
    1.前文回顾在之前的几篇内存管理系列文章中,笔者带大家从宏观角度完整地梳理了一遍Linux内存分配的整个链路,本文的主题依然是内存分配,这一次我们会从微观的角度来探秘一下Linux内核中用于零散小内存块分配的内存池——slab分配器。在本小节中,笔者还是按照以往的风格先带......
  • freeRTOS任务死锁
    一、freeRTOS任务死锁FreeRTOS任务死锁是一种常见的问题,通常发生在多个任务相互等待对方释放资源的情况下。以下是一个简单的例子,用于说明FreeRTOS任务死锁的情况:假设有两个任务Task1和Task2,它们需要共享两个资源ResourceA和ResourceB。每个任务都需要同时访问这两个资源才能完成它......
  • OS-Windows-计算机内存型号配置查看
    OS-Windows-计算机内存型号配置查看近来想给计算机配置内存,在网上百度到了如何查看型号和配置的方法,整理如下。1.直接在计算机底部查看标识。2.win+R-->cmd-->systeminfo--->结果输出在当前窗口中。3.win+R-->cmd-->dxdiag--->弹出DirectX诊断工具窗口。4.cpu-zhttps......
  • 【Windows】Advanced_System_Care ( v 11.3.5 ) 内存清理插件 大小15.1 MB
    【Windows】Advanced_System_Care(v11.3.5)内存清理插件大小为15.1MBhttps://xcherry.lanzouj.com/il2iOmsobni密码: 3dw3 软件提取自Advanced_System_Care(  v11.3.5  )软件从2018年来,在自己电脑上用到了今天,觉得还不错,分享出来,类似于腾讯电脑管家的小火......
  • Android 加载图片占用内存分析
    作者:XuJie不同Android版本,对一张图片的内存处理方式是不一样的,使用不正确会导致OOM的发生,这篇文章带你梳理内存占用情况,选择适合你的图片加载模式,解决OOM问题。一、背景你知道吗一张5.48MB,宽高像素为4896*6528的24位的静态图片,放在Android工程目录下面的res/drawable-[density]/......