首先,我知道互斥锁通常不被认为是异步安全的。这个问题涉及使用 sigprocmask
在带有异步信号和信号处理程序的多线程程序中使互斥锁安全。
我有一些类似以下概念的代码:
struct { int a, b; } gvars;
void sigfoo_handler(int signo, siginfo_t *info, void *context) {
if(gvars.a == 42 || gvars.b == 13) {
/* run a chained signal handler */
}
}
/* called from normal code */
void update_gvars(int a, int b) {
gvars.a = a;
gvars.b = b;
}
gvars
是一个全局变量,它太大了,无法适应单个 sig_atomic_t
。它由普通代码更新并从信号处理程序中读取。受控代码是链接的信号处理程序,因此必须在信号处理程序上下文中运行(它可以使用 info
或 context
)。因此,所有对 gvars
的访问都必须通过某种同步机制进行控制。更加复杂的是,该程序是多线程的,任何线程都可能接收到 SIGFOO
。
问题: 通过结合 sigprocmask
(或 pthread_sigmask
)和 pthread_mutex_t
,是否可以使用以下代码来保证同步?
struct { int a, b; } gvars;
pthread_mutex_t gvars_mutex;
void sigfoo_handler(int signo, siginfo_t *info, void *context) {
/* Assume SIGFOO's handler does not have NODEFER set, i.e. it is automatically blocked upon entry */
pthread_mutex_lock(&gvars_mutex);
int cond = gvars.a == 42 || gvars.b == 13;
pthread_mutex_unlock(&gvars_mutex);
if(cond) {
/* run a chained signal handler */
}
}
/* called from normal code */
void update_gvars(int a, int b) {
sigset_t set, oset;
sigemptyset(&set);
sigaddset(&set, SIGFOO);
pthread_sigmask(SIG_BLOCK, &set, &oset);
pthread_mutex_lock(&gvars_mutex);
gvars.a = a;
gvars.b = b;
pthread_mutex_unlock(&gvars_mutex);
pthread_sigmask(SIG_SETMASK, &oset, NULL);
}
逻辑如下:在
sigfoo_handler
中,SIGFOO
被阻塞,因此它不能中断pthread_mutex_lock
。在update_gvars
中,在pthread_sigmask
保护的临界区域中,当前线程不会引发SIGFOO
,因此它也无法中断pthread_mutex_lock
。假设没有任何其他信号(我们总是可以阻止任何可能有问题的其他信号),锁定/解锁应该始终以正常、不可中断的方式在当前线程上进行,并且锁定/解锁的使用应确保其他线程不会干扰。我说得对吗?还是应该避免这种方法?