【问题标题】:Trylock on an EOWNERDEAD state mutex尝试锁定 EOWNERDEAD 状态互斥锁
【发布时间】:2021-05-24 04:48:19
【问题描述】:

当我在前一个所有者已死的情况下尝试使用“trylock”锁定互斥锁时,我发现了意外行为 无需解锁。
使用“trylock”的第一个进程按预期获得 EOWNERDEAD 状态,因此使用“解锁”函数释放互斥锁:

lock_status = pthread_mutex_trylock(mutex);
if (lock_status == EOWNERDEAD)
{
    pthread_mutex_unlock(mutex);
    printf("P1 mutex status: EOWNERDEAD\n");
}
else if (lock_status == ENOTRECOVERABLE)
    {printf("P1 mutex status: ENOTRECOVERABLE\n");}
else if (lock_status == EBUSY)
    {printf("P1 mutex status: EBUSY\n");}
else {printf("P1 mutex status: %d\n", lock_status);}

第二个执行相同的代码,获得 ENOTRECOVERABLE 状态,正如预期的那样。
但是当第三个执行相同的操作时,会进入 EBUSY 状态,这是出乎意料的。
状态应该是 ENOTRECOVERABLE 报告的here
我尝试使用 'lock' 功能而不是 'trylock' 并返回正确的状态。
那是一个错误吗?在我的目的中,我必须在执行任何操作之前检查互斥锁状态,所以我不能使用“锁定”功能,否则可能会发生死锁。
我正在考虑在 ENOTRECOVERABLE 状态返回时销毁互斥锁​​:

else if (lock_status == ENOTRECOVERABLE)
{
    pthread_mutex_destroy(mutex);
    printf("P1 mutex status: ENOTRECOVERABLE\n");
}

但也许有比这种激烈的解决方案更好的方法,不是吗? 完整代码:

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>

main(void)
{
int lock_status, mutex_fdesc;
pid_t process_id;
pthread_mutex_t* mutex;
pthread_mutexattr_t m_att;

//Process 1 die with locked mutex
process_id = fork();
if (process_id < 0) {exit(0);}
if (process_id == 0)
{
    usleep(100000);
    mutex_fdesc = shm_open("/mutex", O_RDWR, S_IRWXU | S_IRWXG);
    mutex = (pthread_mutex_t*)mmap(NULL, sizeof(pthread_mutex_t), 
                PROT_READ | PROT_WRITE, MAP_SHARED, mutex_fdesc, 0);
    close(mutex_fdesc);
    pthread_mutex_lock(mutex);
    exit(0);
}
///Process 2 try to lock mutex and gets EOWNERDEAD then make an unlock
process_id = fork();
if (process_id < 0) {exit(0);}
if (process_id == 0)
{
    usleep(200000);
    mutex_fdesc = shm_open("/mutex", O_RDWR, S_IRWXU | S_IRWXG);
    mutex = (pthread_mutex_t*)mmap(NULL, sizeof(pthread_mutex_t), 
                PROT_READ | PROT_WRITE, MAP_SHARED, mutex_fdesc, 0);
    close(mutex_fdesc);
    lock_status = pthread_mutex_trylock(mutex);
    if (lock_status == EOWNERDEAD)
        {
        pthread_mutex_unlock(mutex);
        printf("P2 mutex status: EOWNERDEAD\n");}
    else if (lock_status == ENOTRECOVERABLE)
        {printf("P2 mutex status: ENOTRECOVERABLE\n");}
    else if (lock_status == EBUSY)
        {printf("P2 mutex status: EBUSY\n");}
    else {printf("P2 mutex status: %d\n", lock_status);}
    exit(0);
}
///Process 2 try to lock mutex and gets ENOTRECOVERABLE then do nothing
process_id = fork();
if (process_id < 0) {exit(0);}
if (process_id == 0)
{
    usleep(400000);
    mutex_fdesc = shm_open("/mutex", O_RDWR, S_IRWXU | S_IRWXG);
    mutex = (pthread_mutex_t*)mmap(NULL, sizeof(pthread_mutex_t), 
                PROT_READ | PROT_WRITE, MAP_SHARED, mutex_fdesc, 0);
    close(mutex_fdesc);
    lock_status = pthread_mutex_trylock(mutex);
    if (lock_status == EOWNERDEAD)
        {
        pthread_mutex_unlock(mutex);
        printf("P3 mutex status: EOWNERDEAD\n");}
    else if (lock_status == ENOTRECOVERABLE)
        {printf("P3 mutex status: ENOTRECOVERABLE\n");}
    else if (lock_status == EBUSY)
        {printf("P3 mutex status: EBUSY\n");}
    else {printf("P3 mutex status: %d\n", lock_status);}
    exit(0);
}

mutex_fdesc = shm_open("/mutex", O_RDWR | O_CREAT | O_EXCL, S_IRWXU | S_IRWXG);
ftruncate(mutex_fdesc, sizeof(pthread_mutex_t));
mutex = (pthread_mutex_t*)mmap(NULL, sizeof(pthread_mutex_t), 
            PROT_READ | PROT_WRITE, MAP_SHARED, mutex_fdesc, 0);
close(mutex_fdesc);
pthread_mutexattr_init(&m_att);
pthread_mutexattr_setpshared(&m_att, PTHREAD_PROCESS_SHARED);
pthread_mutexattr_setrobust(&m_att, PTHREAD_MUTEX_ROBUST);
pthread_mutex_init(mutex, &m_att);
pthread_mutexattr_destroy(&m_att);
///Parent process try to lock the mutex and gets EBUSY
usleep(800000);
lock_status = pthread_mutex_trylock(mutex);
if (lock_status == EOWNERDEAD)
        {printf("Pparent mutex status: EOWNERDEAD\n");}
    else if (lock_status == ENOTRECOVERABLE)
        {printf("Pparent mutex status: ENOTRECOVERABLE\n");}
    else if (lock_status == EBUSY)
        {printf("Pparent mutex status: EBUSY\n");}
    else {printf("Pparent mutex status: %d\n", lock_status);}

pthread_mutex_destroy(mutex);
munmap((void*)mutex, sizeof(pthread_mutex_t));
shm_unlink("/mutex");
wait(NULL);
wait(NULL);
wait(NULL);
exit(0);
}

【问题讨论】:

  • 为什么不将原始所有者死亡的信息存储在您可以明确检查的正常状态下,并使用pthread_mutex_consistent修复互斥锁?
  • 我认为这是唯一的方法,因为在返回 ENOTRECOVERABLE 状态后我不能原子地销毁互斥锁​​,并且其他线程可以使用“trylock”获取 EBUSY 状态。尽管如此,我认为这是一个错误。我在哪里可以举报?

标签: c pthreads mutex


【解决方案1】:

我认为您所看到的是,当它返回 EOWNERDEAD(这是恢复语义所必需的,因此只有一个线程尝试恢复)和 时,锁定互斥锁的尝试被认为是成功的当它返回ENOTRECOVERABLE 时也,我同意这令人惊讶,并且似乎与文档相反。文档说,如果接收EOWNERDEAD 的线程在解锁之前没有使互斥锁保持一致,那么所有后续锁定它的尝试都将返回ENOTRECOVERABLE。我在文档中看不到任何内容表明这不应该适用于 pthread_mutex_trylock(),就像它适用于 pthread_mutex_lock() 一样。

如果您想将其报告为错误,那么您可以针对提供 pthreads 实现的任何库进行报告。这至少会因操作系统而异。

我的目的是在进行任何操作之前检查互斥锁状态,所以我不能使用“锁定”功能,否则可能会发生死锁。

这听起来很可疑。您已经在使用健壮的互斥锁,否则您一开始就不会得到EOWNERDEADENOTRECOVERABLEpthread_mutex_trylock() 可能是矫枉过正,如果不是完全不合适的话。如果您在未能锁定互斥锁的情况下可以做一些有用的工作,您可以使用它。也许在你的情况下,但这将是不寻常的。例如,如果您只是延迟再试一次,那么您最好先使用pthread_mutex_lock()

【讨论】:

  • 我试图用 ENOTRECOVERABLE 状态解锁互斥锁,并且 EPERM 从“pthread_mutex_unlock”返回,所以互斥锁没有锁定。文档说:“所有锁定互斥锁的尝试都将失败并出现错误 [ENOTRECOVERABLE]”。
  • @map_88,我已经同意你的pthread_mutex_trylock()调用应该全部返回ENOTRECOVERABLE,一旦接收到EOWNERDEAD的线程解锁互斥体而不使其保持一致。很遗憾,在ENOTRECOVERABLE 之后解锁它并不能解决您的问题,但由于我们似乎已经处于与规范相反的行为领域,我认为您无法从观察到的行为中得出任何可靠的结论。
  • @map_88,然而,验证事件的顺序可能是值得的。特别是,如果一个线程尝试获取互斥锁,而接收到EOWNERDEAD 的线程仍然持有它,那么EBUSY 将是预期的结果。我不清楚你是如何排除这种可能性的。标准输出上消息的出现顺序不一定是可靠的指标。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-05-23
  • 1970-01-01
  • 2017-06-19
  • 1970-01-01
  • 1970-01-01
  • 2021-12-23
  • 2010-09-16
相关资源
最近更新 更多