FreeRTOS内存管理

heap1是FreeRTOS中内存管理最简单的一个,它简单到只能申请内存。是的,跟你想的一样,一旦申请成功后,这块内存再也无法释放。对于大多数嵌入式系统,特别是对安全要求高的嵌入式系统,这种内存管理策略很有用,因为对系统软件来说,逻辑越简单,越安全。实际上,大多数的嵌入式系统并不需要动态删除任务、信号量和队列等。而是在初始化的时候一次性创建,便一直存在,永不删除。

总结heap1特性如下:

    1、适用于那些一旦创建好任务、信号量和队列就再也不删除的的应用。

    2、具有可确定性(执行所花费的时间大多数一样),而且不会导致内存碎片。

    3、代码实现和内存分配过程都非常简单,内存是从一个静态数组中获取,也就是适用于那些不需要动态内存分配的应用。

我们可以将第一种内存管理看作切面包:初始化的静态数组就像一个完整的长棍面包,每次申请内存,就从一端切下适当长度的面包返还给申请者,直到面包被分配完毕。

heap1的内存管理策略使用两个静态变量来跟踪内存的使用,定义如下:

static size_t xNextFreeByte = ( size_t ) 0;

void *pvPortMalloc( size_t xWantedSize )
{
    static uint8_t *pucAlignedHeap = NULL;
    ...
}

其中 xNextFreeByte 用来保存 pucAlignedHeap  到内存堆剩余内存首地址之间的偏移值,如下图所示:

变量 xNextFreeByte  一开始初始化为0,每次成功申请内存后,都会增加申请内存的大小(对齐会导致实际申请的内存更多)。 变量 pucAlignedHeap 指向每次对齐后的内存堆首地址,内存对齐的好处是硬件访问数据只要单次读取,而且某些类型的内核不支持非对齐访问。STM32F0x系列的单片机要求16位对齐,非对齐读取会导致单片机进入HardFault.

内存申请函数:pvPortMalloc

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;
}

函数逻辑步骤:

1、xWantedSizev 根据宏 portBYTE_ALIGNMENT (实验环境默认为4) 判断是否要字节对齐,调整 xWantedSize 为对齐字节数的倍数。 调整方法是,找到比 xWantedSize 大, 且是最靠近 xWantedSize  的对齐数。假设 xWantedSize 是11,经过调整后,结果为12。

2、注意!内存申请的过程中禁止调度,不能被其他任务打断。FreeRTOS 使用 vTaskSuspendAll  和 xTaskResumeAll  禁止任务调度。

3、不仅申请的内存块大小要求4字节对齐,内存堆的起始地址也必须是4字节对齐。pucAlignedHeapNULL的时候说明是第一次申请内存,需要初始化相关参数。ucHeap 此时的起始地址显然不是4字节的倍数,pucAlignedHeap  不能以ucHeap 的首地址作为内存堆的首地址, pucAlignedHeap 经过调整后变为 0x0041b14c, 以3字节的损失换来了起始地址的对齐。ucHeappucAlignedHeap 第一次初始化的状态如下图所示:

4、进行边界检查,查看内存堆是否足够本次分配,xNextFreeBytexWantedSize的和不能超出可供使用的内存边界。如果内存够分配且不会越界,那么即将申请到的内存首地址赋值给pReturn 并更新全局变量 xNextFreeByte特别说明的是实际可用的内存是 configADJUSTED_HEAP_SIZE而不是configTOTAL_HEAP_SIZE。从代码中我们可以看出,FreeRTOS提前保留一部分字节供内存对齐使用。

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

5、宏 configUSE_MALLOC_FAILED_HOOK  为1说明使能了内存申请失败钩子函数,在申请失败的情况下,会调用钩子函数 vApplicationMallocFailedHook ,这个钩子函数由用户应用程序提供,一般用来打印故障信息。

 参考文章:

原文地址:https://www.cnblogs.com/soga238/p/10280006.html

时间: 2024-10-12 22:38:26

FreeRTOS内存管理的相关文章

FreeRTOS系列第8篇---FreeRTOS内存管理

FreeRTOS提供了几个内存堆管理方案,有复杂的也有简单的.其中最简单的管理策略也能满足很多应用的要求,比如对安全要求高的应用,这些应用根本不允许动态内存分配的. FreeRTOS也允许你自己实现内存堆管理,甚至允许你同时使用两种内存堆管理方案.同时实现两种内存堆允许任务堆栈和其它RTOS对象放置到快速的内部RAM,应用数据放置到低速的外部RAM. 每当创建任务.队列.互斥量.软件定时器.信号量或事件组时,RTOS内核会为它们分配RAM.标准函数库中的malloc()和free()函数有些时候

FreeRTOS高级篇7---FreeRTOS内存管理分析

内存管理对应用程序和操作系统来说都非常重要.现在很多的程序漏洞和运行崩溃都和内存分配使用错误有关.        FreeRTOS操作系统将内核与内存管理分开实现,操作系统内核仅规定了必要的内存管理函数原型,而不关心这些内存管理函数是如何实现的.这样做大有好处,可以增加系统的灵活性:不同的应用场合可以使用不同的内存分配实现,选择对自己更有利的内存管理策略.比如对于安全型的嵌入式系统,通常不允许动态内存分配,那么可以采用非常简单的内存管理策略,一经申请的内存,甚至不允许被释放.在满足设计要求的前提

轻量级操作系统FreeRTOS的内存管理机制(三)

本文由嵌入式企鹅圈原创团队成员朱衡德(Hunter_Zhu)供稿. 轻量级操作系统FreeRTOS的内存管理机制(二)中讲到,heap2.c的内存管理机制会导致内存碎片的问题,系统运行久后会出现无法分配大块内存的情况,heap4.c中的管理机制提供了解决方法,它是在heap2.c的基础上添加了地址相邻空闲块间合并的功能,而heap5.c是对heap4.c的进一步扩展,它能够支持多块不连续分布的RAM空间作为堆使用,本篇将对heap4.c.heap5.c中的管理机制进行分析. 一.heap4.c

轻量级操作系统FreeRTOS的内存管理机制(二)

本文由嵌入式企鹅圈原创团队成员朱衡德(Hunter_Zhu)供稿. 上一篇文章中介绍了FreeRTOS多种内存管理机制中最简单的一种:全局声明一个静态数组ucHeap,然后通过指针偏移记录空间的分配情况,在这种内存机制下无法对内存进行释放.同时也介绍了内存操作过程中字节对齐的细节,本篇文章将会对FreeRTOS源码中第二种内存管理机制heap2.c进行讲解,在heap2.c中同样使用一个全局静态数组ucHeap来表示内存,heap2.c内存管理机制较heap1.c而言增加了内存释放的功能,通过使

FreeRTOS 动态内存管理

本章节为大家讲解 FreeRTOS 动态内存管理,动态内存管理是 FreeRTOS 非常重要的一项功能,前面章节讲解的任务创建. 信号量. 消息队列. 事件标志组. 互斥信号量. 软件定时器组等需要的 RAM 空间都是通过动态内存管理从 FreeRTOSConfig.h 文件定义的 heap 空间中申请的. 动态内存管理介绍FreeRTOS 支持 5 种动态内存管理方案,分别通过文件 heap_1,heap_2,heap_3,heap_4 和 heap_5实现,这 5 个文件在 FreeRTOS

轻量级操作系统FreeRTOS的内存管理机制(一)

本文由嵌入式企鹅圈原创团队成员朱衡德(Hunter_Zhu) 近几年来,FreeRTOS在嵌入式操作系统排行榜中一直位居前列,作为开源的嵌入式操作系统之一,它支持许多不同架构的处理器以及多种编译工具链,具有轻量级.容易移植和使用的特点.本篇文章将会对FreeRTOS提供的几种内存分配策略进行介绍,FreeRTOS允许开发者根据自己的项目实际需要选择不同的内存分配策略或者自定义分配内存策略. 一.FreeRTOS内存分配源码 FreeRTOS在创建任务.队列.互斥量.信号量.软件定时以及事件组的时

linux内存管理

一.Linux 进程在内存中的数据结构 一个可执行程序在存储(没有调入内存)时分为代码段,数据段,未初始化数据段三部分:    1) 代码段:存放CPU执行的机器指令.通常代码区是共享的,即其它执行程序可调用它.假如机器中有数个进程运行相同的一个程序,那么它们就可以使用同一个代码段.     2) 数据段:存放已初始化的全局变量.静态变量(包括全局和局部的).常量.static全局变量和static函数只能在当前文件中被调用.     3) 未初始化数据区(uninitializeddata s

Block内存管理实例分析

在ios开发中,相信说道block大家都不陌生,内存管理问题也是开发者最头疼的问题,网上很多讲block的博客,但大都是理论性多点,今天结合一些实例来讲解下. 存储域 首先和大家聊聊block的存储域,根据block在内存中的位置,block被分为三种类型: NSGlobalBlock NSStackBlock NSMallocBlock 从字面意思上大家也可以看出来 NSGlobalBlock是位于全局区的block,它是设置在程序的数据区域(.data区)中. NSStackBlock是位于

Obstack是C标准库里面对内存管理的GNU扩展

Obstack介绍 Obstack初始化 在Obstack中申请对象 释放对象 申请growing object 获取Obstack状态 数据对齐 以下是来自wiki对obstack的介绍: Obstack是C标准库里面对内存管理的GNU扩展(实际上就是GNU C library了).Obstack===Object stack.没错,Obstack就是一个栈,栈里面的元素是对象object(不是面向对象的对象哦,这里的对象单指数据元素).这些数据是动态的,也就是使用的是动态内存.这种内存管理技