Bedingungsvariable & Mutex zwischen Prozessen teilen: Muss Mutex vorher gesperrt werden?

Lesezeit: 8 Minuten

Benutzer-Avatar
SchützeA

Ich brauche etwas Hilfe, um zu verstehen, wie Bedingungsvariablen in C verwendet werden, um eine Übung zu lösen. Hier ein kleines Beispiel:

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

Das Problem ist, dass der Vater des Prozesses weiterhin gesperrt wird, auch nach der Signalisierung des Sohnes. Alles befindet sich im gemeinsamen Speicher (mithilfe von shm_open und mmap), also sollte die Bedingung für beide Prozesse gleich sein. Mache ich vielleicht einen Fehler, indem ich den Mutex sperre, bevor ich wait oder signal anrufe?

EDIT: Danke an alle die mir geholfen haben. Hier ist der richtige Code mit den markierten KRITISCHEN Teilen:

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

}

  • mögliches Duplikat von Funktionieren pthread Mutexs über Threads hinweg, wenn sie sich im gemeinsamen Speicher befinden?

    – alk

    2. Dezember 2013 um 10:49 Uhr

  • Setzen Sie auch das Attribut für die gemeinsam zu nutzenden Bedingungsvariablen. Bitte beachten Sie auch meine Antwort.

    – alk

    2. Dezember 2013 um 11:07 Uhr


  • Ich habe meinen Kommentar zum alten/neuen Code geschrieben, bevor Sie Ihr Update angewendet haben. Für die Fehlerprüfung habe ich auf die verwiesen pthread_* Anrufe sowie fork(). Jeder Anruf, der so definiert ist, dass er fehlschlagen kann, schlägt einen Tag früher oder später fehl. Von diesen würde ich zumindest die testrelevanten in Betracht ziehen, bei denen ich unbrauchbare Daten hinterlassen kann, wenn sie fehlschlagen.

    – alk

    2. Dezember 2013 um 13:29 Uhr


  • Ich habe keinen Punkt oben bekommen. (Nehmen wir an) Wir greifen zuerst die gemeinsam genutzte Mutex-Sperre im übergeordneten Prozess und warten auf eine Bedingung, die vom untergeordneten Prozess signalisiert werden soll. Der untergeordnete Prozess muss jedoch diesen Mutex haben (bevor die Bedingung signalisiert wird), der bereits vom übergeordneten Prozess gehalten wird. Also, ich verstehe nicht, wie Deadlock hier nicht passiert. Kann mir das bitte jemand erklären?

    – stdout

    28. März 2017 um 19:12 Uhr

  • Egal. Ich habe meine Antwort gefunden. “pthread_cond_wait gibt den Mutex frei und wartet, bis die Bedingung cond_t signalisiert wird”.

    – stdout

    28. März 2017 um 19:25 Uhr

Benutzer-Avatar
alk

Um zwischen Prozessen gemeinsam genutzt werden zu können, muss ein Mutex entsprechend über ein ordnungsgemäß initialisiertes Attribut initialisiert werden: 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); 

(Fehlerprüfung wegen der Lesbarkeit dieses Beispiels weggelassen)

Dasselbe gilt für eine Bedingungsvariable, die zwischen Prozessen geteilt werden soll: 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); 

(Fehlerprüfung wegen der Lesbarkeit dieses Beispiels weggelassen)


Siehe auch diese Antwort: https://stackoverflow.com/a/2390670/694576

  • Jetzt verwende ich C ++ 11. Ich habe also eine Frage, ob C ++ 11 Mutex und Bedingung das tun können? Weil ich nirgendwo nach IPC-Features von C++11 suchen kann

    – Trung Nguyen

    7. November 2016 um 4:44 Uhr

  • Leider behandeln Standard-C++-Synchronisationsprimitive diesen Fall nicht. Eben std::mutex::native_handle wird hier nicht helfen, da ein Attribut vor der primitiven Konstruktion initialisiert werden muss.

    – Dení

    28. August 2018 um 22:48 Uhr

  • Eine andere Frage, wie kann sichergestellt werden, dass der Mutex im Falle einer Prozessbeendigung entsperrt wird?

    – Dení

    28. August 2018 um 22:49 Uhr

  • @Denis: Die Frage, wie mit dem Fall umzugehen ist, dass ein Prozess, der einen Mutex enthält, stirbt, sollte separat gestellt werden. Um in diesen nicht trivialen Fall einzutauchen, möchten Sie vielleicht Folgendes lesen: pubs.opengroup.orga/onlinepubs/9699919799/functions/…

    – alk

    29. August 2018 um 9:18 Uhr


  • @alk Ich habe Trung Nguyen geantwortet. Danke für den Link, ich schaue mir das robuste Attribut an.

    – Dení

    29. August 2018 um 13:32 Uhr

Benutzer-Avatar
HAL9000

Dem Warten auf eine Bedingung sollte eine while-Anweisung vorangestellt werden, etwa so:

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

während die Signalisierung auf folgende Weise erfolgen sollte:

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

  • Das Signal muss nicht mit verriegeltem Mutex erfolgen (vorausgesetzt, der Kellner verwaltet das falsche Aufwecken ordnungsgemäß, was er immer tun sollte). Dem Rest stimme ich zu. Offensichtlich der Riegel ist zum Modifizieren des Prädikats jedoch erforderlich. Aber die letzten beiden Zeilen kann umgekehrt werden.

    – WhozCraig

    2. Dezember 2013 um 10:07 Uhr


  • Der Link ist tot. Das ist „Advanced Linux Programming“ von Mark Mitchell, Alex Samuel, Jeffrey Oldham – richtig? Es kann gegoogelt werden, aber ich sehe nichts, was zu einem dauerhaften Link gemacht werden kann. Sieht auch etwas veraltet aus. Abdeckungen /proc aber nicht /sys.

    – Viktor Sergijenko

    8. November 2017 um 23:27 Uhr


Ja, Mutex muss vorher gesperrt werden pthread_cond_waitmuss aber nicht für pthread_cond_signal. Wenn Sie sich Ihren Code noch einmal ansehen, sehen Sie, dass der Mutex zweimal entsperrt wird, was ein Zeichen für einen Fehler ist … es ist auch möglich, dass das Kind unlock auf einem Mutex aufruft, der vom Elternteil zerstört wurde …

Übrigens garantiert das Schlafen nicht, dass der Elternteil zuerst ausgeführt wird. Um dies sicherzustellen, benötigen Sie … eine Bedingungsvariable …

  • Ich habe versucht, das Lock/Unclock-Mutex vor dem Signal zu entfernen, aber es funktioniert immer noch nicht … 🙁

    – SchützeA

    2. Dezember 2013 um 10:08 Uhr

  • Sorry Jungs … Ich verstehe das Problem nicht wirklich. Ist es vielleicht nicht möglich, dass Bedingungsvariablen zwischen Prozessen geteilt werden, sondern nur zwischen Threads?

    – SchützeA

    2. Dezember 2013 um 10:26 Uhr

1358900cookie-checkBedingungsvariable & Mutex zwischen Prozessen teilen: Muss Mutex vorher gesperrt werden?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy