我正在使用以下示例(基于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线程的信号?
答案 0 :(得分:1)
来自无效内存访问的SIGSEGV
(与kill
或sigqueue
发送的“假”相反)被发送到执行无效内存访问的线程,而不是整个过程。因此,您不能拥有专用的segfault-handler线程。如果要处理它,必须在发生它的线程中处理它。 (你看到shell打印Segmentation fault
的原因是,当线程中阻塞SIGSEGV
并发生段错误时,内核执行杀死进程的默认操作。实际上它是每个POSIX的UB ,但这就是Linux处理UB的方式。)
但是,请注意,您可以让SIGSEGV
的信号处理程序通过专用线程触发操作。执行此操作的一种丑陋方式是使用另一个信号,您可以通过sigqueue
发送(以及参数)。更简洁的方法(在我看来,至少)要做SIGSEGV
处理程序使用sem_post
,这是异步信号安全的,可用于唤醒等待的另一个线程信号量。