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

        现在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.rhkb.cn/news/498321.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux硬盘分区 --- fdisk命令MBR分区、添加硬盘、lsblk命令

一、MBR分区 如果想对硬盘进行分区可以使用“ fdisk ”命令,它会采用MBR格式将硬盘进行分区。MBR是传统的分区机制,支持 32 位和 64 位系统,最多只能创建 4 个主分区,或者 3 个主分区和 1 个扩展分区,只支持不超过 2T…

GraphRAG 框架哪家强?选择最适合你智能问答系统的框架

GraphRAG 框架哪家强?选择最适合你智能问答系统的框架 点击进入:GraphRAG系列文章-Nano-GraphRAG:打造轻量级医疗诊断助手 点击进入:GraphRAG系列文章-突破传统知识管理瓶颈:LlamaIndex GraphRAG 让企业知识问答更智能…

day-102 二进制矩阵中的最短路径

思路 BFS 解题过程 从起点依次向八个方向尝试(之后也一样),如果某个位置在矩阵内且值为0且没有访问过,将其添加到一个队列中,依次类推,直到到达出口 Code class Solution {public int shortestPathBinar…

vue3学习笔记(10)-$subscribe,store组合式写法

1.$subscribe订阅,监视vuex中数据得修改 2.localStorage里面穿的都是字符串,关掉浏览器数据还在 只能获取字符串,用ts语法写明,作为字符串使用 3.组合式写法

WAP短信格式解析及在Linux下用C语言实现

WAP短信格式解析及在Linux下用C语言实现 一、引言二、WAP短信格式概述三、WAP短信头的内容四、UDHI与WAP短信体的关系五、在Linux下用C语言解析WAP短信头及短信体内容一、引言 在移动通信领域,短信作为一种古老却稳定的通信方式,一直扮演着重要的角色。随着技术的发展,短信…

从 Coding (Jenkinsfile) 到 Docker:全流程自动化部署 Spring Boot 实战指南(简化篇)

前言 本文记录使用 Coding (以 Jenkinsfile 为核心) 和 Docker 部署 Springboot 项目的过程,分享设置细节和一些注意问题。 1. 配置服务器环境 在实施此过程前,确保服务器已配置好 Docker、MySQL 和 Redis,可参考下列链接进行操作&#xff1…

华为消费级QLC SSD来了

近日,有关消息显示,华为的消费级SSD产品线,eKitStor Xtreme 200E系列,在韩国一家在线零售商处首次公开销售,引起了业界的广泛关注。 尽管华为已经涉足服务器级别的SSD制造多年,但直到今年6月才正式推出面向…

visual studio连接sql server数据库

目录 1、为什么要建立连接2、在sql server中建立数据库3、visual studio连接sql server数据库4、学生信息管理系统页面布局5、添加事件逻辑 5.1 页面跳转5.2 读取学生信息5.3 查询学生信息5.4 修改学生信息5.5 删除学生信息5.6 添加学生信息 bilibili演示视频 github源码 1、…

HTML——30.视频引入

<head><meta charset"UTF-8"><title>视频引入</title></head><body><!--video:在网页中引入音频IE8以及之前版本不支持属性名和属性值一样&#xff0c;可以只写属性名src属性:指定视频文件路径&#xff0c;必须要有controls属…

基于Pytorch和yolov8n手搓安全帽目标检测的全过程

一.背景 还是之前的主题&#xff0c;使用开源软件为公司搭建安全管理平台&#xff0c;从视觉模型识别安全帽开始。主要参考学习了开源项目 https://github.com/jomarkow/Safety-Helmet-Detection&#xff0c;我是从运行、训练、标注倒过来学习的。由于工作原因&#xff0c;抽空…

如何使用MySQL的group_concat函数快速做关联查询?

当我们需要做一对多的关联查询时&#xff0c;会很容易想到用left join来实现。例如&#xff0c;现有country表和city表之间建立了一对多的关联关系。如果要展示各国家以及城市列表&#xff0c;会很容易想到以下SQL&#xff1a; SELECT country, city FROM country LEFT JOI…

plsql :用户system通过sysdba连接数据库--报错ora-01031

一、winR cmd通过命令窗口登录sys用户 sql sys/[password]//localhost:1521/[service_name] as sysdba二、输入用户名:sys as sysdba 三、输入密码:自己设的 四、执行grant sysdba to system; 再去PL/SQL连接就可以了

循环神经网络(RNN)入门指南:从原理到实践

目录 1. 循环神经网络的基本概念 2. 简单循环网络及其应用 3. 参数学习与优化 4. 基于门控的循环神经网络 4.1 长短期记忆网络&#xff08;LSTM&#xff09; 4.1.1 LSTM的核心组件&#xff1a; 4.2 门控循环单元&#xff08;GRU&#xff09; 5 实际应用中的优化技巧 5…

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

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

1 数据库(下):多表设计 、多表查询 + SQL中的with查询语法(MySQL8.0以后版本才支持这种新语法)+ 数据库优化(索引优化)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、多表设计1 多表设计-概述2 三种多表关系一对多&#xff08;多对一&#xff09;&#xff08;1&#xff09;无外键约束&#xff08;逻辑外键&#xff09;&…

python读写文件的三种做法

对于文件操作&#xff0c;python提供了3种做法&#xff1a;open(), os.open() 和with open()语句。 1. open()函数&#xff1a;一般用于更高级的文件读写操作&#xff0c;即人能读懂的用法&#xff0c;如果是写入数据&#xff0c;可用传入字符串。 用法&#xff1a;open(path…

RocketMQ(二)RocketMQ实战

文章目录 一、RocketMQ实战1.1 批量消息发送1.2 消息发送队列自选择1.3 事务消息1.4 SpringCloud集成RocketMQ 二、最佳实践2.1 生产者2.1.1 发送消息注意事项2.1.2 消息发送失败处理方式 2.2 消费者2.2.1 消费过程幂等2.2.2 消费打印日志 2.3 Broker 三、相关问题3.1 为什么要…

产品原型设计

&#x1f923;&#x1f923;目录&#x1f923;&#x1f923; 一、Axure原型设计&#xff08;Axure RP 9 &#xff09;1.1 软件下载安装1.2 产品原型展示1.3 产品原型下载1.4 视频课程推荐 二、磨刀原型设计2.1 软件下载安装2.2 产品原型展示2.3 产品原型下载2.4 视频课程推荐 什…

C++ 编译过程全解析:从源码到可执行文件的蜕变之旅

引言 C 作为一种广泛应用于系统开发、游戏编程、嵌入式系统等领域的高级编程语言&#xff0c;其代码需要经过编译才能转换为计算机可执行的机器语言。编译过程涵盖多个复杂阶段&#xff0c;每个阶段对最终生成的可执行文件的性能、稳定性及兼容性都有着深远影响。深入理解 C 编…

【漏洞复现】NetMizer 日志管理系统 hostdelay.php 前台RCE漏洞复现

免责声明 请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,作者不为此承担任何责任。工具来自网络,安全性自测,如有侵权请联系删除。本次测试仅供学习使用,如若非法他用,与平台和本文作…