|
楼主 |
发表于 2011-8-15 10:54:53
|
显示全部楼层
先了解一下所谓的硬堆栈和软堆栈。ICCAVR 使用两个堆栈:一个用于子程序调用和中断操作的硬件堆栈,一个用于传递参数、临时变量和局部变量的软件堆栈。他们都属于向
下生长型,如果函数的调用层次太深,有可能会发生硬件堆栈溢出到软件堆栈中,改变了软件堆栈中数据的内容,同样,当定义了太多的局部变量或一个局部集合变量太多也有可
能出现软件堆栈溢出到数据区或者堆区(如果使用了void _NewHeap(void *start, void *end)分配了堆区的话,就是软堆栈生长到了堆区,注意堆是
向上生长,但是堆区在向上的过程中是不会超过堆区申请分配的空间大小,至少我在AVRSTDIO里仿真是这样,如果超过了堆的容限,将返回空指针),两个堆栈都有可能溢出,如果堆栈溢出,会引起不可预测的错误。那我们怎么才知道是否溢出了呢?这时我们就得调用堆栈检查函数,大家先看看堆栈检查函数源代码吧!
void _StackCheck(void)
{
extern char *_HWStackBottom(void); //声明一个返回指向char型指针的函数,该函数的功能就是返回硬堆栈底部的地址
extern void _StackOverflowed(char); //声明一个根据堆栈溢出类型-0表示软堆栈溢出,1表示硬堆栈溢出,的处理函数
extern char _bss_end; //声明_bss_end变量,该变量就是警界字节
char *hwstk = _HWStackBottom();
if (hwstk[0] != 0xAA) //检验硬堆栈是否溢出
_StackOverflowed(1);
if (_bss_end != 0xAA)
_StackOverflowed(0); //检验软堆栈是否溢出
}
我在后面加上注释后大家应该很快明白了吗!其实就是在启动代码中在硬件堆栈和软件堆栈的最低字节分别写进一个代码(0xaa),把这个代码称为警戒线。如果硬件堆栈和软件堆栈如果溢出过,则警戒字节的代码(0xaa)就会被改变,堆栈检查函数就是通过检查这两个堆栈的最低字节的代码是否被改变来判断两个堆栈是否溢出。有关这部分的代码如下
; init.s
;
; to be included by the crt*.s files
;
; initialize stacks
;
; set the hardware stack to ram_end, and the software stack some
; bytes below that
ldi R28,<ram_end
ldi R29,>ram_end
out $3D,R28
out $3E,R29
subi R28,<hwstk_size
sbci R29,>hwstk_size
ldi R16,0xAA ; sentenial
std Y+0,R16 ; put sentenial at bottom of HW stack
clr R0
ldi R30,<__bss_start
ldi R31,>__bss_start
ldi R17,>__bss_end
_bss_end 其实就是一个在软堆栈和数据区的一个警界字节或者称之为边界标记字节。
在使用堆栈检查函数时应注意以下几点:
1、在使用堆栈检查函数时,前必须用#i nclude "macros.h"预处理。
2、如果使用自己的启动文件,在ICCAVR6.20以后的版中,如果使用的启动文件中没有警戒线的内容,ICCAVR也会自动添加警戒线。而在ICCAVR6.20以前的版本中,必须自己添加该部分内容,否则生成的代码中堆栈分配将不带警戒线。
3、如果使用动态内存分配,必须跳过警戒线字节_bss_end来分配您的堆(即增加一个字节)。
4、当_StackCheck(void)函数检测到警戒线字节被改变,则会调用一个默认的_StackOverflowed 函数来跳转到程序存储器0的位置(复位向量地址)。可以指定或重新编写一个新的函数来代替它,例如可以用新函数来指示是哪个堆栈溢出等,但这个函数也不可能执行太多的功能或让程序恢复到正常状态。因为堆栈溢出后,会更改掉一些有用的数据,引起不可预测的错误,甚至使程序死机。
似乎将到这里基本都将完了,但是其实好戏还正开始,接下来也正是我写这贴子的重中之重,也望各位高人指点。 |
|