嵌入式如何防止栈内存溢出 |
您所在的位置:网站首页 › 栈溢出的原因及解决办法 › 嵌入式如何防止栈内存溢出 |
今天是虎年开工的第一天,也是Aven公众号开张的第一天。Aven搞电子行业也有12年多了,搞过硬件、嵌入式软件,总觉得应该写点什么记录一下。那今天就从嵌入式软件开篇吧。 Aven手头用的嵌入式软件是IAR,那么就以实际项目来说说如何在调试和离线运行中避免栈内存溢出。
我们都知道栈的特点,其实际上是一个LIFO,也就是后进先出,具备内存回收机制。其是通过硬件机制完成这一操作的,嵌入式软件工程师无需关心。无需关心不等于不用关心,程序调试或运行中最怕的是---不知道为啥宕机了。栈内存溢出就是其中一种可怕的宕机,总耽误我们好多时间。 闲话少说,上干货。 1.集成开发环境自带栈溢出指示 以IAR为例,在主菜单栏“view”中调出“breakpoint”,如图1所示: 图 1 右键选择“New Breakpoint”,再选择“code”,弹出图2界面: 图2 图2中显示Break At的位置,为了防止调试中出现栈溢出,可在此处填写“栈底”地址,Aven此处的栈底地址是0x20000000。设置完成,点击确定,那么在断点处就多了一个0x20000000的code断点。我们都知道栈的生长方向是“由顶而下”,当栈不够用的时候,程序强制停留在了栈底位置,明确告诉程序猿,此处的程序使用栈方法有问题,要么修改栈大小,要么调整程序使用方法。
如何知道栈底在哪里,其方法有三: 打开调试窗口,在“view”菜单中选择“stack”,那么会弹出图3所示: 图3 在图3中,将鼠标点在CSTACK后面的红色方框处,将会显示出栈底到栈顶的范围。 b. 查看icf分散加载文件,IAR中默认为symbol为CSTACK,可在里面查看CSTACK放置在内存的哪个位置,其大小设置是多少。需要说明的是CSTACK为默认的栈名,无特殊情况没必要更改。 c. 对于内存较小,设置较为简单的项目,可直接在集成环境中直接查看,以IAR为例,如图4所示: 图4 图4中点击edit,就能看到CSTACK的设置,由于Aven现有的项目利用的都是icf文件,在此就不展开了。 2.利用函数离线检查是否有栈溢出 第一步:初始化栈空间 大家都知道,栈空间需要在程序运行之前准备好,这部分在启动汇编文件中完成,如下程序段1所示。 程序段1 CPSID I ; Mask interrupts LDR R0, =0xE000ED08 LDR R1, =__vector_table STR R1, [R0] LDR R2, [R1] MSR MSP, R2 LDR R0, =SystemInit BLX R0;initialize manually cstack EXTERN init_cstack LDR R0, =init_cstack BLX R0 CPSIE I ; Unmask interrupts LDR R0, =__iar_program_start BX R0程序段1中的init_cstack是C函数编写的栈初始化函数,如程序段2所示。
程序段2 //初始化栈空间void init_cstack(void){ uint32_t num; uint8_t *cstack_begin = __section_end("CSTACK"); uint8_t *cstack_end = __section_begin("CSTACK"); num = cstack_begin - cstack_end; while (num) { cstack_begin = cstack_begin - 1; *cstack_begin= 0xcd; num--; }}程序段2给栈空间完成了赋值,Aven在这里面赋值的是0xcd,赋值效果可以在memory空间中查到,只需在main函数中随机设个断点,查看0x20000000(栈底)处的值就可。因为在main函数之前,启动汇编文件已完成对栈的初始化,如图5所示。 图5 完成了栈的初始化,那咱们就有办法处理栈是否会溢出了。因为栈的处理是硬件完成的,用到的栈空间都被CPU改变了“赋值”,未用到的栈空间仍然是咱们初始化的值。Aven在栈初始化的时候手动赋值了栈底处的值为0xcd,只要判断此处的值未被改写,那么就说明栈空间仍有富余。判断办法如程序段3所示。 程序段3 //检查是否有栈溢出void check_overflow_cstack(void){ uint32_t *cstack_end = __section_begin("CSTACK"); if(*cstack_end != 0xcdcdcdcd) { printf("the stack is overflow\r\n"); }}
以上阐述了在调试环境和离线环境下判断栈溢出的方法,编程者只需按步骤去做即可。对于ram空间较大的CPU,程序员可尽量把栈空间放大大点,只要单一函数不是过于庞大,基本上不会有问题。对于ram空间较为紧凑的cpu,程序员加入这两种方法,在编程时可起到事半功倍的效果。
好了,今天的文章就写到这里吧,咱们下期见。 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |