使用进程和2个线程在C中进行信号处理不起作用

时间:2011-09-09 22:29:18

标签: c linux multithreading

我正在使用以下示例(基于linux中pthread_sigmask的联机帮助页中的示例):

#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 */
}

根据手册页,这应该捕获由silly_worker线程生成的SIGSEGV。但事实并非如此。事实上,我不确定哪个机构正在接收信号。程序运行时,我得到以下输出:

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

您可以看到信号处理程序不输出“Segmentation fault”字符串,因此它必须来自默认处理程序。如果是默认值,那么它会破坏示例的目的 - 设置信号处理程序并捕获信号并对它们执行某些操作。

我可以找到许多处理程序的例子,但是它们都不适用于这种情况:它们都没有演示导致非常明显的SIGSEGV的线程,并且在其自定义处理程序中捕获并报告错误。

问题仍然存在:如何获得自定义信号处理程序以获取来自此SIGSEGV线程的信号?

1 个答案:

答案 0 :(得分:1)

来自无效内存访问的SIGSEGV(与killsigqueue发送的“假”相反)被发送到执行无效内存访问的线程,而不是整个过程。因此,您不能拥有专用的segfault-handler线程。如果要处理它,必须在发生它的线程中处理它。 (你看到shell打印Segmentation fault的原因是,当线程中阻塞SIGSEGV并发生段错误时,内核执行杀死进程的默认操作。实际上它是每个POSIX的UB ,但这就是Linux处理UB的方式。)

但是,请注意,您可以让SIGSEGV的信号处理程序通过专用线程触发操作。执行此操作的一种丑陋方式是使用另一个信号,您可以通过sigqueue发送(以及参数)。更简洁的方法(在我看来,至少)要做SIGSEGV处理程序使用sem_post,这是异步信号安全的,可用于唤醒等待的另一个线程信号量。