我們在生產系統中存在一個錯誤,其中一個進程在保持共享內存互斥的同時發生段錯誤。死亡時,我們希望它釋放鎖。我們使用sem_wait()/ sem_post(),但這樣做我的功課,我發現這個API不允許這樣的行爲:如何(正確)使用健壯的pthreads進行進程同步?
http://www.usenetmessages.com/view.php?c=computer&g=1074&id=78029&p=0
答案,文章說,利用強大的pthreads API。我發現這個話題下面的文章:
http://www.embedded-linux.co.uk/tutorial/mutex_mutandis
但是,已經實施了下面的代碼,我有一個不可靠的行爲,也就是說,我應該告訴進程3,例如,對段錯誤,代碼工作得很好。其他進程醒來,認識到一個進程在持有互斥體時死亡並恢復。但是,如果我告訴進程0死掉,或者我應該刪除第63行的睡眠呼叫,其他進程不會在令人懷疑的進程自殺時喚醒。難道我做錯了什麼?
#include <stdio.h>
#include <stdlib.h>
#include <features.h>
#define __USE_POSIX
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#define __USE_MISC
#include <sys/mman.h>
#include <fcntl.h>
#include <errno.h>
#define __USE_GNU /* Necessario para usar a API PTHREAD_MUTEX_ROBUST_NP */
#define __USE_UNIX98 /* Necessario para usar a funcao pthread_mutexattr_settype */
#include <pthread.h>
#include <sys/wait.h>
static void *shrd;
static int child_main(int slot, int segfault) {
pthread_mutex_t *lock = (pthread_mutex_t *) shrd;
int err;
if (0 != (err=pthread_mutex_lock(lock))) {
switch(err) {
case EINVAL:
printf("Lock invalido no filho [%d]\n", slot);
goto excecao;
case EDEADLK:
printf("O filho [%d] tentou travar um lock que jah possui.\n", slot);
break;
case EOWNERDEAD:
printf("Filho [%d] foi informado que o processo que estava com o lock morreu.\n", slot);
if (0 == pthread_mutex_consistent_np(lock)) {
printf("Filho [%d] retornou o lock para um estado consistente.\n", slot);
} else {
fprintf(stderr, "Nao foi possivel retornar o lock a um estado consistente.\n");
goto desistir;
}
if (0 != (err=pthread_mutex_lock(lock))) {
fprintf(stderr, "Apos recuperar o estado do lock, nao foi possivel trava-lo: %d\n", err);
goto desistir;
}
case ENOTRECOVERABLE:
printf("O filho [%d] foi informado de que o lock estah permanentemente em estado inconsistente.\n", slot);
goto desistir;
default:
printf("Erro desconhecido ao tentar travar o lock no filho [%d]: [%d]\n", slot, err);
goto excecao;
}
}
printf("Filho [%d] adquiriu o lock.\n", slot);
if (segfault == slot) {
printf("Matando o PID [%d] com SIGSEGV.\n", getpid());
kill(getpid(), SIGSEGV);
} else {
sleep(1);
}
if (0 != (err = pthread_mutex_unlock(lock))) {
switch (err) {
case EPERM:
printf("O filho [%d] tentou liberar o lock, mas nao o possui.\n", slot);
break;
default:
fprintf(stderr, "Erro inesperado ao liberar o lock do filho [%d]: [%d]\n", slot, err);
}
} else {
printf("Filho [%d] retornou o lock.\n", slot);
}
return 0;
excecao:
fprintf(stderr, "Programa terminado devido excecao.\n");
return 1;
desistir:
fprintf(stderr, "A execucao do sistema nao deve prosseguir. Abortando todos os processos.\n");
kill(0, SIGTERM);
/* unreachable */
return 1;
}
int main(int argc, const char * const argv[]) {
pid_t filhos[10];
int status;
pid_t p;
int segfault = -1;
pthread_mutexattr_t attrs;
if (argc > 1) {
segfault = atoi(argv[1]);
if (segfault < 0 || segfault > 9)
segfault = -1;
}
if ((shrd = mmap(NULL, sizeof(pthread_mutex_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0)) == MAP_FAILED) {
perror("Erro ao criar shrd mem:\n");
exit(1);
}
pthread_mutexattr_init (&attrs);
pthread_mutexattr_settype (&attrs, PTHREAD_MUTEX_RECURSIVE_NP);
pthread_mutexattr_setrobust_np (&attrs, PTHREAD_MUTEX_ROBUST_NP);
pthread_mutexattr_setpshared (&attrs, PTHREAD_PROCESS_SHARED);
/*
Devido a um BUG na glibc 2.5 (que eh a usada pelo CentOS 5,
a unica forma de fazer os mutexes robustos funcionarem eh
setando o protocolo para PTHREAD_PRIO_INHERIT:
http://sourceware.org/ml/libc-help/2010-04/msg00028.html
*/
pthread_mutexattr_setprotocol (&attrs, PTHREAD_PRIO_INHERIT);
pthread_mutex_init ((pthread_mutex_t*) shrd, &attrs);
pthread_mutexattr_destroy (&attrs);
for (size_t i=0; i<sizeof(filhos)/sizeof(pid_t); ++i) {
if ((filhos[i]=fork()) == 0) {
return child_main((int) i, segfault);
} else {
if (filhos[i] < 0) {
fprintf(stderr, "Erro ao criar o filho [%zu]. Abortando.\n", i);
exit(1);
}
}
}
for (size_t i=0; i<sizeof(filhos)/sizeof(pid_t); ++i) {
do {
p = waitpid(filhos[i], &status, 0);
} while (p != -1);
}
printf("Pai encerrou a sua execucao.\n");
return 0;
}
BTW:我編譯在CentOS 5,64位:
$ uname -rm
2.6.18-194.el5 x86_64
glibc-2.5-49
gcc-4.1.2-48.el5
(對不起,在代碼中的句子和意見是在葡萄牙,我的母語)
謝謝你回覆@caruccio先生。實際上,ENOTRECOVERABLE之前的中斷是在代碼中。在複製到網站之前,我可能會刪除它,同時刪除夾板標記。正如你所指出的那樣,在pthread_mutex_consistent_np()之後移除鎖似乎在原始代碼所處的相同環境下工作。我在那裏,因爲mutex_mutandis文章說這是必需的。但是,在將互斥量返回到一致狀態後,無論是否使用鎖,事實是,如果沒有第63行的睡眠,或將0作爲參數傳遞,程序將在段錯誤後掛起。 –