打印

救火车的火灾预警----小心keil的堆栈隐患。

[复制链接]
楼主: 救火车
手机看帖
扫描二维码
随时随地手机跟帖
21
对了,请问MCU内部的xram 在用keil时是用定义为xdata  然后选择什么模式好点是\, impact or large
jack.king 发表于 2010-7-8 13:59


这和编译模式没有必然模式,编译模式只决定默认变量的分配方式,当然任何时候变量是可以显示声明的

比如

char data delay;
char idata delay;
char xdata delay;

使用特权

评论回复
22
冷漠| | 2010-7-9 08:54 | 只看该作者
本帖最后由 冷漠 于 2010-7-9 09:39 编辑
多次调整变量类型的编译结果表明,C51对于堆栈空间需求大小不作计算,任何代码都只是按堆栈空间只有1个字节需求来分配(在我眼里看来这明显是胡来,稍复杂点的子程序调用都不可能只要1个字节就能完成现场保护),由于堆栈只能分配在data区和idata区,因此当一个程序为了优化而data区占用太多时,虽然编译器能编译成功,但往往SP堆栈指针被分配在data区的最后面,很容易造成堆栈空间不够而溢出。为保险起见,最好保证编译后的SP值安排在F0H之前,那样至少有16个字节的堆栈空间,才能最大限度保证程序不会跑飞。
    看样子不能太相信Keil C51,以后编译完后,还得查看一下M51才能确保程序的质量,不知道这个算不算Keil C51的bug


唉,还是没讲清为什么。

请问救火车版主,你的程序是否调用了C库函数?例如printf( )之类;

    由此提示一点:中断堆栈的深度,和模块中是否有“可再入函数”深刻联系。?STACK 深度为1字节,绝对正确!前提就是模块中不包含reentrant函数,有其深刻的设置算法原理。——C51的bug ? 怎么动不动就是别人错?中国的高手suoma 各个都比Keil厉害?到底谁的bug ?
     提示到此,高手继续……

使用特权

评论回复
评分
参与人数 1威望 +1 收起 理由
yan2626156 + 1 我很赞同
23
HWM| | 2010-7-9 09:01 | 只看该作者
理论上,没有一个人敢给STACK上保险。
除非你的软件非常的简单。

使用特权

评论回复
24
ayb_ice| | 2010-7-9 09:20 | 只看该作者
本帖最后由 ayb_ice 于 2010-7-9 09:23 编辑

千万不要乱说什么KEIL这BUG,那BUG的

堆栈是可以配置大小的,但KEIL为什么默认只是大小1呢
答案很简单
首先必须有一个大小,才能编译通过,实际上KEIL是把没有被使用的所有IDATA空间都当成了堆栈,定义的堆栈空间实际是是首地址
至于配置在启动文件STARUP.A51中

分析:
假如编译器已经使用了200个字节的IDATA空间,那么从201开始自然就是堆栈空间,堆栈定义成1或56没有任何区别,不影响任何使用
但是如果把堆栈定义成56,那么上面的编译结果变成了编译器使用了256字节的IDATA空间了,这样当然也没有问题<可能有些人觉得有点危险,因为直觉是RAM用完成了,但其实已经确保堆栈不会小于56了>
但是如果此时把堆栈改成57呢,结果可想而知连编译都过不了,但实际99.9%情况下,56字节的堆栈足够了

结论: 堆栈大小定义1个字节是最科学的
看编译结果时要确保有一定的堆栈
据我的经验一般情况64字节堆栈足以应对大部分情况<不使用IDATA重入堆栈,两级中断>
注: KEIL手册中一句话,一个复杂的数**算几乎可以耗掉所有的堆栈<其实在51中几乎不用这些复杂的数运学算,估计是什么LOG一类的>
....

使用特权

评论回复
25
救火车|  楼主 | 2010-7-9 09:26 | 只看该作者

查看仿真状态的寄存器值。可以看到SP的最大值。要保证这个值不溢出0xFF。有仿真器的可以长时间开着看看。
但是没有仿真器怎么办?软仿真时,程序有跳不到的地方。

使用特权

评论回复
26
306970351| | 2010-7-9 09:34 | 只看该作者
对于内部自带XRAM的MCU,如果需要用到自带的XRAM,定义变量是是否需要加xdata前缀??
就是用  char Data;       定义  
还是     char xdata Data;   定义?

使用特权

评论回复
27
ayb_ice| | 2010-7-9 09:42 | 只看该作者
软件仿真是可以看堆栈情况,但实际意义不大

因为一个复杂的程序很难用软件仿真的<理论是可以>,程序和硬件关系太大

用类似UCOS的方法可以大致了解堆栈的使用情况

初始化时将所有堆栈清零,检查堆栈程序从堆栈的最后开始检查发现不为0则可能是堆栈的最大时候
显然这只是可能情况,但程序经过一段较长时间的运行后,检查情况还是有意义的,当然首先必须保证运行中堆栈本身不会溢出,否则程序本身已经出错了
...

使用特权

评论回复
28
救火车|  楼主 | 2010-7-10 09:05 | 只看该作者
大家把可行的想法都说出来吧。就算方法不好,也讲出来研究研究。
就算是抛“砖”了。说不定洪七公等人就会把“玉”扔出来了。

使用特权

评论回复
29
elec921| | 2010-7-10 12:56 | 只看该作者
学习

使用特权

评论回复
30
xlsbz| | 2010-7-10 22:34 | 只看该作者
太简单了! 没人能弄出来!

我觉得不少人的编程思路有问题!

一般情况 对速度根本没有什么要求的啊  所以放到XDATA区就可以了!

data区域 放个 50多个 已经算多的了!

如果对速度有要求  就换片子吧! 哈哈 可能你说不想换片子 那么就可以证明 你对速度没有要求 那么放到XDATA区 就得了吧

这个讨论根本就是误解的啊

注意不要用递归啊 什么之类的 就OK了!

使用特权

评论回复
31
冷漠| | 2010-7-10 22:53 | 只看该作者

一点都不简单耶

本帖最后由 冷漠 于 2010-7-10 23:01 编辑

所有C库函数都是递归可重入的。总不能说不要调用C51库函数?像scanf()之类。
很深刻的讨论题目,绝对不是误解,C编译器有一套最优化堆栈管理方法的。

冷漠明天要开讲啦。有版主在,捣乱的也不敢来吧。

使用特权

评论回复
32
xlsbz| | 2010-7-11 20:42 | 只看该作者
太简单了! 没人能弄出来!


xlsbz 发表于 2010-7-10 22:34

我的意思是:这个讨论的最终结局很简单:那就是没有人能弄出来!

使用特权

评论回复
33
xlsbz| | 2010-7-11 20:43 | 只看该作者
所有C库函数都是递归可重入的。总不能说不要调用C51库函数?像scanf()之类。
很深刻的讨论题目,绝对不是误解,C编译器有一套最优化堆栈管理方法的。

冷漠明天要开讲啦。有版主在,捣乱的也不敢来吧。 ...
冷漠 发表于 2010-7-10 22:53


冷漠 和 computer00 等高人出来讲讲的话,倒是很值得一听啊!!!

使用特权

评论回复
34
ayb_ice| | 2010-7-12 08:12 | 只看该作者
谁说所有库函数是重入的,至少KEIL有部分函数不是重入的
...

使用特权

评论回复
35
highgear| | 2010-7-12 09:31 | 只看该作者
赞同ayb_ice. 说所有C库函数可都是重入也还算勉强, 但所有C库函数都是递归基本上不着边际。

使用特权

评论回复
36
冷漠| | 2010-7-12 09:54 | 只看该作者
等一等。冷漠所有发言都不是自己的思想,都是书上讲的。我帮你找一下书上的证据。
你说的不可重入,可能不是函数,是宏什么的。
正好也与LZ的题目有关,不妨具体举几个例子看看?我举任意一个库C函数f1(),假定:

后台超循环程序正在调用一个f1(),中断随机到来,ISR也要调用f1(),那么f1()必须是可重入函数,对吧。C库中有不是这样的函数吗?举一个例子研究研究。——也许叫原子语句,那是首先要关中断的,直到函数执行完。那也许属于操作系统库函数什么的,属于特殊特殊不是一般。

还有,冷漠的讲义内容包含了为什么C库函数一定是、必须是可重入的。——它和C编译器规则有关,是看了多本书上都是这样讲的;当然也是Keil说明书上的内容。绝不是冷漠自己个人想象、信口胡说。我会举几个实际例子。
待冷漠抽时间。这样讨论一下有很多好处,谢谢34楼。

使用特权

评论回复
37
highgear| | 2010-7-12 10:02 | 只看该作者
Keil 说得很清楚:

Functions (including many library routines) written using the C51 compiler are typically NOT reentrant.

In C, a function may be passed arguments and may return a value to the caller. Many C compilers pass arguments on the stack and return values in registers.

Due to the limited size of the 8051 stack, the C51 compiler must pass arguments in fixed memory locations. The linker builds a call tree and overlays parts of memory that are mutually exclusive.

A function that is reentrant may be passed arguments and may return a value to the caller--just like a function that is non-reentrant. The difference is that a function that is reentrant does not use variables that can be overwritten by another instance (or simultaneous invocation) of that function. An example will help better illustrate this.

http://www.keil.com/support/docs/1861.htm

任何书上都不会有所有C库函数都是递归的这一论断的, 递归完全是另一个概念, 虽然递归一定是可重入.

使用特权

评论回复
评分
参与人数 1威望 +1 收起 理由
sheriff + 1
38
ayb_ice| | 2010-7-12 10:10 | 只看该作者
本帖最后由 ayb_ice 于 2010-7-12 10:27 编辑

关于递归与重入: 递归首先必须重入,重入不一定能递归

与冷漠打了些交通
我发现冷漠这人很是主观,听不得建议,意见...
看来你对KEIL真是不太了解
KEIL C编译器与标准C最大的区别就是重入支持问题
费话少说,直接上图
图中没有reentrant的就是非重入函数,KEIL的库函数中还有一些非重入的函数

未命名.GIF (85.2 KB )

未命名.GIF

使用特权

评论回复
评分
参与人数 1威望 +1 收起 理由
sheriff + 1
39
sheriff| | 2010-7-12 10:45 | 只看该作者
谢谢楼上ayb_ice的图和highgear的讲解,学习了.

使用特权

评论回复
40
冷漠| | 2010-7-12 11:20 | 只看该作者
……关于递归与重入: 递归首先必须重入,重入不一定能递归……


绝对同意38楼,——冷漠并没有否认过这一点。而且keil还能通过配置命令指定(限制)递归(可重入)的次数!

请问众高手:这和中断?STACK堆栈指针有什么联系?
所以,在冷漠还没有开讲之前,最好先别否认他人。

冷漠的缺点绝对有时候自以为是,其实他是“以书为是”,因为他守着图书馆呢。总不见得拜那些不看书的人为师?冷漠只崇拜书而已,请多多谅解。
    冷漠80后是不是需要谦虚点,以便让那些时刻需要他人“关照自己的自尊”,又没有先有所成就的人感觉舒服点(这是谁说的)?
     如今是一个张扬个性的年代。谁有什么个性,就自己张扬,别因为自己是70后、60后遇到冷漠就不舒服,别压抑少年狂,让年少之人谦虚点,然后自己年长随便怎么吹。

使用特权

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

本版积分规则