新闻中心

EEPW首页>嵌入式系统>设计应用> 51堆栈的安全(精确)设置

51堆栈的安全(精确)设置

作者: 时间:2016-11-28 来源:网络 收藏
几个问题:
1、编译器、连接器把堆栈段定位在IDATA内所有段的最后面,也即内存IDATA高端;
2、中断堆栈被定位在堆栈段内的最后面,即IDATA最顶端;所以堆栈段的安全余量设置,实际上是中断堆栈深度的配置。
3、如果不考虑系统堆栈的安全余量设置,一个没有二级中断嵌套的一级中断堆栈深度应该是13字节。——为什么?
4、系统中断的安全余量配置应该是……字节。——为什么?

下面详细说明:

1、整个51内存256字节。C51首先分配全局静态变量在IDATA低端,接着分配的是项目中所有函数的参数和局部变量共享覆盖区——也属于全局静态变量区(段)。(有没有动态数组区段冷漠不知道也不关心,)然后就是系统堆栈段,其栈底指针?STACK——由C51自动生成,栈顶应该是IDATA顶端——0xFF。系统堆栈深度=0xFF-(?STACK)。
如果项目中含有汇编模块, 那么系统堆栈?STACK中还包含汇编模块私有堆栈STACK(指针指向)如果项目中包含有reentrant 重入函数 foo,那么系统堆栈中还包含有每个foo所属的私有模拟堆栈simulatedstack;它也是C51为每个reentrant 函数自动分配的。对于interrupt 属性函数,C51为其分配中断函数私有堆栈……还有硬件堆栈hardwarestack 的概念(函数调用CALL或者中断发生时由硬件自动压入PC的堆栈。)
所以,一个系统堆栈段内分配了很多私有堆栈,所谓私有,应该是操作系统概念,用在这里是为了与系统堆栈?STACK 区分概念,例如,当一个中断事件发生时,它所打断的后台程序F1 所正在使用的堆栈,是不可能与中断函数堆栈共享的。——2者堆栈都是私有的。
由于中断函数具有最高优先权,且堆栈独立,所以我说中断函数堆栈必然处在所有其它私有STACK之上,是IDATA最顶端的堆栈;——安全余量必是在中断堆栈之上分配。

……Cx51编译器会(自动——冷漠注释)产生一个?STACK的堆栈段,该段将被自动定位到IDATA空间的顶部。……一般不需要特别指定?STACK 的位置,对于具有几个堆栈的汇编程序才需要采用STACK命令。需要注意的是,重新定位?STACK 段必须非常小心,因为可能会破坏 DATA 或 IDATA 空间的变量而导致程序无**常运行。

——摘自《Keil Cx51 V7.0单片机高级语言编程与uVision2应用实践》 徐爱钧编著 P640

冷漠同学啊, 引用就引用吧, 干么断章取义,还夹杂私货, 这样不好嘛。

1)中断堆栈最大可能深度是15(2+5+8)字节, 不是 13。原因已经给出, 不再重复。

2)8051 运行时有只有一个统一stack, 不存在 中断堆栈 以及 非中断堆栈, os 或程序人为操纵stack 另当别论。

3)堆栈段定位在IDATA所有段的最后面, 把空白区全部作为stack 这非常自然, 而且 8051 stack grow *UP*, 用脚后跟想想就会明白。c51:

While the 8051 architecture restricts the stack to internal memory, it may be located at any point therein. The stack typically starts following the last individual variable allocation in internal memory and is free to grow *up* through whatever memory remains.

4)私有堆栈是****编译时*****的中间产物, 用于最后连接计算, 连接时linker 统一定位。目标代码*****运行时*******,只有一个stack. 所有的函数调用, 中断断点, 以及中断register保护都在一个 stack 里。这是最基本的****常识***问题!!!! os 的人为分割切换 stack 完全是另一个概念。

请教 冷漠大师:

您所言的“后台堆栈”,实际上是C语言中的软堆栈,负责分配在全局静态变量空间共享覆盖区,其长度为每个独立的后台函数的私有堆栈之和!
您所言的“前台堆栈”,实际是51单片机中真正的硬件堆栈,负责中断响应需要保护的变量,子函数调用等硬性的压栈出栈操作!

C语言的运行是依靠这一软一硬两个堆栈协调工作,您所言的:一高(端)一低(端),一小一大,一前(台)一后(台)。就是指这一软一硬两个堆栈。

不知俺理解的对不对?

提一个问题:什么是堆栈?

如果,堆栈是指硬件堆栈,那个由堆栈指针SP所控制的,那当然只有一个堆栈,没有什么私有的堆栈,包括中断等,都是往这个堆栈上放,

如果自己在函数里(操作系统也不过是一些函数,一些公用的函数集而已)定义一个堆栈,那个堆栈就不好说,那叫软堆栈。

一个处理系统有且只有一个当前堆栈,就是有SP寄存器控制的那个。不管是中断发生还是函数调用,都是用这个SP定位。其余什么私有堆栈不过是程序修改SP而实现的。
堆栈的深度是不宜精确设置的,除非你的程序很简单,或根本就没有中断嵌套,这样你可以很容易计算出系统可能最大堆栈。不然没什么好说的,将必要的内存设置好,其余的统统留给堆栈(反正闲着也是闲着)。

特地说明一点:
“reentrant"的 "simulated stack" 只是为了模拟通过 stack 传递参数(这是大多数c 编译器的做法)以实现重入,实际实现上有点复杂,是Rn寄存器再加上一个“simulated stack" 数据区(具体情况可以看看反汇编)。这个“simulated stack" 自顶向下, 参数通过 r0, r1或 dptr 存取, 是一个特别数据区, 不是一个真正的 stack,与真正的硬件自动 stack (通过 sp push/pop) 无关.

具体没看过编译手册,不过对于这句有些疑问:
其栈底指针?STACK——由C51自动生成,栈顶应该是IDATA顶端——0xFF

51堆栈是向上生长型,刚开始栈顶应该在栈底那里,每次PUSH,往上加,直到最大0XFF.但是你这里说栈顶应该在IDATA的顶端--OXFF,应该不对。

负责分配在全局静态变量空间共享覆盖区,其长度为每个独立的后台函数的私有堆栈之和!

“软堆栈”的提法非常赞同。绝不是一般人所能理解到如此深刻的。其它先不多说,——我还没讲到。但是上面红线部分我有异议:就像后台所有非重入函数的参数传递和局部变量被分配在共享覆盖区一样,其长度应该是占用内存字节数最多的那个函数所占有的区域,其它函数分时共享这个区域。——而不是所有之和。这才是共享、覆盖的操作系统内存管理的方法和意义吧?
还有,私有堆栈之和可能忽略了一件事,所有私有堆栈都是动态意义的,当这个函数未被调用时,它是不活动的,所以它的私有堆栈也不存在(长度等于零),这也是覆盖与共享的前提吧?
我表达的不好,感谢老许是真正懂的、真正讨论问题的人。

同意12楼,说的太好了。这里确实不好得出唯一结论;记得ayb_ice详细论证过,堆栈长度开始为什么是1字节。建议去看看他的帖子。我不能确定唯一结论的理由:
1、?STACK是由编译器自动生成的系统堆栈段,——所有含有PUSH / POP指令操作的函数,都在这个系统堆栈段里享有自己的连续堆栈空间(注意是动态的。)?STACK所指向的系统堆栈段被分配在系统所有其它RAM段的最后面。?STACK肯定是始终指向系统堆栈栈底,连接定位之后就不可移动的;

2、“最后面的”后面还能安排有东西么?冷漠认为编译器不可能出尔反尔,所以认为没有了,所以冷漠说栈顶是IDATA的顶。12楼可以说栈顶是移动的,移动上限是IDATA顶端。冷漠坚决同意。
当然通过设置可以让IDATA的顶不等于0xFF;例如等于0xEE什么的。这是冷漠说话不严格。

?STACK的定位是连接器确定的,和编译器无关。LZ已经举了书上的Keil说明书译文, 连接器是否自动把?STACK定位在IDATA顶端?还是需要人工执行STACK连接命令之后才行?冷漠也有疑问?不太清楚。

S
请你仔细看我的原话,没有理解就不要乱引用
我是说KEIL默认的堆栈长度是1字节<,可以改,只要编译器能分配一个字节的空间,就不会报错>,但其实所有没有被编译器使用的IDATA空间都是堆栈,当然中间的间隙不算

对自己在12楼的话解释一下
关于栈顶的理解其实和LZ是一样的,只是说法稍微不同。
堆栈指针SP的范围,最小叫栈底,最大叫栈顶。SP在栈底和栈顶之间活动。所以本例中,栈顶是IDATA顶端0XFF,这么理解没错。

我12楼这么说,我是这么理解的
因为堆栈指针SP的值是栈顶的地址,所以SP活动,那么栈顶也跟着活动的。(SP指向栈顶)
栈顶的范围:最小值即栈底,最大值即 最大栈顶值。

其实和LZ的意思是一样的。

冷漠有另一观点:

C51对用户来说没有SP的概念,你是C51用户你不是编译器作者,要么你说自己只用汇编,那我们讨论不在一个层次,一定注意不要以汇编的概念来理解C编 译器,?STACK 就是编译器生成的堆栈段,而且我认为这个?STACK 以上编译器不可能分配其它数据段。所以从?STACK以上都是堆栈段,即使有很多不用的空间,也不可能被谁占用,编译器没有让谁用。——这不是汇编语言编 程,程序员无法控制SP的。冷漠才不管它SP在哪。愿意在哪在哪,编译器一定比我做的高明。是吧。小问题,无所谓对错。都不影响使用C51。

上一页 1 2 下一页

关键词:51堆栈安全设

评论


技术专区

关闭