0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

什么是堆栈溢出?如何分配堆栈空间大小?

嵌入式情报局 来源:最后一个bug 2023-11-08 09:52 次阅读

1、正文部分

1先说几句

前些日子bug交流群里的小哥调试了一个堆栈溢出的bug,动不动数据就被篡改了,应该也是搞得焦头烂额,头皮发麻!当时bug菌看了下,于是抛出了自己的一些调试经验,一般这样的问题80%是越界和堆栈溢出造成的,没想到还真是堆栈溢出。

4de160de-7d6f-11ee-939d-92fbcf53809c.png

4e029f56-7d6f-11ee-939d-92fbcf53809c.png

所以对于一些问题的处理不仅仅是经验的积累,还需要多多交流!堆栈溢出问题bug菌和他算是“老朋友”了,所以非常想让相关文章跟大家见面,没想到这几天事情颇多,每天回家都没有太多的精力去更文,但是作为一名有态度的号主还是要坚持为大家带来点东西!

2理一理堆栈溢出

1堆栈名称

认识堆栈溢出首先我们要知道什么是" 堆栈 " ? 堆栈从名字上理解似乎是堆和栈的结合,而我们在数据结构中知道堆和栈是两种不同的数据结构,但这里的堆栈指的仅仅是栈,从英文名我们就可以知道 : 堆栈(stack)和堆(heap) , 至于把stack叫做堆栈是有一定的历史和翻译原因的,bug菌就不追溯了。 4e213df8-7d6f-11ee-939d-92fbcf53809c.png 对于栈,在bug菌的往期文章中也有提及,其实就是一种先进后出的数据结构;而在CPU层面有着堆栈寄存器,push和pop堆栈操作指令等等都是用于操作栈区的。 在C语言环境中栈是为了保存现场的信息,当程序需要执行函数调用,任务切换等等都会把相应的数据push到栈中,一旦回到原来函数和任务又会pop弹出之前的数据继续往下执行。 但栈是有具体大小的,一旦入栈的数据过多,就会导致罪恶的"堆栈溢出"问题。

2图解堆栈溢出

来我们首先看一个函数:

voidRecvData(void);
{
intCnt;
intBuff[6];
......
dosomething...
}
这样的代码打死我也不敢相信会有什么大问题,然而一名经验老道、饱经bug洗礼的嵌入式程序员会自然而然的考虑是否有堆栈溢出的风险,如下图所示:

4e3d1352-7d6f-11ee-939d-92fbcf53809c.png

上图就不区分堆栈增长方向了,仅仅只是表述堆栈溢出现象,由于SP_end以外的内容未知,一般都由编译器分配决定,如果编译器把重要数据分配到此区域,一旦程序访问到Buff[3]往下的数据便会导致数据篡改,从而程序发生一些奇怪的行为,甚至奔溃。

那么很多朋友就会想,直接给这个任务或者系统分配一个1024或者4096个字节的堆栈,这总不会造成堆栈溢出了吧!我只想说:"你太秀了!"。

3如何分配堆栈空间大小

1堆栈内容

盲目的分配过大的堆栈空间,无非就是对资源的浪费。如果你的项目能够让你这样任性,那你们产品成本估算就真是个形式。所以合理的分配堆栈大小是非常重要的,首先我们得看看堆栈中主要放些什么 ?

局部变量的分配。

函数调用嵌套的返回地址等等数据的push,这个需要根据具体的CPU进行函数调用约定来进行分析。

函数的参数,因为有时候编译器为了增加执行效率会把相关参数放在寄存器中传递,但是毕竟这样的寄存器有限,过多的参数还是会通过堆栈来传递。

当我们触发中断CPU一般会自动把相应的信息压入堆栈中,从而保存中断现场。

对于RTOS进行任务切换、中断等过程中一般系统仅自动保存了部分寄存器等信息,而为了全面的保存好现场,还需要手动的压入一些其他的信息,比如stm32中的FPU相关寄存器信息等。

2计算最大堆栈空间难题

有了前面堆栈中放了些啥的分析,要确定堆栈的空间大小自然而然的就会想到把一个个加起来算堆栈最大暂用情况,算出该值以后预留一定的空间就再合适不过了。

现在对于比较强大的IDE,比如keil和IAR,都可以提供计算堆栈占用最大的情况,而对于我们采用函数指针这样的间接调用函数的方式或者是C嵌入式汇编等等,那IDE也无能为力。

更加可怕的是使用printf这种可变参数的函数,其堆栈的占用情况是根据参数的多少而动态变化的,其并不那么容易确定。

当然还有最让bug菌难以忘记的情况 : 递归 , 递归就是反复的函数调用,那么一系列的返回现场数据都会压入栈中,堆栈占用情况也是未知的,所以在嵌入式中使用递归一定要限制递归的深度,防止堆栈溢出。

4确定堆栈大小的好办法

既然正面计算堆栈占用最糟糕的情形如此麻烦,那我们从侧面出击,那就是我们常用的检测堆栈使用峰值法,实时的采集和输出堆栈的使用信息,我们根据堆栈的最大值*1.5倍的样子,基本上就可以把堆栈大小确定下来。

像目前的RTOS(如ucosfreertos等)都提供了对应的堆栈信息输出API,比如ucos中的OSTaskStkChk函数 :

typedefstructos_stk_data
{
INT32UOSFree;/*Numberoffreeentriesonthestack*/
INT32UOSUsed;/*Numberofentriesusedonthestack*/
}OS_STK_DATA;
......
INT8UOSTaskStkChk(
INT8Uprio,
OS_STK_DATA*p_stk_data
);


通过调用该函数获得已经使用的和没有使用的堆栈大小,便可以获得堆栈的使用情况,如:

堆栈占用率 = (OSUsed/(OSUsed + OSFree)) * 100%

从而可以将该参数输出作为我们评估每个任务分配的堆栈是否合适,当然你需要让程序运行足够长的时间和尽量多的情况,从而获得最差的情况,再考虑预留>20%的空间,最终重新调整每个堆栈大小到合适状态。

版权声明:本文来源公众号最后一个bug

审核编辑:汤梓红



声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 寄存器
    +关注

    关注

    31

    文章

    5308

    浏览量

    119934
  • cpu
    cpu
    +关注

    关注

    68

    文章

    10816

    浏览量

    210994
  • 函数
    +关注

    关注

    3

    文章

    4299

    浏览量

    62376
  • 堆栈溢出
    +关注

    关注

    0

    文章

    9

    浏览量

    7901

原文标题:" 堆栈溢出 "的来龙去脉,讲明白了~

文章出处:【微信号:嵌入式情报局,微信公众号:嵌入式情报局】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Embedded Studio堆栈溢出预防功能

    为了识别运行的嵌入式系统中的堆栈溢出问题,SEGGER编译器通过为每个函数生成检测代码的方式来检查堆栈溢出。该功能可以使用命令行开关-mstack-overflow-check来使能。
    发表于 07-14 11:08 578次阅读

    TLE9893如何配置堆栈溢出检测?

    的 startup_tle989x.s 文件为堆栈分配大小为 512 字节: 此外,在主入口点,堆栈指针寄存器的值为 0x180023b0,所以我猜这是
    发表于 01-19 06:11

    freertos与STM32如何分配堆栈空间

    freertos与STM32分析栈、堆、全局区、常量区、代码区、RAM、ROM,及如何分配堆栈空间基于STM32分析栈、堆、全局区、常量区、代码区、RAM、ROM FreeRTOS任务栈大小
    发表于 08-03 06:36

    怎样去设置堆栈空间大小

    :0x400(1024Byte),Heap堆的大小为:0x200(512Byte)。这也是为什么一个基础的工程编译后,RAM的空间也占用了1.6K左右的原因,因为堆栈空间
    发表于 08-04 09:14

    了解堆栈分配避免堆栈溢出环境

    一、通过map文件了解堆栈分配(STM32、MDK5)--避免堆栈溢出环境:STM32F103C8T6,MDK5在最近的一个项目的开发中,每当调用到一个函数,程序就直接跑飞。debug
    发表于 08-24 07:26

    如何分配freertos的堆栈空间

    freertos堆栈空间有哪些?如何分配freertos的堆栈空间
    发表于 10-08 09:17

    FreeRTOS中的任务堆栈溢出检测机制

    在FreeRTOS中,每个任务都拥有自己的堆栈,该堆栈大小由创建任务时xTaskCreate函数的函数参数所决定。但当任务所使用的堆栈空间
    发表于 10-15 13:51

    如何设置应用任务的堆栈大小

    基于RTOS的应用中,每个任务都拥有自己的堆栈空间堆栈设置过大,会造成内存资源浪费;设置过小,可能导致运行过程中的任务栈溢出,从而导致一些奇怪的系统行为。事实上,当应用程序行为“奇怪
    发表于 06-08 15:11

    堆栈溢出怎么解决方式

    要的是PUSH和POP。 PUSH操作在堆栈的顶部加入一 个元素。POP操作相反, 在堆栈顶部移去一个元素, 并将堆栈大小减一。
    发表于 11-28 11:16 2.9w次阅读
    <b class='flag-5'>堆栈</b><b class='flag-5'>溢出</b>怎么解决方式

    RTOS任务的堆栈大小与代码量有啥关系吗?

    需要提前分配堆栈大小,也就是在创建任务的时候分配堆栈大小。 比如uCOS创建一个检测(Che
    的头像 发表于 05-26 09:34 2122次阅读

    STM32堆栈空间大小设置

    :0x400(1024Byte),Heap堆的大小为:0x200(512Byte)。这也是为什么一个基础的工程编译后,RAM的空间也占用了1.6K左右的原因,因为堆栈空间
    发表于 12-17 18:36 11次下载
    STM32<b class='flag-5'>堆栈</b><b class='flag-5'>空间</b><b class='flag-5'>大小</b>设置

    STM32 堆栈溢出检测

    释放,存放函数调用,局部变量等数据。堆heap用于动态内存分配堆栈可以在启动文件或者链接脚本中指定大小,但在实际开发中,尤其工程量较大的项目中难以确定堆栈使用量,容易造成
    发表于 12-27 18:32 22次下载
    STM32 <b class='flag-5'>堆栈</b><b class='flag-5'>溢出</b>检测

    stm32修改堆栈大小堆栈空间不足导致死机)

    :0x400(1024Byte),Heap堆的大小为:0x200(512Byte)。这也是为什么一个基础的工程编译后,RAM的空间也占用了1.6K左右的原因,因为堆栈空间
    发表于 12-27 19:09 22次下载
    stm32修改<b class='flag-5'>堆栈</b><b class='flag-5'>大小</b>(<b class='flag-5'>堆栈</b><b class='flag-5'>空间</b>不足导致死机)

    Embedded Studio堆栈溢出预防简析

    为了识别运行的嵌入式系统中的堆栈溢出问题,SEGGER编译器通过为每个函数生成检测代码的方式来检查堆栈溢出
    的头像 发表于 07-14 11:07 885次阅读

    堆栈和内存的基本知识

    本文主要聊聊关于堆栈的内容。包括堆栈和内存的基本知识。常见和堆栈相关的 bug,如栈溢出,内存泄漏,堆内存分配失败等。后面介绍软件中
    的头像 发表于 08-29 14:10 378次阅读
    <b class='flag-5'>堆栈</b>和内存的基本知识