• FreeRTOS的堆内存管理(heap_1 ~ heap_5)


    FreeRTOS的堆管理

    上文对FreeRTOs的目录结构进行了说明,其中提到了FreeRTOSSourceportableMemMang目录下的五个heap_n.c文件,本文将对这个五个文件的作用、差异、使用场景进行对比,以便选择出适合自己项目的堆管理模式。

    • FreeRTOS使用pvPortMalloc()来分配内存。
    • vPortFree()来释放内存。

    Heap_1.c

    主要用于小型专一嵌入式系统。内核在任何实时任务执行之前先分配内存,一次分配永久使用并不再改变,可靠性较高。

    堆的总容量 configTOTAL_HEAP_SIZE 在 FreeRTOSConfig.h 文件中配置

    每创建一个任务都会分配一个堆控制块(TCB:Task control block)和一个栈(Stack)

    • A:代表整个可分配空间
    • B:当一个任务被创建出来
    • C:当三个任务被创建出来

    Heap_2.c

    • Heap_2 保留的主要目的是向后兼容,不推荐在新项目中使用。可使用Heap_4作为替代。
    • Heap_2 采用最佳适配算法,适用于需要频繁创建和删除需要分配固定栈内存的任务。

    Heap_3.c

    • 在heap3.c中 configTOTAL_HEAP_SIZE的配置将不再生效。
    • Heap_3通过暂时挂起FreeRTOS的调度来实现malloc()和free()的线程安全。(待补充)

    Heap_4.c

    • Heap_4采用首次适应算法来分配内存。heap4将相邻未分配的内存结合成为整个大内存来减少碎片内存。

    Heap5.c

    • heap_5和heap_4的使用完全一致。
    • heap_5可以对任意位置的空间进行分配,
    • heap_5在使用之前需要通过vPortDefineHeapRegions()函数进行初始化,之后才可以使用pvPortMalloc()进行内存分配。
    • PortDefineHeapRegions()的作用是明确每个分散空间的初始位置和大小。
      • 原型描述: void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions );
      • 返回值结构
    typedef struct HeapRegion
    {
    /* 内存块的起始地址将成为堆的一部分.*/
    uint8_t *pucStartAddress;
    /* 堆的容量大小bytes. */
    size_t xSizeInBytes;
    } HeapRegion_t;
    

    下图表示vPortDefineHeapRegions函数的具体使用场景RAM1,RAM2,RAM3分别代表三个空闲空间

    /* 图最左侧堆:A   定以RAM1-3的基本信息. */
    #define RAM1_START_ADDRESS ( ( uint8_t * ) 0x00010000 )
    #define RAM1_SIZE ( 65 * 1024 )
    #define RAM2_START_ADDRESS ( ( uint8_t * ) 0x00020000 )
    #define RAM2_SIZE ( 32 * 1024 )
    #define RAM3_START_ADDRESS ( ( uint8_t * ) 0x00030000 )
    #define RAM3_SIZE ( 32 * 1024 )
    
    const HeapRegion_t xHeapRegions[] =
    {
     { RAM1_START_ADDRESS, RAM1_SIZE },
     { RAM2_START_ADDRESS, RAM2_SIZE },
     { RAM3_START_ADDRESS, RAM3_SIZE },
     { NULL, 0 } /* 标志数组的结尾. */
    };
    int main( void )
    {
     /* 初始化heap_5 */
     vPortDefineHeapRegions( xHeapRegions );
     
    /* 编码区域。*/
    }
    
    • A仅仅展示了RAM结构,图B 包含了堆分配的一些细节

    • 由于RAM的管理需要链接脚本,图B RAM1包含了链接脚本,RAM2,和RAM3为空。RAM1被分为两个区域,0x10000-0x01nnnn用来存放连接脚本,只有0x01nnnn-0x01FFFF可用,即heap5的可用空间为0x01nnnn-0x01FFFF,RAM2,RAM3。此时如果起始位置依然以0x010000作为起点将覆盖存放变量的内存,所以必须从0x0001nnnn作为起点,可以在HeapRegion_t结构中使用xHeapRegions[] 数组作为起始地址,但由于起始地址难判定,后续结构的必要更新,堆重叠的问题此方案并不推荐。

    • 完美推荐方案C

    * 定以没有被链接器使用的两个起始地址和容量 */
    #define RAM2_START_ADDRESS ( ( uint8_t * ) 0x00020000 )
    #define RAM2_SIZE ( 32 * 1024 )
    #define RAM3_START_ADDRESS ( ( uint8_t * ) 0x00030000 )
    #define RAM3_SIZE ( 32 * 1024 )
    /* 定义一个数组为heap_5使用的一部分,此数组将会被链接器放置于RAM1 */
    #define RAM1_HEAP_SIZE ( 30 * 1024 )
    static uint8_t ucHeap[ RAM1_HEAP_SIZE ];
    /* 定义一个数组HeapRegion_t,第一个入口只定义了ucHeap数组。像之前一样HeapRegion_t结构定以仍需地址从小到大排列。*/
    const HeapRegion_t xHeapRegions[] =
    {
     { ucHeap, RAM1_HEAP_SIZE },
     { RAM2_START_ADDRESS, RAM2_SIZE },
     { RAM3_START_ADDRESS, RAM3_SIZE },
     { NULL, 0 } /* 标志数组的结束. */
    };
    
    

    优势

    • 初始地址不再是常量
    • 链接器自动设置HeapRegion_t结构
    • 内存分配给heap_5的数据不会被链接器覆盖
    • 如果ucHeap太大应用将不会链接

    堆分配相关函数

    • size_t xPortGetFreeHeapSize( void ); 当被调用时返回堆中可用字节,可用于优化堆大小。当时用heap_3分配方案时此函数不生效。
    • size_t xPortGetMinimumEverFreeHeapSize( void ); 当调用时返回FreeRTOS应自开始运行从未存在于堆中的最小未分配字节。 可用于了解是否存在堆溢出情况。 **只可用于heap_4,heap_5堆分配方案中。
    • void vApplicationMallocFailedHook( void );
      内存分配的情况无处不在,可在应用中直接调用,此外在freeRTOS创建任务,队列,信号量等操作时也会调用pvPortMalloc()函数。当pvPortMalloc()未找到符合大小的RAM空间时返回NULL,此时可调用回调函数vApplicationMallocFailedHook。
      • 当在FreeRTOSConfig.h配置了configUSE_MALLOC_FAILED_HOOK为1 则表示内存分配出错时必须执行vApplicationMallocFailedHook函数。
  • 相关阅读:
    HDU
    洛谷- P1306 斐波那契公约数
    HDU
    HDU
    POJ3321
    SPOJ
    HDU
    POJ 1236 Network of Schools
    POJ-3169 Layout (差分约束+SPFA)
    POJ-1062 昂贵的聘礼 (最短路)
  • 原文地址:https://www.cnblogs.com/Pomelos/p/14806826.html
Copyright © 2020-2023  润新知