我试图在linux上使用强大的互斥锁来保护进程之间的资源,似乎在某些情况下它们不会以“健壮”的方式运行。通过“健壮”的方式,我的意思是如果拥有锁的进程已经终止,pthread_mutex_lock应该返回EOWNERDEAD。
以下是不起作用的情况:
2处理p1和p2。 p1创建健壮的互斥锁并等待它(在用户输入之后)。 p2有2个线程:线程1映射到互斥锁并获取它。线程2(在线程1获取互斥锁之后)也映射到相同的互斥锁并等待它(因为线程1现在拥有它)。另请注意,p2-thread1已经获取后,p1开始等待互斥锁。
现在,如果我们终止p2,p1永远不会解除阻塞(意味着它的pthread_mutex_lock永远不会返回),这与所谓的“健壮性”相反,其中p1应该以EOWNERDEAD错误解除阻塞。
以下是代码:
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;
}
首先,运行p1,然后运行p2并等待它打印“从另一个线程锁定”。在p1的shell上按Enter键锁定互斥锁,然后在p2的shell上按Enter键终止p2,或者你可以用其它方式杀掉它。您将看到p1打印“锁定...”并且pthread_mutex_lock永远不会返回。
问题实际上并不是一直发生,看起来它取决于时间。如果你在p1开始锁定之后和终止p2之前经过一段时间,那么有时候它会工作并且p2的pthread_mutex_lock返回130(EOWNERDEAD)。但是如果你在p1开始等待互斥锁之后或者很短的时间内终止p2,那么p1将永远不会解锁。
还有其他人遇到过同样的问题吗?
答案 0 :(得分:1)
在Linux Kernel 2.6.32及更新版本上使用glibc版本:2.11.1验证了行为。
我的第一个发现:Iff你在p1之前点击了输入&#34;从另一个线程锁定&#34;在p2(2s内)中,健壮的互斥体可以正常工作。正如人们所料。结论:等待线程的顺序很重要。
第一个等待的线程被唤醒了。不幸的是,在那时,p2中的线程被杀死了。
有关问题的说明,请参阅https://lkml.org/lkml/2013/9/27/338。
我不知道是否有内核修补程序/补丁。甚至不知道它是否被认为是一个bug。
对于整个混乱,似乎有一种解决方法。使用健壮的互斥锁与PTHREAD_PRIO_INHERIT:
pthread_mutexattr_setprotocol(&ma, PTHREAD_PRIO_INHERIT);
内核(futex.c)而不是 handle_futex_death() exit_pi_state_list()中的其他一些机制确实可以处理其他互斥服务器的唤醒。它似乎解决了这个问题。
答案 1 :(得分:0)
尝试简化您的问题。似乎你的问题是在运行序列 总是考虑最坏的情况:即使你跑A然后B,B仍然可以在A刚开始跑的时候完成。如有必要,添加互斥锁控件 以下是A(生产者)和B(消费者)的简单示例:
Main:
Call A
A:
Lock
Call B
Produce
Unlock
B:
Lock
Consume
Unlock