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

时间:2014-07-08 23:49:55

标签: c multithreading pthreads signals

我无法获取线程来捕获正确的信号。

例如,

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

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

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

在主题2中,我使用SIGUSR1function2()的信号处理程序注册到signal(2)

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

从第3个主题,我使用pthread_kill(1, SIGUSR1)向线程1发送信号。

然而,function2()被调用,而不是function1()

这种行为是否有意,或者是否需要更改以使这些信号处理程序正常工作?

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

2 个答案:

答案 0 :(得分:9)

除了alk's answer

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

注意:信号会传递到任何未明确阻止其传递的线程。这并没有改变这一点。您仍然需要使用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;
}

我喜欢上述内容,因为它不需要pthread,并且非常强大和可靠。除了使用预处理器逻辑来选择使用哪种原子内置函数之外的视觉混乱,如果仔细观察它也会非常简单。

GCC 4.7及更高版本提供类似C ++ 11的__atomic built-ins,较旧的GCC版本和其他编译器(ICC,Pathscale,Portland Group)提供__sync legacy built-ins。线程本地存储的__thread keyword应该同样适用于所有当前的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;
}

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

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

答案 1 :(得分:6)

无法安装“每线程”信号处理程序。

来自man 7 signal(我强调):

  

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

然而, 可以将每个信号类型指向不同的线程,方法是在“每线程”基础上屏蔽任意数量的信号类型。

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