多线程环境中的信号处理程序行为

Signal handler behavior in multi threaded environment

我有以下程序,其中只有一个线程安装信号处理程序。但是当我通过向每个线程发送信号来测试代码时,所有线程都执行信号处理程序。 所有线程是否共享相同的信号处理程序。我假设它只会在产生这些线程的主进程安装信号处理程序时发生(线程共享信号处理程序)。

还有一个问题是关于信号处理程序执行的上下文。是否保证发送到特定线程的信号将在给定场景的相同线程上下文中执行?

void handler(int signo, siginfo_t *info, void *extra)
{
        printf("handler id %d and thread id %d\n",syscall( SYS_gettid ),pthread_self());
}
void signalHandler()
{
   struct sigaction sa;
   sa.sa_flags = SA_SIGINFO;
   sa.sa_sigaction = handler;
   sigaction(SIGSEGV, &sa, NULL);
   //sigaction(SIGINT, &sa, NULL);
}
void *threadfn0(void *p)
{
        signalHandler();
        printf("thread0\n");
        while ( 1 )
        {
                pause();
        }
}
void *threadfn1(void *p)
{
        while(1){
                printf("thread1\n");
                sleep(15);
        }
        return 0;
}
void *threadfn2(void *p)
{
        while(1){
                printf("thread2\n");
                sleep(15);
        }
        return 0;
}
int main()
{
        pthread_t t0,t1,t2;
        pthread_create(&t0,NULL,threadfn0,NULL);
        printf("T0 is %d\n",t0);
        pthread_create(&t1,NULL,threadfn1,NULL);
        printf("T1 is %d\n",t1);
        pthread_create(&t2,NULL,threadfn2,NULL);
        printf("T2 is %d\n",t2);
        sleep(10);
        pthread_kill(t2,SIGSEGV);
        sleep(10);
        pthread_kill(t1,SIGSEGV);
        pthread_join(t1,NULL);
        pthread_join(t2,NULL);
        pthread_join(t0,NULL);
        return 0;
}

输出:

T0 is 1110239552
T1 is 1088309568
T2 is 1120729408
thread0
thread1
thread2
handler id 18878 and thread id 1120729408
thread2
thread1
handler id 18877 and thread id 1088309568
thread1

来自manpage for signal(7)

The signal disposition is a per-process attribute: in a multithreaded application, the disposition of a particular signal is the same for all threads.

所以所有线程共享相同的处理程序,是的。如果您使用 pthread_kill() 向特定线程发送信号,则该线程应执行处理程序(当然,取决于使用 pthread_sigmask() 设置的线程信号掩码)。

另请注意,您不能安全地在信号处理程序中使用 printf() 或其他 stdio 函数。请参阅 signal-safety(7).

中允许的函数列表

所有线程共享信号处理程序。您可以使用 pthread_sigmask() 到 select,哪些线程具有哪些信号被阻塞或未被阻塞,因此可以执行处理程序。如果多个线程具有相同的未阻塞信号,则它们中的任何一个都可以执行处理程序。

所以清理和修复示例如下所示:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/syscall.h>

static void handler (int signo, siginfo_t *info, void *extra)
{
    printf ("SIGNAL %u, handler id %lu and thread id %lu\n", signo, syscall (SYS_gettid), pthread_self ());
}

static void signalHandler (void)
{
   struct sigaction sa;
   sa.sa_flags = SA_SIGINFO;
   sa.sa_sigaction = handler;
   sigaction (SIGSEGV, &sa, NULL);
}

static void *threadfn0 (void *p)
{
    signalHandler ();

    sigset_t set;
    sigfillset (&set);
    pthread_sigmask (SIG_UNBLOCK, &set, NULL);

    printf ("thread0\n");
    while (1) {
        pause ();
    }
}

static void *threadfn1 (void *p)
{
    while (1) {
        printf ("thread1\n");
        sleep (15);
    }
    return 0;
}

static void *threadfn2 (void *p)
{
    while (1) {
        printf ("thread2\n");
        sleep (15);
    }
    return 0;
}

int main (int argc, char *argv[])
{
    pthread_t t0, t1, t2;

    // By default, block all signals in all threads and
    // unblock them only in one thread after signal handler
    // is set up, to avoid race conditions
    sigset_t set;
    sigfillset (&set);
    pthread_sigmask (SIG_BLOCK, &set, NULL);

    pthread_create (&t0, NULL, threadfn0, NULL);
    printf ("T0 is %lu\n", t0);
    pthread_create (&t1, NULL, threadfn1, NULL);
    printf ("T1 is %lu\n", t1);
    pthread_create (&t2, NULL, threadfn2, NULL);
    printf ("T2 is %lu\n", t2);

    pthread_kill (t2, SIGSEGV);
    pthread_kill (t1, SIGSEGV);
    pthread_kill (t0, SIGSEGV);

    pthread_join (t2, NULL);
    pthread_join (t1, NULL);
    pthread_join (t0, NULL);

    return 0;
}