堆栈是什么意思
不知道你指的堆栈是真实的堆栈还是函数调用。
前者很容易,只要通过堆栈头就可以一路向下找到堆栈尾,用个变量数目统计就可以了。
至于函数调用只是用了一个比喻而已,这个堆栈是由编译器完成的,这可能是你调用函数嵌套太多,或者陷入调用的死循环,从而引起堆栈溢出。
至于检查堆栈是否溢出,不管你是以上哪种原因引起的,一旦堆栈溢出,程序就会崩溃,即使捕捉异常也无济于事,合理使用内存及编码是防止堆栈溢出的唯一途径。
详细来讲比较复杂,有问题可以给我发消息留言
如何:使用“调用堆栈”窗口
窗口可以查看当前堆栈上的函数或过程调用。
“调用堆栈”窗口显示每个函数的名称以及编写它所用的编程语言。
函数或过程名可能包含可选信息,如模块名、行号、字节偏移量以及参数的名称、类型和值。
可以打开或关闭这些可选信息的显示。
一个黄色箭头标识执行指针当前所位于的堆栈帧。
默认情况下,该帧的信息显示在源、“反汇编”、“局部变量”、“监视”和“自动”窗口中。
如果想将上下文更改为堆栈上的另一个帧,可以在“调用堆栈”窗口中执行相应的操作。
当调试符号对部分调用堆栈不可用时,“调用堆栈”窗口也许就不能显示那部分调用堆栈的正确信息。
将出现以下表示法:[下面的帧可能不正确和/或缺失,没有为 name.dll 加载符号]默认情况下在托管代码中,“调用堆栈”窗口隐藏非用户代码的信息。
在隐藏信息处出现以下表示法:[非用户代码是指除“我的代码”以外的任何代码。
有关“我的代码”的更多信息,请参见使用快捷菜单可以选择显示非用户代码的调用堆栈信息。
可以使用快捷菜单选择查看线程之间的调用。
注意显示的对话框和菜单命令可能会与“帮助”中的描述不同,具体取决于您的当前设置或版本。
若要更改设置,请在“工具”菜单上选择“导入和导出设置”。
有关更多信息,请参见使用设置。
如何使堆栈指针指向任意处
我不知道你是怎么设置堆栈的,是写汇编代码写的,还是用软件选项卡设置,如果是后者你的软件可能过于智能化了,我用的KEIL arm,就不会有堆栈溢出这种检测,只是某些不可写的存储空间进行写操作会报错,完全是当裸机处理的。
。
。
堆栈只要是RAM里,随便分配的,在汇编内声明内存块占用就行。
。
。
实际情况下,没有内存管理器的CPU根本不具备堆栈溢出检测的能力,你的应该是软件提个醒,未必不能运行。
。
。
ostaskcreate就有一个参数是 ptos,这个参数就是栈顶的位置,只要你ptos的值在0x1bc0 -0x17c0之间就行,这样根据CPU的增长方向,ptos+size或者ptos-size的范围就是任务堆栈,不要出总体栈的范围就行。
。
。
。
对于UCOS-ii这种小系统,想不溢出,你就大致感觉下需要的栈大小,设置ptos的值就行了,没有限定堆栈大小。
。
。
。
ucosii任务堆栈的作用是什么呢?
在uCOS中,每一个任务都有一个独立的任务堆栈。
为了深入理解任务堆栈的作用,不妨分析任务从“出生”到“消亡”的整个过程,具体就是分析任务的建立,运行,挂起几种状态中任务堆栈的变化情况。
现在假设系统运行着一个由用户创建的用以完成打印工作的任务TPrint。
TPrint最初通过OSTaskCreate()函数创建,在该函数中与任务堆栈有关的第一段代码是大家比较熟悉的函数OSTaskStkInit(),这个函数是在uCOS移植过程中必须实现的,其作用是“初始化堆栈”,其实就是预先在RAM中的一块区域中把任务将来运行开始时CPU寄存器应处的状态(正确值)准备好,之后,任务第一次被内核调度器调度运行时,将这些准备好的数据(寄存器的值)推到CPU的寄存器中,如果数据设计的合理,CPU便会按照我们预先设计好的思路运行。
所以,“初始化堆栈”实际上是做了一个“未雨绸缪”的工作。
这个过程中有两点是必须慎重考虑的,一是PC该如何定位,二是CPU的其它寄存器(除PC之外)该怎么处理。
先说第一点,因为任务是第一次运行,而任务从本质上将就是一段代码,所以PC指针应该定位到这段代码的第一行处,即所谓的入口地址(Entry Point)处,这个地址由任务指针保存着,所以把该指针值赋给PC即可。
第二,这段代码还未被执行过,所以代码中的变量与CPU的其它寄存器一点关系也没有,因此R0-R12,R14可随便给值,或者不赋值也可,让这些寄存器保持原来的值,显然后者更为简单。
最后再给CPSR赋值,用户可以根据实际需要使系统运行于系统模式或管理模式。
经过入栈和出栈,此时SP指向任务堆栈的最底端(就是已经定义好的任务堆栈数组的最后一个元素)。
之后任务代码开始正式运行,因为CPU的寄存器是有限的,所以在运行时不可避免地要把一些临时变量暂时保存到堆栈中。
具体应保存到哪个地址呢,不用担心,SP知道(任务第一次运行时,这个地址就是任务堆栈数组的最后一个元素的地址)。
任务堆栈的大小和任务代码中临时变量的数目有关,如果这段代码临变量特别多,堆栈就应设计的大一些。
然后,TPrint任务由于某种原因将要被挂起,所以应把任务的运行现场放到堆栈里保护起来,TPrint任务再次运行时再把这个现场还原,任务就能从上次断点处紧接着运行。
那么,这个现场是什么呢?从本质上讲,TPrint任务的运行过程就是CPU在执行一段特定的代码,所以这个现场就是CPU的现场,也就是寄存器的值。
这些寄存器的值包含了代码执行时的所有信息,包括当前运行到了这段代码的哪个位置处(由PC值指明)。
因此,把CPU的寄存器的值推入堆栈,然后记住栈顶指针的位置(SP由OSTCBCur->OSTCBStkPtr保存),当任务再次将要运行前,从SP指向的地址处依次把先前保存的CPU寄存器的值放到CPU的寄存器中,任务就可以从上次中断的地方准确无误地执行。
这个过程就像突然把任务冻结了,与任务有关的任何东西都不能动了,一段时间之后又把任务解冻,与它有关的东西又变得可用,于是任务又可以活蹦乱跳地跑起来了。
从以上分析可以看出,任务堆栈至始至终伴随着任务,与之生死与共,它的作用可以概括为两点:第一,当任务运行时,它用来保存一些局部变量;第二,当任务挂起时,它负责保存任务的运行现场,也就是CPU寄存器的值。
有些朋友正是忽视了第一点,产生了“任务堆栈大小应是固定值的疑问”。
我感觉,这可能与对函数OSTaskStkInit()的理解有关,我们都称之为堆栈初始化函数,但此处的“初始化”与我们理解的初始化不太一样,平时讲的(变量的)初始化似乎指的是将变量的所有成员都一一初始化。
而此处的堆栈的初始化仅仅是初始化了很大一个堆栈的一小部分,因为当前只有这部分是有用的,而剩余的大部分用不到,所以不用初始化,就像有些变量不用初始化一样(有默认值或随机值)。
而且,任务每次挂起前用来保存当前CPU寄存器这一连续区域在整个任务堆栈空间中是浮动的
堆栈是干什么的??位于哪呢??
这个是OD设置之后局部变量的显示方法,[LOCAL]就是局部变量的意思,例如[LOCAL.1]就是第一个局部变量,存放在栈里的[EBP-4]位置,[LOCAL.2]就是[EBP-8],你图片上的命令其实就是MOV EAX,[EBP-4]如果不想这样显示而是直接显示栈的话,可以在OD的调试设置-分析里面有个选项把勾去掉即可。
借用网上的一个图片补充一下:你图片上的[ARG]是参数的表示方法,同理[ARG.1]就是第一个参数,等价于[EBP+8]
小丶土逗