【FreeRTOS】FreeRTOS内存管理的五种方式

2023-05-16

内存管理

1. FreeRTOS为什么要实现自己的内存管理

内存的动态管理是C语言程序的知识范围,并不属于FreeRTOS的知识范畴,但是它跟FreeRTOS关系是如此紧密。

在C语言的库函数中,有mallc、free等函数,但是在FreeRTOS中,它们不适用:

  • 不适合用在资源紧缺的嵌入式系统中
  • 这些函数的实现过于复杂、占据的代码空间太大
  • 并非线程安全的(thread-safe)
  • 运行有不确定性:每次调用这些函数时花费的时间可能都不相同
  • 内存碎片化
  • 使用不同的编译器时,需要进行复杂的配置
  • 有时候难以调试

注意:“堆栈”混着说,但是并不是同一个东西

  • 堆,heap,就是一块空闲的内存,需要提供管理函数。
    • malloc:从堆里划出一块空间给程序使用。
    • free:用完后,再把它标记为“空闲”的,可以再次使用。
  • 栈,stack,函数调用时局部变量保存在栈中,当前程序的环境也是保存在栈中
    • 可以从堆中分配一块空间用作栈

2. FreeRTOS的5种内存管理方法

FreeRTOS中内存管理的接口函数为:pvPortMalloc、vPortFree,对应于C库的malloc、free。

文件在 FreeRTOS/Source/portable/MemMang 下,它也是放在 portable 目录下,表示你可以提供自己的函数。

源码中默认提供了5个文件,对应内存管理的5种方法。

文件优点缺点
heap_1.c分配简单,时间确定只分配、不回收
heap_2.c动态分配、最佳匹配碎片、时间不定
heap_3.c调用标准库函数速度慢、时间不定
heap_4.c相邻空闲内存可合并可解决碎片问题、时间不定
heap_5.c在heap_4基础上支持分隔的内存块可解决碎片问题、时间不定

2.1 heap_1.c

它只实现了pvPortMalloc,没有实现vPortFree。

如果你的程序不需要删除内核对象,那么可以使用heap_1:

  • 实现最简单
  • 没有碎片问题
  • 一些要求非常严格的系统里,不允许使用动态内存,就可以使用heap_1

它的实现原理很简单,首先定义一个大数组:

/* Allocate the memory for the heap. */
##if ( configAPPLICATION_ALLOCATED_HEAP == 1 )
/* The application writer has already defined the array used for the RTOS
* heap - probably so it can be placed in a special segment or address. */
extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
##else
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
##endif /* configAPPLICATION_ALLOCATED_HEAP */

然后,对于pvPortMalloc调用时,从这个数组中分配空间。

FreeRTOS在创建任务时,需要2个内核对象:task control block(TCB)、stack。

使用heap_1时,内存分配过程如下图所示:

  1. 创建任务之前整个数组都是空闲的。
  2. 创建第一个任务之后,蓝色区域被分配出去了。
  3. 创建3个任务之后的数组使用情况。

在这里插入图片描述

2.2 heap_2.c

heap_2之所以还保留,只是为了兼容以前的代码。新设计中不再推荐使用heap_2。建议使用heap_4来替代heap_2,更加高效。

heap_2也是在数组上分配内存,跟heap_1不一样的地方在于:

  • heap_2使用**最佳匹配算法(best fit)**来分配内存。
  • 它支持vPortFree

最佳匹配算法:

  • 假设heap有3块空闲内存:5字节、25字节、100字节
  • pvPortMalloc想申请20字节
  • 找出最小的、能满足pvPortMalloc的内存:25字节
  • 把它划分为20字节、5字节
    • 返回这20字节的地址
    • 剩下的5字节仍然是空闲状态,留给后续的pvPortMalloc使用

与Heap_4相比,Heap_2不会合并相邻的空闲内存,所以Heap_2会导致严重的"碎片化"问题。

但是,如果申请、分配内存时大小总是相同的,这类场景下Heap_2没有碎片化的问题。所以它适合这种场景:频繁地创建、删除任务,但是任务的栈大小都是相同的(创建任务时,需要分配TCB和栈,TCB总是一样的)。

虽然不再推荐使用heap_2,但是它的效率还是远高于malloc、free。

使用heap_2时,内存分配过程如下图所示:

  1. 创建了3个任务
  2. 删除了一个任务,空闲内存有3部分:顶层的、被删除任务的TCB空间、被删除任务的Stack空间
  3. 创建了一个新任务,因为TCB、栈大小跟前面被删除任务的TCB、栈大小一致,所以刚好分配到原来的内存
    在这里插入图片描述

2.3 heap_3.c

heap_3使用标准C库里的malloc、free函数,所以堆大小由链接器的配置决定,配置项configTOTAL_HEAP_SIZE不再起作用。

C库里的malloc、free函数并非线程安全的,heap_3中先暂停FreeRTOS的调度器,再去调用这些函数,使用这种方法实现了线程安全。

2.4 heap_4.c

跟heap_1、heap_2一样,heap_4也是使用大数组来分配内存。

heap_4使用首次适应算法(first fit)来分配内存。它还会把相邻的空闲内存合并为一个更大的空闲内存,这有助于较少内存的碎片问题。

首次适应算法:

  • 假设堆中有3块空闲内存:5字节、200字节、100字节
  • pvPortMalloc想申请20字节
  • 找出第1个能满足pvPortMalloc的内存:200字节
  • 把它划分为20字节、180字节
    • 返回这20字节的地址
    • 剩下的180字节仍然是空闲状态,留给后续的pvPortMalloc使用

heap_4会把相邻空闲内存合并为一个大的空闲内存,可以较少内存的碎片化问题。适用于这种场景:频繁地分配、释放不同大小的内存。

heap_4的使用过程举例如下:

  1. 创建了3个任务
  2. 删除了一个任务,空闲内存有2部分:
    • 顶层的
    • 被删除任务的TCB空间、被删除任务的Stack空间合并起来的
  3. 分配了一个Queue,从第1个空闲块中分配空间
  4. 分配了一个User数据,从Queue之后的空闲块中分配
  5. 释放的Queue,User前后都有一块空闲内存
  6. 释放了User数据,User前后的内存、User本身占据的内存,合并为一个大的空闲内存

在这里插入图片描述

Heap_4执行的时间是不确定的,但是它的效率高于标准库的malloc、free。

2.5 heap_5

Heap_5分配内存、释放内存的算法跟Heap_4是一样的。

相比于Heap_4,Heap_5并不局限于管理一个大数组:它可以管理多块、分隔开的内存。

在嵌入式系统中,内存的地址可能并不连续,这种场景下可以使用Heap_5。

既然内存时分隔开的,那么就需要进行初始化:确定这些内存块在哪、多大:

  • 在使用pvPortMalloc之前,必须先指定内存块的信息
  • 使用vPortDefineHeapRegions来指定这些信息

怎么指定一块内存?使用如下结构体:

typedef struct HeapRegion
{
uint8_t * pucStartAddress; // 起始地址
size_t xSizeInBytes; // 大小
} HeapRegion_t;

怎么指定多块内存?使用一个HeapRegion_t数组,在这个数组中,低地址在前、高地址在后。

比如:

HeapRegion_t xHeapRegions[] =
{
    { ( uint8_t * ) 0x80000000UL, 0x10000 }, // 起始地址0x80000000,大小0x10000
    { ( uint8_t * ) 0x90000000UL, 0xa0000 }, // 起始地址0x90000000,大小0xa0000
    { NULL, 0 } // 表示数组结束
};

vPortDefineHeapRegions函数原型如下:

void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions );

把xHeapRegions数组传给vPortDefineHeapRegions函数,即可初始化Heap_5。

3. heap相关的函数

3.1 pvPortMalloc/vPortFree

函数原型:

void * pvPortMalloc( size_t xWantedSize );
void vPortFree( void * pv );

作用:分配内存、释放内存。

如果分配内存不成功,则返回值为NULL。

3.2 xPortGetFreeHeapSize

函数原型:

size_t xPortGetFreeHeapSize( void );

当前还有多少空闲内存,这函数可以用来优化内存的使用情况。比如当所有内核对象都分配好后,执行此函数返回2000,那么configTOTAL_HEAP_SIZE就可减小2000。

注意:在heap_3中无法使用。

3.3 xPortGetMinimumEverFreeHeapSize

函数原型:

size_t xPortGetMinimumEverFreeHeapSize( void );

返回:程序运行过程中,空闲内存容量的最小值。

注意:只有heap_4、heap_5支持此函数。

3.4 malloc失败的钩子函数

在pvPortMalloc函数内部:

void * pvPortMalloc( size_t xWantedSize )vPortDefineHeapRegions
{
    ......
    #if ( configUSE_MALLOC_FAILED_HOOK == 1 )
    {
        if( pvReturn == NULL )
        {
            extern void vApplicationMallocFailedHook( void );
            vApplicationMallocFailedHook();
        }
    }
    #endif
    
	return pvReturn;
}

所以,如果想使用这个钩子函数:

  • 在FreeRTOSConfig.h中,把configUSE_MALLOC_FAILED_HOOK定义为1
  • 提供vApplicationMallocFailedHook函数
  • pvPortMalloc失败时,才会调用此函数
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【FreeRTOS】FreeRTOS内存管理的五种方式 的相关文章

  • antdv动态表单回显

    在项目中经常遇到动态表单的业务 在编辑动态表单时需要回显新增后的内容 本文介绍Ant Design of Vue 的动态表单回显 比如新增时添加了两行动态表单 xff0c 在第二次编辑时需要进行回显 下面是表单的HTML代码 span cl
  • IMU在slam系统中的应用(三)

    在IMU在slam系统中的应用 二 中给出了IMU预积分计算推导以及代码 xff0c 这里给出IMU误差递推方程的推导 xff08 一 xff09 一段时间多个IMU数据预积分量协方差 单个IMU数据作为测量值 xff0c 其噪声方差我们可
  • docker 启动 jenkins 挂载目录权限问题 Permission denied

    通过 docker 挂载目录的 jenkins xff0c 查看 jenkins 日志发现存在一些文件无法写入或者 Permission denied 这样的异常错误 xff0c 基本可以判定是文件所有权问题 比如你宿主机挂载的文件目录是
  • VMware虚拟机硬盘扩展是灰色的

    问题描述 VMware虚拟机硬盘空间不足时 xff0c 想扩展空间 xff0c 但VMware虚拟机硬盘扩展是灰色的 xff0c 无法完成扩展操作 解决方案 进入虚拟机快照管理器 xff0c 删除快照 xff0c 删除后就可以扩容
  • ORB_SLAM2安装与编译报错的解决办法

    目录 1 安装教程 xff08 本教程参考网络课程资料 xff09 1 1 安装前准备 1 2 安装Pangolin 建议源码安装 1 3 安装OpenCV3 4 建议源码安装 1 4 安装Eigen3 3 7 建议源码安装 1 5 安装运
  • ROS安装与报错的解决办法

    目录 1 安装步骤 xff08 1 xff09 添加ROS软件源 xff08 2 xff09 添加秘钥 xff08 3 xff09 安装ROS xff08 4 xff09 初始化rosdep xff08 5 xff09 设置环境变量 xff
  • OpenCV - C++实战(06) — Grabcut图像分割

    目录 第6章 图像分割 6 1 Grabcut实现 6 1 1 定义前景和背景 6 1 2 cv grabCut 6 1 3 cv compare 6 1 4 算法实现 Github代码地址 xff1a GitHub Qinong Open
  • 相机标定 - (02) - 相机标定步骤与原理

    目录 2 相机标定步骤 2 1 张正有标定操作步骤 2 2 张正有标定原理 2 相机标定步骤与原理 参考文章 xff1a 三步骤详解张正友标定法 谜之 摄影爱好者的博客 CSDN博客 1998年 xff0c 张正友提出了基于二维平面靶标的标
  • 相机标定 - (03) - matlab单目相机标定

    目录 3 matlab单目相机标定 3 1 环境配置 3 2 matlab单目相机标定 3 matlab单目相机标定 3 1 环境配置 参考文章 xff1a https www cnblogs com Sandals little p 17
  • SLAM面试笔记(4) — 企业面试汇总

    目录 1 大疆 一面 xff08 50min xff09 二面 xff08 30min xff09 三面 xff08 30min xff09 2 华为 一面 xff08 30min xff09 二面 xff08 30min xff09 三面
  • ORB-SLAM2的稠密重建实战(1) — 流程与函数功能说明

    目录 0 系统整体流程 1 输出信息 2 初始化文件 3 初始化并运行追踪线程Tracking Step1 xff1a 地图初始化 Step2 xff1a 初始化成功 xff08 mbOnlyTracking xff09 Step3 xff
  • SLAM面试笔记(5) — ROS面试

    目录 1 ROS概述 2 ROS通信机制 问题 xff1a 服务通信概念 问题 xff1a 服务通信理论模型 问题 xff1a 参数服务器概念 问题 xff1a 参数服务器理论模型 问题 xff1a 参数服务器实现函数 3 ROS常用命令
  • 多线程常见的面试题

    多线程常见的面试题 xff1a 1 什么是线程和进程 线程与进程的关系 区别及优缺点 xff1f 进程是程序的一次执行过程 xff0c 是系统运行程序的基本单位 xff0c 因此进程是动态的 系统运行一个程序即是一个进程从创建 xff0c
  • docker 错误 WARNING: IPv4 forwarding is disabled. Networking will not work.

    这个错误出现在使用 docker 的服务器上 xff0c 当你进入 docker 容器时 xff0c 如果出现这样的 WARNING 提示 xff0c 则表示服务器没有开启 IPv4 转发 xff0c 按照本文的方法配置开启转发即可 错误提
  • 嵌入式中异常/中断/事件的区别

    嵌入式中异常 xff0f 中断 xff0f 事件的区别 Cortex M3在内核水平上搭载了一个异常响应系统 xff0c 支持为数众多的系统异常和外部中断 其中 xff0c 编号1 15的对应系统异常 xff0c 大于等于16的则都是外部中
  • 【起航】OpenHarmony远征02

    OpenHarmony内核 目前OpenHarmony针对不同量级的系统 xff0c 可以使用不同形态的内核 xff0c 轻量和小型的系统可以使用Liteos xff0c 小型和标准系统可以使用Linux Liteos系统是面向IOT领域的
  • momenta面经总结

    一 C 43 43 01 堆和栈的区别 xff0c 栈的静态分配和动态分配 xff1f 栈由操作系统分配释放 xff0c 用于存放函数的参数值 局部变量等 xff0c 栈中存储的数据的生命周期随着函数的执行完成而结束 堆由开发人员分配和释放
  • Orbslam2使用opencv4解决cv_bridge冲突问题

    在研究orbslam2在opencv4下使用会和cv bridge4冲突 解决方法 xff1a 1条消息 ROS学习 xff1a cv bridge与opencv版本冲突三种解决方案 Mr Qin 的博客 CSDN博客 cv bridge
  • 智能车浅谈——方向控制篇

    文章目录 前言自动控制理论人工控制系统自动控制 方向控制典型环节对应典型环节分析给定环节与给定量比较环节与偏差量控制环节与控制量执行机构舵机PWM技术舵机中值及限幅转向控制 被控对象与被控量反馈对象与反馈量 控制思路结束语智能车系列文章汇总
  • 嵌入式学习笔记——使用寄存器编程操作GPIO

    使用寄存器编程操作GPIO 前言GPIO相关的寄存器GPIO 端口模式寄存器 GPIOx MODER x 61 A I 位操作 GPIO 端口输出类型寄存器 GPIOx OTYPER x 61 A I GPIO 端口输出速度寄存器 GPIO

随机推荐

  • 2021-基于卷积和LSTM神经网络的视频分类时间融合方法在暴力检测中的应用

    A Temporal Fusion Approach for Video Classification with Convolutional and LSTM Neural Networks Applied to Violence Dete
  • uni-app(Vue)直播拉流

    html span class token tag span class token tag span class token punctuation lt span div span span class token attr name
  • Docker 容器常见故障排查及处理

    Docker 容器常见故障排查及处理 本文来源于公司论坛发表的帖子 Docker是一种相对使用较简单的容器 xff0c 我们可以通过以下几种方式获取信息 xff1a 1 通过docker run执行命令 xff0c 或许返回信息 2 通过d
  • CMake教程

    本文参考 xff1a 1 https blog csdn net whahu1989 article details 82078563 2 https blog csdn net kai zone article details 82656
  • docker / containerd 错误 panic: invalid freelist page: 162, page type is leaf

    错误描述 containerd 服务启动失败 xff0c 使用命令 journalctl xe u containerd 查看日志如下所示 xff1a span class token punctuation span root 64 k8
  • 制作自己的rosgo

    制作自己的rosgo 首先 xff0c 什么是rosgo xff1f ROS2GO xff08 ROS To Go xff09 是一款随时可用的高速USB闪存驱动器 xff0c 已安装Ubuntu ROS和常用软件包 xff0c 可以从外部
  • 解决libssl.so.1.0.0 => not found以及libcrypto.so.1.0.0 => not found

    现在的apt源中 xff0c libssl1 0的版本一般是libssl1 0 2 xff0c libcrypto1 0的版本一般是libcrypto1 0 2 但是很多应用要使用libssl so 1 0 0和libcrypto so 1
  • cmake设置默认CMAKE_BUILD_TYPE

    原文 xff1a https cmake org pipermail cmake 2009 June 030311 html 在CMakeLists txt里写入 IF NOT CMAKE BUILD TYPE set CMAKE BUIL
  • 进程:fork的使用

    1 用fork函数创建一个进程 pid t fork void fork函数调用成功 xff0c 返回两次 返回值为0 xff0c 代表当前进程是子进程 返回值非负数 xff08 子进程的进程ID xff09 xff0c 代表当前进程为父进
  • 8.15美团笔试和奇葩赛码网的输入坑

    第一次用赛码网 xff0c 两个小时的笔试居然有一个半小时在查如何正确输入 xff0c 最要命的是在本地IDE上可以的输入到了网页上就GG了 看来是leetcode刷多了 xff0c 文件流那块学的也很糟糕 总结一下下次避免跳坑吧 一行的就
  • 大小端与字节序转换

    大端小端 不同机器内部对变量的字节存储顺序不同 xff0c 有的采用大端模式 big endian xff0c 有的采用小端模式 little endian 大端模式是指高字节数据存放在低地址处 xff0c 低字节数据放在高地址处 小端模式
  • 解决ubuntu安装后只有一个800x600的分辨率,且系统设置显示里也只有这个分辨

    1 修改 etc default grub 打开终端用命令 xff1a sudo gedit etc default grub 会出现下面的代码 xff1a If you change this file run update grub a
  • DRV8833直流电机驱动模块代替 TB6612FNG电机驱动

    江科大自化协TB6612使用DRV8833代替 xff0c 以及使用方法 文章目录 前言一 两种模块对比二 HAL库驱动的使用步骤1 配置CubeMX 前言 购买了国产DRV8833模块驱动直流电机 xff0c 单片机3 3V的GPIO口无
  • Spring boot Json字符串传输 " 转义问题解决

    在接收前端返回过来的json字符串时 xff0c 发现出现 34 被转义的情况 xff0c 这里记录下解决方式 xff0c 获取到json字符串的时候我们可以使用工具类去进行转就可以了 xff0c 使用 org apache commons
  • 计算机三级Linux应用与开发技术

    第1章 计算机体系结构与操作系统 一 选择题 1 操作系统负责管理计算机系统的 xff08 C xff09 xff0c 其中包括处理器 储存器 设备和文件 A 程序 B 文件 C 资源 D 进程 2 操作系统是一种 xff08 B xff0
  • Linux 命令 cp mv 花括号用法

    Linux 命令行使用 mv 重命名文件时 xff0c 巧妙的使用 可以很方便 xff0c 最常见的就是把一个文件加个后缀临时备份 例如 xff1a span class token function mv span files local
  • FreeRTOS的调度器源码分析及系统滴答SysTick

    1 PendSV系统调用 查遍了C站上所有关于FreeRTOS调度器的分析 xff0c 发现大家分析完vTaskStartScheduler 之后就戛然而止了 xff0c 我就会比较迷糊 xff0c 这个仅开启了调度器的调度 xff0c 而
  • 从零适配IMX6ULL开发板Linux系统——uboot移植

    串口驱动搜 CP210 下载 目的 xff1a 当拿到一块儿完全没有系统的开发板 xff0c 厂家并没有提供给我们对应的Linux系统 xff0c 从芯片厂家提供的原厂资料中 xff0c 修改并适配自己的开发板 我们需要做的步骤如下 xff
  • NXP IMX6ULL芯片时钟系统全概况

    本文主要针对IMX6ULL芯片的时钟系统进行分析 xff0c 并没有针对哪款开发板 xff0c 毕竟是芯片的问题 一 硬件原理图分析 对于IMX6ULL芯片有两个时钟源 xff0c 我使用的开发板是百问网IMX6ULL xff0c 其他IM
  • 【FreeRTOS】FreeRTOS内存管理的五种方式

    内存管理 1 FreeRTOS为什么要实现自己的内存管理 内存的动态管理是C语言程序的知识范围 xff0c 并不属于FreeRTOS的知识范畴 xff0c 但是它跟FreeRTOS关系是如此紧密 在C语言的库函数中 xff0c 有mallc