Поведение обработчика сигнала в многопоточной среде - PullRequest
1 голос
/ 24 марта 2020

У меня есть следующая программа, где только один поток устанавливает обработчик сигнала. Но когда я проверил код, отправив сигнал каждому потоку, все потоки выполняют обработчик сигнала. Все ли потоки используют один и тот же обработчик сигналов? Я предполагал, что это произойдет (потоки совместно используют обработчик сигналов) только тогда, когда основной процесс, который порождает эти потоки, установит обработчик сигналов.

И еще один вопрос касается контекста, в котором выполняется обработчик сигналов. Гарантируется ли, что сигнал, отправленный конкретному потоку, будет выполняться в том же контексте потока для данного сценария?

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

output:

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

Ответы [ 2 ]

2 голосов
/ 24 марта 2020

Из справочной страницы для сигнала (7) :

Расположение сигнала является атрибутом процесса: в многопоточном приложении расположение определенного сигнала является одинаково для всех потоков.

Так что все потоки используют одни и те же обработчики, да. Если вы используете pthread_kill() для отправки сигнала в указанный поток c, этот поток должен выполнить обработчик (в зависимости от маски сигнала потока, установленной с помощью pthread_sigmask(), конечно).

Также обратите внимание, что вы не можете безопасно использовать printf() или другие функции stdio в обработчике сигналов. См. Список разрешенных функций в сигнальная безопасность (7) .

0 голосов
/ 24 марта 2020

Все потоки имеют общий обработчик сигнала. Вы можете использовать pthread_sigmask (), чтобы выбрать, какие потоки имеют какие сигналы заблокированы или разблокированы и, следовательно, могут выполнять обработчик. Если несколько потоков имеют одинаковый разблокированный сигнал, любой из них может выполнить обработчик.

Таким образом, пример очистки и исправления выглядит примерно так:

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