ChatGPT解决这个技术问题 Extra ChatGPT

大多数现代系统中堆栈增长的方向是什么?

我正在准备一些 C 语言培训材料,我希望我的示例适合典型的堆栈模型。

C 堆栈在 Linux、Windows、Mac OSX(PPC 和 x86)、Solaris 和最新的 Unix 中的发展方向是什么?

为什么向下版本:stackoverflow.com/questions/2035568/…

1
15 revs, 4 users 82%

堆栈增长通常不取决于操作系统本身,而是取决于运行它的处理器。例如,Solaris 在 x86 和 SPARC 上运行。 Mac OSX(如您所述)在 PPC 和 x86 上运行。 Linux 可以运行在从我的工作中的大型 honkin' System z 到 puny little wristwatch 的所有设备上。

如果 CPU 提供任何类型的选择,操作系统使用的 ABI / 调用约定指定如果您希望您的代码调用其他所有人的代码,您需要做出哪种选择。

处理器及其方向是:

x86:下降。

SPARC:可选。标准 ABI 使用羽绒。

PPC:下来,我想。

System z:在链表中,我没有骗你(但仍然下降,至少对于 zLinux)。

ARM:可选,但 Thumb2 仅对向下进行紧凑编码(LDMIA = 之后递增,STMDB = 之前递减)。

6502:下降(但只有 256 个字节)。

RCA 1802A:任何你想要的方式,取决于 SCRT 的实施。

PDP11:下降。

8051:上。

在最后几台上显示我的年龄,1802 是用于控制早期穿梭机的芯片(我怀疑,根据它的处理能力来感应门是否打开 :-) 和我的第二台计算机 COMX-35(按照我的ZX80)。

here 收集的 PDP11 详细信息,从 here 收集的 8051 详细信息。

SPARC 体系结构使用滑动窗口寄存器模型。架构上可见的细节还包括寄存器窗口的循环缓冲区,这些缓冲区在内部有效并缓存,当溢出/下溢时会出现陷阱。有关详细信息,请参阅 here。作为 the SPARCv8 manual explains,SAVE 和 RESTORE 指令类似于 ADD 指令加上寄存器窗口轮换。使用正常数而不是通常的负常数会产生向上增长的堆栈。

上述 SCRT 技术是另一种 - 1802 使用了一些或它的 16 个 16 位寄存器用于 SCRT(标准调用和返回技术)。一个是程序计数器,您可以通过 SEP Rn 指令将任何寄存器用作 PC。一个是堆栈指针,两个被设置为始终指向 SCRT 代码地址,一个用于调用,一个用于返回。 没有寄存器被以特殊方式处理。请记住,这些细节来自记忆,它们可能并不完全正确。

例如,如果 R3 是 PC,R4 是 SCRT 调用地址,R5 是 SCRT 返回地址,R2 是“堆栈”(引用,因为它是在软件中实现的),SEP R4 会将 R4 设置为 PC 并开始运行 SCRT 调用代码。

然后它将 R3 存储在 R2“堆栈”上(我认为 R6 用于临时存储),向上或向下调整它,抓取 R3 后面的两个字节,将它们加载到 R3,然后执行 { 1} 并在新地址运行。

要返回,它将 SEP R5 从 R2 堆栈中拉出旧地址,向其添加两个(以跳过调用的地址字节),将其加载到 R3 和 SEP R3 以开始运行先前的代码。

在所有基于 6502/6809/z80 堆栈的代码之后,最初很难绕开你的脑袋,但仍然以一种撞墙的方式优雅。此外,该芯片的一大卖点是一整套 16 个 16 位寄存器,尽管您立即丢失了其中的 7 个(5 个用于 SCRT,2 个用于 DMA 和内存中断)。啊,营销对现实的胜利:-)

System z 实际上非常相似,使用它的 R14 和 R15 寄存器进行调用/返回。


要添加到列表中,ARM 可以在任一方向上增长,但可以通过特定的硅实现设置为一个或另一个(或者可以通过软件进行选择)。我处理过的少数人一直处于成长模式。
到目前为止,在我所看到的 ARM 世界的一小部分(ARM7TDMI)中,堆栈完全由软件处理。返回地址存储在一个寄存器中,如果需要,该寄存器由软件保存,并且前/后递增/递减指令允许将它和其他东西以任一方向放在堆栈上。
一个HPPA,堆栈长大了!在相当现代的建筑中相当罕见。
对于好奇的人,这里有一个关于堆栈如何在 z/OS 上工作的好资源:www-03.ibm.com/systems/resources/Stack+and+Heap.pdf
我不确定我对 a stack grows down 的表述是什么(例如,它向东增长)。 “向下”是否意味着堆栈指针的值随着 push 操作而减小并随着 pop 操作而增加?
p
phuclv

在 C++ 中(适用于 C)stack.cc

static int
find_stack_direction ()
{
    static char *addr = 0;
    auto char dummy;
    if (addr == 0)
    {
        addr = &dummy;
        return find_stack_direction ();
    }
    else
    {
        return ((&dummy > addr) ? 1 : -1);
    }
}

哇,好久没看到“auto”关键字了。
(&dummy > addr) 未定义。仅当两个指针指向同一数组或结构时,才定义将两个指针提供给关系运算符的结果。
试图调查你自己的堆栈的布局——C/C++ 根本没有指定的东西——一开始是“不可移植的”,所以我真的不在乎。不过,看起来这个功能只能正常工作一次。
无需为此使用 static。相反,您可以将地址作为参数传递给递归调用。
另外,通过使用 static,如果您多次调用它,后续调用可能会失败......
m
mP.

向下增长的优势在于,在旧系统中,堆栈通常位于内存的顶部。程序通常从底部开始填充内存,因此这种内存管理最大限度地减少了测量堆栈底部并将其放置在合理位置的需要。


不是“优势”,而是重言式。
不是重言式。正如@valenok 指出的那样,关键是要有两个不断增长的内存区域不会干扰(除非内存已满)。
M
Michael

堆栈在 x86 上向下增长(由架构定义,pop 递增堆栈指针,push 递减。)


s
sh-

只是对其他答案的一个小补充,据我所知,这还没有触及这一点:

让堆栈向下增长使堆栈内的所有地址都具有相对于堆栈指针的正偏移量。不需要负偏移,因为它们只会指向未使用的堆栈空间。当处理器支持堆栈指针相对寻址时,这简化了访问堆栈位置。

许多处理器的指令允许使用相对于某个寄存器的仅正偏移量进行访问。其中包括许多现代建筑,以及一些古老的建筑。例如,ARM Thumb ABI 为堆栈指针相关访问提供了在单个 16 位指令字中编码的正偏移量。

如果堆栈向上增长,所有相对于堆栈指针的有用偏移量都将为负数,这不太直观且不太方便。它也与寄存器相对寻址的其他应用程序不一致,例如访问结构的字段。


p
phuclv

在 MIPS 和许多现代 RISC architectures(如 PowerPC、RISC-V、SPARC...)中,没有 pushpop 指令。这些操作是通过手动调整堆栈指针显式完成的,然后相对于调整后的指针加载/存储值。所有寄存器(除了零寄存器)都是通用的,所以理论上任何寄存器都可以是堆栈指针,堆栈可以在程序员想要的任何方向增长

也就是说,堆栈通常在大多数体系结构上增长,可能是为了避免堆栈和程序数据或堆数据增长并相互冲突的情况。还有提到的重要寻址原因sh-'s answer。一些例子:MIPS ABI 向下增长并使用 $29 (AKA $sp) 作为堆栈指针,RISC-V ABI 也向下增长并使用 x2 作为堆栈指针

在 Intel 8051 中,堆栈增长,可能是因为内存空间非常小(原始版本为 128 字节)以至于没有堆,并且您不需要将堆栈放在顶部,以便将其与堆增长分开从底部

您可以在 https://en.wikipedia.org/wiki/Calling_convention 中找到有关各种架构中堆栈使用的更多信息

也可以看看

为什么堆栈向下增长?

堆栈向下增长有什么好处?

为什么堆栈通常会向下增长?

堆栈是向上还是向下增长?


C
Community

在大多数系统上,堆栈会增长,我在 https://gist.github.com/cpq/8598782 的文章解释了它增长的原因。很简单:如何在一块固定的内存中布局两个不断增长的内存块(堆和栈)?最好的解决方案是把它们放在相反的两端,让它们相互生长。


那个要点现在似乎已经死了:(
@Ven - 我可以做到
K
Kai

它会增长,因为分配给程序的内存有“永久数据”,即程序本身的代码在底部,然后是堆在中间。您需要另一个固定点来引用堆栈,这样您就可以在顶部。这意味着堆栈会向下增长,直到它可能与堆上的对象相邻。


P
PSkocik

这个宏应该在没有 UB 的情况下在运行时检测到它:

#define stk_grows_up_eh() stk_grows_up__(&(char){0})
_Bool stk_grows_up__(char *ParentsLocal);

__attribute((__noinline__))
_Bool stk_grows_up__(char *ParentsLocal) { 
    return (uintptr_t)ParentsLocal < (uintptr_t)&ParentsLocal;
}