与异步信号保证互斥安全(Guaranteeing mutex safety with async s

2019-07-18 10:56发布

首先,我知道的互斥体不被认为是异步安全正常。 这个问题涉及使用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_handlerSIGFOO被阻塞,因此它不能中断pthread_mutex_lock 。 在update_gvarsSIGFOO不能在在当前线程提出pthread_sigmask -保护的关键区域,因此它不能中断pthread_mutex_lock无论是。 假设没有其他信号(和我们总是能够阻止任何其他信号可能是有问题的),锁定/解锁应该总是在当前线程上的正常的,不间断的方式进行,并利用锁定/解锁的应确保其他线程唐“T干涉。 我说得对,还是应该避免使用此方法?

Answer 1:

你明明知道你是为未定义行为的领土与sig_atomic_t提及。 话虽这么说,我可以看到没有工作在现代类Unix系统中,这个例子确切的唯一途径是,如果信号被设置了SA_NODEFER。

互斥是足以确保不同的线程(包括信号处理程序在另一个线程中运行)和sigmask将防止在这个线程递归互斥信号处理程序之间的正确同步。

话虽这么说,你是在深水中与内部信号处理程序锁。 一个信号处理程序可能是足够安全的,但如果你有两个信号处理程序做与你最终锁定顺序死锁不同的锁同样的伎俩。 这可以通过应用程序sigmasks而不是线程sigmasks有所缓解。 在信号处理一个简单的调试fprintf中肯定会违反锁定顺序,例如。

我想往回走,并重新设计我的应用程序,因为这样的东西在一个讯号处理器是它变得​​太复杂,太容易突破的迹象。 信号处理触摸一个sig_atomic_t是在C标准的唯一定义的事情,因为获得的任何其他权利的爆炸复杂性。



Answer 2:

我发现本文https://www.cs.purdue.edu/homes/rego/cs543/threads/signals.pdf ,讨论中的签名处理器运行AS-不安全的代码安全

  1. 在正常的上下文代码AS-不安全块屏蔽掉信号(探索作为效率较低)OR
  2. 保护的正常上下文代码AS-不安全块有防止AS-不安全代码在处理程序从如果设置被输入一个全球sig_atomic挥发性标志(探索作为有效)

这种做法符合POSIX标准,指出调用SIG-处理AS-不安全功能仅被视为不安全的一部分,如果sighandler中断的AS-不安全的功能( 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);

从SIG-处理程序与任何 AS-不安全代码冲突运行而只是与此相同/相似的AS-不安全的代码处理这个互斥体和这些变量。

不幸的是,POSIX似乎只具有信号安全的唯一代码的概念:一个功能或者是安全或不安全的,无论它的参数。

然而,国际海事组织,信号量/互斥体没有充分的理由对任何数据操作或OS处理比那些包含在互斥/旗语他们通过其他的,所以我想调用sem_wait(&sem) / pthread_mutex_lock(&mx); 从信号处理程序应该如果它保证从未有交锋是安全sem_wait / pthread_mutex_lock到同一个互斥体,即使POSIX标准技术上说,它不应该是安全的(大受欢迎的反驳更多)。



文章来源: Guaranteeing mutex safety with async signals