首先,我知道互斥通常不被认为是异步安全的。这个问题涉及使用 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
中,SIGFOO
不能在 pthread_sigmask
保护的临界区期间在当前线程中引发,因此它不能中断 pthread_mutex_lock
要么。假设没有 other 信号(并且我们总是可以阻止任何其他可能有问题的信号),锁定/解锁应该始终在当前线程上以正常、不间断的方式进行,并且使用 lock/unlock 应确保其他线程不会干扰。我是对的,还是应该避免这种方法?
最佳答案
我找到了这篇论文 https://www.cs.purdue.edu/homes/rego/cs543/threads/signals.pdf通过
讨论在 sig 处理程序中安全运行 AS 不安全代码- 屏蔽掉正常上下文代码的 AS 不安全 block 中的信号(探索效率较低)或
- 使用全局 sig_atomic volatile 标志保护正常上下文代码的 AS 不安全 block ,如果设置该标志可防止处理程序中的 AS 不安全代码被输入(探索为有效)
这种方法满足了 POSIX 标准的一部分,即 在 sig-handlers 中调用 AS-unsafe 函数仅在 sighandler 中断 AS-unsafe 函数时才被视为不安全(http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03_03:向下滚动到函数列表后的第 1 段)
我认为你在这里玩弄的本质上是这个想法的更细粒度的版本,因为你并没有试图阻止
pthread_mutex_lock(&gvars_mutex);
int cond = gvars.a == 42 || gvars.b == 13;
pthread_mutex_unlock(&gvars_mutex);
从信号处理程序运行,避免与任何 AS 不安全代码发生冲突,而只是使用处理此互斥锁和这些变量的相同/相似 AS 不安全代码。
不幸的是,POSIX 似乎只有一个纯代码的信号安全概念:无论其参数如何,函数要么安全要么不安全。
但是,IMO,信号量/互斥量没有充分的理由对除了它们传递的互斥量/信号量中包含的数据或操作系统句柄进行操作,所以我认为调用 sem_wait(&sem)
/pthread_mutex_lock(&mx);
如果信号处理程序保证永远不会与 sem_wait
发生冲突,那么它应该是安全的/pthread_mutex_lock
到同一个互斥量,即使 POSIX 标准在技术上说它不应该是安全的(反驳比欢迎更受欢迎)。
关于c - 使用异步信号保证互斥安全,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14421951/