分段故障-为什么以及如何工作

Segmentation fault - why and how does it work?

本文关键字:何工作 工作 故障 为什么 分段      更新时间:2023-10-16

在下面定义的两个函数中,它都试图在堆栈中分配10M内存。但是分割错误只发生在第二种情况下,而不是第一种情况,我正在努力理解为什么会这样

函数定义1:

a(int *i)
{
    char iptr[50000000]; 
    *i = 1;
}

函数定义2:

a()
{
    char c;
    char iptr[5000000];
    printf("&c     = 0x%lx, iptr = 0x%x  ...  ", &c, iptr);
    fflush(stdout); 
    c = iptr[0]; 
    printf("okn");
}

根据我的理解,在局部变量没有动态分配内存的情况下,这些变量存储在程序的堆栈部分。所以我想,在编译时,编译器会检查变量是否适合堆栈。

因此,如果上述是真的,那么在这两种情况下(即,在情况1中)都应该发生分段故障。

网站(http://web.eecs.utk.edu/courses/spring2012/cs360/360/notes/Memory/lecture.html)从我选择的地方可以看出,当代码试图在堆栈上为printf调用推送iptr时,segfault发生在a中的函数2中。这是因为堆栈指针指向的是空白 如果我们没有在堆栈指针处引用任何内容,我们的程序应该已经工作了

我需要帮助理解最后的发言以及我之前对此的怀疑。

所以我想,在编译时,编译器会检查变量是否适合堆栈。

不,那是不可能的。编译函数时,编译器不知道调用函数时的调用堆栈是什么,因此它会假设您知道自己在做什么(可能是这样,也可能不是这样)。还要注意,堆栈空间的大小可能会受到编译时间和运行时限制的影响(在Linux中,您可以在启动进程的shell上使用ulimit设置堆栈大小)。

我需要帮助理解最后的发言以及我之前对此的怀疑。

我不会试图过多地研究该声明,它不是标准,而是基于对特定实现的了解,而这些知识甚至没有在其中描述,因此是建立在一些不一定正确的假设之上的。

它假设分配数组的行为不会"接触"分配的内存(在某些调试构建中,在某些实现中,这是错误的),因此,无论您试图分配1字节还是100M,如果您的程序没有接触到数据,分配都是——事实并非如此。

它还假设函数printf的参数是在堆栈中传递的(由于函数的变参数性质,在我所知道的所有实现中都是这种情况)。根据前面的假设,数组会溢出堆栈(假设堆栈<10M),但不会因为内存未被访问而崩溃,但为了能够调用printf,参数的值将被推送到数组之外的堆栈。这将向内存写入,并且写入将超出为堆栈和崩溃分配的空间。

同样,所有这些都是实现,而不是由语言定义的。

以下代码引发代码错误:

; Find next lower page and probe
cs20:
        sub     eax, _PAGESIZE_         ; decrease by PAGESIZE
        test    dword ptr [eax],eax     ; probe page. "**This line throws the error**"
        jmp     short cs10
_chkstk endp
        end

来自chkstk.asm文件,该文件提供对过程条目的堆栈检查。这个文件明确定义了:

_PAGESIZE_      equ     1000h

现在作为你的问题的解释,这个问题告诉你所需要的一切,如Shafik Yaghmour

所述

您的printf格式字符串假定指针、int(%x)和long(%lx)的大小都相同;这在您的平台上可能是错误的,从而导致未定义的行为。请改用%p。我本想对此发表评论,但现在还不能。

我很惊讶没有人注意到第一个函数分配的空间是第二个函数的10倍。在第一个函数中,5之后有7个零,而第二个函数在5:-之后有6个零。)

我用gcc-4.6.3编译了它,在第一个函数上出现了分段错误,但在第二个函数上没有。在我删除了第一个函数中的额外零之后,seg故障就消失了。在第二个函数中添加一个零引入了seg故障。所以,至少在我的情况下,这个seg错误的原因是程序无法在堆栈上分配所需的空间。我很高兴听到与上述不同的意见。