进程间共享条件变量和互斥量:在进程间共享时,是否需要先锁定互斥量?

27

我需要一点帮助来理解如何在C语言中使用条件变量来解决一个练习。以下是一个小例子:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>

#define OKTOWRITE "/oktowrite"
#define MESSAGE "/message"
#define MUTEX "/lock"

int main(int argc, char** argv)
{
  pthread_cond_t* condition;
  pthread_mutex_t *mutex;
  char* message;
  int des_cond, des_msg, des_mutex;
  int mode = S_IRWXU | S_IRWXG;

  des_mutex = shm_open(MUTEX, O_CREAT | O_RDWR | O_TRUNC, mode);

  if (des_mutex < 0)
  {
    perror("failure on shm_open on des_mutex");
    exit(1);
  }

  if (ftruncate(des_mutex, sizeof(pthread_mutex_t)) == -1)
  {
    perror("Error on ftruncate to sizeof pthread_cond_t\n");
    exit(-1);
  }

  mutex = (pthread_mutex_t*) mmap(NULL, sizeof(pthread_mutex_t),
      PROT_READ | PROT_WRITE, MAP_SHARED, des_mutex, 0);

  if (mutex == MAP_FAILED )
  {
    perror("Error on mmap on mutex\n");
    exit(1);
  }

  pthread_mutex_init(mutex, NULL );

  des_cond = shm_open(OKTOWRITE, O_CREAT | O_RDWR | O_TRUNC, mode);

  if (des_cond < 0)
  {
    perror("failure on shm_open on des_cond");
    exit(1);
  }

  if (ftruncate(des_cond, sizeof(pthread_cond_t)) == -1)
  {
    perror("Error on ftruncate to sizeof pthread_cond_t\n");
    exit(-1);
  }

  condition = (pthread_cond_t*) mmap(NULL, sizeof(pthread_cond_t),
      PROT_READ | PROT_WRITE, MAP_SHARED, des_cond, 0);

  if (condition == MAP_FAILED )
  {
    perror("Error on mmap on condition\n");
    exit(1);
  }

  pthread_cond_init(condition, NULL );

  if (!fork())
  {
    sleep(3);
    pthread_mutex_lock(mutex);
    pthread_cond_signal(condition);
    pthread_mutex_unlock(mutex);
    printf("son signaled\n");
    exit(0);
  }
  else
  {
    printf("wait on condition\n");

    pthread_mutex_lock(mutex);
    pthread_cond_wait(condition, mutex);
    pthread_mutex_unlock(mutex);

    printf("Signaled by son process, wake up\n");

    pthread_mutex_destroy(mutex);
    pthread_cond_destroy(condition);

    shm_unlink(OKTOWRITE);
    shm_unlink(MESSAGE);
    shm_unlink(MUTEX);

    return 0;
  }
}
问题是进程的父进程在儿子的信号后仍然被锁定。一切都在共享内存中(使用shm_openmmap),因此两个进程的条件应该相同。 在调用wait或signal之前锁定互斥锁是否可能是我的错误? 编辑: 感谢所有帮助我的人。 这是带有CRITICAL部分标记的正确代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>

#define OKTOWRITE "/condwrite"
#define MESSAGE "/msg"
#define MUTEX "/mutex_lock"

int main(int argc, char** argv) {

pthread_cond_t* condition;
pthread_mutex_t* mutex;
char* message;
int des_cond, des_msg, des_mutex;
int mode = S_IRWXU | S_IRWXG;

des_mutex = shm_open(MUTEX, O_CREAT | O_RDWR | O_TRUNC, mode);

if (des_mutex < 0) {
    perror("failure on shm_open on des_mutex");
    exit(1);
}

if (ftruncate(des_mutex, sizeof(pthread_mutex_t)) == -1) {
    perror("Error on ftruncate to sizeof pthread_cond_t\n");
    exit(-1);
}

mutex = (pthread_mutex_t*) mmap(NULL, sizeof(pthread_mutex_t),
        PROT_READ | PROT_WRITE, MAP_SHARED, des_mutex, 0);

if (mutex == MAP_FAILED ) {
    perror("Error on mmap on mutex\n");
    exit(1);
}

des_cond = shm_open(OKTOWRITE, O_CREAT | O_RDWR | O_TRUNC, mode);

if (des_cond < 0) {
    perror("failure on shm_open on des_cond");
    exit(1);
}

if (ftruncate(des_cond, sizeof(pthread_cond_t)) == -1) {
    perror("Error on ftruncate to sizeof pthread_cond_t\n");
    exit(-1);
}

condition = (pthread_cond_t*) mmap(NULL, sizeof(pthread_cond_t),
        PROT_READ | PROT_WRITE, MAP_SHARED, des_cond, 0);

if (condition == MAP_FAILED ) {
    perror("Error on mmap on condition\n");
    exit(1);
}


         /* HERE WE GO */
/**************************************/

    /* set mutex shared between processes */
pthread_mutexattr_t mutexAttr;
pthread_mutexattr_setpshared(&mutexAttr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(mutex, &mutexAttr);

/* set condition shared between processes */
pthread_condattr_t condAttr;
pthread_condattr_setpshared(&condAttr, PTHREAD_PROCESS_SHARED);
pthread_cond_init(condition, &condAttr);

    /*************************************/

if (!fork()) {

    sleep(10);

    pthread_mutex_lock(mutex);
    pthread_cond_signal(condition);
    printf("son signaled\n");
    pthread_mutex_unlock(mutex);
    exit(0);
}

else {

    printf("father waits on condition\n");

     pthread_mutex_lock(mutex);
     pthread_cond_wait(condition, mutex);
     pthread_mutex_unlock(mutex);

     printf("Signaled by son process, wake up!!!!!!!!\n");

    pthread_condattr_destroy(&condAttr);
    pthread_mutexattr_destroy(&mutexAttr);
    pthread_mutex_destroy(mutex);
    pthread_cond_destroy(condition);

    shm_unlink(OKTOWRITE);
    shm_unlink(MESSAGE);
    shm_unlink(MUTEX);

}

return 0;

}

如果在您的问题中添加新代码,请不要删除旧代码,因为这将使已经发布在旧问题上的答案变得无用或者至少难以理解(没有旧问题的上下文)。另外,请注意正确格式化(主要是缩进)要在此处发布的代码。谢谢。 - alk
1
在你应用更新之前,我就已经写了关于旧/新代码的评论了。为了错误检查,我参考了pthread_*调用以及fork()。每一个被定义为可能失败的调用迟早都会失败。其中,至少需要考虑那些对测试有影响的调用,因为它们如果失败可能会留下无法使用的数据。 - alk
1
我不明白其中一个点。(假设)我们首先在父进程中获取共享互斥锁,并开始等待一个条件,这个条件应该由子进程发出信号。然而,子进程必须拥有该互斥锁(在发出信号之前),而该互斥锁已经被父进程持有。因此,我不明白为什么这里不会发生死锁。请问有人可以解释一下吗? - stdout
1
没事了,我找到答案了。“pthread_cond_wait释放互斥锁并等待条件cond_t被触发”。 - stdout
显示剩余5条评论
3个回答

34

为了在进程之间能够共享,互斥锁需要通过正确初始化的属性进行初始化:http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutexattr_setpshared.html

#include <pthread.h>

...

pthread_mutex_t * pmutex = NULL;
pthread_mutexattr_t attrmutex;

/* Initialise attribute to mutex. */
pthread_mutexattr_init(&attrmutex);
pthread_mutexattr_setpshared(&attrmutex, PTHREAD_PROCESS_SHARED);

/* Allocate memory to pmutex here. */

/* Initialise mutex. */
pthread_mutex_init(pmutex, &attrmutex);

/* Use the mutex. */

/* Clean up. */
pthread_mutex_destroy(pmutex);
pthread_mutexattr_destroy(&attrmutex); 

(为了方便阅读,本示例省略了错误检查)

同样适用于应该在进程之间共享的条件变量:http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_condattr_setpshared.html

#include <pthread.h>

...

pthread_cond_t * pcond = NULL;
pthread_condattr_t attrcond;

/* Initialise attribute to condition. */
pthread_condattr_init(&attrcond);
pthread_condattr_setpshared(&attrcond, PTHREAD_PROCESS_SHARED);

/* Allocate memory to pcond here. */

/* Initialise condition. */
pthread_cond_init(pcond, &attrcond);

/* Use the condition. */

/* Clean up. */
pthread_cond_destroy(pcond);
pthread_condattr_destroy(&attrcond); 

(出于可读性考虑,本示例略去错误检查)


还可以参考这个答案:https://dev59.com/MnE95IYBdhLWcg3wQ7qb#2390670


3
现在,我正在使用C++11,所以我有一个问题,C++11的互斥锁和条件变量能做到这一点吗?因为我无法找到任何关于C++11 IPC功能的资料。 - Trung Nguyen
1
不幸地,标准的 C++ 同步原语无法处理这种情况。即使使用 std::mutex::native_handle 也无法帮助解决,因为在构造原语之前必须先初始化属性。 - Dení
1
另一个问题是,如何确保在进程终止的情况下互斥锁得到解锁? - Dení
@Denis:如何处理持有互斥锁的进程死亡的问题值得单独提出。要开始深入探讨这个非常规的情况,您可能会喜欢阅读这篇文章:http://pubs.opengroup.orga/onlinepubs/9699919799/functions/pthread_mutex_consistent.html - alk
@alk 我正在回复Trung Nguyen的留言。 感谢链接,我正在查看robust属性。 - Dení

10

等待条件应该先使用while语句,像这样:

pthread_mutex_lock(mutex);
while(!conditionSatisfied)
    pthread_cond_wait(condition, mutex);
pthread_mutex_unlock(mutex);

在发信号时应该按照以下方式进行:

pthread_mutex_lock(mutex);
conditionSatisfied = true;
pthread_cond_signal(condition);
pthread_mutex_unlock(mutex);

2
信号量不需要使用互斥锁(假设服务员正确管理虚假唤醒,这总是应该的)。我同意其余部分。显然,修改谓词时需要锁定,但最后两行可以颠倒。 - WhozCraig
链接已失效。这是Mark Mitchell、Alex Samuel和Jeffrey Oldham的《高级Linux编程》对吧?它可以在谷歌上搜索到,但我没有看到任何可以成为永久链接的东西。 看起来有点过时了。涵盖了/proc,但没有涵盖/sys - Victor Sergienko

4

是的,在使用pthread_cond_wait之前必须锁定互斥量,但在使用pthread_cond_signal时不需要。如果您回顾一下您的代码,您会发现互斥量将被解锁两次,这是一个错误的迹象...子进程还可能对父进程销毁的互斥量执行解锁操作...

顺便说一下,睡眠并不能保证父进程先执行。要确保这一点,您需要使用...条件变量...


我尝试在发送信号之前解除锁定/解锁互斥锁,但仍然无法正常工作... :( - SagittariusA
抱歉各位...我真的不太理解这个问题。也许,条件变量只能在线程之间共享,而不能在进程之间共享? - SagittariusA

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接