Ich versuche, robuste Mutexe auf Linux zu verwenden, um Ressourcen zwischen Prozessen zu schützen, und es scheint, dass sie sich in manchen Situationen nicht "robust" verhalten. Mit "robust" meine ich, dass pthread_mutex_lock EOWNERDEAD zurückgeben sollte, wenn der Prozess, der die Sperre besitzt, beendet wurde. HierFehler mit robustem Mutex
ist das Szenario, in dem es nicht funktioniert:
2 Prozesse P1 und P2. p1 erstellt robusten Mutex und wartet darauf (nach Benutzereingabe). p2 hat 2 Threads: Thread 1 maps in den Mutex und erwirbt es. Thread 2 (nachdem Thread 1 den Mutex erworben hat) mappt ebenfalls in den gleichen Mutex und wartet darauf (da Thread 1 ihn jetzt besitzt). Beachten Sie auch, dass p1 auf den Mutex wartet, nachdem p2-thread1 es bereits erfasst hat.
Wenn wir jetzt p2 beenden, wird p1 niemals entsperrt (was bedeutet, dass pthread_mutex_lock niemals zurückkehrt), im Gegensatz zur angenommenen "Robustheit", wo p1 mit dem EOWNERDEAD-Fehler entsperren sollte. Hier
ist der Code:
p1.cpp:
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
struct MyMtx {
pthread_mutex_t m;
};
int main(int argc, char **argv)
{
int r;
pthread_mutexattr_t ma;
pthread_mutexattr_init(&ma);
pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED);
pthread_mutexattr_setrobust_np(&ma, PTHREAD_MUTEX_ROBUST_NP);
int fd = shm_open("/test_mtx_p", O_RDWR|O_CREAT, 0666);
ftruncate(fd, sizeof(MyMtx));
MyMtx *m = (MyMtx *)mmap(NULL, sizeof(MyMtx),
PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
//close (fd);
pthread_mutex_init(&m->m, &ma);
puts("Press Enter to lock mutex");
fgetc(stdin);
puts("locking...");
r = pthread_mutex_lock(&m->m);
printf("pthread_mutex_lock returned %d\n", r);
puts("Press Enter to unlock");
fgetc(stdin);
r = pthread_mutex_unlock(&m->m);
printf("pthread_mutex_unlock returned %d\n", r);
puts("Before pthread_mutex_destroy");
r = pthread_mutex_destroy(&m->m);
printf("After pthread_mutex_destroy, r=%d\n", r);
munmap(m, sizeof(MyMtx));
shm_unlink("/test_mtx_p");
return 0;
}
p2.cpp:
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
struct MyMtx {
pthread_mutex_t m;
};
static void *threadFunc(void *arg)
{
int fd = shm_open("/test_mtx_p", O_RDWR|O_CREAT, 0666);
ftruncate(fd, sizeof(MyMtx));
MyMtx *m = (MyMtx *)mmap(NULL, sizeof(MyMtx),
PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
sleep(2); //to let the first thread lock the mutex
puts("Locking from another thread");
int r = 0;
r = pthread_mutex_lock(&m->m);
printf("locked from another thread r=%d\n", r);
}
int main(int argc, char **argv)
{
int r;
int fd = shm_open("/test_mtx_p", O_RDWR|O_CREAT, 0666);
ftruncate(fd, sizeof(MyMtx));
MyMtx *m = (MyMtx *)mmap(NULL, sizeof(MyMtx),
PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
//close (fd);
pthread_t tid;
pthread_create(&tid, NULL, threadFunc, NULL);
puts("locking");
r = pthread_mutex_lock(&m->m);
printf("pthread_mutex_lock returned %d\n", r);
puts("Press Enter to terminate");
fgetc(stdin);
kill(getpid(), 9);
return 0;
}
Zuerst laufen p1, dann p2 laufen und warten, bis er druckt „Locking von einem anderen Thread ". Drücken Sie die Eingabetaste in der Shell von p1, um den Mutex zu sperren, und drücken Sie dann die Eingabetaste in der Shell von p2, um p2 zu beenden, oder Sie können ihn einfach auf andere Weise beenden. Sie werden sehen, dass p1 "locking ..." und pthread_mutex_lock niemals zurückgibt.
Das Problem passiert eigentlich nicht die ganze Zeit, sieht aus wie es vom Timing abhängt. Wenn Sie eine gewisse Zeit verstreichen lassen, nachdem p1 zu sperren beginnt und bevor Sie p2 beenden, funktioniert es irgendwann und pthread_mutex_lock von p2 gibt 130 (EOWNERDEAD) zurück. Aber wenn Sie p2 kurz nach oder kurz nachdem p1 auf den Mutex wartet, beenden, wird p1 niemals entsperren.
Hat noch jemand das gleiche Problem?
Ich habe auch den Code von p2.cpp geändert, um die Zuordnung in den gemeinsamen Speicher zweimal zu vermeiden, indem MyMtx * m globale Variable und für threadFunc, um es anstelle des Aufrufs von mmap verwenden. Ich bekomme das gleiche Ergebnis. –
Seltsam, wenn ich SIGTERM für Ihr SIGKILL ersetze, scheint es wie erwartet zu funktionieren. Bezieht sich die Spezifikation auf variierendes Verhalten basierend auf dem Signal? – Duck
Hmm, ich habe versucht mit SIGTERM und es reproduziert immer noch. Das selbe mit Ctrl-C von p2 (was SIGINT ist, denke ich). Wenn ich p2 gleich nach dem Start von p1 beende, gibt es eigentlich immer für mich wieder. –