今天是虎年开工的第一天,也是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,程序员加入这两种方法,在编程时可起到事半功倍的效果。
好了,今天的文章就写到这里吧,咱们下期见。