如果我重复运行这个程序,为什么在seg-fault之前打印的最后一个数字会有所不同?

时间:2017-07-31 14:25:12

标签: c linux operating-system segmentation-fault stack

问题是linux如何处理堆栈。当我遇到运行此代码的分段错误时,为什么不确定?

#include <stdio.h>
#include <stdlib.h>
#include <sys/resource.h>

void step(int n) {
    printf("#%d\n", n);
    step(n + 1);
}

int main() {
    step(1);
    return 0;
}

2 个答案:

答案 0 :(得分:2)

看起来非确定性结果是内核在启动新程序时使用的环境随机化策略的结果。让我们尝试下一个代码:

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>

int main(int argc, char **argv) {
    char c;
    uintptr_t addr = (uintptr_t)&c;
    unsigned pagesize = (unsigned)sysconf(_SC_PAGE_SIZE);
    printf("in-page offset: %u\n", (unsigned)(addr % pagesize));
    return 0;
}

在我的64位Linux上,它提供下一个输出:

$ ./a.out
in-page offset: 3247
$ ./a.out
in-page offset: 2063
$ ./a.out
in-page offset: 863
$ ./a.out
in-page offset: 1871

每次c在其堆栈页面中获得新的偏移量,并且知道内核总是为堆栈分配离散数量的页面 - 很容易看出每次程序的分配堆栈数量略有不同。因此,问题中描述的程序对于每个调用的帧具有非恒定的堆栈量。

坦率地说,我不确定是否内核调整了堆栈指针的初始值,或者它可能是来自动态链接器的一些技巧。无论如何 - 用户代码每次都会在随机环境中运行。

答案 1 :(得分:1)

因为堆栈溢出是未定义的行为。一个实现可以自由地测试它不会发生,在这种情况下,程序应该在堆栈已满时以错误结束。但是环境也可以提供具有取决于空闲内存的大小的堆栈。或者更可能的是,在与io系统的交互中可能会出现各种内存覆盖问题,这可能是非确定性问题。或者......(基本上UB意味着任何事情都可能发生)。