【发布时间】:2023-03-03 20:31:01
【问题描述】:
我正在尝试在 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 并等待它打印“Locking from another thread”。在 p1 的 shell 上按 Enter 以锁定互斥锁,然后在 p2 的 shell 上按 Enter 以终止 p2,或者您可以通过其他方式杀死它。你会看到 p1 打印“locking...”并且 pthread_mutex_lock 永远不会返回。
这个问题实际上并不总是发生,看起来它取决于时间。如果在 p1 开始锁定之后和终止 p2 之前等了一段时间,有时它会起作用并且 p2 的 pthread_mutex_lock 返回 130 (EOWNERDEAD)。但是,如果您在 p1 开始等待互斥体之后立即或短时间内终止 p2,则 p1 永远不会解除阻塞。
有没有人遇到过同样的问题?
【问题讨论】:
-
我还更改了 p2.cpp 的代码以避免两次映射到共享内存,方法是使 MyMtx *m 全局变量和 threadFunc 使用它而不是调用 mmap。我得到了同样的结果。
-
奇怪的是,当我用 SIGTERM 代替您的 SIGKILL 时,它似乎按预期工作。规范是否说明了基于信号的不同行为?
-
嗯,我尝试使用 SIGTERM,但它仍然可以重现。与 p2 的 Ctrl-C 相同(我猜这是 SIGINT)。如果我在 p1 开始等待后立即终止 p2,它实际上总是为我重现。
-
SIGINT 也适用于我。 p1 返回 retcode = 130(所有者死亡)。我有点摆弄你的代码,但我现在看到的唯一区别是我注释掉了 p2 中的 ftruncates。
-
您在哪个系统上运行?我在 Oracle Linux 5 上。
标签: c++ linux pthreads ipc mutex