RT-Thread中堆和栈怎么跟单片机内存相联系

news2025/1/3 3:04:19

        现在RT-Thread+MCU的应用方式越来越普遍,RT-Thread需要配置MCU中的RAM到的系统中,进入系统内存管理,才能提供给基于实时系统的应用程序使用,比如给应用程序提供malloc、free等函数调用功能。在嵌入式软件开发中,我们经常会提到堆和栈,实际上堆和栈都是RAM上的物理内存空间,只是使用方式不同而已。栈和堆都是单片机RAM中一段连续的存储空间,该段空间一般在启动文件或链接脚本中指定,最后在C库的_main函数中进行初始化。STM32中的堆栈内存空间分配就在启动文件中完成:

栈(STACK):由编译器自动分配和释放
堆(HEAP):有的地方也叫堆栈,一般由用户自行分配和释放,因此在分配好使用完成后要及时释放内存,否则会导致系统可用的内存越来越少,我们管这种情况叫做内存泄漏
使用MDK5进行STM32项目开发时,当点击全部编译后会在Build Output窗口生成如下信息: 

那么这些信息究竟是代表什么意思?
Code:代码段,存放程序的代码部分
RO-Data:只读数据段,存放程序中定义的常量
RW-Data:读写数据段,存放初始化为非0值的全局变量
ZI-Data:0数据段,存放初始化为0的全局变量或未初始化的全局变量(程序运行时会对未初始化的全局变量自动清0)需要注意的是,启动文件中定义的STACK和HEAP内存空间都是被包含到ZI-Data中的,如下图片可对比说明:

 

当STACK空间大小设置为0x2000时ZI-Data空间大小为129836Byte,设置为0x1000时ZI-Data空间大小为125740Byte。129836-125740 = 4096 = 0x1000,因此可以说明栈空间是被包含在ZI-Data中的。将HEAP空间大小由0x2000设置为0x1000时,ZI-Data空间大小由129836变为125740,129836-125740 = 4096 = 0x1000,因此可以证明堆空间是被包含在ZI-Data中的。

在工程编译完成后也会生成的对应.map文件,.map文件中详细描述了各个函数在ROM中的存储地址和大小,也可以看到程序中定义的全局变量、全局数组、常量等在RAM中的存储地址和大小,因此.map文件是非常重要的一个文件。在生成的.map的最后几行,也可以看到如下信息:

 那么这些信息究竟是代表什么意思?
RO Size:程序在ROM中的存储大小,包括Code段和RO-Data段
RW Size:程序运行时占用RAM空间的大小,包括RW-Data段和ZI-Data段
ROM Size:程序烧写到ROM上时占用的空间大小,包括Code段和RO-Data段、RW-Data段(这里不包含ZI-Data段是因为ZI-Data段全部是0,将其存储在ROM中毫无意义,因此只需记录ZI-Data段占用的内存空间,在程序运行时在RAM上开辟对应大小的内存空间并将该区域清0即可)

const int ci_max_len = 100;        //RO段
static int si_loop_time = 300;     //RW
char *p1;                          //ZI
int main(void)
{
int i = 0;                       //STACK
char c_say_a[] = "hello world";  //c_say_a存储于STACK,"hello world"存储于RO
char *p= &s[0];                  //STACK
static int si_i = 5;             //RW
char *p2;                        //STACK
p1 = (char *)malloc(100);        //HEAP
p2 = (int *)malloc(100);         //HEAP
...
free(p1);
free(p2);
return 0;
}

Cortex-M3和Cortex-M4在设计之初就考虑到了对OS的高效支持,主要有3点:
1、它们都具有一个内置的简单的向下计数24位计数器SysTick。之所以要在处理器内增加一个定时器,主要是为了提高软件移植的方便性。所有基于Cortex-M3和Cortex-M4内核的处理器具有相同的SysTick定时器,在一种Cortex-M3/M4处理器上实现的OS也能适用于其它Cortex-M3/M4处理器。因此,嵌入式OS的源码可以很容易的移植到其它Cortex-M3/M4内核的处理器上,无需为设备相关的定时器做修改。
2、Cortex-M3内核支持双堆栈机制,即主堆栈MSP和线程堆栈PSP。OS内核和系统中的中断或异常使用MSP,用户创建的多线程任务使用PSP。
3、Cortex-M3/M4内核支持特权模式和非特权模式,可以将用户创建的多线程任务在非特权操作模式中运行,这样可以限制其对一些寄存器的访问,如NVIC、CONTROL寄存器等,以免因为不可靠的程序引起整个系统的崩溃。
        在裸机系统中,所有的变量、函数调用、中断处理等使用的栈空间均是MSP。在使用RTOS的多线程系统中,每个线程都是完全独立互不干扰的,因此需要为每个线程分配独立的栈空间,这个栈空间可以是预先分配好的全局数组(即存储于RW-Data段或ZI-Data段),也可以是动态分配的一段内存空间(注意:这里动态分配的空间不是启动文件(注:启动文件就是单片机相关的.s文件,如startup_hc32f4a0sitb.s)中定义的堆空间,而是由RTOS管理的内存空间),但本质上它们都是RAM上的一段内存空间。在RT-Thread中若需要动态内存管理,则需要先调用board.c--->rt_hw_board_init()--->rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END);函数进行动态内存管理范围的配置,即将HEAP_BEGIN和HEAP_END之间的内存空间作为动态内存空间交由RT-Thread进行管理。在RT-Thread Master和RT-Thread Nano版本中,对于rt_system_heap_init()函数调用时填入的参数有些许差异,下面进行对比说明。
RT-Thread Master版本:

 RT-Thread Master版本默认开启RT-Thread中的动态内存分配,并将将HEAP_BEGIN和HEAP_END之间的内存空间作为动态内存空间交由RT-Thread进行管理。其中在board.h文件中有定义,具体如下:

ZI$$Limit是一个链接器导出的符号,代表ZI-Data段的结束,也即程序执行时所占RAM空间的结束地址,也是未使用RAM 空间的起始地址。因此RT-Thread Master版本中会默认将RAM中剩余的内存空间全部交给RT-Thread进行动态内存管理。

RT-Thread Nano版本: 

 RT-Thread Nano版本默认不开启RT-Thread中的动态内存分配,因此凡是涉及到使用RT-Thread动态内存分配的函数都不能使用,如在创建线程时无法使用rt_thread_create()函数,只能使用rt_thread_init()函数等。用户需开启rtconfig.h中的RT_USING_HEAP宏开启使用RT-Thread进行动态内存分配的功能,因为动态内存分配中有使用到信号量,因此还需打开RT_USING_SEMAPHORE宏。rt_system_heap_init()函数中使用的参数rt_heap_begin_get(), rt_heap_end_get()均在board.c文件中有定义,具体如下:

 可以看到RT-Thread Nano版本中是将用户定义好的大小为1024 * 4个字节的内存空间交由RT-Thread进行动态内存管理,而非将RAM中剩余的内存空间全部交给RT-Thread进行动态内存管理。

  这里需要明白的是,程序中并不是将RAM空间用完的,假使RAM为20KByte,实际上只使用了8560Bytes,则剩余的RAM空间全部闲置在那儿。因此在RT-Thread Nano版本中使用数组作为动态内存堆时,可能会有一部分RAM空间是闲置的。

 当把RT_HEAP_SIZE由1024 * 4Byte改为256 * 4Byte时,生成的工程信息中只有ZI-Data发生变化。由11124变为8052,即11124 - 8052 = 3072Byte,和程序中的变化值相等。因此,我们可以理解为:RT-Thread Nano是通过定义全局数组的方式在ZI-Data段占据了一段空间,并将这段空间交由RT-Thread进行动态内存分配。如果不想使用RT-Thread-Nano中使用数组作为动态内存堆的方式,也可以使用和RT-Thread-Master中同样的方法将ZI段结束作为动态内存堆起始地址,将RAM空间结束地址作为动态内存堆的结束地址。这样可以将程序运行所需RAM空间之余的全部空间作为动态内存堆使用,即RAM上没有闲置的内存空间。

编译后生成的.map文件如下所示,可以看到用户自定义的数组消失了,程序使用栈顶地址0x20002b90和RAM空间结束地址0x20005000之间的RAM空间作为RT-Thread的动态内存堆。 

 

当然,RT-Thread Master中也可以使用RT-Thread Nano中默认的使用用户自定义的数组作为动态内存堆的方式。具体如何使用RAM空间,是很灵活的可以由用户自行指定的。当你深入了解RAM空间的分配原理后(一定要研究.map文件,开发中很有用的一个文件),就会发现用户就像是单片机的上帝,上帝可以任意支配手中的资源。而如何让单片机中这些资源按照用户的意愿去分配、去实现既定的功能是我们用户需要去好好研究的。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2268392.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2、Bert论文笔记

Bert论文 1、解决的问题2、预训练微调2.1预训练微调概念2.2深度双向2.3基于特征和微调(预训练下游策略) 3、模型架构4、输入/输出1.输入:2.输出:3.Learned Embeddings(学习嵌入)1. **Token Embedding**2. **Position Embedding**3…

TiDB 的MPP架构概述

MPP架构介绍: 如图,TiDB Server 作为协调者,首先 TiDB Server 会把每个TiFlash 拥有的region 会在TiFlash上做交换,让表连接在一个TiFlash上。另外 TiFlash会作为计算节点,每个TiFlash都负责数据交换,表连接…

3、redis的高可用

主从复制 主从复制:这是redis高可用的基础。哨兵模式和集群都是建立在此基础之上。 主从模式和数据库的主从模式是一样的,主负责写入,然后把写入的数据同步到从,从节点只能读不能写。read only。 不能做高可用的切换&#xff…

【架构-38】如何选择通信协议和数据格式

一、通信协议选择 不同的协议适用于不同的应用场景,关键在于数据传输的需求,如:实时性、带宽、可靠性等。下面是几种常见通信协议的适用场景: WebSocket 适用场景:实时、双向数据传输、低延迟、持久连接 特点&#x…

SpringCloudAlibaba 技术栈—Sentinel

1、什么是sentinel? Sentinel是一个用于微服务架构的流量管理和控制系统,它通过限制和控制进入系统的流量,来保护系统免受过载和故障的影响,确保服务的稳定性。简而言之,它就是一个帮助微服务在高负载情况下也能稳定运行的工具。…

初学STM32 ---高级定时器互补输出带死区控制

互补输出,还带死区控制,什么意思? 带死区控制的互补输出应用之H桥 捕获/比较通道的输出部分(通道1至3) 死区时间计算 举个栗子(F1为例):DTG[7:0]250,250即二进制&#x…

RoboMIND:多体现基准 机器人操纵的智能规范数据

我们介绍了 RoboMIND,这是机器人操纵的多体现智能规范数据的基准,包括 4 个实施例、279 个不同任务和 61 个不同对象类别的 55k 真实世界演示轨迹。 工业机器人企业 埃斯顿自动化 | 埃夫特机器人 | 节卡机器人 | 珞石机器人 | 法奥机器人 | 非夕科技 | C…

Hadoop HA安装配置(容器环境),大数据职业技能竞赛模块A平台搭建,jdk+zookeeper+hadoop HA

HA概述 (1) 所谓HA(High Availablity),即高可用(7*24小时不中断服务)。 (2) 实现高可用最关键的策略是消除单点故障,HA严格来说应该分为各个组件的HA机制,H…

国产文本编辑器EverEdit - 如何让输出窗口的日志具有双击跳转到文件指定行的功能

1 开发参考:编写脚本时如何向输出窗口打印可跳转到文件位置的日志 1.1 应用场景 编写脚本时,有时对文本进行分析,需要将提示信息打印到输出窗口,同时希望将文本的行、列信息也打印在日志中, 最好是双击日志信息可以跳…

《云原生安全攻防》-- K8s安全配置:CIS安全基准与kube-bench工具

在本节课程中,我们来了解一下K8s集群的安全配置,通过对CIS安全基准和kube-bench工具的介绍,可以快速发现K8s集群中不符合最佳实践的配置项,及时进行修复,从而来提高集群的安全性。 在这个课程中,我们将学习…

3、redis的集群模式

主从模式 哨兵模式 集群 主从模式:这是redis高可用的基础,哨兵模式和集群都是建立在此基础之上。 主从模式和数据库的主从模式是一样的,主负责写入,然后把写入的数据同步到从, 从节点只能读不能写,rea…

计算机图形学知识点汇总

一、计算机图形学定义与内容 1.图形 图形分为“图”和“形”两部分。 其中,“形”指形体或形状,存在于客观世界和虚拟世界,它的本质是“表示”;而图则是包含几何信息与属性信息的点、线等基本图元构成的画面,用于表达…

自动化测试模型(一)

8.8.1 自动化测试模型概述 在自动化测试运用于测试工作的过程中,测试人员根据不同自动化测试工具、测试框架等所进行的测试活动进行了抽象,总结出线性测试、模块化驱动测试、数据驱动测试和关键字驱动测试这4种自动化测试模型。 线性测试 首先&#…

医疗数仓数据仓库设计

医疗数仓数据仓库设计 数据仓库构建流程数据调研明确数据域构建业务总线矩阵明确统计指标交易主题医生主题用户主题评价主题 维度模型设计汇总模型设计 数据仓库构建流程 数据仓库分层规划 优秀可靠的数仓体系,需要良好的数据分层结构。合理的分层,能够…

Go-知识 注释

Go-知识 注释 行注释块注释包注释结构体&接口注释函数&方法注释废弃注释文档 在 go 语言中注释有两种,行注释和块注释 行注释 使用双斜线 // 开始,一般后面紧跟一个空格。行注释是Go语言中最常见的注释形式,在标准包中,…

1230作业

思维导图 作业 将广播发送和接收端实现一遍&#xff0c;完成一个发送端发送信息&#xff0c;对应多个接收端接收 自实验 //广播发送端 #include <myhead.h> #define G_PORT 8765 #define G_IP "192.168.124.255" int main(int argc, const char *argv[]) {//…

U盘格式化工具合集:6个免费的U盘格式化工具

在日常使用中&#xff0c;U盘可能会因为文件系统不兼容、数据损坏或使用需求发生改变而需要进行格式化。一个合适的格式化工具不仅可以清理存储空间&#xff0c;还能解决部分存储问题。本文为大家精选了6款免费的U盘格式化工具&#xff0c;并详细介绍它们的功能、使用方法、优缺…

Windows系统使用Koodo Reader轻松搭建在线私人图书馆远程看书

文章目录 前言1. Koodo Reader 功能特点1.1 开源免费1.2 支持众多格式1.3 多平台兼容1.4 多端数据备份同步1.5 多功能阅读体验1.6 界面简洁直观 2. Koodo Reader安装流程2.1 安装Git2.2 安装Node.js2.3 下载koodo reader 3. 安装Cpolar内网穿透3.1 配置公网地址3.2 配置固定公网…

开关电源调试思维导图

开关电源辐射发射问题调试一直以来都是工程师们非常头疼的事情&#xff0c;也不知道如何下手&#xff0c;今天就通过几个思维导图把开关电源辐射发射问题调试的思路呈现给广大工程师们&#xff0c;希望能照亮大家辐射调试的黑暗道路。 01、反激电路辐射发射问题调试思维导图 0…

【brainpan靶场渗透】

文章目录 一、基础信息 二、信息收集 三、反弹shell 四、提权 一、基础信息 Kali IP&#xff1a;192.168.20.146 靶机 IP&#xff1a;192.168.20.155 二、信息收集 似乎开放了9999&#xff0c;10000端口&#xff0c;访问页面没有太多内容&#xff0c;扫描一下目录 dirs…