Подтвердить что ты не робот

Предоставить переменную состояния и мьютекс между процессами: нужно ли блокировать мьютекс раньше?

Мне нужно немного помочь понять, как использовать переменные условия в 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_open и mmap), поэтому условие должно быть одинаковым для обоих процессов. Возможно, я ошибаюсь, заблокировав мьютекс, прежде чем вызывать ожидание или сигнал?

EDIT: Спасибо всем, кто мне помог. Здесь правый код с КРИТИЧЕСКИМИ деталями отмечен:

#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;

}
4b9b3361

Ответ 1

Чтобы быть совместимым между процессами, мьютекс должен быть инициализирован соответствующим образом с помощью правильно инициализированного атрибута: 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); 

(ошибка проверки оставлена ​​для удобства чтения этого примера)


Также см. этот ответ: fooobar.com/questions/282318/...

Ответ 2

Ожидание условия должно предшествовать оператору 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);

Я также предлагаю вам очень полезное чтение о многопоточности в Linux.

Ответ 3

да, мьютекс должен быть заблокирован до pthread_cond_wait, но не нужен для pthread_cond_signal. Если вы посмотрите на свой код, вы увидите, что мьютекс будет разблокирован дважды, что является признаком ошибки... также возможно, чтобы ребенок вызывал разблокировку на мьютексе, который был уничтожен родителем...

Btw sleep не гарантирует, что родитель выполнит сначала. Для этого вам понадобится... переменная условия...