【问题标题】:semaphore operation in linux,receive SIGSEGV and segmentation fault,which part is wrong?linux中的信号量操作,收到SIGSEGV和分段错误,哪一部分错了?
【发布时间】:2015-07-28 06:52:59
【问题描述】:

我的线程函数是:

#include"stdio.h"
#include"sys/types.h"
#include"pthread.h"
#include"semaphore.h"

sem_t sem;
int running = 1;
int ret;
void *pf(void *arg)          //producer function
{
    int semval;
    while(running)
    {
        sleep(1);
        sem_post(&sem); 
        sem_getvalue(&sem,&semval); 
        printf("produce : %d\n",semval);
    }

}

void *cf(void *arg)         /*consumer function*/
{
    int semval;
    while(running)
    {
        sleep(1);
        sem_wait(&sem); 
        sem_getvalue(&sem,&semval);
        printf("consume : %d\n",semval);
    }
}

主要功能是:

int main()
{
    pthread_t pf, cf;
    ret = sem_init(&sem,0,16);
    pthread_create(&pf,NULL,(void *)pf,NULL);   /*create producer*/
    pthread_create(&cf,NULL,(void *)cf,NULL);   /*create consumer*/
    sleep(1);
    running = 0;
    pthread_join(pf,NULL);
    pthread_join(cf,NULL);
    sem_destroy(&sem);  
    return 0;
}

当我运行可执行文件时,它返回分段错误。我认为程序可能访问了无效内存,但我不知道我的代码的哪一部分是错误的!

【问题讨论】:

    标签: c linux pthreads


    【解决方案1】:

    您已将线程变量和函数命名为相同名称:pfcf。所以变量shadow 是函数名。变量和函数使用相同的名称绝不是一个好主意。

    改变

    pthread_create(&pf,NULL,(void *)pf,NULL);   /*create producer*/
    pthread_create(&cf,NULL,(void *)cf,NULL);   /*create consumer*/
    

    pthread_create(&pf,NULL,producer,NULL);   /*create producer*/
    pthread_create(&cf,NULL,consumer,NULL);   /*create consumer*/
    

    并将您的函数分别重命名为 producerconsumer。请注意,我已删除的铸造也是错误的(即使你正确铸造也是不必要的)。

    您正在从线程函数返回任何值。线程函数应该返回void *。所以你需要调用pthread_exit(NULL);或者返回一个空指针。

    另一个主要问题是您在没有任何同步的情况下访问变量running,这会导致race condition。这是undefined behaviour。根据线程调度,如果main 线程在线程开始执行之前将running 设置为0,那么您的线程可能根本不会执行while 循环。

    【讨论】:

    • 感谢您的详细解释!我刚开始学习网络编程。
    • 关于 running 标志:这不完全是 UB。这不是 OP 所期望的。 (一般来说,访问这样一个标志的顺序并不重要)
    • @FelixPalmen 不。这是 UB:任何比赛条件都是 UB。
    • 考虑到C11 之前的标准并没有说明任何关于并发的信息(这个例子使用的是pthreads),不,它不是标准的UB。您无法预测线程的执行顺序,但这是不同的。
    • @FelixPalmen C11 声明:C11, 5.1.2.4, 如果程序的执行包含不同线程中的两个冲突操作,则程序的执行包含数据竞争,其中至少一个不是原子的,两者都没有发生在另一个之前。任何此类数据竞争都会导致未定义的行为。 事实是 running 被访问(多个线程的主要写入和线程函数没有任何同步。我不认为 posix 将竞争条件定义为不是 UB。跨度>
    【解决方案2】:
    pthread_t pf, cf;
    ret = sem_init(&sem,0,16);
    pthread_create(&pf,NULL,(void *)pf,NULL);   /*create producer*/
    pthread_create(&cf,NULL,(void *)cf,NULL);   /*create consumer*/
    

    您实际上是在将您的 线程描述符 作为启动例程传递给 pthread_create()。它们与您在此处似乎指的功能具有相同的名称,但对其进行了遮蔽。另请注意void * 是指向数据的指针,它与函数指针不兼容 -> 此处 强制转换为void *

    正确的代码例如看起来像这样:

    pthread_t pt, ct;
    ret = sem_init(&sem,0,16);
    pthread_create(&pt,NULL,pf,NULL);   /*create producer*/
    pthread_create(&ct,NULL,cf,NULL);   /*create consumer*/
    

    顺便说一句,一般提示:启用编译器警告:gcc -Wall -Wextra 会告诉你出了什么问题。

    编辑:在讨论 Blue Moon 的回答之后:仅使用 int 关闭您的线程是有问题的确实 .在大多数情况下,此处发生的数据竞争在实践中并不重要,因为通常情况下,您只想告诉线程停止并且不在乎,当这种情况发生时。但是(这是一个很大的但是):您的编译器会看到以下代码:

    while(running)
    {
        [...]
    }
    

    while 的条件是 only 访问此函数中的running。在不了解并发的情况下,它可以合法地假设running,一旦读取,就永远不会改变。所以,读取一次并永远使用这个读取值(例如存储在寄存器中)作为循环条件将是一个有效优化,绝对不是你想要的。

    哦,将volatile 添加到running 不是解决方案,但对此的解释往往很冗长,只需谷歌即可。

    一种可能性是也使用信号量来停止线程。例如。我的一个线程项目中有这个:

    /* check whether daemon shutdown was requested */
    if (!sem_trywait(&forceExit))
    {
        /* pass on to next thread */
        sem_post(&forceExit);
        rcout = -2;
        break;
    }
    

    主线程只执行一个sem_post(&forceExit) 来关闭所有其他线程。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-06-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多