工作线程中堆栈溢出时的信息性退出

Informative exit on stack overflow in worker thread

本文关键字:信息性 退出 栈溢出 线程 堆栈 工作      更新时间:2023-10-16

我正在编写一个C++程序,该程序将在递归数据上运行一堆工作线程,这样,即使我增加了默认的堆栈空间,线程也可能遇到堆栈溢出。

理想的情况是根据需要动态扩展堆栈,但如果这不可能,则可以接受程序失败,用户在用更大的堆栈大小重新编译后重试。

程序在没有错误消息的情况下崩溃的默认行为的问题是,用户无法知道问题是什么,也无法知道该怎么办;据用户所知,该程序可能试图除以零或取消引用空指针;因此,如果程序必须崩溃,我希望它首先将"堆栈溢出"打印到stderr。

很明显,在可移植C++中不会有解决方案,但我会很高兴有一个在Windows上工作的解决方案,另一个在Linux上工作。

在Windows上,我一直在阅读有关矢量化和结构化异常处理的文档,寻找让程序退出并显示信息性错误消息的方法;一个问题是,这些似乎是线程本地的,线程无法安全地写入stderr;充其量你会得到一个比赛条件。

有已知的处理方法吗?

操作系统(至少是Linux或Unix版本)允许您捕获堆栈错误。

类似这样的东西:

 // Note: Calling printf here is probably not a brilliant idea, 
 // as we're in a signal handler. It is NOT well-defined what happens.
 void handler(int arg)
 {
    fprintf(stderr, "Crashed due to signal handlern"); 
    exit(42);
 }

然后主要是这样的。。。

 struct sigaction sa = { handler, NULL, 0, 0, NULL };
 struct sigaction oldsa;
 sigaction(SIGSTKFLT, sa, oldsa);

我将尝试提出一个更"完整"的解决方案,并在一点时间内进行一些实验。

(我相信更换堆栈是可能的,但我不认为你可以以一种有意义的方式,在那一点上继续,只是让你以一种比简单崩溃更理智的方式恢复!)

这似乎奏效了:

#include <signal.h>
#include <unistd.h>
#include <iostream>
#include <cstdlib>
void handler(int arg)
{
    write(2, "stack overflown", 15);
    _exit(42);
}
void* duh(void *arg)
{
    if(duh(arg))
    {
        return duh(NULL);
    }
    else
    {
        return duh(arg);
    }
}
void* crash_wrapper(void *arg)
{
    static char stack[SIGSTKSZ];
    stack_t ss = {};
    ss.ss_sp = stack;
    ss.ss_size = SIGSTKSZ;
    sigaltstack(&ss, 0);
    struct sigaction sa = {};
    sa.sa_handler = handler;
    sa.sa_flags = SA_ONSTACK,
    sigfillset(&sa.sa_mask);
    sigaction(SIGSEGV, &sa, 0);
    return duh(arg);
}
int main()
{
    pthread_t t;
    int status = pthread_create(&t, 0, crash_wrapper, 0 );
    for(;;)
    {
        std::cout << "Still going..." << std::endl;
        sleep(1);
    }
}

我对处理程序中的write并不完全满意,但我尝试的所有其他方法似乎也不起作用…:(