每个程序分配一个固定的堆栈大小?谁为每个运行的应用程序定义堆栈内存量? [英] Each program allocates a fixed stack size? Who defines the amount of stack memory for each application running?

查看:0
本文介绍了每个程序分配一个固定的堆栈大小?谁为每个运行的应用程序定义堆栈内存量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我们运行代码时,编译后的编译器会检测所需的堆栈内存量吗?因此,每个程序都有自己的堆栈内存。

还是每个程序的堆栈内存由操作系统定义?

谁为每个运行的应用程序定义堆栈内存量?

或者我们没有此功能,并且每个程序都可以根据需要使用所有堆栈内存?

推荐答案

在x86-64Linux上,堆栈默认分配8MB。点击此处浏览Ciro Santilli对x86 Linux内存布局的回答:Where is the stack memory allocated from for a Linux process?

例如,您可以拥有如下内容:

Content                       Virtual address
_______________________________________________________________________

----------------------------- 0xFFFF_FFFF_FFFF_FFFF
Kernel
----------------------------- 0xFFFF_8000_0000_0000
Unavailable due to the canonical address requirement (unless 5 lvl paging)
----------------------------- 0x0000_8000_0000_0000
Stack grows downward from the top here
v v v v v v v v v
Maximum stack size is here
----------------------------
Process
----------------------------- 0x400000

有关不可用部分,请参阅Peter Corde的答案:Why does QEMU return the wrong addresses when filling the higher half of the PML4?

本身,加载器不必读取堆栈大小的可执行文件。堆栈大小通常不存储在ELF文件中。操作系统只是假定默认堆栈大小足以满足大多数程序。

您似乎误解了分配堆栈空间意味着什么。堆栈是在编译期间分配的。它是通过功能所需空间的RSP减去来分配的。当进程进入函数(包括Main)时,它将:

  1. 在堆栈上推送RBP;

  2. 将RSP放入RBP;

  3. 减去为函数分配的堆栈空间的RSP。

步骤3为函数在其分配的堆栈空间内工作扫清了道路。在这3个步骤之后,通过使用RBP的相对负偏移量来访问堆栈。我有一个最近删除的答案,它具体对应于这个问题,所以我将它的文本复制到这里:

局部变量在堆栈上分配。内存是为您在运行时使用系统调用使用new初始化的变量/对象分配的。局部变量使用RBP的负相对偏移量访问,全局变量使用RIP的相对偏移量访问(默认情况下)。

我必须研究一下它是如何工作的,因为我一直在编写x86-64操作系统,为了继续我的开发,我必须了解这些东西。

现在,对于初学者来说,这很令人困惑,所以让我们来看一个具体的例子来说明这意味着什么。创建一个main.cpp文件并将以下内容放入其中:

int global_variable = 3;

void func(){
    int local_variable = 10;
    global_variable = 10;
    local_variable++;
}

int main(){
    int local_variable = 4;
    global_variable = 5;
    local_variable += 4;
    func();
    return 0;
}

使用以下命令进行编译:

g++ --entry main -static -ffreestanding -nostdlib main.cpp -omain.elf

在这里,我们使用--entry main将条目设置为主函数,并使用-static将代码全部包含在可执行文件中,并使用-nostdlib从代码中删除标准库。这是为了简化objdump -d main.elf(可执行文件的反汇编)的输出,如下所示:

user@user-System-Product-Name:~$ objdump -d main.elf

main.elf:     file format elf64-x86-64


Disassembly of section .text:

0000000000401000 <_Z4funcv>:
  401000:   f3 0f 1e fa             endbr64 
  401004:   55                      push   %rbp
  401005:   48 89 e5                mov    %rsp,%rbp
  401008:   c7 45 fc 0a 00 00 00    movl   $0xa,-0x4(%rbp)
  40100f:   c7 05 e7 2f 00 00 0a    movl   $0xa,0x2fe7(%rip)        # 404000 <global_variable>
  401016:   00 00 00 
  401019:   83 45 fc 01             addl   $0x1,-0x4(%rbp)
  40101d:   90                      nop
  40101e:   5d                      pop    %rbp
  40101f:   c3                      retq   

0000000000401020 <main>:
  401020:   f3 0f 1e fa             endbr64 
  401024:   55                      push   %rbp
  401025:   48 89 e5                mov    %rsp,%rbp
  401028:   48 83 ec 10             sub    $0x10,%rsp
  40102c:   c7 45 fc 04 00 00 00    movl   $0x4,-0x4(%rbp)
  401033:   c7 05 c3 2f 00 00 05    movl   $0x5,0x2fc3(%rip)        # 404000 <global_variable>
  40103a:   00 00 00 
  40103d:   83 45 fc 04             addl   $0x4,-0x4(%rbp)
  401041:   e8 ba ff ff ff          callq  401000 <_Z4funcv>
  401046:   b8 00 00 00 00          mov    $0x0,%eax
  40104b:   c9                      leaveq 
  40104c:   c3                      retq

这里我们看到main函数和func函数去掉了任何不必要的开销以简化示例。当我们在C++中进入一个函数时,代码将在堆栈上推送RBP,将RSP放入RBP中,然后递减为该函数分配的堆栈空间的RSP。此分配的堆栈空间在编译时始终是直接已知的,因为静态分配的变量使用的空间在编译期间始终是已知的。

之后,所有内容要么是相对于RIP的相对偏移量(用于访问全局变量),要么是相对于RBP的负相对偏移量(用于访问局部变量)。特别是,movl $0x4,-0x4(%rbp)行访问名为local_variable的局部变量,并将4放入其中。然后movl $0x5,0x2fc3(%rip)行访问名为global_variable的全局变量并使其变为5。

当您使用new分配变量时,编译器无法在编译时知道分配的大小,因为它是动态分配的变量。因此,内存分配将被编译为将参数放入某些寄存器中,然后使用syscall汇编指令获得一些内存。

其中大部分是动态链接的。这意味着标准库不包括在可执行文件中,而是在可执行文件启动时由动态链接器链接到可执行文件。标准库的功能在库(libstdc++)中定义。此库是共享对象,包含不同C++标准函数(包括new)的所有符号。

当您从C++调用new时,调用动态分配内存的函数的符号将保留在最终可执行文件中。动态加载器将在运行前(在启动时)确定该函数地址(从何处调用以访问该函数)。因为libstdc++是一个可重定位的共享对象,所以函数的位置可以在任何地方。动态加载程序将使用算法确定这一点。

这篇关于每个程序分配一个固定的堆栈大小?谁为每个运行的应用程序定义堆栈内存量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆