考虑以下测试程序:
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <strings.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
pthread_mutex_t mutex;
pthread_mutexattr_t mattr;
pthread_t thread1;
pthread_t thread2;
pthread_t thread3;
void mutex_force_unlock(pthread_mutex_t *mutex, pthread_mutexattr_t *mattr)
{
int e;
e = pthread_mutex_destroy(mutex);
printf("mfu: %s\n", strerror(e));
e = pthread_mutex_init(mutex, mattr);
printf("mfu: %s\n", strerror(e));
}
void *thread(void *d)
{
int e;
e = pthread_mutex_trylock(&mutex);
if (e != 0)
{
printf("thr: %s\n", strerror(e));
mutex_force_unlock(&mutex, &mattr);
e = pthread_mutex_unlock(&mutex);
printf("thr: %s\n", strerror(e));
if (e != 0) pthread_exit(NULL);
e = pthread_mutex_lock(&mutex);
printf("thr: %s\n", strerror(e));
}
pthread_exit(NULL);
}
void * thread_deadtest(void *d)
{
int e;
e = pthread_mutex_lock(&mutex);
printf("thr2: %s\n", strerror(e));
e = pthread_mutex_lock(&mutex);
printf("thr2: %s\n", strerror(e));
pthread_exit(NULL);
}
int main(void)
{
/* Setup */
pthread_mutexattr_init(&mattr);
pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_ERRORCHECK);
//pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_NORMAL);
pthread_mutex_init(&mutex, &mattr);
/* Test */
pthread_create(&thread1, NULL, &thread, NULL);
pthread_join(thread1, NULL);
if (pthread_kill(thread1, 0) != 0) printf("Thread 1 has died.\n");
pthread_create(&thread2, NULL, &thread, NULL);
pthread_join(thread2, NULL);
pthread_create(&thread3, NULL, &thread_deadtest, NULL);
pthread_join(thread3, NULL);
return(0);
}
现在,当这个程序运行时,我得到以下输出:
Thread 1 has died.
thr: Device busy
mfu: Device busy
mfu: No error: 0
thr: Operation not permitted
thr2: No error: 0
thr2: Resource deadlock avoided
现在我知道这个问题已经问过很多次了,但是有没有办法强行解锁一个互斥体呢?该实现似乎只允许锁上互斥锁的线程解锁,就像它似乎主动检查的那样,即使使用普通互斥类型也是如此。
我为什么要这么做?这与编写防弹网络服务器有关,该服务器能够从大多数错误(包括线程意外终止的错误)中恢复。此时,我看不到从与锁定互斥锁不同的线程中解锁互斥锁的方法。在我看来,我有几个选择:
我已经问过这个在此之前了,但是,那些绝对想要这个功能的力量,他们不会接受否定的答案(我已经试过了),所以我有点困在这个问题上了。我不是这样设计的,我真的很想杀了那个人,但这也不是一个选择。
在有人说之前,我对pthread_kill的使用在POSIX...I检查下是合法的。
我忘了提到,这是我们正在使用的FreeBSD 9.3。
发布于 2015-05-14 20:27:37
我想出了一个可行的方法来处理这种情况。正如我前面提到的,FreeBSD不支持健壮的互斥,因此该选项被取消。还有一个线程锁定了互斥锁,它不能以任何方式解锁。
因此,为了解决这个问题,我所做的就是放弃互斥,把它的指针放到一个列表上。由于锁包装器代码使用pthread_mutex_trylock,然后在失败时放弃CPU,所以没有线程会在等待永久锁定的互斥体时被卡住。在鲁棒互斥锁的情况下,如果将EOWNERDEAD作为返回代码,则锁定互斥锁的线程将能够恢复它。
以下是定义的一些内容:
/* Checks to see if we have access to robust mutexes. */
#ifndef PTHREAD_MUTEX_ROBUST
#define TSRA__ALTERNATE
#define TSRA_MAX_MUTEXABANDON TSRA_MAX_MUTEX * 4
#endif
/* Mutex: Mutex Data Table Datatype */
typedef struct mutex_lock_table_tag__ mutexlock_t;
struct mutex_lock_table_tag__
{
pthread_mutex_t *mutex; /* PThread Mutex */
tsra_daclbk audcallbk; /* Audit Callback Function Pointer */
tsra_daclbk reicallbk; /* Reinit Callback Function Pointer */
int acbkstat; /* Audit Callback Status */
int rcbkstat; /* Reinit Callback Status */
pthread_t owner; /* Owner TID */
#ifdef TSRA__OVERRIDE
tsra_clnup_t *cleanup; /* PThread Cleanup */
#endif
};
/* ******** ******** Global Variables */
pthread_rwlock_t tab_lock; /* RW lock for mutex table */
pthread_mutexattr_t mtx_attrib; /* Mutex attributes */
mutexlock_t *mutex_table; /* Mutex Table */
int tabsizeentry; /* Table Size (Entries) */
int tabsizebyte; /* Table Size (Bytes) */
int initialized = 0; /* Modules Initialized 0=no, 1=yes */
#ifdef TSRA__ALTERNATE
pthread_mutex_t *mutex_abandon[TSRA_MAX_MUTEXABANDON];
pthread_mutex_t mtx_abandon; /* Abandoned Mutex Lock */
int mtx_abandon_count; /* Abandoned Mutex Count */
int mtx_abandon_init = 0; /* Initialization Flag */
#endif
pthread_mutex_t mtx_recover; /* Mutex Recovery Lock */
下面是一些锁恢复的代码:
/* Attempts to recover a broken mutex. */
int tsra_mutex_recover(int lockid, pthread_t tid)
{
int result;
/* Check Prerequisites */
if (initialized == 0) return(EDOOFUS);
if (lockid < 0 || lockid >= tabsizeentry) return(EINVAL);
/* Check Mutex Owner */
result = pthread_equal(tid, mutex_table[lockid].owner);
if (result != 0) return(0);
/* Lock Recovery Mutex */
result = pthread_mutex_lock(&mtx_recover);
if (result != 0) return(result);
/* Check Mutex Owner, Again */
result = pthread_equal(tid, mutex_table[lockid].owner);
if (result != 0)
{
pthread_mutex_unlock(&mtx_recover);
return(0);
}
/* Unless the system supports robust mutexes, there is
really no way to recover a mutex that is being held
by a thread that has terminated. At least in FreeBSD,
trying to destory a mutex that is held will result
in EBUSY. Trying to overwrite a held mutex results
in a memory fault and core dump. The only way to
recover is to abandon the mutex and create a new one. */
#ifdef TSRA__ALTERNATE /* Abandon Mutex */
pthread_mutex_t *ptr;
/* Too many abandoned mutexes? */
if (mtx_abandon_count >= TSRA_MAX_MUTEXABANDON)
{
result = TSRA_PROGRAM_ABORT;
goto error_1;
}
/* Get a read lock on the mutex table. */
result = pthread_rwlock_rdlock(&tab_lock);
if (result != 0) goto error_1;
/* Perform associated data audit. */
if (mutex_table[lockid].acbkstat != 0)
{
result = mutex_table[lockid].audcallbk();
if (result != 0)
{
result = TSRA_PROGRAM_ABORT;
goto error_2;
}
}
/* Allocate New Mutex */
ptr = malloc(sizeof(pthread_mutex_t));
if (ptr == NULL)
{
result = errno;
goto error_2;
}
/* Init new mutex and abandon the old one. */
result = pthread_mutex_init(ptr, &mtx_attrib);
if (result != 0) goto error_3;
mutex_abandon[mtx_abandon_count] = mutex_table[lockid].mutex;
mutex_abandon[mtx_abandon_count] = mutex_table[lockid].mutex;
mtx_abandon_count++;
mutex_table[lockid].mutex = ptr;
#else /* Recover Mutex */
/* Try locking the mutex and see what we get. */
result = pthread_mutex_trylock(mutex_table[lockid].mutex);
switch (result)
{
case 0: /* No error, unlock and return */
pthread_unlock_mutex(mutex_table[lockid].mutex);
return(0);
break;
case EBUSY: /* No error, return */
return(0);
break;
case EOWNERDEAD: /* Error, try to recover mutex. */
if (mutex_table[lockid].acbkstat != 0)
{
result = mutex_table[lockid].audcallbk();
if (result != 0)
{
if (mutex_table[lockid].rcbkstat != 0)
{
result = mutex_table[lockid].reicallbk();
if (result != 0)
{
result = TSRA_PROGRAM_ABORT;
goto error_2;
}
}
else
{
result = TSRA_PROGRAM_ABORT;
goto error_2;
}
}
}
else
{
result = TSRA_PROGRAM_ABORT;
goto error_2;
}
break;
case EDEADLK: /* Error, deadlock avoided, abort */
case ENOTRECOVERABLE: /* Error, recovery failed, abort */
/* NOTE: We shouldn't get this, but if we do... */
abort();
break;
default:
/* Ambiguous situation, best to abort. */
abort();
break;
}
pthread_mutex_consistant(mutex_table[lockid].mutex);
pthread_mutex_unlock(mutex_table[lockid].mutex);
#endif
/* Housekeeping */
mutex_table[lockid].owner = pthread_self();
pthread_mutex_unlock(&mtx_recover);
/* Return */
return(0);
/* We only get here on errors. */
#ifdef TSRA__ALTERNATE
error_3:
free(ptr);
error_2:
pthread_rwlock_unlock(&tab_lock);
#else
error_2:
pthread_mutex_unlock(mutex_table[lockid].mutex);
#endif
error_1:
pthread_mutex_unlock(&mtx_recover);
return(result);
}
因为FreeBSD是一个不断发展的操作系统,就像Linux一样,我已经做了一些准备,允许在将来使用健壮的互斥。因为没有健壮的互斥,所以如果支持鲁棒互斥,就无法进行增强的错误检查。
对于鲁棒互斥,执行增强的错误检查以验证是否需要恢复互斥。对于不支持健壮互斥的系统,我们必须信任调用方来验证所讨论的互斥对象是否需要恢复。此外,还有一些检查,以确保只有一个线程执行恢复。在互斥锁上阻塞的所有其他线程都被阻塞。我已经考虑过如何向其他线程发出恢复正在进行的信号,所以这个例程的这个方面仍然需要工作。在恢复的情况下,我正在考虑比较指针值,看看互斥是否被替换。
在这两种情况下,可以将审计例程设置为回调函数。审计例程的目的是验证和纠正受保护数据中的任何数据差异。如果审计无法更正数据,则调用另一个回调例程--数据重新初始化例程。这样做的目的是重新初始化受互斥体保护的数据。如果失败,则调用abort()来终止程序执行,并为调试目的删除一个核心文件。
对于废弃的互斥对象,指针不会被丢弃,而是放置在列表中。如果太多的互斥被放弃,那么程序就会被中止。如上所述,在互斥锁例程中,使用pthread_mutex_trylock而不是pthread_mutex_lock。这样,任何线程都不能永久地阻塞在死互斥体上。因此,一旦指针在互斥表中切换到指向新互斥对象,等待互斥的所有线程将立即切换到新互斥锁。
我确信这段代码中存在错误/错误,但这是一项正在进行的工作。虽然还没有完全完成和调试,我觉得这里有足够的理由来回答这个问题。
发布于 2015-04-26 20:32:00
如果互斥对象是处于不一致状态的鲁棒互斥体,则可以使用pthread_mutex_consistent()函数将互斥所引用的互斥保护的状态再次标记为一致。 如果鲁棒互斥锁的所有者在保存互斥锁时终止,则互斥锁变得不一致,获取互斥锁的下一个线程将通过返回值EOWNERDEAD通知状态。在这种情况下,在状态标记为一致之前,互斥体不会再次正常使用。 如果获得返回值EOWNERDEAD的互斥锁的线程在调用pthread_mutex_consistent()或pthread_mutex_unlock()之前终止,则返回值EOWNERDEAD将通知获取互斥锁的下一个线程互斥锁的状态。
发布于 2015-04-26 20:30:22
好吧,你不能用普通的线程互斥锁来做你想做的事,因为,正如你所说,你只能从锁定它的线程中解锁一个互斥体。
您可以做的是包装一个互斥锁/解锁,这样你就有了一个线程取消处理程序,如果这个线程终止了,它就会解锁互斥锁。给你一个主意:
void cancel_unlock_handler(void *p)
{
pthread_mutex_unlock(p);
}
int my_pthread_mutex_lock(pthread_mutex_t *m)
{
int rc;
pthread_cleanup_push(cancel_unlock_handler, m);
rc = pthread_mutex_lock(&m);
if (rc != 0) {
pthread_cleanup_pop(0);
}
return rc;
}
int my_pthread_mutex_unlock(pthread_mutex_t *m)
{
pthread_cleanup_pop(0);
return pthread_mutex_unlock(&m);
}
现在,您需要使用my_p线程_mutex_lock/my_p线程_mutex_unlock,而不是使用p线程锁/解锁函数。
现在,线程并不真正“意外地”终止,它调用pthread_exit或它结束,或者您将终止它,在这种情况下,上面的内容就足够了(还请注意,线程只在某些取消点退出,因此不存在推动清理处理程序和锁定互斥对象的竞争条件e.g.between ),但是逻辑错误或未定义的行为可能会留下影响整个进程的错误状态,您最好重新启动整个进程。
https://stackoverflow.com/questions/29882647
复制相似问题