可以在gdb中运行,直接运行时分段错误

问题描述:

我的程序在我正常运行时出现分段错误。然而,如果我使用gdb run,它工作得很好。此外,当我增加philo函数中的睡眠时间时,分段故障的比例增加。我使用的是Ubuntu 12.04。任何帮助或指点表示赞赏。这里是我的代码可以在gdb中运行,直接运行时分段错误

#define _GNU_SOURCE 

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <sched.h> 

#include <signal.h> 
#include <sys/wait.h> 
#include <time.h> 
#include <semaphore.h> 
#include <errno.h> 

#define STACKSIZE 10000 
#define NUMPROCS 5 
#define ROUNDS 10 

int ph[NUMPROCS]; 
//cs[i] is the chopstick between philosopher i and i+1 
sem_t cs[NUMPROCS], dead; 

int philo() { 
    int i = 0; 
    int cpid = getpid(); 
    int phno; 

    for (i=0; i<NUMPROCS; i++) 
     if(ph[i] == cpid) phno = i; 

    for (i=0; i < ROUNDS ; i++){ 
     // Add your entry protocol here 
     if (sem_wait(&dead) != 0) { 
      perror(NULL); 
      return 1; 
     } 
     if (sem_wait(&cs[phno]) != 0) { 
      perror(NULL); 
      return 1; 
     } 
     if (sem_wait(&cs[(phno-1+NUMPROCS) % NUMPROCS]) != 0){ 
      perror(NULL); 
      return 1; 
     } 

     // Start of critical section -- simulation of slow n++ 
     int sleeptime = 20000 + rand()%50000; 
     printf("philosopher %d is eating by chopsticks %d and %d\n",phno,phno,(phno-1+NUMPROCS)%NUMPROCS); 
     usleep(sleeptime) ; 
     // End of critical section 

     // Add your exit protocol here 
     if (sem_post(&dead) != 0) { 
      perror(NULL); 
      return 1; 
     } 
     if (sem_post(&cs[phno]) != 0) { 
      perror(NULL); 
      return 1; 
     } 
     if (sem_post(&cs[(phno-1+NUMPROCS) % NUMPROCS]) != 0){ 
      perror(NULL); 
      return 1; 
     } 
    } 
    return 0; 
} 

int main(int argc, char ** argv){ 
    int i; 
    void* stack[NUMPROCS]; 
    srand(time(NULL)); 

    //initialize semaphores 
    for (i=0; i<NUMPROCS; i++) { 
     if (sem_init(&cs[i],1,1) != 0){ 
      perror(NULL); 
      return 1; 
     } 
    } 
    if (sem_init(&dead,1,4) != 0){ 
     perror(NULL); 
     return 1; 
    } 

    for (i = 0; i < NUMPROCS; i++){ 
     stack[i] = malloc(STACKSIZE) ; 
     if (stack[i] == NULL) { 
      printf("Error allocating memory\n") ; 
      exit(1) ; 
     } 

     // create a child that shares the data segment 
     ph[i] = clone(philo, stack[i]+STACKSIZE-1, CLONE_VM|SIGCHLD, NULL) ; 
     if (ph[i] < 0) { 
      perror(NULL) ; 
      return 1; 
     } 
    } 

    for (i=0; i < NUMPROCS; i++) wait(NULL); 
    for (i=0; i < NUMPROCS; i++) free(stack[i]); 

    return 0 ; 
} 
+0

如果你建立与'-static'程序是否仍然崩溃?我在第一次通过PLT调用(即'gettpid()')期间观察到子进程中的glibc动态加载器符号解析崩溃了。 – scottt 2013-03-12 19:59:40

一个典型的海森堡:如果你看它,它会消失。根据我的经验,仅在gdb之外获取segv或反之亦然是使用未初始化的内存或依赖实际指针地址的标志。正常运行valgrind无情准确地检测这些。不幸的是(我的)valgrind无法在pthread上下文之外处理您的clone

视觉检查表明它不是内存问题。只有堆被分配在堆上,并且它们的使用看起来不错。除了你用void *指针对待它们,然后添加一些内容,这在标准C(一个GNU扩展)中是不允许的。适当的将使用char *,但GNU扩展做你想要的。

从堆栈的顶部地址减去一个可能是没有必要的,并可能导致对clone简单实现定位错误,但同样,我不认为这是问题,因为clone极有可能会再次对准栈顶。无可否认,clone的手册页并不十分清楚地址的确切位置:“内存空间的最高地址”。

只是等待孩子的状态改变,并假设它死了有点草率,然后拿走它的堆栈可能会导致分段错误,但我不认为这是问题,因为你可能不是疯狂地发送信号给你的哲学家。

如果我运行你的应用程序,哲学家可以完成他们的晚餐,不受干扰gdb内部和外部,所以以下是猜测。我们称之为克隆哲学家“桌子”的父母过程。一旦哲学家被克隆,表格会将返回的pid存储在ph中,请将该号码分配给椅子。哲学家所做的第一件事就是寻找他的椅子。如果他没有找到他的椅子,他将有一个未初始化的phno,用于访问他的信号灯。现在这可能会导致分段错误。

该实现假设在哲学家开始之前将控制权返回给表。我无法在手册页中找到这样的保证,我真的希望这不是真的。此外,克隆接口可能会将进程标识放置在孩子和父级之间共享的内存中,这表明这是一个公认的问题(请参阅参数pidctid)。如果使用这些,则在表或刚克隆的哲学家得到控制之前写入pid。

由于gdb非常了解在其监督下产生的进程,并且可能会以不同于操作系统的方式对待它们,所以此错误很有可能解释了内部和外部的差异gdb

或者,您可以为表分配一个信号量。所以没有人坐在桌子旁边,直到桌子说出来,显然是在分配所有椅子之后。这对于信号量dead会更好。

顺便说一句。你当然完全意识到,你的解决方案的设置确实适用于所有哲学家最终都有一个叉子(筷子)并饿死等待另一个叉子的情况。幸运的是,发生这种事情的机会非常渺茫。

ph[i] = clone(philo, stack[i]+STACKSIZE-1, CLONE_VM|SIGCHLD, NULL) ; 

这将创建一个执行线程,它的glibc一无所知。因此,glibc不会创建它需要的任何线程特定的内部结构,例如,动态符号分辨率。

使用这种设置,调用philo函数中的任何glibc函数都会调用未定义的行为,并且有时会崩溃(因为动态加载器将使用主线程的私有数据执行符号解析,并且由于加载程序假定每个线程都有它自己的私人区域,但是你违反了这个假设,创建clone s共享单个私人区域“在glibc的背后”)。

如果你看一个核心转储,很有可能在ld.so发生实际崩溃,这将确认我的猜测。

不要有没有直接使用clone(除非你知道你在做什么)。改为使用pthread_create

以下是我在我刚刚得到了核心看到(这正是我所描述的问题):

Program terminated with signal 4, Illegal instruction. 
#0 _dl_x86_64_restore_sse() at ../sysdeps/x86_64/dl-trampoline.S:239 
239    vmovdqa %fs:RTLD_SAVESPACE_SSE+0*YMM_SIZE, %ymm0 
(gdb) bt 
#0 _dl_x86_64_restore_sse() at ../sysdeps/x86_64/dl-trampoline.S:239 
#1 0x00007fb694e1dc45 in _dl_fixup (l=<optimized out>, reloc_arg=<optimized out>) at ../elf/dl-runtime.c:127 
#2 0x00007fb694e0dee5 in _dl_runtime_resolve() at ../sysdeps/x86_64/dl-trampoline.S:42 
#3 0x00000000004009ec in philo() 
#4 0x00007fb69486669d in clone() at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112