2014-07-08 160 views
11

我无法获得线程来捕获正确的信号。Unix线程和信号:每个线程的信号处理程序

例如,

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

然后,它使用signal(2)将函数SIGUSR1的信号处理函数设置为function1()。

主线程创建新线程,与TID 2.

在螺纹2,I寄存器使用signal(2)SIGUSR1function2()用于信号处理程序。

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

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

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

这是行为的目的,还是有什么我需要改变,让这些信号处理程序的工作?

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

+3

信号是一个进程调用,而不是每个线程一个,如果你调用它,它会设置进程中所有线程的处理程序。信号和线程是一个复杂的话题。你最好问自己该做什么。 –

+3

在多线程程序中使用'signal'基本上是未定义的行为。它在手册中是这样说的。 –

+0

源代码请。 –

回答

7

除了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; 
} 

我喜欢上面的,因为它不需要并行线程,并且非常稳定和可靠的。除了由于具有预处理器逻辑来选择使用哪种类型的原子内置插件而造成的视觉混乱之外,如果仔细观察,它也非常简单。

GCC 4.7及更高版本提供C++ 11-like __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。 (可以用一个实时信号做到这一点,但双信号模型实现起来更简单并且更容易维护。)

这样的信号反射或扇出有时是有用的,并且它没有什么不同这种方法。尽管如此,如果有人对此感兴趣,还是有足够的差别来保证自己的问题。

+0

你对我的设计进行反思是绝对正确的。我选择使用pthread_cancel,因为它似乎很适合我的需求。但是,您的列表中可能存在一个小问题。 ''pthread_getspecific()'根据手册页不是异步信号安全的,我不认为'__thread'存储访问也是。 – tomKPZ

+0

@ user1887231:实际上,[它们是](http://sourceware.org/ml/libc-alpha/2012-06/msg00372.html)。这些标准还没有解决异步信号安全线程特定变量的问题。至少在GNU工具中,'pthread_getspecific()'和'__thread'变量在实践中是异步信号安全的*。 (在这里,信号处理程序只读取特定于线程的变量,所以“不在TLS之外”的情况不适用)。如果您担心这一点,请告诉我,我会想出一个解决方法(我想我知道一种方式)。 –

4

不可能安装“每线程”信号处理程序。

man 7 signal(由我强调):

的信号配置是每进程属性:在多线程应用程序,一个特定信号的配置是所有线程的相同。

它然而能够指导每个信号型到一个不同的线程,通过在“每线程”基地屏蔽掉任何数量的信号类型的接收。

在如何将一组信号类型的定向到一个特定的线程,你可能会喜欢有看看这个答案:https://stackoverflow.com/a/20728819/694576