为什么在大多数操作系统的运行时不能增加堆栈?

How come the stack cannot be increased during runtime in most operating system?

提问人:Olle Härstedt 提问时间:1/9/2021 最后编辑:Priyanshul GovilOlle Härstedt 更新时间:1/11/2021 访问量:222

问:

是为了避免碎片化吗?还是其他原因?内存分配的设置生存期是一个非常有用的结构,与手动生存期相比。malloc()

c 操作系统 堆栈分配

评论

0赞 Support Ukraine 1/9/2021
为什么这被标记为 C?我在这里没有看到任何特定于 C 的内容 - 除了对 .malloc
0赞 Olle Härstedt 1/9/2021
@4386427什么是更好的标签?
0赞 Support Ukraine 1/9/2021
也许操作系统会更合适。
1赞 Olle Härstedt 1/10/2021
@EricPostpischil 你能把它写成一个答案吗?

答:

6赞 Eric Postpischil 1/11/2021 #1

在程序执行期间,用于堆栈的空间会频繁增加和减少,因为函数被调用和返回。堆栈允许的最大空间通常是一个固定限制。

在较旧的计算机中,内存是一种非常有限的资源,它可能仍然存在于小型设备中。在这些情况下,硬件功能可能会对最大堆栈大小施加必要的限制。

在具有大量内存的现代系统中,可能有大量内存可用于堆栈,但允许的最大值通常设置为某个较低的值。这提供了一种捕捉“失控”程序的方法。通常,控制程序使用的堆栈空间量是软件工程中被忽视的部分。根据实践经验设定了限制,但我们可以做得更好。1

从理论上讲,一个程序可以被赋予一个很小的初始限制,如果它发现自己正在处理一个“大”问题,它可以告诉操作系统它打算使用更多。这仍然可以捕获大多数“失控”的程序,同时允许精心设计的程序使用更多空间。但是,总的来说,我们设计的程序使用堆栈进行程序控制(管理函数调用和返回,以及为本地数据提供适度的空间)和其他用于操作数据的内存。因此,堆栈空间很大程度上是程序设计(固定的)的函数,而不是问题的大小。该模型运行良好,因此我们继续使用它。

脚注

1 例如,对于每个不使用具有运行时变量大小的对象的例程,编译器可以报告该例程在通过它的任何路径中使用的最大空间。对于任何调用树 [因此没有循环] 链接器或其他工具可以报告使用的最大堆栈空间。其他工具可以帮助分析具有潜在递归的调用图中的堆栈使用情况。

5赞 Basile Starynkevitch 1/11/2021 #2

为什么在大多数操作系统的运行时不能增加堆栈?

这对 Linux 来说是错误的。在最近的 Linux 系统上, 每个线程都有自己的调用堆栈 (参见 pthreads(7)), 并且应用程序可以(通过巧妙的技巧)在查询调用堆栈后使用 mmap(2) 和 mremap(2) 增加一些调用堆栈(参见 proc(5) 和 use ),就像 pmap(1 一样。/proc//proc/self/maps

当然,此类代码是特定于体系结构的,因为在某些情况下,调用堆栈会朝着增加地址的方向增长,而在其他情况下,调用堆栈会朝着减少地址的方向增长。

另请阅读操作系统:Three Easy PiecesOSDEV wiki,并研究 GNU libc 的源代码。

顺便说一句,Appel 的书 Compiling with Continuations,他的旧论文 Garbage Collection 可能比 Stack Allocation 更快,这篇关于使用 Continuations 和 LLVM 编译的论文可能会让您感兴趣,两者都与您的问题非常相关:有时,几乎没有“没有调用堆栈”,并且“增加它”是没有意义的。

评论

1赞 Eric Postpischil 1/11/2021
这是支持的事情,比如呼吁“让我的堆栈变大”,还是自己动手做的事情,比如“找到我的堆栈映射的末端在哪里,并在它之外映射更多的内存”?如果是后者,如何找到堆栈映射的末尾?明确给出的东西告诉你终点在哪里(或者,等效地,堆栈有多大)或其他东西?