简体   繁体   中英

Signal handling in C with process and 2 threads doesn't work

I am working with the following example (which is based on the example in the manpage for pthread_sigmask in linux):

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <string.h>

/* Simple error handling functions */

#define handle_error_en(en, msg) \
        do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)

static void * silly_worker(void *arg)
{
  for(int index=0,max=5; index<max; ++index) {
    printf("waiting %d of %d\n",index,max);
    sleep(1);
  }
  puts("Finished waiting.  Here comes the SIGSEGV");
  strcpy(NULL,"this will crash");
}

static void *
sig_thread(void *arg)
{
    sigset_t *set = (sigset_t *) arg;
    int s, sig;

    for (;;) {
        s = sigwait(set, &sig);
        if (s != 0)
            handle_error_en(s, "sigwait");
        printf("Signal handling thread got signal %d\n", sig);
    }
}

int
main(int argc, char *argv[])
{
    pthread_t thread;
    pthread_t thread2;
    sigset_t set;
    int s;

    /* Block SIGINT; other threads created by main() will inherit
       a copy of the signal mask. */

    sigemptyset(&set);
    sigaddset(&set, SIGQUIT);
    sigaddset(&set, SIGUSR1);
    sigaddset(&set, SIGSEGV);
    s = pthread_sigmask(SIG_BLOCK, &set, NULL);
    if (s != 0)
        handle_error_en(s, "pthread_sigmask");

    s = pthread_create(&thread, NULL, &sig_thread, (void *) &set);
    if (s != 0)
        handle_error_en(s, "pthread_create");

    /* Main thread carries on to create other threads and/or do
       other work */

    s = pthread_create(&thread2, NULL, &silly_worker, (void *) &set);
    if (s != 0)
        handle_error_en(s, "pthread_create");

    pause();            /* Dummy pause so we can test program */
}

According to the man page, this should catch the SIGSEGV generated by the silly_worker thread. But it doesn't. In fact I'm not sure what agency is getting the signal at all. When the program runs, I get the following output:

waiting 0 of 5
waiting 1 of 5
waiting 2 of 5
waiting 3 of 5
waiting 4 of 5
Finished waiting.  Here comes the SIGSEGV
Segmentation fault

You can see that the signal handler doesn't output a "Segmentation fault" string, so it must be coming from the default handler. If default, then it kind of spoils the purpose of the example - to set a signal handler and capture signals and do something with them.

I can find a lot of examples of handlers, but none of them work for this situation: none of them demonstrate a thread which causes a really obvious SIGSEGV, and catches and reports the error in its custom handler.

The question remains: how does one get a custom signal handler to get the signal from this SIGSEGV'ing thread?

The SIGSEGV from invalid memory accesses (as opposed to a "fake" one sent by kill or sigqueue ) is sent to the thread that performed the invalid memory access, not to the whole process. Thus you cannot have a dedicated segfault-handler thread. If you want to handle it, you must handle it in the thread where it happened. (The reason you're seeing the shell print Segmentation fault is because, when SIGSEGV is blocked in the thread and a segfault occurs, the kernel performs the default action of killing the process. Actually it's UB per POSIX, but this is how Linux handles the UB.)

Note, however, that you could have the signal handler for SIGSEGV trigger an action by a dedicated thread. An ugly way to do this would be with another signal, which you could send (along with an argument) via sigqueue . The cleaner way (in my opinion, at least) to do it would be to have the SIGSEGV handler use sem_post , which is async-signal-safe and can be used to wake another thread waiting on a semaphore.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM