zoukankan      html  css  js  c++  java
  • FreeRTOS --(2)内存管理 heap1

    转载自https://blog.csdn.net/zhoutaopower/article/details/106631237

    FreeRTOS 提供了5种内存堆管理方案,分别对应heap1/heap2/heap3/heap4/heap5,提供内存管理是作为 OS 的一项基本功能,FreeRTOS 根据具体的使用场景,将内存管理按需切分成为了 5 部分,以供不同的场景来针对性使用;

    其实库函数的 malloc 和 free 已经是提供了内存的动态管理功能,但是呢介于一下几个原因:

    • 在嵌入式系统中,它们并不总是可以使用的;
    • 它们会占用更多宝贵的代码空间;
    • 它们没有线程保护;
    • 它们不具有确定性(每次调用执行的时间可能会不同);

    在小型实时嵌入式 OS 中,使用 malloc 和 free,并不是最明智的选择;所以,FreeRTOS 使用了:pvPortMallo() 和 vPortFree() 函数来代替 malloc() 和 free() 函数,来进行内存管理;

    FreeRTOS 内存管理相关的 SourceCode 放置在:

    FreeRTOSSourceportableMemMang

    一共 5 个,今天就来看看第一种内存管理 heap1 是如何实现的;

    1、内存大小

    heap1 中有几个关键的宏定义:

    configTOTAL_HEAP_SIZE

    这个是需要用户根据自己的芯片具体情况来定义的,FreeRTOS 管理的内存就来自于这个值:

    static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];

    ucHeap 就是管理的对象;

     

    2、对齐

    有的处理器是对内存对齐有要求的,比如 ARM-CM3 等,AAPCS规则要求堆栈保持8字节对齐。给任务分配栈时需要保证栈是8字节对齐的。所以这里 FreeRTOS 就需要涉及到对齐操作;针对 ARM-CM3 这类处理器来说,在portmacro.h 文件中,定义了对齐的字节数:

     
    /* Hardware specifics. */
     
    #define portBYTE_ALIGNMENT 8

    而在 portable.h 中,定义了对应的 Mask(8字节对齐,那么都要是 8 的倍数,也就是二进制的 4'b1000,所以 MASK 是 4'b0111 也就是 0x07):

     
    #if portBYTE_ALIGNMENT == 8
    #define portBYTE_ALIGNMENT_MASK ( 0x0007 )
    #endif

     

    3、分配内存

    分配内存使用了 pvPortMalloc 函数,入参是希望分配的 Size,返回值是分配到内存的起始地址,失败的话返回 NULL:

    /* Index into the ucHeap array. */
    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. */
                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;
    }

    pvReturn 就是如果被分配到内存后的起始地址,初始化成为 NULL;

    这里注意一下,pucAlignedHeap 是一个 static 变量,它记录了对齐后,内存 heap 的起始地址;

    如果 portBYTE_ALIGNMENT != 1,也就是使用了对齐,那么需要按需来调整分配内存的大小,也就是 xWantedSize,使其能够达到对齐的效果;

    接着调用 vTaskSuspendAll();,暂时关闭 OS 调度;

    接着对初始的 ucHeap[ configTOTAL_HEAP_SIZE ] 进行对齐操作:

    /* Ensure the heap starts on a correctly aligned boundary. */
    pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );

    这里为什么是 ucHeap[ portBYTE_ALIGNMENT ] 呢,因为如果 ucHeap 的起始地址本就不对齐的话,那么经过对齐操作后得到的地址,便可能小于真实的 ucHeap,所以这里往前挪了一点;

    接下来便是检查是否能够正常分配到想要的 Size 的内存,xNextFreeByte 记录了不断的分配内存后,可用于分配内存在 ucHeap 的起始地址;这里的 configADJUSTED_HEAP_SIZE 很关键,它被定义为:

    /* A few bytes might be lost to byte aligning the heap start address. */
     
    #define configADJUSTED_HEAP_SIZE    ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT )

    也就是在 configTOTAL_HEAP_SIZE 基础之上预留出了 portBYTE_ALIGNMENT 的空间,这个就是用于了刚刚上一步说的 ucHeap[ portBYTE_ALIGNMENT ] 这部分空间;

    如果分配成功,那么 pvReturn 被更新为 ucHeap 对齐后的起始地址,加上可用内存的起始地址指针 xNextFreeByte;

    同时,更新 xNextFreeByte 指针;

    整个过程,保证了 ucHeap 的起始地址对齐到 pucAlignedHeap,分配内存的时候,Size 是对齐的;

    然后再调用 xTaskResumeAll(); 来恢复 OS 调度;

    当然,如果定义了 configUSE_MALLOC_FAILED_HOOK,在分配失败的时候,会去调用 vApplicationMallocFailedHook 回调;

     

    4、小结

    heap1 不提供 Free 内存的接口,也就是说,这套内存管理是只提供的分配,一旦申请成功后,这块内存再也不能被释放;实际上,大多数的嵌入式系统并不需要动态删除任务、信号量、队列等,而是在初始化的时候一次性创建好,便一直使用,永远不用删除。所以这个内存管理策略实现简洁、安全可靠,使用的非常广泛。

  • 相关阅读:
    HTML <h1>
    HTML <body> 标签
    如何挑选深度学习 GPU?
    视频动作定位的分层自关注网络:ICCV2019论文解析
    三维点云去噪无监督学习:ICCV2019论文分析
    摄像头定位:ICCV2019论文解析
    深度学习扫地机器人
    细粒度语义分割:ICCV2019论文解析
    目标形体形状轮廓重建:ICCV2019论文解析
    2-2.5-3D的室内场景理解
  • 原文地址:https://www.cnblogs.com/FZLGYZ/p/13784765.html
Copyright © 2011-2022 走看看