在这种特定情况下,为什么在堆栈上分配大元素不会失败? [英] Why does allocating a large element on the stack not fail in this specific case?

查看:91
本文介绍了在这种特定情况下,为什么在堆栈上分配大元素不会失败?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在C中的堆栈上分配 int 以及大型数组时,程序执行无错误。但是,如果我事先在堆栈上初始化变量,则它会因段错误而崩溃(可能是因为大型数组超出了堆栈的大小)。如果在声明数组后初始化变量,这对我来说很有意义。
是什么原因导致这种行为,从内存角度来看?



给我的印象是,只需在堆栈上声明一个变量,就可以分配所需的空间,从而在分配非常大的数据类型时立即崩溃。 / p>

我怀疑它与编译器对其进行优化有关,但考虑到我没有更改 foo <,这没有任何意义。 / code>在第二个示例中也是如此。



我正在使用 gcc 7.2.0 进行编译,没有任何标志设置。在Ubuntu 17.10上执行。



运行没有错误:

  int main(){
int i;
unsigned char foo [1024 * 1024 * 1024];
返回0;
}

而这立即崩溃:

  int main(){
int i = 0;
unsigned char foo [1024 * 1024 * 1024];
返回0;
}

有人可以给我一些见解吗?

解决方案

注意:以下是实现细节。 C标准不涵盖此问题。



崩溃不是由分配空间引起的。崩溃是由写入不可写的页面或从不可读的页面读取引起的。



您可以看到声明实际上并不需要读取或不一定写任何内存:

  int i; 

但是如果已初始化,则必须输入值:

  int i = 0; 

这会触发崩溃。请注意,确切的行为将取决于您使用的编译器和您拥有的优化设置。不同的编译器将以不同的方式分配变量,而优化的编译器通常会从函数中完全删除 i foo ,因为不需要它们。一些编译器还会在某些配置下将变量初始化为垃圾值,以帮助调试。



分配堆栈空间仅涉及更改堆栈指针(即寄存器)。如果分配过多的堆栈空间,则堆栈指针将指向无效的内存区域,并且程序在尝试读取或写入这些地址时将发生段错误。大多数操作系统都有保护页,因此有效内存不会放置在堆栈旁边,以确保程序在大多数情况下都能成功崩溃。



以下是一些输出 Godbolt

  main:
push rbp
mov rbp,rsp
sub rsp,1073741720;为本地人分配空间
mov DWORD PTR [rbp-4],0;初始化i = 0
mov eax,0;返回值= 0
离开
ret

请注意,此版本不会崩溃,因为 i 放置在堆栈的顶部(向下递增)。如果将 i 放在堆栈的底部,则可能会崩溃。编译器可以自由将变量按任何顺序放入堆栈,因此它是否真正崩溃将在很大程度上取决于您使用的特定编译器。



您还可以看到更清楚地说明分配为何不会崩溃:

 ;只是一个整数减法。为什么会崩溃? 
sub rsp 1073741720


When allocating a int as well as a large array on the stack in C, the program executes without error. If I however, initialize the variable on the stack beforehand, it crashes with a segfault (probably because the stack size was exceeded by the large array). If initializing the variable after declaring the array this would make sense to me. What causes this behavior, memory wise?

I was under the impression, that by simply declaring a variable on the stack, the needed space would be allocated, leading to an immediate crash when allocating very large datatypes.

My suspicion is that it has something to do with the compiler optimizing it away, but it does not make sense, considering I am not changing foo in the second example either.

I am using gcc 7.2.0 to compile, without any flags set. Executed on Ubuntu 17.10.

This runs without errors:

int main(){
  int i;
  unsigned char foo [1024*1024*1024];
  return 0;
}

while this crashes immediately:

int main(){
  int i = 0;
  unsigned char foo [1024*1024*1024];
  return 0;
}

Can somebody give me some insight what is happening here?

解决方案

Note: What follows are implementation details. The C standard does not cover this.

The crash is not caused by allocating space. The crash is caused by writing to pages which are not writable, or reading from pages which are not readable.

You can see that a declaration doesn't actually need to read or write any memory, not necessarily:

int i;

But if it is initialized, you have to write the value:

int i = 0;

This triggers the crash. Note that the exact behavior will depend on the compiler you use and the optimization settings you have. Different compilers will allocate variables in different ways, and an optimizing compiler will normally remove both i and foo from the function entirely, since they aren't needed. Some compilers will also initialize variables to garbage values under certain configurations, to aid with debugging.

Allocating stack space just involves changing the stack pointer, which is a register. If you allocate too much stack space, the stack pointer will point to an invalid region of memory, and the program will segfault when it tries to read or write to those addresses. Most operating systems have "guard pages" so valid memory will not be placed next to the stack, ensuring that the program successfully crashes in most scenarios.

Here is some output from Godbolt:

main:
  push rbp
  mov rbp, rsp
  sub rsp, 1073741720        ; allocate space for locals
  mov DWORD PTR [rbp-4], 0   ; initialize i = 0
  mov eax, 0                 ; return value = 0
  leave
  ret

Note that this version does not crash, because i is placed at the top of the stack (which grows downwards). If i is placed at the bottom of the stack, this will likely crash. The compiler is free to put the variables on the stack in any order, so whether it actually crashes will depend heavily on the specific compiler you are using.

You can also see more clearly why the allocation won't crash:

; Just an integer subtraction. Why would it crash?
sub rsp 1073741720

这篇关于在这种特定情况下,为什么在堆栈上分配大元素不会失败?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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