我在教科书中读到堆栈通过减少内存地址而增长;即从高地址到低地址。这可能是一个糟糕的问题,但我没有正确理解这个概念。你可以解释吗?
4 回答
首先,它依赖于平台。在某些架构中,堆栈从地址空间的底部开始分配并向上增长。
假设像 x86 这样的架构从地址空间的顶部向下堆栈,这个想法非常简单:
=============== Highest Address (e.g. 0xFFFF)
| |
| STACK |
| |
|-------------| <- Stack Pointer (e.g. 0xEEEE)
| |
. ... .
| |
|-------------| <- Heap Pointer (e.g. 0x2222)
| |
| HEAP |
| |
=============== Lowest Address (e.g. 0x0000)
要增加堆栈,您需要减少堆栈指针:
=============== Highest Address (e.g. 0xFFFF)
| |
| STACK |
| |
|.............| <- Old Stack Pointer (e.g. 0xEEEE)
| |
| Newly |
| allocated |
|-------------| <- New Stack Pointer (e.g. 0xAAAA)
. ... .
| |
|-------------| <- Heap Pointer (e.g. 0x2222)
| |
| HEAP |
| |
=============== Lowest Address (e.g. 0x0000)
如您所见,要增加堆栈,我们将堆栈指针从 0xEEEE减少到 0xAAAA,而要增加堆,您必须增加堆指针。
显然,这是对内存布局的简化。实际的可执行文件,数据部分,...也加载到内存中。此外,线程有自己的堆栈空间。
你可能会问,为什么堆栈要向下增长。好吧,正如我之前所说,有些架构会反过来,使堆向下增长而堆栈向上增长。将堆栈和堆放在相对的两侧是有意义的,因为它可以防止重叠并允许两个区域自由增长,只要您有足够的可用地址空间。
另一个有效的问题可能是:程序不应该减少/增加堆栈指针本身吗?架构如何将一个架构强加给程序员?为什么它不依赖于程序,而是依赖于架构?虽然您几乎可以与架构作斗争并以某种方式从相反的方向离开您的堆栈,但一些指令,特别是call
直接ret
修改堆栈指针的指令将假定另一个方向,从而造成混乱。
如今,这主要是因为它已经这样做了很长时间,并且许多程序都认为它是这样做的,并且没有真正的理由来改变它。
回到恐龙在地球上漫游的时候,如果幸运的话,计算机有 8kB 的内存,但是,这是一个重要的空间优化。你把堆栈的底部放在内存的最顶端,向下增长,你把程序和它的数据放在最底部,随着malloc
区域的增长。这样,堆栈大小的唯一限制是程序 + 堆的大小,反之亦然。如果堆栈从 4kB(例如)开始并逐渐增大,即使程序只需要几百字节的堆栈,堆也永远不会超过 4kB(减去程序的大小)。
Man CLONE : child_stack 参数指定子进程使用的堆栈的位置。由于子进程和调用进程可能共享内存,因此子进程不可能与调用进程在同一个堆栈中执行。因此,调用进程必须为子堆栈设置内存空间,并将指向该空间的指针传递给 clone()。堆栈在所有运行 Linux 的处理器(HP PA 处理器除外)上向下增长,因此 child_stack 通常指向为子堆栈设置的内存空间的最高地址。
在 x86 上,栈向内存地址递减方向增长的主要原因是PUSH
指令递减栈指针:
递减堆栈指针,然后将源操作数存储在堆栈顶部。
见第 英特尔® 64 和 IA-32 架构软件开发人员手册中的 4-511 。