在多线程环境中捕获信号 [英] trapping signals in a multithreaded environment

查看:428
本文介绍了在多线程环境中捕获信号的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个大型程序,需要使其尽可能具有弹性,并具有大量线程. 我需要捕获所有信号SIGBUS SIGSEGV,并在必要时重新初始化问题线程,或者禁用该线程以继续使用功能降低的

.

我的第一个想法是执行setjump,然后设置可以记录问题的信号处理程序,然后执行longjump回到线程中的恢复点.存在一个问题,即信号处理程序将需要确定信号来自哪个线程,以使用适当的跳转缓冲区,因为跳转回错误的线程将毫无用处.

有人知道如何确定信号处理程序中有问题的线程吗?

解决方案

在我的Linux机器上,使用syscall(SYS_gettid)可以为我工作:gcc pt.c -lpthread -Wall -Wextra

//pt.c
#define _GNU_SOURCE
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <setjmp.h>
#include <signal.h>
#include <string.h>
#include <ucontext.h>
#include <stdlib.h>

static sigjmp_buf jmpbuf[65536];

static void handler(int sig, siginfo_t *siginfo, void *context)
{
    //ucontext_t *ucontext = context;
    pid_t tid = syscall(SYS_gettid);

    printf("Thread %d in handler, signal %d\n", tid, sig);
    siglongjmp(jmpbuf[tid], 1);
}

static void *threadfunc(void *data)
{
    int index, segvindex = *(int *)data;
    pid_t tid = syscall(SYS_gettid);

    for(index = 0; index < 500; index++) {
        if (sigsetjmp(jmpbuf[tid], 1) == 1) {
            printf("Recovery of thread %d\n", tid); 
            continue;
        }
        printf("Thread %d, index %d\n", tid, index);
        if (index % 5 == segvindex) {
            printf("%zu\n", strlen((char *)2)); // SIGSEGV
        }
        pthread_yield();
    }
    return NULL;
}

int main(void)
{
    pthread_t thread1, thread2, thread3;
    int segvindex1 = rand() % 5;
    int segvindex2 = rand() % 5;
    int segvindex3 = rand() % 5;
    struct sigaction sact;

    memset(&sact, 0, sizeof sact);
    sact.sa_sigaction = handler;
    sact.sa_flags = SA_SIGINFO;
    if (sigaction(SIGSEGV, &sact, NULL) < 0) {
        perror("sigaction");
        return 1;
    }
    pthread_create(&thread1, NULL, &threadfunc, (void *) &segvindex1);
    pthread_create(&thread2, NULL, &threadfunc, (void *) &segvindex2);
    pthread_create(&thread3, NULL, &threadfunc, (void *) &segvindex3);
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    pthread_join(thread3, NULL);
    return 0;
}

为了更便于携带,可以使用 pthread_self .这是异步信号安全的.

但是获得SIGSEGV的线程应该通过异步信号安全的方式启动一个新线程,并且不应该执行siglongjmp,因为它可能导致调用非异步信号安全的功能. /p>

I have a large program that needs to be made as resilient as possible, and has a large number of threads. I need to catch all signals SIGBUS SIGSEGV, and re-initialize the problem thread if necessary, or disable the thread to continue with reduced functionality.

My first thought is to do a setjump, and then set signal handlers, that can log the problem, and then do a longjump back to a recovery point in the thread. There is the issue that the signal handler would need to determine which thread the signal came from, to use the appropriate jump buffer as jumping back to the wrong thread would be useless.

Does anyone have any idea how to determine the offending thread in the signal handler?

解决方案

Using syscall(SYS_gettid) works for me on my Linux box: gcc pt.c -lpthread -Wall -Wextra

//pt.c
#define _GNU_SOURCE
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <setjmp.h>
#include <signal.h>
#include <string.h>
#include <ucontext.h>
#include <stdlib.h>

static sigjmp_buf jmpbuf[65536];

static void handler(int sig, siginfo_t *siginfo, void *context)
{
    //ucontext_t *ucontext = context;
    pid_t tid = syscall(SYS_gettid);

    printf("Thread %d in handler, signal %d\n", tid, sig);
    siglongjmp(jmpbuf[tid], 1);
}

static void *threadfunc(void *data)
{
    int index, segvindex = *(int *)data;
    pid_t tid = syscall(SYS_gettid);

    for(index = 0; index < 500; index++) {
        if (sigsetjmp(jmpbuf[tid], 1) == 1) {
            printf("Recovery of thread %d\n", tid); 
            continue;
        }
        printf("Thread %d, index %d\n", tid, index);
        if (index % 5 == segvindex) {
            printf("%zu\n", strlen((char *)2)); // SIGSEGV
        }
        pthread_yield();
    }
    return NULL;
}

int main(void)
{
    pthread_t thread1, thread2, thread3;
    int segvindex1 = rand() % 5;
    int segvindex2 = rand() % 5;
    int segvindex3 = rand() % 5;
    struct sigaction sact;

    memset(&sact, 0, sizeof sact);
    sact.sa_sigaction = handler;
    sact.sa_flags = SA_SIGINFO;
    if (sigaction(SIGSEGV, &sact, NULL) < 0) {
        perror("sigaction");
        return 1;
    }
    pthread_create(&thread1, NULL, &threadfunc, (void *) &segvindex1);
    pthread_create(&thread2, NULL, &threadfunc, (void *) &segvindex2);
    pthread_create(&thread3, NULL, &threadfunc, (void *) &segvindex3);
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    pthread_join(thread3, NULL);
    return 0;
}

To be more portable pthread_self can be used. It is async-signal-safe.

But the thread which got the SIGSEGV should start a new thread by async-signal-safe means and should not do a siglongjmp as it could result in the invocation of non-async-signal-safe functions.

这篇关于在多线程环境中捕获信号的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆