除了其他答案,我还要补充一点,在堆栈和堆空间之间划分RAM时,还需要考虑静态非恒定数据(例如,文件全局,函数静态和程序范围内的数据)的空间。从C的角度来看全局变量,对于C ++则可能是其他观点)。
堆栈/堆分配的工作方式
值得注意的是,启动程序集文件是定义区域的一种方法。工具链(您的构建环境和运行时环境)大多关心定义堆栈空间的开始(用于将初始堆栈指针存储在向量表中)以及堆空间的开始和结束(由动态使用)的符号内存分配器,通常由您的libc提供)
在OP的示例中,仅定义了2个符号,堆栈的大小为1kiB,堆的大小为0B。这些值在其他地方用于实际产生堆栈空间和堆空间
在@Gilles示例中,定义了大小,并在汇编文件中使用了大小,以设置从任意位置开始并持续该大小的堆栈空间(由符号Stack_Mem标识),并在末尾设置标签__initial_sp。对于堆,空间也是符号Heap_Mem(大小为0.5kiB),但在开头和结尾都有标签(__heap_base和__heap_limit)。
这些由链接器处理,该链接器不会在堆栈空间和堆空间内分配任何内容,因为该内存已被占用(由Stack_Mem和Heap_Mem符号占用),但是它可以将这些内存和所有全局变量放在需要的位置。标签最终成为在给定地址没有长度的符号。链接时,__ initial_sp直接用于向量表,运行时代码将__heap_base和__heap_limit直接使用。链接器根据放置符号的位置分配符号的实际地址。
正如我上面所提到的,这些符号实际上不必来自startup.s文件。它们可以来自您的链接器配置(Keil中的Scatter Load文件,GNU中的linkerscript),在这些文件中,您可以更好地控制布局。例如,您可以强制堆栈位于RAM的开头或结尾,或者使全局变量远离堆或其他任何对象。您甚至可以指定放置全局变量后,HEAP或STACK仅占据剩余的RAM。注意,尽管如此,但要注意,增加更多静态变量会使其他内存减少。
但是,每个工具链都是不同的,如何编写配置文件以及动态内存分配器将使用哪些符号将取决于特定环境的文档。
堆栈大小
至于如何确定堆栈大小,如果您不使用递归或函数指针,许多工具链可以通过分析程序的函数调用树来为您提供最大的堆栈深度。如果确实使用了这些值,则估计堆栈大小并用基值预先填充(也许通过main之前的输入功能),然后在程序运行了一段时间后,即最大深度为(基值所在)结束)。如果您已充分行使程序的极限,则将相当准确地知道是否可以缩小堆栈,或者,如果程序崩溃或没有基本值,则需要增加堆栈并重试。
堆大小
确定堆大小取决于应用程序。如果仅在启动期间进行动态分配,则可以仅添加启动代码中所需的空间(加上一些内存管理开销)。如果可以访问内存管理器的源代码,则可以确切知道开销是多少,甚至可以编写代码遍历内存以提供使用情况信息。对于需要动态运行时内存的应用程序(例如,为入站以太网帧分配缓冲区),我建议的最佳选择是仔细调整堆栈大小,并将堆和静态数据后剩下的一切都交给Heap。
最后说明(RTOS)
OP的问题被标记为裸机,但我想为RTOS添加注释。通常(总是吗?)每个任务/进程/线程(为简单起见,我将在这里写出任务)在创建任务时都会分配一个堆栈大小,除了任务堆栈外,可能还会有一个小的OS堆栈(用于中断等)
任务计费结构和堆栈必须从某个位置分配,这通常是从应用程序的整个堆空间分配的。在这些情况下,初始堆栈大小通常无关紧要,因为操作系统仅在初始化期间使用它。例如,我知道将链接期间的所有剩余空间分配给HEAP,并将初始堆栈指针放在堆的末尾以增长到堆中,因为我知道OS将从堆的开头开始分配,并且将在放弃initial_sp堆栈之前分配OS堆栈。然后,所有空间都用于分配任务堆栈和其他动态分配的内存。