STM32 堆栈空间分布
参考
- 运行时访问
__initial_sp
和__heap_base
无RTOS时的情况
在以上配置的情况下,生成工程。在工程的startup.s
文件中,由如下代码:
Stack_Size EQU 0x400AREA STACK, NOINIT, READWRITE, ALIGN=3
__Stack_top ; 自己添加
Stack_Mem SPACE Stack_Size
__initial_spIF :DEF:__MICROLIB EXPORT __initial_spEXPORT __heap_baseEXPORT __heap_limitEXPORT __Stack_top ; 自己添加
ELSEIMPORT __use_two_region_memoryEXPORT __user_initial_stackheap
__user_initial_stackheapLDR R0, = Heap_MemLDR R1, =(Stack_Mem + Stack_Size)LDR R2, = (Heap_Mem + Heap_Size)LDR R3, = Stack_MemBX LRALIGN
ENDIF
通过以上代码可以看出,需要使能MicroLib
才会默认导出__initial_sp
,__Stack_top
,__heap_base
,__heap_limit
这几个变量。(如果不使能MicroLib
,则需要在上面代码的ELSE
下面也添加EXPORT
语句将这几个变量导出)。
然后在main.c
中添加如下代码,查看以上这些变量的值:
extern uint32_t __heap_base, __heap_limit;extern uint32_t __Stack_top, __initial_sp;extern uint32_t __Vectors, __Vectors_End, __Vectors_Size;while (1){// 0x200013A8 ~ 0x200015A8 size:0x200 RAMprintf("heapS[0x%08x], heapE[0x%08x]\r\n", (uint32_t)&__heap_base, (uint32_t)&__heap_limit);// 0x200019A8 ~ 0x200015A8 size:0x400 RAMprintf("stackS[0x%08x], stackE[0x%08x]\r\n", (uint32_t)&__initial_sp, (uint32_t)&__Stack_top); // 0x08000000 ~ 0x080000EC ROMprintf("VectS[0x%08x], VectE[0x%08x], VectSize[0x%x]\r\n\r\n", (uint32_t)&__Vectors, (uint32_t)&__Vectors_End, (uint32_t)&__Vectors_Size);}
从上可以看到,__Stack_top
和__heap_limit
是一样的,说明这里分配的堆和栈是紧邻的。而且地址刚好也和我们在cubeMx
中定义的一致。堆和栈是在RAM
中,而中断向量表是在Flash
中。
添加FreeRTOS
从以上三张图中,可以发现,我们给FreeRTOS总共分配了3072(0xC00) Bytes HEAP
空间。而我们定义了一个defaultTask
并分配了256 * 4 = 1024 Bytes
,但是在最后的FreeRTOS Heap Usage
页面看到,defaultTask
实际使用了1144 Bytes
,剩余3072 - 1144 = 1928 Bytes
。(除了defaultTask
多使用的1144 - 1024 = 120 Bytes
(用于任务控制块TCB
)外,其余都是可以对上的。)
生成工程后,我们还是将之前的那些堆栈指针地址打印出来,看一下MCU是如何进行地址分配的。只是这里还需要添加FreeRTOS
中的堆栈信息了。首先通过追踪configTOTAL_HEAP_SIZE
可以发现,堆空间定义为了一个数组:
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];uint8_t* osHeapPoint = ucHeap; // 由于以上变量是 static 类型,所有这里再定义一个指针指向这个 Heap 地址,以便在外部访问它;
然后我们就可以在defaultTask
任务里面添加输出以上变量地址的代码了:
extern uint32_t __heap_base, __heap_limit;extern uint32_t __Stack_top, __initial_sp;extern uint32_t __Vectors, __Vectors_End, __Vectors_Size;extern uint8_t* osHeapPoint;extern uint32_t local_addr1, local_addr2;void* os_mal_buf = malloc(50);uint32_t tast_local_val = 1;for(;;) {// 0x20002D10 ~ 0x20002F10 size:0x200 RAMprintf("HeapS[0x%08x], HeapE[0x%08x]\r\n", (uint32_t)&__heap_base, (uint32_t)&__heap_limit);// 0x20003310 ~ 0x20002F10 size:0x400 RAMprintf("StackS[0x%08x], StackE[0x%08x]\r\n", (uint32_t)&__initial_sp, (uint32_t)&__Stack_top); // // 0x08000000 ~ 0x080000EC ROMprintf("VectS[0x%08x], VectE[0x%08x], VectSize[0x%x]\r\n", (uint32_t)&__Vectors, (uint32_t)&__Vectors_End, (uint32_t)&__Vectors_Size); // 中断向量表// 0x20002110 size: 3072 Bytes(0xC00)printf("OsHeapPoint[0x%08x]\r\n", (uint32_t)osHeapPoint); // freeRTOS中定义的HEAP数组// 0x20002D18printf("Os_mal_buf[0x%08x]\r\n", (uint32_t)os_mal_buf);// 0x20002504printf("&Os_mal_buf[0x%08x]\r\n", (uint32_t)&os_mal_buf);// 0x20002508printf((char*)buf, "&tast_local_val[0x%08x]\r\n\r\n", (uint32_t)&tast_local_val); // 在freeRTOS任务中定义的局部变量// 0x20000008, 0x2000000Cprintf("&global_var1[0x%08x], &global_var2[0x%08x]\r\n", (uint32_t)&global_var1, (uint32_t)&global_var2); // 函数外部定义的全局变量// 0x20003304, 0x20003308printf("local_addr1[0x%08x], local_addr2[0x%08x]\r\n\r\n", local_addr1, local_addr2); // 这两个全局变量保存了在freeRTOS初始化前,在main()函数中定义的两个局部变量的地址}
根据以上输出总结如下:
- 从
local_addr
的地址(靠近__initial_sp
)可以看出,栈空间的地址值是向下增长的,即栈顶(__initial_sp = 0x20003310
)在高地址; - 从全局变量的地址可以看出,全局变量是默认分配在
RAM
的起始地址; - 在
freeRTOS
任务中定义的局部变量(&tast_local_val = 0x20002508
)存储在定义freeRTOS
时定义的HEAP
里(0x20002110~0x20002D10
); - 再看一下
&tast_local_val = 0x20002508
这个数据,发现0x20002508
大概等于0x20002110 + 256*4 = 0x20002510
。这就再次说明,freeRTOS
定义任务时,是从配置好的HEAP
地址初始空间的开始部分给新任务分配Task stack
的,而在任务中使用这个task stack
时,还是符合栈的使用规范(即从高地址开始分配); - 从
s_mal_buf = 0x20002D18
可以知道,即使在freeRTOS
任务中动态分配,也是在任务之外的堆中分配空间。
疑问
- 通过以上总结,发现在
freeRTOS
任务中定义局部变量和在freeRTOS
任务之外定义局部变量,他们分配的地址空间是不一样的。那么,如果有以下函数:
void addr_test(void)
{uint32_t loc_var = 1;printf("&loc_var = 0x%08x\r\n", (uint32_t)&loc_var);
}
我分别在freeRTOS
初始化之前调用,以及在freeRTOS
的任务中调用,其输出会是什么呢?
经过验证,在freeRTOS
初始化之前调用,输出的地址范围在0x20002F10 - 0x20003310
之间;而在freeRTOS
的任务中调用,输出的地址范围在0x20002110 - 0x20002D10
之间。
相关文章:

STM32 堆栈空间分布
参考 运行时访问__initial_sp和__heap_base 无RTOS时的情况 在以上配置的情况下,生成工程。在工程的startup.s文件中,由如下代码: Stack_Size EQU 0x400AREA STACK, NOINIT, READWRITE, ALIGN3 __Stack_top ; 自己添加 Stack_Mem…...

小程序制作(超详解!!!)第十五节 自动随机变化的三色旗
1.例题描述 设计一个小程序,开始时界面上显示一个三色旗和一个按钮,当点击按钮时,三色旗的颜色会发生随机变化,即使不点击按钮,三色旗的颜色也会每隔一定时间自动发生变化。 2.index.wxml <view class"box&…...
MySQL_主从复制_环境搭建
MySQL主从复制配置 CentOS 7 配置 阿里云 yum 源 sudo mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup sudo wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo sudo yum clean all sudo yum makeca…...

Linux 设置静态IP(Ubuntu 20.04/18.04)
以Ubuntu20.04示例 第一步:查看当前网络信息 ifconfig 本机网卡名为:ens32,IP地址为:192.168.15.133,子网掩码为:255.255.255.0 第二步:查看当前网关信息 route -n 网关地址为:1…...

计网----累积应答,TCP的流量控制--滑动窗口,粘包问题,心跳机制,Nagle算法,拥塞控制,TCP协议总结,UDP和TCP对比,中介者模式
计网----累积应答,TCP的流量控制–滑动窗口,粘包问题,心跳机制,Nagle算法,拥塞控制,TCP协议总结,UDP和TCP对比,中介者模式 一.累积应答 1.什么是累计应答 每次发一些包࿰…...
OpenCV 直方图和归一化
直方图可以反映图片的整体统计信息, 使用函数 CalcHist() 实现. 但CalcHist() 统计出的数量信息和图像大小相关, 如果要剔除图像大小因素, 需要做归一化处理, 归一化处理后的信息, 反映出各个颜色值得占比情况, 这样更方便不同size图像做对比, 归一化的函数为 Normalize(). ///…...

Flink架构
1、Apache Flink集群的核心架构: 1、client(作业客户端):提交任务的地方叫做客户端 2、JobManager(作业管理器):作用是用于管理集群中任务 3、TaskManager(任务管理器)&a…...
Packet Tracer路由器连接终端设备怎么配置?
在Packet Tracer中配置一台路由器和三台终端设备可以帮助你建立一个简单的局域网,以下是配置的基本步骤: 打开Packet Tracer,从左侧设备栏中拖拽一个路由器和三个终端设备到工作区。 连接设备:使用网线将路由器的端口与每台终端设…...

评估APP网页小程序代码UI开发H5估价师怎么评估开发精确研发价格?
作为一名应用程序开发评估师,可能涉及到的主要任务是为特定的应用程序提供估算开发成本和所需时间预测。为了为一个应用程序更准确地评估价格,须遵循以下几个步骤: 问: 如何让一个App更好、更精确地评估出价格? 答: 以下是一个可…...

16 Linux 内核定时器
一、Linux 时间管理和内核定时器简介 1. 内核时间管理简介 Linux 内核中有大量的函数需要时间管理,比如周期性的调度程序、延时程序、定时器等。 硬件定时器提供时钟源,时钟源的频率可以设置,设置好以后就周期性的产生定时中断,系…...
C++11 shared_ptr类型智能指针学习
智能指针和普通指针的用法类似,但是智能指针可以在适当时机自动释放分配的内存。 C++11有三种类型的智能指针,shared_ptr、unique_ptr 以及 weak_ptr; 先学习shared_ptr类型; shared_ptr<T> 的定义位于<memory>头文件,并位于 std 命名空间中; T 表示指针指…...

网络流量分类概述
1. 什么是网络流量? 一条网络流量是指在一段特定的时间间隔之内,通过网络中某一个观测点的所有具有相同五元组(源IP地址、目的IP地址、传输层协议、源端口和目的端口)的分组的集合。 比如(10.134.113.77,47.98.43.47,TLSv1.2&…...
JavaWeb篇_02——服务器简介及Tomcat服务器简介
服务器简介 硬件服务器的构成与一般的PC比较相似,但是服务器在稳定性、安全性、性能等方面都要求更高,因为CPU、芯片组、内存、磁盘系统、网络等硬件和普通PC有所不同。软件服务器(英文名称Server),也称伺服器。指一个…...
2311d游戏引擎适配ios
原文 通过遵循arsd:simpledisplay(v11.0.0之前)上的一些旧代码,Apple的文档和Jacob的这一惊人贡献桥, 我已从金属绑定中删除了所有extern(Objective-C)代码,现在,所有Objective-C桥接代码都是使用D的反射生成的. 因此,给定此例代码: import core.attribute : selector; extern…...

网络唤醒(Wake-on-LAN, WOL)
远程唤醒最简单的方法:DDNSTOOpenwrt网络唤醒,完美实现。 原帖-远程唤醒_超详细windows设置远程唤醒wol远程连接(远程开机) WOL Web# 访问 Wake on Lan Over The Interweb by Depicus 可以无需借助软件很方便的从网页前端唤醒远…...

接口测试框架实战(一) | Requests 与接口请求构造
Requests 是一个优雅而简单的 Python HTTP 库,其实 Python 内置了用于访问网络的资源模块,比如urllib,但是它远不如 Requests 简单优雅,而且缺少了许多实用功能。所以,更推荐掌握 Requests 接口测试实战技能࿰…...

【C++】详解 void*
文章目录 1. void *是什么?2. void*详解3. 和void的区别4. 应用场景4.1 函数传参时不确定类型,或者要支持多类型的传参;4.2 当函数的返回值不考虑类型指关心大小的时候 5. 总结 今天看到一段代码,觉得非常有意思。 void* say_hell…...
Linux家目录变成了-bash-4.2$
Linux家目录变成了-bash-4.2$ Mark a workarround: 使用root用户,执行cp -a /etc/skel/. /home/zookeeper/(不是root用户也可以) 其中/home/zookeeper/目录是对应自己的家目录地址~ 若有帮到你,记得点赞,收藏呀…...
Python和SQLite游标处理多行数据
如果您需要处理多行数据,使用游标或其他适当的方法是更好的选择。以下是一些处理多行数据的方法: 使用游标:游标可以逐行处理查询结果,这对于大量数据或需要逐行处理的场景非常有用。以下是一个使用Python和SQLite的游标示例&…...
安全测试之PHP 漏洞全解
PHP 漏洞全解(一)-PHP的攻击方式 针对 PHP 的网站主要存在下面几种攻击方式: 1、命令注入(Command Injection) 2、eval 注入(Eval Injection) 3、客户端脚本攻击(Script Insertion) 4、跨网站脚本攻击(Cross Site Scripting, XSS) 5、SQL 注入攻击(SQL injection) 6、跨网站…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...

国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...

地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...