【问题标题】:Sporadic stack pointer segmentation fault of forked process分叉进程的零星堆栈指针分段错误
【发布时间】:2012-12-01 16:44:50
【问题描述】:

我正在尝试调试一个进程,该进程偶尔会在访问堆栈指针时出现分段错误。

main() 第一行之前用于segfault的进程{ 现在段错误移到了下线的另一个地方。

如果它没有崩溃,那么该进程将无限期地继续正确运行。

我猜测这与进程实例化的方式有关:

pid_t COsAbstraction::Start(const uint32_t coreId, const char* argv[])
{
    pid_t pid;
    sigset_t mask;
    sigset_t save;

    /*
     * Block SIGCHLD
     */
    sigemptyset(&mask);
    sigaddset(&mask, SIGCHLD);
    pthread_sigmask(SIG_BLOCK, &mask, &save);

    pid = fork();
    if(pid < 0)
    {
        _exit(false);
    }
    if (pid != 0)
    {
        // I am the parent
        pthread_sigmask(SIG_UNBLOCK, &save, NULL);
        return pid;;
    }
    // I am the child
    // Replace myself with the desired process
    execvp(argv[ 0 ],  const_cast<char**>(argv))

    _exit(0);

}

崩溃代码(零星):

(gdb) disassemble 
Dump of assembler code for function main:
0x0804c663 <main+0>:    lea    0x4(%esp),%ecx
0x0804c667 <main+4>:    and    $0xfffffff0,%esp
0x0804c66a <main+7>:    pushl  -0x4(%ecx)
0x0804c66d <main+10>:   push   %ebp
0x0804c66e <main+11>:   mov    %esp,%ebp
0x0804c670 <main+13>:   push   %edi
0x0804c671 <main+14>:   push   %esi
0x0804c672 <main+15>:   push   %ebx
0x0804c673 <main+16>:   push   %ecx
0x0804c674 <main+17>:   sub    $0x525a8,%esp
0x0804c67a <main+23>:   call   0x804a22e <__i686.get_pc_thunk.bx>
0x0804c67f <main+28>:   add    $0x6125,%ebx
0x0804c685 <main+34>:   mov    (%ecx),%eax
0x0804c687 <main+36>:   mov    %eax,-0x52578(%ebp)
0x0804c68d <main+42>:   mov    0x4(%ecx),%ecx
0x0804c690 <main+45>:   mov    %ecx,-0x5257c(%ebp)
0x0804c696 <main+51>:   movl   $0x0,-0x418(%ebp)
0x0804c6a0 <main+61>:   movl   $0x400,0x8(%esp)
0x0804c6a8 <main+69>:   movl   $0x0,0x4(%esp)
0x0804c6b0 <main+77>:   lea    -0x410(%ebp),%eax
0x0804c6b6 <main+83>:   mov    %eax,(%esp)
0x0804c6b9 <main+86>:   call   0x8049c4c <memset@plt>

(gdb) info registers 
eax            0xf746d564       -146352796
ecx            0xf746d4e0       -146352928
edx            0xf746d500       -146352896
ebx            0x444a7ff4       1145733108
esp            0xf741af10       0xf741af10
ebp            0xf746d4c8       0xf746d4c8
esi            0x8050b30        134548272
edi            0x8049d90        134520208
eip            0x804c67a        0x804c67a <main+23>
eflags         0x210282 [ SF IF RF ID ]
cs             0x23     35
ss             0x2b     43
ds             0x2b     43
es             0x2b     43
fs             0x0      0

此代码在 24 核 RedHat Linux 2.6 设备上运行。

更新:

原来崩溃与调用进程无关! 简单的“hello world”在 shell 执行数百次时也会崩溃:

#include <stdio.h>
int main(int argc, char* argv[])
{
    char a[100*1024];
    a[0] = '1';
    printf("Hello There!\n");
    return 0;
}

我应该提交一个新问题还是修改这个问题?

【问题讨论】:

  • 那么它在哪一行代码崩溃了?
  • 它在 get_pc_thunk 的 main() 的 { 处崩溃。 eip = 0x804c67a
    。较新的崩溃发生在 movl $0x04, 0x4(%esp) 上。尝试访问该地址的堆栈会导致段错误。有时会在堆栈太短的情况下实例化一个进程。
  • 这对我来说就像坏内存。
  • 或全局范围对象的构造函数,在调用main()之前运行...
  • 所以你的 hello world 程序在从 shell 运行时会崩溃?如果没有大的 char[] 分配,它会这样做吗?显而易见的问题是,如果相同的二进制文件在更适度、兼容的 ABI 机器上运行而不会崩溃,并且您是否在现成的预构建程序中看到崩溃。另外,您是否尝试过不同的编译器版本?

标签: c++ linux process embedded-linux


【解决方案1】:

C 程序崩溃,因为您正在退出程序堆栈。

关键是像这样的变量

char a[100*1024];

在堆栈上分配,它的大小是有限的,当您指向数组的开头时,如果该进程,您将退出堆栈。

举个例子,我只是修改了代码,因为我在amd64上,所以我有

#include <stdio.h>
int main(int argc, char* argv[])
{
    char a[12*1024*1024];
    a[0] = '1';
    printf("Hello There!\n");
    return 0;
}

所以我试图在堆栈中放入一个 12 MB 的变量。如您所见,堆栈的默认大小是 8MB,当我尝试执行它时,我得到了这个:

ottavio@magritte:/tmp$ ulimit -s
8192
ottavio@magritte:/tmp$ ./x 
Segmentation fault

但如果我增加堆栈大小,它会起作用

ottavio@magritte:/tmp$ ulimit -s 16384
ottavio@magritte:/tmp$ ./x 
Hello There!
ottavio@magritte:/tmp$

【讨论】:

  • 默认堆栈大小 8Meg 远高于示例代码中分配的 100K。崩溃在 500 次运行中发生了一两次。如果堆栈太小,它应该每次都崩溃。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-12
  • 2016-01-02
  • 2019-12-05
  • 1970-01-01
  • 2021-09-01
  • 1970-01-01
  • 2015-12-28
相关资源
最近更新 更多