如何使用 C++ 确定 ARM Cortex 的适当堆栈和堆大小
Cortex M3 处理器启动文件允许您指定专用于堆栈和堆的 RAM 量。对于 C++ 代码库,是否有一般的经验法则或可能有一些更明确的方法来确定堆栈和堆大小的值?例如,您会计算唯一对象的数量和大小,还是使用编译后的代码大小?
The cortex M3 processor startup file allows you to specify the amount of RAM dedicated to the stack and the heap. For a c++ code base, is there a general rule of thumb or perhaps some more explicit way to determine the values for the stack and heap sizes? For example, would you count the number and size of unique objects, or maybe use the compiled code size?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这不是 Cortex-M3 的功能,而是开发工具链提供的启动代码。这是 M3 的 Keil ARM-MDK 默认启动文件的工作方式。这有点不寻常;更常见的是,您会指定堆栈大小,并且链接器分配堆栈和静态内存后的任何剩余内存都将成为堆;这可以说更好,因为您最终不会得到一个无法使用的内存池。您可以修改它并使用替代方案,但您需要知道自己在做什么。
如果您使用 Keil ARM-MDK,则链接器选项 --info=stack 和 --callgraph 会将信息添加到映射文件中,以帮助堆栈需求分析。这些技术和其他技术在此处进行了描述。
如果您使用 RTOS 或多任务内核,则每个任务都有自己的堆栈。操作系统可能提供堆栈分析工具,Keil 的 RTX 内核查看器显示当前堆栈使用情况,但不显示峰值堆栈使用情况(因此大多无用,并且仅适用于具有默认堆栈长度的任务)。
如果您必须自己实现堆栈检查工具,通常的方法是用已知值填充堆栈,然后从高地址开始检查该值,直到找到第一个不是填充字节的值,这将给出堆栈的利克利高潮标记。您可以实现代码来执行此操作,也可以从调试器手动填充内存,然后在调试器内存窗口中监视堆栈使用情况。
堆要求将取决于代码的运行时行为;你必须自己分析,但是在 ARM/Keil Realview 中,当 C++ 的
new
抛出异常时,将调用 MemManage 异常处理程序;我不确定malloc()
是否会这样做或只是返回 NULL。您可以在异常处理程序中放置断点或修改处理程序以发出错误消息以在测试期间检测堆耗尽。还有一个可以使用的 __heapstats() 函数输出堆信息。它有一个有点麻烦的界面,我这样包装它:That is not a feature of the Cortex-M3, but rather the start-up code provided by your development toolchain. It is the way the Keil ARM-MDK default start-up files for M3 work. It is slightly unusual; more commonly you would specify a stack size, and any remaining memory after stack and static memory allocation by the linker becomes the heap; this is arguably better since you do not end up with a pool of unusable memory. You could modify that and use an alternative scheme, but you'd need to know what you are doing.
If you are using Keil ARM-MDK, the linker options --info=stack and --callgraph add information to the map file that aids stack requirement analysis. These and other techniques are described here.
If you are using an RTOS or multi-tasking kernel, each task will have its own stack. The OS may provide stack analysis tools, Keil's RTX kernel viewer shows current stack usage but not peak stack usage (so is mostly useless, and it only works correctly for tasks with default stack lengths).
If you have to implement stack checking tools yourself, the normal method is to fill the stack with a known value, and starting from the high address, inspect the value until you find the first value that is not the fill byte, this will give the likley high tide mark of the stack. You can implement code to do this, or you can manually fill the memory from the debugger, and then monitor stack usage in a debugger memory window.
Heap requirement will depend on the run-time behaviour of your code; you'll have to analyse that yourself however in ARM/Keil Realview, the MemManage Exception handler will be called when C++'s
new
throws an exception; I am not sure ifmalloc()
does that or simply returns NULL. You can place a breakpoint in the exception handler or modify the handler to emit an error message to detect heap exhaustion during testing. There is also a a __heapstats() function that can be used to output heap information. It has a somewhat cumbersome interface, I wrapped it thus:编译后的代码大小不会有任何帮助,因为代码不在堆栈或堆中运行。 Cortex-M3 设备通常在具有内置闪存和相对少量 RAM 的微控制器上实现。在此配置中,代码通常从闪存运行。
堆用于动态内存分配。计算唯一对象的数量将为您提供粗略的估计,但您还必须考虑使用动态内存分配的任何其他元素(在 C++ 中使用
new
关键字)。通常,在嵌入式系统中避免动态内存分配,因为堆大小难以管理。堆栈将用于异常处理例程期间的变量传递、局部变量和上下文保存。通常很难很好地了解堆栈的使用情况,除非您的代码分配了大块本地内存或大对象。一种可能有帮助的技术是为堆栈分配所有可用的 RAM。用已知模式填充堆栈(0x00 或 0xff 不是最佳选择,因为这些值经常出现),运行系统一段时间,然后检查堆栈以查看使用了多少。诚然,这不是一种非常精确也不科学的方法,但在许多情况下仍然有帮助。
The compiled code size will not help as the code does not run in the stack nor the heap. Cortex-M3 devices are typically implemented on microcontrollers with built in Flash and a relatively small amount of RAM. In this configuration, the code will typically run from Flash.
The heap is used for dynamic memory allocation. Counting the number of unique objects will give you a rough estimate but you also have to account for any other elements that use dynamic memory allocation (using the
new
keyword in C++). Generally, dynamic memory allocation is avoided in embedded systems for the precise reason that heap size is hard to manage.The stack will be used for variable passing, local variables, and context saving during exception handling routines. It is generally hard to get a good idea of stack usage unless you're code allocates a large block of local memory or a large objects. One technique that may help is to allocate all of the available RAM you have for the stack. Fill the stack with a known pattern (0x00 or 0xff are not the best choices since these values occur frequently), run the system for a while then examine the stack to see how much was used. Admittedly, this not a very precise nor scientific approach but still helpful in many cases.
最新版本的 IAR 编译器有一个功能,可以根据代码的静态分析(假设没有任何递归)来确定您需要的堆栈大小。
如果您没有确切的数字,一般方法是尽可能大,然后当您开始耗尽内存时,开始修剪堆栈,直到您的程序由于堆栈溢出而崩溃。我希望这是一个玩笑,但通常都是这样做的。
The latest version of the IAR Compiler has a feature that will determine what stack size you need, based on a static analysis of your code (assuming you don't have any recursion).
The general approach, if you don't have an exact number, is to make as big as you can, and then when you start running out of memory, start trimming the stack down until your program crashes due to a stack over flow. I wish that was a joke, but that is the way it is usually done.
减少直到崩溃是一种快速的临时方法。您还可以使用已知值(例如 0xCCCC)填充堆栈,然后通过扫描 0xCCCC 来监视最大堆栈使用情况。
它并不完美,但比寻找崩溃要好得多。
其基本原理是,减少堆栈大小并不能保证堆栈溢出会破坏“可见”的内容。
Reducing until it crashes is a quick ad-hoc way. You can also fill the stack with a known value, say, 0xCCCC, and then monitor maximum stack usage by scanning for the 0xCCCC.
It's imperfect, but much better than looking for a crash.
The rationale being, reducing stack size does not guarantee that stack overflow will munch something "visible".