我们在生产系统中存在一个错误,其中一个进程在保持共享内存互斥的同时发生段错误。死亡时,我们希望它释放锁。我们使用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作为参数传递,程序将在段错误后挂起。 –