Unix pthread和信号:每个线程的信号处理程序 [英] Unix pthreads and signals: per thread signal handlers

查看:72
本文介绍了Unix pthread和信号:每个线程的信号处理程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm having trouble getting threads to catch the correct signals.

例如

我首先启动一个主线程(tid 1).

I first start a main thread (tid 1).

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

Then, it sets a signal handler for SIGUSR1 to function1(), using signal(2).

主线程创建一个新的tid 2线程.

The main thread creates a new thread, with tid 2.

在线程2中,我使用signal(2)SIGUSR1function2()注册了一个信号处理程序.

In thread 2, I register a signal handler for SIGUSR1 to function2() using signal(2).

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

Thread 1 then creates a thread 3 (tid 3).

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

From thread 3, I use pthread_kill(1, SIGUSR1) to send a signal to thread 1.

但是,调用function2()而不是调用function1().

However, function2() gets called, not function1().

此行为是故意的,还是我需要更改某些东西才能使这些信号处理程序正常工作?

Is this behavior intended, or is there something I need to change to get these signal handlers to work?

我已经进行了一些调试,结果是信号正在发送到线程1,但是出于某些原因从线程1调用了function2().有解决方法吗?

I've done a bit of debugging and it turns out that the signal IS getting sent to thread 1, however function2() is getting called from thread 1 for some reason. Is there a workaround for this?

推荐答案

除了 alk的答案:

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

You can use a per-thread function pointer to choose which function is executed when a certain signal is delivered, on a per-thread manner.

注意:信号将传递到未明确阻止其传递的 any 线程.这不会改变这一点.您仍然需要使用pthread_kill()或类似的机制将信号定向到特定线程.引发或发送给进程(而不是特定线程)的信号,仍将由随机线程(不阻塞线程的信号)进行处理.

Note: Signals are delivered to any thread that is not explicitly blocking its delivery. This does not change that. You still need to use pthread_kill() or similar mechanisms to direct the signal to a specific thread; signals that are raised or sent to the process (instead of a specific thread), will still be handled by a random thread (among those that do not block it).

我想不出任何我个人更喜欢这种方法的用例;迄今为止,总有其他方法,更简便,更好.因此,如果您正在考虑为一个实际的应用程序实现这样的事情,请退后一步,重新考虑您的应用程序逻辑.

I cannot think of any use case where I'd personally prefer this approach; thus far there has always been some other way, something else easier and better. So, if you are considering implementing something like this for a real application, please step back and reconsider your application logic.

但是,由于该技术是 possible 的可能,因此,我将按照以下方式实施该技术:

But, since the technique is possible, here's how I might implement it:

#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;
}

我喜欢上面的内容,因为它不需要pthread,并且非常健壮和可靠.除了由于使用预处理器逻辑来选择所使用的原子内置样式的方式而造成的视觉混乱之外,如果仔细查看,这也非常简单.

I like the above because it does not require pthreads, and is very robust and reliable. Aside from the visual mess due to having that preprocessor logic to select which style of atomic built-ins are used, it's very simple too, if you look at it carefully.

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

GCC 4.7 and later provide C++11-like __atomic built-ins, older GCC versions and other compilers (ICC, Pathscale, Portland Group) provide __sync legacy built-ins. The __thread keyword for thread-local storage should similarly be available in all current POSIX-y systems.

如果您使用的是旧系统,或坚持遵循标准,则以下代码应具有大致等效的行为:

If you have an archaic system, or insist on standards compliance, the following code should have roughly equivalent behaviour:

#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;
}

我认为与我实际上曾经使用过的类似代码的最接近的真实生活是这样一个环境:我将一个实时信号(SIGRTMIN+0)阻塞在一个线程之外的所有线程中,用作反射器:它发送了另一个多个工作线程的实时信号(SIGRTMIN+1),以中断阻塞的I/O. (可以使用单个实时信号来执行此操作,但是两个信号模型更易于实现且更易于维护.)

I think the closest real-life equivalent to code like this that I've actually ever used, is one where I used one realtime signal (SIGRTMIN+0) blocked in all but one thread, as a reflector: it sent another realtime signal (SIGRTMIN+1) to a number of worker threads, in order to interrupt blocking I/O. (It is possible to do this with a single realtime signal, but the two-signal model is simpler to implement and easier to maintain.)

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

Such signal reflection or fanout is sometimes useful, and it's not that different from this approach. Different enough to warrant its own question, though, if someone is interested.

这篇关于Unix pthread和信号:每个线程的信号处理程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆