堆栈:低内存与高内存地址实际位置?

时间:2018-05-19 02:06:43

标签: c stack

我在一些教程中一直在阅读有关堆栈和内存地址的信息,并想知道为什么他们对低内存和高内存位置的引用有所不同?

这令人困惑。

E.g。

低内存地址位于顶部,而高内存地址位于底部 enter image description here

低内存地址位于底部,高内存地址位于顶部 enter image description here

当我使用简单的C程序进行尝试时,低内存地址似乎位于顶部。来自bfbf7498 > bfbf749c > bfbf74a0 > bfbf74a4 > bfbf74a8 > bfbf74ac

user@linux:~$ cat > stack.c                     
#include <stdio.h>

int main()
{
        int A = 3;
        int B = 5;
        int C = 8;
        int D = 10;
        int E = 11;
        int F;
        F = B + D;

        printf("+-----------+-----+-----+-----+\n");
        printf("| Address   | Var | Dec | Hex |\n");
        printf("|-----------+-----+-----+-----|\n");
        printf("| %x  | F         | %d  | %X   |\n",&F,F,F);
        printf("| %x  | E         | %d  | %X   |\n",&E,E,E);
        printf("| %x  | D         | %d  | %X   |\n",&D,D,D);
        printf("| %x  | C         | 0%d  | %X   |\n",&C,C,C);
        printf("| %x  | B         | 0%d  | %X   |\n",&B,B,B);
        printf("| %x  | A         | 0%d  | %X   |\n",&A,A,A);
        printf("+-----------+-----+-----+-----+\n");
}
user@linux:~$ 

user@linux:~$ gcc -g stack.c -o stack ; ./stack 
+-----------+-----+-----+-----+
| Address   | Var | Dec | Hex |
|-----------+-----+-----+-----|
| bfbf7498  | F   | 15  | F   |
| bfbf749c  | E   | 11  | B   |
| bfbf74a0  | D   | 10  | A   |
| bfbf74a4  | C   | 08  | 8   |
| bfbf74a8  | B   | 05  | 5   |
| bfbf74ac  | A   | 03  | 3   |
+-----------+-----+-----+-----+
user@linux:~$ 

2 个答案:

答案 0 :(得分:1)

Why does the stack address grow towards decreasing memory addresses?

这个帖子对你的问题有很好的答案。它也有很好的视觉效果。

https://unix.stackexchange.com/questions/4929/what-are-high-memory-and-low-memory-on-linux?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa

这也是一个很好的解释(但与unix / linux特别相关)

基本上它完全依赖于平台

解释为什么一些堆栈存储器的增长方式不同:

使用了许多不同的方法,具体取决于操作系统(Linux实时与正常)以及下面的语言运行时系统:

1)动态,按页面错误

通常会将一些实际页面预先分配到更高的地址,并将初始sp分配给它。堆栈向下增长,堆向上增长。如果页面错误发生在堆栈底部以下,则会分配和映射丢失的中间页面。自动有效地从顶部向底部增加堆叠。通常存在执行这样的自动分配的最大值,其可以在环境(ulimit),exe-header中指定,也可以不指定,或者由程序通过系统调用(rlimit)动态调整。特别是这种可调整性在不同的OS之间变化很大。通常还有一个限制,即距离&#34;从堆栈底部看,页面错误被认为是正常的并且自动增长发生。请注意,并非所有系统都是&#39;堆栈向下增长:在HPUX下它(使用?)向上增长所以我不确定PA-Risc上的linux是什么(可以有人对此发表评论)。

2)固定大小

其他操作系统(特别是在嵌入式和移动环境中)根据定义具有固定大小,或者在exe标头中指定,或者在创建程序/线程时指定。特别是在嵌入式实时控制器中,这通常是配置参数,并且各个控制任务获得修复堆栈(以避免失控的线程占用更高的prio控制任务的内存)。当然,在这种情况下,内存可能只是虚拟分配,直到真正需要。

3)pagewise,spaghetti和类似的

这种机制往往被遗忘,但仍在某些运行时系统中使用(我知道Lisp / Scheme和Smalltalk系统)。它们根据需要动态分配和增加堆栈。但是,不是作为一个单一的可信区段,而是作为多页块的链接链。它需要由编译器生成不同的函数入口/出口代码,以便处理段边界。因此,这种方案通常由语言支持系统实现,而不是操作系统本身(过去曾经 - 叹气)。原因是,当您在交互式环境中有许多(比如说1000个)线程时,预先分配说1Mb只会填满您的虚拟地址空间而您无法支持以前未知某个线程的线程需求的系统(这是通常是在动态环境中的情况,其中使用可以将eval-code输入到单独的工作空间中)。因此,上面的方案1中的动态分配是不可能的,因为在其他方式中会有其他线程拥有自己的堆栈。堆栈由较小的段(比如8-64k)组成,这些段从池中分配和解除分配并链接到堆栈段链中。对于诸如continuation,coroutines等事物的高性能支持,也可能需要这样的方案。

现代unixes / linuxes和(我猜,但不是100%确定)windows使用方案1)作为你的exe的主线程,2)用于额外的(p-)线程,需要一个修复堆栈大小给定线程创建者最初。大多数嵌入式系统和控制器使用固定(但可配置)的预分配(在许多情况下甚至是物理预分配)。

很抱歉,如果这个答案有点密集,我不确定是否只是提供有效的解释。至于为什么你在C中给出的示例位于顶部的低内存地址,最简单的解释方法是C就是这样构建的。

答案 1 :(得分:0)

目前还不清楚你的问题是什么。 Arjun回答的是为什么堆栈内存增长(减少内存地址)而不是up(增加内存地址),对此的简单回答是它是任意的。它确实无关紧要,但是架构必须选择其中一种 - 通常有cpu指令来操作堆栈,它们期望特定的实现。

您可能会问的另一个可能的问题与来自多个来源的视觉参考有关。在上面的示例中,您有一个图表在顶部显示低地址,而另一个图表在底部显示低地址。它们都显示堆栈从较大地址向下扩展到较小地址。同样,这是任意的,作者需要选择一个或另一个,他们正在与您沟通他们的选择。如果你想并排比较它们,你可能想要翻转一个它们有相似的方向。

顺便说一句,您的示例代码显示堆栈确实从高地址开始并且长大(首先分配&#39; A&#39;的内存地址,并且具有比其他地址更高的内存地址)