可在gdb中运行,直接运行时出现分段故障

可在gdb中运行,直接运行时出现分段故障,gdb,segmentation-fault,clone,Gdb,Segmentation Fault,Clone,我的程序在正常运行时出现分段错误。不过,如果我使用gdb run,它就可以正常工作。此外,当我在philo函数中增加睡眠时间时,分段错误的比率会增加。我正在使用ubuntu 12.04。感谢您的帮助和指点。这是我的密码 #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sched.h> #include <sig

我的程序在正常运行时出现分段错误。不过,如果我使用gdb run,它就可以正常工作。此外,当我在philo函数中增加睡眠时间时,分段错误的比率会增加。我正在使用ubuntu 12.04。感谢您的帮助和指点。这是我的密码

#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 ;
}
定义GNU源
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#定义堆栈大小10000
#定义NUMPROCS 5
#定义第10轮
int-ph[NUMPROCS];
//cs[i]是介于哲学家i和i+1之间的筷子
sem_t cs[NUMPROCS],死亡;
int philo(){
int i=0;
int cpid=getpid();
int phno;

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

目视检查表明这不是内存问题。只有堆栈被分配到堆上,它们的使用看起来正常。除非您使用
void*
指针处理它们,然后向其中添加一些东西,这在standard-C(GNU扩展)中是不允许的。正确的做法是使用
char*
,但GNU扩展可以满足您的需要

从堆栈的顶部地址减去一个可能是不必要的,并且可能会导致
clone
的简单实现上的对齐错误,但我也不认为这是问题所在,因为
clone
很可能会再次对齐堆栈顶部。诚然,
clone
的手册页对这一点不是很清楚地址的确切位置:“内存空间的最高地址”

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

如果我运行您的应用程序,哲学家可以在gdb内外不受干扰地完成他们的用餐,因此下面是一个猜测。让我们将克隆哲学家的父进程称为“表”。一旦克隆了哲学家,表将返回的pid存储在
ph
中,比如将该数字分配给椅子。哲学家做的第一件事就是寻找他的椅子。如果他没有找到他的椅子,他将有一个未初始化的
phno
,用于访问他的信号量。现在这很可能会导致分段错误。

该实现假设在哲学家开始之前将控制权返回到表中。我在手册页中找不到这样的保证,实际上我希望这不是真的。克隆接口还可以将进程ID放在子进程和父进程之间共享的内存中,这表明这是一个公认的进程ID错误(请参阅参数
pid
ctid
)。如果使用这些参数,则pid将在表格或刚克隆的系统获得控制之前写入

此错误极有可能解释了内部和外部
gdb
之间的差异,因为
gdb
非常清楚在其监督下生成的进程,并且可能会以与操作系统不同的方式对待它们

或者,您可以为桌子分配一个信号灯。因此,在桌子上没有人坐在桌子上,直到桌子上说了这句话,显然是在分配了所有的椅子之后。这将更好地使用信号灯
dead

顺便说一句,你当然完全知道,你的解决方案的设置确实允许所有哲学家最终每人都有一把叉子(呃筷子),然后等着另一把叉子饿死。幸运的是,发生这种情况的可能性非常小

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

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

如果您查看一个内核转储,实际崩溃很有可能发生在
ld.so
中,这将证实我的猜测

不要直接使用
clone
(除非您知道自己在做什么)。请改用
pthread\u create

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

程序以信号4终止,指令非法。
#0_dl_x86_64_restore_sse(),位于../sysdeps/x86_64/dl trampoline.S:239
239 vmovdqa%fs:RTLD_存储空间_SSE+0*YMM_大小,%ymm0
(gdb)英国电信
#0_dl_x86_64_restore_sse(),位于../sysdeps/x86_64/dl trampoline.S:239
#1 0x00007fb694e1dc45位于../elf/dl运行时的_dl_fixup(l=,reloc_arg=)中。c:127
#2 0x00007fb694e0dee5在../sysdeps/x86_64/dl trampoline.S:42的运行时解析()中
#3 0x00000000004009ec(单位:菲律宾)
#位于../sysdeps/unix/sysv/linux/x86_64/clone.S:112的克隆()中的4 0x00007fb69486669d

如果您使用
-static
构建程序,它还会崩溃吗?在通过PLT的第一次调用期间,我在子进程中的glibc动态加载程序符号解析中观察到这种崩溃(即
getpid()
ph[i] = clone(philo, stack[i]+STACKSIZE-1, CLONE_VM|SIGCHLD, NULL) ;
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