打印

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

[复制链接]
17195|40
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
冷漠|  楼主 | 2010-7-15 20:46 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 冷漠 于 2010-7-15 21:25 编辑

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

下面详细说明:

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


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

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


2、   待续……
评分
参与人数 1威望 +1 收起 理由
xuyiyi + 1

相关帖子

沙发
xuyiyi| | 2010-7-15 21:15 | 只看该作者
沙发!

使用特权

评论回复
板凳
highgear| | 2010-7-15 21:57 | 只看该作者
冷漠同学啊, 引用就引用吧, 干么断章取义,还夹杂私货, 这样不好嘛。

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 完全是另一个概念。

冷漠同学, 你把 编译时的中间临时"私有堆栈" 当成 目标代码*运行时*的 stack 啦。

使用特权

评论回复
地板
xuyiyi| | 2010-7-15 22:14 | 只看该作者
请教 冷漠大师:

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

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

不知俺理解的对不对?

使用特权

评论回复
5
highgear| | 2010-7-15 22:26 | 只看该作者
搬个板凳, 看看冷漠童鞋对于 一个 mcu 多个 stack 各自表述的解释。

使用特权

评论回复
6
highgear| | 2010-7-15 22:34 | 只看该作者
俺把 “徐爱钧编著 P640“ 下载后看了一下, 果不其然, p640 是关于 linker (连接定位器) 的命令参数 stack, 这个一般在 makefile 里用。

使用特权

评论回复
7
sheriff| | 2010-7-15 22:56 | 只看该作者
对于第三点“如果不考虑系统堆栈的安全余量设置,一个没有二级中断嵌套的一级中断堆栈深度应该是13字节”  比较费解:

int sum(char a, char b, char c, char d)
{
return a + b + c +d;
}

int sum1(char a, char b, char c, char d)
{
  return(sum(a, b, c, d));
}

int sum2(char a, char b, char c, char d)
{
  return(sum1(a, b, c, d));
}

void isr0(void) interrupt 0
{
  sum2(1,2,3,4);
}
用keil simulater运行,在isr0里堆栈深度为15(不算进入中断时压栈的2字节PC),如何解释??

使用特权

评论回复
8
呆板书生| | 2010-7-16 05:29 | 只看该作者
提一个问题:什么是堆栈?

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

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

使用特权

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

使用特权

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

请继续。

使用特权

评论回复
11
ayb_ice| | 2010-7-16 09:11 | 只看该作者
可以肯定,冷漠同志肯定没有多少实际项目经验

堆栈只能按相对保守来设置

使用特权

评论回复
12
yuyetufu| | 2010-7-16 09:39 | 只看该作者
具体没看过编译手册,不过对于这句有些疑问:
其栈底指针?STACK——由C51自动生成,栈顶应该是IDATA顶端——0xFF

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

使用特权

评论回复
13
timothy2010| | 2010-7-16 10:15 | 只看该作者
关注

使用特权

评论回复
14
冷漠|  楼主 | 2010-7-16 10:20 | 只看该作者
本帖最后由 冷漠 于 2010-7-16 11:21 编辑

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

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

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

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

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

使用特权

评论回复
15
ayb_ice| | 2010-7-16 10:53 | 只看该作者
LS
请你仔细看我的原话,没有理解就不要乱引用
我是说KEIL默认的堆栈长度是1字节<,可以改,只要编译器能分配一个字节的空间,就不会报错>,但其实所有没有被编译器使用的IDATA空间都是堆栈,当然中间的间隙不算

未命名.GIF (64.59 KB )

未命名.GIF

使用特权

评论回复
16
xuyiyi| | 2010-7-16 14:05 | 只看该作者
谢谢 14楼: 冷漠大虾 回贴,纠正俺的理解。

俺继续听 冷漠大虾 讲课。

使用特权

评论回复
17
yuyetufu| | 2010-7-16 14:44 | 只看该作者
对自己在12楼的话解释一下
关于栈顶的理解其实和LZ是一样的,只是说法稍微不同。
堆栈指针SP的范围,最小叫栈底,最大叫栈顶。SP在栈底和栈顶之间活动。所以本例中,栈顶是IDATA顶端0XFF,这么理解没错。

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

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

使用特权

评论回复
18
冷漠|  楼主 | 2010-7-16 16:47 | 只看该作者
冷漠有另一观点:

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

使用特权

评论回复
19
一级菜鸟| | 2010-7-16 17:45 | 只看该作者
看了半天没看出来LZ怎么把堆栈精确设置,在一个大的代码里,有非常多分枝与条件执行的代码,有很多的中断的代码,甚至不需要有重入的代码里,精确设置堆栈几乎是不可能的,唯一的做法仅仅是尽可能保留最多的空间给堆栈。

使用特权

评论回复
20
highgear| | 2010-7-16 21:19 | 只看该作者

例子更清楚

本帖最后由 highgear 于 2010-7-16 22:19 编辑

哈哈, 冷漠同学高深莫测,意识流的运用堪比大师. 好吧, 为了清晰起见, 我替冷漠同学总结一下:

1) "假定项目中有3个汇编程序模块A.a51,B.a51,C.a51,它们当然每个模块都有自己的私有堆栈"
"BL51 A.OBJ,B.OBJ,C.OBJ  STACK(?DT?A(50H),B(60H),C(70H))
C51下的公有堆栈指针是?STACK ,而每个模块的私有堆栈指针是STACK,一个公有?STACK里包含一个或者多个私有STACK,?STACK指针由编译器确定分配在 idata 内所有段的最后面"

2) "只有PUSH / POP指令才能操作STACK,硬件自动压入的属于不可控的系统控制栈,STACK根本不指向!2字节压入PC根本不影响STACK指针"

3) "C程序中硬件自动压入的PC在私有STACK指向下面就完成了,用户程序根本看不见的。好像称为系统控制栈内容"

4) " 私有堆栈(每个后台函数的私有STACK,和C编译器中的?STACK是两回事)被编译器分配在RAM低端,从全局静态变量区(包括共享覆盖区)后面开始,即初始SP所指向区域,直到?STACK所指向为结束。

?STACK所指向的是前台堆栈,被编译器默认自动分配在所有段(包括所有私有STACK段)的最后面(RAM高端)——栈顶部分!关键的是从这里开始,直到栈顶,才存在溢出危险。——它是一个独立ISR函数(不是多个后台函数)的堆栈"

5)"对于interrupt 属性函数,C51为其分配中断函数私有堆栈……还有硬件堆栈hardware  stack 的概念"

引用结束.
假设:
有A.obj, B.obj, C.obj 三个模块构成的一个8051单片系统, 使用了一个定时中断, 简单起见, 没有使用 reentrant。其中a调用了b中的函数, b调用了c, c调用了a, 期间有中断,而且没有重新设置过sp。  

请问冷漠同学:

1) 按照冷漠同学的解释, 此系统有: 中断函数私有堆栈, abc模块私有堆栈, 还有硬件堆栈hardware  stack. 一共 1+3+1 = 5 个栈   
2) 如果不是 5个栈, 这个系统总共有多少个栈? 请给出明确的数字。
3)每个栈是如何操作的? 请给出说明。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:要想让别人关照自己的自尊,首先自己先要有所成就才行。

17

主题

921

帖子

4

粉丝