Unix pthreads和信号:每个线程信号处理程序

tom*_*KPZ 16 c multithreading signals pthreads

我无法让线程捕获正确的信号.

例如,

我首先开始一个主线程(tid 1).

然后,它使用设置SIGUSR1函数1()的信号处理程序signal(2).

主线程使用tid 2创建一个新线程.

在螺纹2,I寄存器的信号处理程序SIGUSR1,以function2()使用signal(2).

然后,线程1创建一个线程3(tid 3).

从线程3,我pthread_kill(1, SIGUSR1)用来向线程1发送信号.

然而,function2()被召唤,而不是function1().

是出于这种行为,还是需要更改以使这些信号处理程序工作?

编辑:我已经做了一些调试,结果发现信号IS被发送到线程1,但是function2()由于某种原因从线程1调用.这有解决方法吗?

Nom*_*mal 9

除了alk的答案:

您可以使用每线程函数指针以每线程方式选择在传递特定信号时执行的函数.

注意:信号将传递给未明确阻止其传递的任何线程.这并没有改变这一点.您仍然需要使用pthread_kill()或类似的机制将信号指向特定的线程; 引发或发送到进程(而不是特定线程)的信号仍将由随机线程(在那些不阻塞它的线程中)处理.

我想不出任何个人喜欢这种方法的用例; 到目前为止,总会有其他方式,其他方式更容易和更好.因此,如果您正在考虑为实际应用程序实现类似的功能,请退一步并重新考虑您的应用程序逻辑.

但是,由于该技术是可行的,这里我可以实现它:

#include <signal.h>

/* Per-thread signal handler function pointer.
 * Always use set_thread_SIG_handler() to change this.
*/
static __thread void (*thread_SIG_handler)(int, siginfo_t *, void *) = (void *)0;

/* Process-wide signal handler.
*/
static void process_SIG_handler(int signum, siginfo_t *info, void *context)
{
    void (*func)(int, siginfo_t *, void *);

#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
    func = __atomic_load_n(&thread_SIG_handler, __ATOMIC_SEQ_CST);
#else
    func = __sync_fetch_and_add(&thread_SIG_handler, (void *)0);
#endif

    if (func)
        func(signum, info, context);
}

/* Helper function to set new per-thread signal handler
*/
static void set_thread_SIG_handler(void (*func)(int, siginfo_t *, void *))
{
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
    __atomic_store_n(&thread_SIG_handler, func, __ATOMIC_SEQ_CST);
#else
    void (*oldfunc)(int, siginfo_t *, void *);
    do {
        oldfunc = thread_SIG_handler;
    } while (!__sync_bool_compare_and_swap(&thread_SIG_handler, oldfunc, func));
#endif
}

/* Install the process-wide signal handler.
*/
int install_SIG_handlers(const int signum)
{
    struct sigaction act;
    sigemptyset(&act.sa_mask);
    act.sa_sigaction = process_SIG_handler;
    act.sa_flags = SA_SIGACTION;
    if (sigaction(signum, &act, NULL))
        return errno;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我喜欢上面因为它不需要pthreads,并且非常强大和可靠.除了由于使用预处理器逻辑选择使用哪种样式的原子内置函数而造成的视觉混乱之外,如果仔细观察它,它也非常简单.

GCC 4.7及更高版本提供类似C++ 11的__atomic内置函数,较旧的GCC版本和其他编译器(ICC,Pathscale,Portland Group)提供__sync遗留内置函数.线程本地存储的__thread关键字应该同样适用于所有当前的POSIX-y系统.

如果您有一个过时的系统,或坚持遵守标准,则以下代码应具有大致相同的行为:

#include <pthread.h>
#include <signal.h>
#include <errno.h>

static pthread_key_t  thread_SIG_handler_key;

static void process_SIG_handler(int signum, siginfo_t *info, void *context)
{
    void (*func)(int, siginfo_t *, void *);

    *((void **)&func) = pthread_getspecific(thread_SIG_handler_key);
    if (func)
        func(signum, info, context);
}

static int set_thread_SIG_handler(void (*func)(int, siginfo_t *, void *))
{
    sigset_t block, old;
    int result;

    sigemptyset(&block);
    sigaddset(&block, SIG); /* Use signal number instead of SIG! */
    result = pthread_sigmask(SIG_BLOCK, &block, &old);
    if (result)
        return errno = result;

    result = pthread_setspecific(thread_SIG_handler_key, (void *)func);
    if (result) {
        pthread_sigmask(SIG_SETMASK, &old, NULL);
        return errno = result;
    }

    result = pthread_sigmask(SIG_SETMASK, &old, NULL);
    if (result)
        return errno = result;

    return 0;
}

int install_SIG_handlers(const int signum)
{
    struct sigaction act;
    int result;

    result = pthread_key_create(&thread_SIG_handler_key, NULL);
    if (result)
        return errno = result;

    sigemptyset(&act.sa_mask);
    act.sa_sigaction = process_SIG_handler;
    act.sa_flags = SA_SIGACTION;
    if (sigaction(signum, &act, NULL))
        return errno;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我认为与我实际使用过的最接近真实生活的代码就是我使用一个实时信号(SIGRTMIN+0)除了一个线程之外的一个线程,作为反射器:它发送另一个实时信号(SIGRTMIN+1)到一个工作线程数,以便中断阻塞I/O. (可以使用单个实时信号执行此操作,但双信号模型实现起来更简单,更易于维护.)

这种信号反射或扇出有时是有用的,并且与这种方法没有什么不同.但是,如果有人感兴趣的话,不同的是保证自己的问题.


alk*_*alk 6

无法安装"每线程"信号处理程序.

man 7 signal(我强调):

信号处理是每进程属性:在多线程应用程序中,特定信号的处置对于所有线程都是相同的.

然而,可以指导每个信号型到一个不同的线程,通过在"每线程"基地屏蔽掉任何数量的信号类型的接收.

关于如何将一组信号类型指向特定的线程,您可能希望看看这个答案:https://stackoverflow.com/a/20728819/694576