我的程序在正常运行时会出现分段错误。但是,如果我使用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 ;
}
答案 0 :(得分:2)
典型的Heisenbug:如果你看它,它会消失。根据我的经验,只在gdb之外获取segv,反之亦然是使用未初始化的内存或依赖于实际指针地址的标志。通常运行valgrind
在检测时非常准确。很遗憾(我的)valgrind
无法在clone
上下文之外处理您的pthread
。
目视检查表明它不是内存问题。只在堆上分配堆栈并且它们的使用看起来没问题。除了你用void *
指针处理它们然后添加一些东西,这在标准-C(GNU扩展)中是不允许的。适当的方法是使用char *
,但GNU扩展可以满足您的需求。
从堆栈的顶部地址中减去一个可能不是必需的,可能会导致clone
的简单实现上的对齐错误,但我再也不认为这是问题,因为{{1}很可能会再次对齐堆栈顶部。诚然,clone
的手册页并不十分清楚地址的确切位置:&#34;内存空间的最顶层地址&#34;。
只是等待一个孩子的状态改变并假设它已经死了有点草率然后带走它的堆栈可能会导致分段错误,但我再也不认为这是问题所在,因为你很可能不要疯狂地向你的哲学家发送信号。
如果我运行你的应用程序,哲学家可以完成他们的餐厅在gdb内外不受干扰,所以以下是一个猜测。让我们称之为克隆哲学家的父进程&#34;表&#34;。克隆一位哲学家后,该表将返回的pid存储在clone
中,比如将该编号分配给一把椅子。哲学家做的第一件事就是找他的椅子。如果他没有找到他的椅子,他将会有一个未初始化的ph
用于访问他的信号量。现在这很可能导致分段错误。
实现假设在哲学家开始之前将控制权返回到表中。我无法在手册页中找到这样的保证,我实际上预计这不是真的。此外,克隆接口可以将进程ID放在子进程和父进程之间共享的内存中,这表明这是一个公认的问题(请参阅参数phno
和pid
)。如果使用这些pid将在表格或刚刚克隆的哲学家获得控制权之前编写。
此错误很可能解释了内部和外部ctid
之间的差异,因为gdb
非常了解在其监督下生成的进程,并且可能与操作系统的处理方式不同
或者,您可以为表分配一个信号量。所以没有人坐在桌旁,直到桌子这么说,显然是在它分配了所有椅子之后。这样可以更好地使用信号量gdb
。
顺便说一句。你当然完全清楚,你的解决方案的设置确实允许所有哲学家最终都有一个叉子(筷子)和饿死等待另一个。幸运的是,发生这种情况的可能性很小。
答案 1 :(得分:1)
ph[i] = clone(philo, stack[i]+STACKSIZE-1, CLONE_VM|SIGCHLD, NULL) ;
这会创建一个执行线程,glibc对此一无所知。因此,glibc不会创建它需要的任何特定于线程的内部结构。动态符号解析。
通过这样的设置,从philo
函数调用任何glibc函数会调用未定义的行为,并且有时会崩溃(因为动态加载器将使用主线程的私有数据来执行符号解析,并且因为加载器假定每个线程都有自己的私有区域,但是你通过创建共享单个私有区域“glibc后面”的clone
来违反这个假设。)
如果您查看核心转储,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