【发布时间】:2017-08-17 00:44:42
【问题描述】:
我正在 Debian 操作系统上运行一个带有递归调用的程序。我的堆栈大小是
-s: stack size (kbytes) 8192
据我所知,堆栈大小必须是固定的,并且应该与每次运行时分配给程序的大小相同,除非使用ulimit 显式更改。
递归函数是递减一个给定的数字,直到它达到0。这是用 Rust 编写的。
fn print_till_zero(x: &mut i32) {
*x -= 1;
println!("Variable is {}", *x);
while *x != 0 {
print_till_zero(x);
}
}
并且值被传递为
static mut Y: i32 = 999999999;
unsafe {
print_till_zero(&mut Y);
}
由于分配给程序的堆栈是固定的,理论上不能改变,我希望每次堆栈溢出的值都相同,但事实并非如此,这意味着堆栈分配是可变的。
运行 1:
====snip====
Variable is 999895412
Variable is 999895411
thread 'main' has overflowed its stack
fatal runtime error: stack overflow
运行 2:
====snip====
Variable is 999895352
Variable is 999895351
thread 'main' has overflowed its stack
fatal runtime error: stack overflow
虽然差异很细微,但理想情况下不应该是在同一个变量上导致堆栈溢出吗?为什么它发生在不同的时间,意味着每次运行的堆栈大小不同?这不是 Rust 特有的;在 C 中观察到类似的行为:
#pragma GCC push_options
#pragma GCC optimize ("O0")
#include<stdio.h>
void rec(int i){
printf("%d,",i);
rec(i-1);
fflush(stdout);
}
int main(){
setbuf(stdout,NULL);
rec(1000000);
}
#pragma GCC pop_options
输出:
运行 1:
738551,738550,[1] 7052 segmentation fault
运行 2:
738438,738437,[1] 7125 segmentation fault
【问题讨论】:
-
只有在堆栈页面错误时才会发生溢出。那是当堆栈指针运行到未加载/无主页面时。堆栈开始的位置不必在确切的页面边界上,但可以取决于程序加载的位置,因此溢出条件(页面错误)触发器会有所不同。
-
this 看起来相似吗?
-
@RichardCritten 那么分配的堆栈大小之外的任何页面都必须是无主页面,对吗?如果我错了,请纠正我。
-
这取决于堆栈的增长方式。它可能会增长到您的程序静态数据或堆中。这将取决于架构和实现细节。例如,实现者可以在堆栈帧的末尾放置一个保护页。我们非常接近 Undefined Behavior is Undefined Behavior,任何解释都需要目标硬件和实现细节。
-
对于你的 Rust 实现,你不需要声明一个 static 可变的。局部变量也可以正常工作,同时也无需使用
unsafe代码。
标签: c linux rust out-of-memory stack-overflow