每个程序分配一个固定的堆栈大小?谁为每个运行的应用程序定义堆栈内存量? [英] Each program allocates a fixed stack size? Who defines the amount of stack memory for each application running?
问题描述
还是每个程序的堆栈内存由操作系统定义?
谁为每个运行的应用程序定义堆栈内存量?
或者我们没有此功能,并且每个程序都可以根据需要使用所有堆栈内存?
推荐答案
在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)时,它将:
在堆栈上推送RBP;
将RSP放入RBP;
减去为函数分配的堆栈空间的RSP。
局部变量在堆栈上分配。内存是为您在运行时使用系统调用使用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。此分配的堆栈空间在编译时始终是直接已知的,因为静态分配的变量使用的空间在编译期间始终是已知的。
movl $0x4,-0x4(%rbp)
行访问名为local_variable
的局部变量,并将4放入其中。然后movl $0x5,0x2fc3(%rip)
行访问名为global_variable
的全局变量并使其变为5。
当您使用new分配变量时,编译器无法在编译时知道分配的大小,因为它是动态分配的变量。因此,内存分配将被编译为将参数放入某些寄存器中,然后使用syscall
汇编指令获得一些内存。其中大部分是动态链接的。这意味着标准库不包括在可执行文件中,而是在可执行文件启动时由动态链接器链接到可执行文件。标准库的功能在库(libstdc++)中定义。此库是共享对象,包含不同C++标准函数(包括new)的所有符号。
当您从C++调用new时,调用动态分配内存的函数的符号将保留在最终可执行文件中。动态加载器将在运行前(在启动时)确定该函数地址(从何处调用以访问该函数)。因为libstdc++是一个可重定位的共享对象,所以函数的位置可以在任何地方。动态加载程序将使用算法确定这一点。
这篇关于每个程序分配一个固定的堆栈大小?谁为每个运行的应用程序定义堆栈内存量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!