程序栈和堆,它们是如何工作的?

Posted

技术标签:

【中文标题】程序栈和堆,它们是如何工作的?【英文标题】:Program stack and heap, how do they work? 【发布时间】:2011-01-05 05:52:30 【问题描述】:

我知道每个正在运行的进程在虚拟内存中都有与之关联的页面,并且很少有页面会根据需要加载到主内存中。我也知道该程序将有一个堆栈和一个堆来分配动态内存。这是我的问题。

    堆栈是否也是主内存中某些页面的一部分? 当程序进入等待状态时会发生什么?堆栈指针、程序计数器等信息存放在哪里? 为什么栈会变小而堆会变大? L1、L2 缓存可以只包含一块连续的内存,还是可以包含堆栈和堆的一部分?

你能推荐一本涵盖这些内容的好书吗?

【问题讨论】:

【参考方案1】:

堆栈也是主内存中某些页面的一部分吗?

是的 - 堆栈通常存储在内存的“低”地址中,并向上填充到其上限。堆通常存储在地址空间的“顶部”并朝着堆栈增长。

当程序进入等待状态时会发生什么?堆栈指针、程序计数器等信息存放在哪里?

操作系统为每个正在运行的进程存储一个“上下文”。保存和恢复进程状态的操作称为“上下文切换”。

为什么栈变小,堆变大?

据我所知,这只是一个约定。堆栈并没有真正“增长”它有固定的分配。

L1、L2 缓存可以只包含一块连续的内存,还是可以包含堆栈和堆的一部分?

缓存仅包含已使用(最近或附近)的部分 RAM 的快照。在任何时候,他们都可以从其中的地址空间的任何部分获得内存。显示在哪里很大程度上取决于缓存的结构参数(块长度、关联性、总大小等)。

我建议将Computer Architecture: A Quantitative Approach 作为底层硬件和任何有关如何“管理”硬件的操作系统书籍的良好参考。

【讨论】:

佩普/8!!这是一本很好的教育学书籍,但我发现它对于一般学习来说相当可悲。【参考方案2】:

这是我对这些问题的理解:

    堆栈也是主内存中某些页面的一部分吗?

    是的,堆栈通常也存储在进程地址空间中。

    程序进入等待状态会发生什么,堆栈指针、程序计数器等信息存放在哪里?

    当操作系统将进程从活动状态变为等待状态时,它会将所有寄存器(包括堆栈指针和程序计数器)存储在内核的进程表中。然后,当它再次激活时,操作系统会将所有信息复制回原处。

    为什么栈变小,堆变大?

    这是因为它们通常必须共享相同的地址空间,并且为了方便起见,它们都从地址空间的一端开始。然后他们彼此相向而行,形成一种从小到大的行为。

    L1、L2 缓存可以只包含一块连续的内存还是可以包含堆栈和堆的一部分?

    CPU 缓存将存储最近使用的内存块。因为堆栈和堆都存储在主内存中,所以缓存可以包含两者的一部分。

【讨论】:

这是我在这组答案中第二次看到你的#3。你能指出我在哪里找到这个推理吗?我从来没有见过他们使用相同地址空间的保证,甚至任何暗示它很常见的东西。 它一定曾经是真的,在早已被淘汰和废弃的系统上。如今,每个堆栈的末尾都有一个未映射的页面,因此堆栈溢出通常会导致段错误(与踩踏其他内存并继续执行相比,这不太可能成为可利用的安全漏洞)。一个进程中可能有多个堆栈;例如,每个线程都有一个。也可能有多个堆。【参考方案3】:

3.为什么栈变小,堆变大?

请注意,在某些系统(例如某些 HP 系统)上,堆栈会向上而不是向下增长。而在其他系统(例如 IBM/390)上,没有真正的硬件堆栈,而是从用户空间内存动态分配的页面池。

通常,堆可以在任何方向增长,因为它可能包含许多分配和释放漏洞,因此最好将其视为松散的页面集合而不是 LIFO -stack类型结构。话虽如此,大多数堆实现都会在预定的地址范围内扩展其空间使用量,并根据需要扩大和缩小它。

【讨论】:

【参考方案4】:

当使用受保护模式的操作系统(如 Windows 或 Linux)时,每个进程都有大量内存页面可供给定进程使用。如果需要更多内存,可以调入更多内存。

通常,该进程将分配给它的内存分为两部分。一个是堆,另一个是堆栈。堆栈的底部由 arm 上的堆栈指针 r13 和 x86 上的 esp 指定。当一个人在堆栈上创建一个变量时,堆栈指针被移动以允许所需的额外空间。这是由汇编指令 PUSH 完成的。同样,当变量超出范围时,它会从堆栈中弹出。

通常 PUSH 会导致堆栈指针递减,使堆栈指针值上方的值“在堆栈上”。

内存的其他部分可以用于堆。然后可以使用 malloc 或 new 进行分配。每个线程必须有自己的堆栈,但可以与进程中的其他线程共享堆。

当内核重新调度线程时,它会存储堆栈寄存器并将堆栈寄存器更改为新堆栈。是否需要存储程序计数器,具体取决于调度方式。

缓存与堆栈或堆无关。它由处理器管理,并提供了一种方法来确保 CPU 所需的数据就在手边,这样它就不必等待总线来获取它。完全由 CPU 来确保主内存中的内容与缓存中存储的内容相同。唯一真正需要担心缓存的是使用 DMA 时。必须手动刷新或同步缓存,以确保 CPU 不信任缓存并实际从主内存中获取数据。

【讨论】:

【参考方案5】:

你应该看看我教授的slides,来自我的建筑课。第 6 单元。真的帮助我理解了所有你所问的和其他人已经回答的,以及更多,如果你想要更深入的知识。

【讨论】:

如果你从那里引用一些有用的信息会很好,或者至少,将链接提交到archive.org,这样如果将来这仍然是一个有用的答案该网页应该永远关闭(或更改)。事实上,我现在只是检查了一下,现在连幻灯片都没有发布。

以上是关于程序栈和堆,它们是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章

JVM栈和堆的详解

Java栈和堆内存管理

为啥会有栈和堆?

为啥会有栈和堆?

栈和队列的区别,栈和堆的区别

栈和堆