【问题标题】:SegFault when using pthreads使用 pthread 时的 SegFault
【发布时间】:2015-11-22 09:47:32
【问题描述】:

我正在尝试为我的操作系统类开发一个项目,并且在使用 pthread 时遇到了 SegFault,但我不确定是什么导致了问题。

至于程序,我正在尝试完成以下程序:

南非克鲁格国家公园某处有一个深峡谷,一根绳索横跨峡谷。 狒狒可以通过在绳索上双手交叉摆动来穿越峡谷,但如果两只相反方向的狒狒在中间相遇,它们会打架并摔死。此外,绳索的强度仅足以容纳三只狒狒。如果绳子上同时有更多的狒狒,它就会断裂。假设我们可以教狒狒使用信号量,我们想设计一个具有以下属性的同步方案。

  1. 一旦狒狒开始穿越,它就可以保证到达另一边而不会碰到另一边的狒狒。
  2. 绳子上的狒狒永远不会超过三只。狒狒穿越绳索的顺序应保持;即他们进入绳索的顺序应该是他们离开绳索的顺序。
  3. 持续不断的狒狒流向一个方向穿越不应无限期地阻止狒狒向另一方向前进(不会挨饿)。解决此要求以保留 FIFO 顺序。也就是说,试图向左/向右穿越的狒狒比试图向相反方向穿越的狒狒先到达绳索。

基本上,我正在读取一个文本文件,然后模拟一个 FIFO 系统,其中一些猴子正试图穿过索桥。 奇怪的是我能够让程序运行几次,但它经常会导致 SegFault。

pthread_create(&eastern[i],NULL,(void *) &east_side,(void *)&id[i]);
pthread_create(&western[i],NULL,(void *) &west_side,(void *)&id[i]);

east_side 和 west_side 位于下方。

void* east_side(void*arg)            
{
    int baboon = *(int*)arg;
    int on_rope;
    sem_wait(&deadlock_protection);
    sem_wait(&east_mutex);
    east++;
    if (east == 1)
    {
        sem_wait(&rope);
        printf("Baboon %d: waiting\n", baboon);
    }
    sem_post(&east_mutex);
    sem_post(&deadlock_protection);
    sem_wait(&counter);
    sem_getvalue(&counter, &on_rope);
    printf("Baboon %d: Cross rope request granted (Current crossing: left to right, Number of baboons on rope: %d)\n", baboon,3-on_rope);
    sleep(travel_time);
    sem_getvalue(&counter, &on_rope);
    printf("Baboon %d: Exit rope (Current crossing: left to right, Number of baboons on rope: %d)\n", baboon, 2-on_rope);
    sem_post(&counter);
    sem_wait(&east_mutex);
    east--;
    if (east == 0)
        sem_post(&rope);
    sem_post(&east_mutex);
}

//thread handling west to east travel
void* west_side(void*arg)    
{
    int baboon = *(int*)arg;
    int on_rope;
    sem_wait(&deadlock_protection);
    sem_wait(&west_mutex);
    west++;
    if (west == 1)
    {
        sem_wait(&rope);
        printf("Baboon %d: waiting\n", baboon);
    }
    sem_post(&west_mutex);
    sem_post(&deadlock_protection);
    sem_wait(&counter);
    sem_getvalue(&counter, &on_rope);
    printf("Baboon %d: Cross rope request granted (Current crossing: right to left, Number of baboons on rope: %d)\n", baboon, 3-on_rope);
    sleep(travel_time);
    sem_getvalue(&counter, &on_rope);
    printf("Baboon %d: Exit rope (Current crossing: right to left, Number of baboons on rope: %d)\n", baboon, 2-on_rope);
    sem_post(&counter);
    sem_wait(&west_mutex);
    west--;
    if (west == 0)
        sem_post(&rope);
    sem_post(&west_mutex);

}

我在

的纯文本文件中使用示例输入
L,R,R,R,R,R,L,L,R

这将创建输出:

sh-4.3$ 主输入.txt 5
输入是
L R R R R R L L R
狒狒 1:请求越过绳索(从左到右)
狒狒 1:等待
狒狒 1:已批准交叉绳索请求(当前交叉:从左到右,绳索上的狒狒数量:1)
狒狒 2:请求越过绳索(从右到左)
狒狒 3:请求越过绳索(从右到左)
狒狒 4:请求越过绳索(从右到左)
狒狒 5:请求越过绳索(从右到左)
狒狒1:出口绳索(当前穿越:从左到右,绳索上的狒狒数量:0)
狒狒 2:等待
狒狒 2:已批准交叉绳索请求(当前交叉:从右到左,绳索上的狒狒数量:1)
狒狒 3:已批准交叉绳索请求(当前交叉:从右到左,绳索上的狒狒数量:2)
狒狒 4:已批准交叉绳索请求(当前交叉:从右到左,绳索上的狒狒数量:3)
狒狒 6:请求越过绳索(从右到左)
狒狒 7:请求越过绳索(从左到右)
狒狒 8:请求越过绳索(从左到右)
狒狒 9:请求越过绳索(从右到左)
分段错误(核心转储)

我已经包含了整个文件,以防问题实际上不是我认为的问题所在。

/*include header files*/

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <semaphore.h>
//#include <stdbool.h>


//compile with command 
//gcc -o main *.c -lpthread -lrt

/*semaphores*/
sem_t rope;
sem_t east_mutex;
sem_t west_mutex;
sem_t deadlock_protection;
sem_t counter;

/*global variables*/
int east = 0;
int west = 0;
int travel_time;    

/*function prototypes*/
void crossing(int x);
void* east_side(void*);
void* west_side(void*);

/*main function*/
int main(int argc, char *argv[])
{ 

    char c;
    int baboonCnt=0;
    char temp[100];

    sem_init(&rope,0,1);                        //ensure mutual exclusion on rope ownership
    sem_init(&east_mutex,0,1);                  //east side on travel
    sem_init(&west_mutex,0,1);                  //west side on travel
    sem_init(&deadlock_protection,0,1);         //used to prevent deadlocks while using semaphores
    sem_init(&counter,0,3);                     //ensure only 3 baboons are allowed on the rope

    //ensure all input arguements are entered
    if ( argc == 3 )                    
    {
        travel_time = atoi(argv[2]);
        FILE *file;
        int baboonCnt=0;
        if (file = fopen(argv[1], "r") )
        {
            while((c=getc(file))!=EOF)
            {
                if(c == 'L'|| c == 'R')
                {
                    temp[baboonCnt] = c;
                    baboonCnt++;
                }
            }
        }
        else   
        {
            printf("Unable to read data from the input file.");
            return 0;
        }
        printf("The input is\n");
        int j=0;
        for(j;j<baboonCnt;++j)
        {
            printf("%c ",temp[j]);
        }
        printf("\n");
        int id[baboonCnt];
        pthread_t eastern[baboonCnt],western[baboonCnt];
        int i=0;
        for(i;i<baboonCnt;++i)
        {
            sleep(1);
            if(temp[i]=='L')
            {
                id[i] = i+1;
                printf("Baboon %d: Request to cross rope (left to right)\n", i+1);
                pthread_create(&eastern[i],NULL,(void *) &east_side,(void *)&id[i]);
            }
            else if(temp[i]=='R')
            {
                id[i] = i+1;
                printf("Baboon %d: Request to cross rope (right to left)\n", i+1);
                pthread_create(&western[i],NULL,(void *) &west_side,(void *)&id[i]);
            }
        }
        int k=0;
        printf("before k loop");
        for(k;k<baboonCnt;++k)
        {

            pthread_join(eastern[k],NULL);
            printf("eastern",k);
            pthread_join(western[k],NULL); 
            printf("western %d",k);         
        }

        //destroy all semaphores
        sem_destroy (&rope); 
        sem_destroy (&east_mutex);
        sem_destroy (&west_mutex);
        sem_destroy (&deadlock_protection);
        sem_destroy (&counter);
        return 0;
    }
    else
    {
        printf("Proper command line usage is: \n<name> <filename> <cross time>\n");
    }
}
//thread handling the east to west to travel
void* east_side(void*arg)            
{
    int baboon = *(int*)arg;
    int on_rope;
    sem_wait(&deadlock_protection);
    sem_wait(&east_mutex);
    east++;
    if (east == 1)
    {
        sem_wait(&rope);
        printf("Baboon %d: waiting\n", baboon);
    }
    sem_post(&east_mutex);
    sem_post(&deadlock_protection);
    sem_wait(&counter);
    sem_getvalue(&counter, &on_rope);
    printf("Baboon %d: Cross rope request granted (Current crossing: left to right, Number of baboons on rope: %d)\n", baboon,3-on_rope);
    sleep(travel_time);
    sem_getvalue(&counter, &on_rope);
    printf("Baboon %d: Exit rope (Current crossing: left to right, Number of baboons on rope: %d)\n", baboon, 2-on_rope);
    sem_post(&counter);
    sem_wait(&east_mutex);
    east--;
    if (east == 0)
        sem_post(&rope);
    sem_post(&east_mutex);
}

//thread handling west to east travel
void* west_side(void*arg)    
{
    int baboon = *(int*)arg;
    int on_rope;
    sem_wait(&deadlock_protection);
    sem_wait(&west_mutex);
    west++;
    if (west == 1)
    {
        sem_wait(&rope);
        printf("Baboon %d: waiting\n", baboon);
    }
    sem_post(&west_mutex);
    sem_post(&deadlock_protection);
    sem_wait(&counter);
    sem_getvalue(&counter, &on_rope);
    printf("Baboon %d: Cross rope request granted (Current crossing: right to left, Number of baboons on rope: %d)\n", baboon, 3-on_rope);
    sleep(travel_time);
    sem_getvalue(&counter, &on_rope);
    printf("Baboon %d: Exit rope (Current crossing: right to left, Number of baboons on rope: %d)\n", baboon, 2-on_rope);
    sem_post(&counter);
    sem_wait(&west_mutex);
    west--;
    if (west == 0)
        sem_post(&rope);
    sem_post(&west_mutex);
}

【问题讨论】:

  • 调试 segfaults 时,gdb(或类似的调试器)绝对是无价之宝,而且学习起来并不难——还有很多可用的教程:google.com/search?q=gdb+tutorial 还有——你会通过发布最小、完整且可验证的示例可能会得到更好的答案 - 更多信息请点击此处:stackoverflow.com/help/mcve
  • +1 获得有关使用调试器的建议 - 从长远来看,如果您自己做硬码而不是求助于基本调试,您将学到更多。如果您已经用尽了调试方法,那么至少您应该准确地告诉我们您的程序的当前行为是什么——您的程序中有很多调试语句(一件好事),所以准确地分享什么是有意义的输出导致崩溃。如果我们要尝试重现问题,我们还需要知道确切的输入。
  • 一个问题:进行pthead_create 调用的循环不会填充westerneastern pid 数组的每个索引。也就是说,这些数组在 pid 值是垃圾的地方存在间隙。然而,您的 pthread_join 循环尝试使用这两个数组中的每个索引。也就是说,一些pthread_join 调用被传递垃圾值。
  • @kaylum 好的,感谢您的建议。我编辑了一些帖子以反映您的建议。至于 pthread_create 循环,我将看看如何更改它,看看是否有帮助。
  • @kaylum 这似乎是我的问题的根源。一旦我对该问题进行了修复,segFault 问题似乎就消失了。我需要尝试继续测试它以确保没有问题,但乍一看,它似乎有效。

标签: c pthreads


【解决方案1】:

运行 valgrind 几乎可以立即检测到您的错误:

==10217== Use of uninitialised value of size 8
==10217==    at 0x4E39241: pthread_join (in /usr/lib64/libpthread-2.18.so)
==10217==    by 0x400E64: main (example.c:103)

第 103 行是pthread_join(eastern[k],NULL);; valgrind 强调在未设置时读取东部数组。

您的数组未完成,您访问未设置的元素。 您可以通过这种方式修改连接循环:

for(k;k<baboonCnt;++k)
{
    if(temp[k]=='L') {
            pthread_join(eastern[k],NULL);
            printf("eastern %d\n",k);
    }
    else if(temp[k]=='R') {
            pthread_join(western[k],NULL);
            printf("western %d\n",k);
    }
}

【讨论】:

  • 是的,在原始帖子的其中一个 cmets 中,有人指出了这一点。此后,我对其进行了更改,为东西方狒狒创建了单独的数组,并使用这些数组来迭代我的 pthread_join()。但是,为此我最终运行了两个单独的循环。所以我最终得到一个循环通过 pthread_join(eastern),一个循环通过 pthread_join(western)。我不再有 SegFault 问题,它运行了。现在,我当前的版本有一个问题,狒狒没有按照到达的顺序穿过。
【解决方案2】:

正如该线程中的其他人所提到的,我在尝试访问未正确初始化的数组元素时遇到了我的代码错误。我在这里总结了我在这篇文章中的变化。非常感谢 @kaylum 帮助我得出这个结论。

这些数组访问发生在这里:

for(k;k<baboonCnt;++k)
    {

        pthread_join(eastern[k],NULL);
        printf("eastern",k);
        pthread_join(western[k],NULL); 
        printf("western %d",k);         
    }

不正确的初始化发生在行中

for(i;i<baboonCnt;++i)
    {
        sleep(1);
        if(temp[i]=='L')
        {
            id[i] = i+1;
            printf("Baboon %d: Request to cross rope (left to right)\n", i+1);
            pthread_create(&eastern[i],NULL,(void *) &east_side,(void *)&id[i]);
        }
        else if(temp[i]=='R')
        {
            id[i] = i+1;
            printf("Baboon %d: Request to cross rope (right to left)\n", i+1);
            pthread_create(&western[i],NULL,(void *) &west_side,(void *)&id[i]);
        }
    }

这两组代码是导致执行问题的原因,包括我遇到的 SegFault。 修改初始化部分以创建 2 个单独的 id 数组,一个引用东方狒狒,另一个引用西方狒狒,而不是简单地将所有 id 保存在一个组中,这是使程序工作的关键。调整id键存储方式后,还需要修改pthread_join循环,使其正确访问数组。

这是修改后的初始化部分:

int eastcnt=0, westcnt=0, eastid[eastBab], westid[westBab], i=0;
    for(i;i<baboonCnt;++i)
      {
        sleep(1);
        if(temp[i]=='L')
          {
        eastid[eastcnt]=i;
        printf("Baboon %d wants to cross left to right\n",i);
        pthread_create(&eastern[eastcnt],NULL, (void *) &east_side,(void *) &eastid[eastcnt] );
        ++eastcnt;
          }
        else if(temp[i]=='R')
          {
        westid[westcnt]=i;
        printf("Baboon %d wants to cross right to left\n",i);
        pthread_create(&western[westcnt],NULL, (void *) &west_side,(void *) &westid[westcnt] );
        ++westcnt;
          }
      }

以及修改后的 pthread_join 循环

int k =0;
    for(k;k<westBab;++k)
    {
        pthread_join(western[k],NULL); 
    }
    k=0;
    for(k;k<eastBab;++k)
    {
        pthread_join(eastern[k],NULL); 
    }

在进行上述更改并进行一些常规清理后,整个程序按规范运行。

【讨论】:

    猜你喜欢
    • 2018-07-21
    • 2023-04-11
    • 2012-12-17
    • 1970-01-01
    • 2017-01-02
    • 1970-01-01
    • 2011-08-11
    • 2013-10-13
    • 2021-05-10
    相关资源
    最近更新 更多