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

  • 相关阅读:
    剑指Offer-30.连续子数组的最大和(C++/Java)
    剑指Offer-29.最小的K个数(C++/Java)
    UVA 1616 Caravan Robbers 商队抢劫者(二分)
    UVA 10570 Meeting with Aliens 外星人聚会
    UVA 11093 Just Finish it up 环形跑道 (贪心)
    UVA 12673 Erratic Expansion 奇怪的气球膨胀 (递推)
    UVA 10954 Add All 全部相加 (Huffman编码)
    UVA 714 Copying Books 抄书 (二分)
    UVALive 3523 Knights of the Round Table 圆桌骑士 (无向图点双连通分量)
    codeforecs Gym 100286B Blind Walk
  • 原文地址:https://www.cnblogs.com/FZLGYZ/p/13784765.html
Copyright © 2011-2022 走看看