c - 使用异步信号保证互斥安全

标签 c synchronization signals posix

首先,我知道互斥通常不被认为是异步安全的。这个问题涉及使用 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。它由普通代码更新并从信号处理程序中读取。受控代码是一个链式信号处理程序,因此它必须在信号处理程序上下文中运行(它可能使用 infocontext)。因此,所有对 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 不安全代码
  1. 屏蔽掉正常上下文代码的 AS 不安全 block 中的信号(探索效率较低)或
  2. 使用全局 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/

相关文章:

synchronization - 删除线程后我可以使用 __syncthreads() 吗?

ios - OS X 崩溃报告中的 "Application specific information"

c - 从信号处理程序调用 fflush 安全吗?

c - 关闭未使用的管道文件描述符

段错误的原因;函数调用后指针值丢失

c++ - 从 2 个进程读取/写入同一文件

c - 我在函数 `yylex' : lex. yy.c :(. text+0x2ac) 中遇到错误: undefined reference

linux - 保持主目录与 Linux Box 同步

python - 在 Numpy C 扩展中返回可变长度数组?

c++ - 如何在 emacs lisp 中获取 c/c++ 头系统包含目录?