在分段错误处理程序中调用 pthread_exit 时终止调用而没有活动异常

terminate called without an active exception when calling pthread_exit in segmentation fault handler

你好吗?
我将在 Ubuntu 18.04.
修复工作线程中的分段错误 我的代码如下。

#include <thread>
#include <signal.h>
#include <string.h>
#include <pthread.h>
#include <opencv2/opencv.hpp>

void sigsegv_handler(int signum, siginfo_t *info, void *data)
{
    printf("The thread was crashed\n");
    pthread_exit(NULL);
}

void sleep_ms(int milliseconds)
{
#ifdef WIN32
    Sleep(milliseconds);
#elif _POSIX_C_SOURCE >= 199309L
    struct timespec ts;
    ts.tv_sec = milliseconds / 1000;
    ts.tv_nsec = (milliseconds % 1000) * 1000000;
    nanosleep(&ts, NULL);
#else
    usleep(milliseconds * 1000);
#endif
}

void thread_func(int i)
{
    if(i == 3)
    {
        int *p = 0;
        *p = 10;
    }
    printf("A new thread ran successfully\n");
}

int main()
{
    /* Set SIGSEGV handler. */
    struct sigaction  handler;
    sigemptyset(&handler.sa_mask);
    handler.sa_sigaction = &sigsegv_handler;
    handler.sa_flags = SA_SIGINFO;
    if (sigaction(SIGSEGV, &handler, NULL) == -1) 
        fprintf(stderr, "Cannot set SIGSEGV handler: %s.\n", strerror(errno));
    int i = 0;
    while(1)
    {
        std::thread writer_thread(thread_func, i);
        writer_thread.detach();
        sleep_ms(1000);
        printf("%d\n", i++);
    }
    return 0;
}

代码运行良好。
此代码的输出如下。
新线程运行成功
0
新线程运行成功
1
新线程运行成功
2
线程崩溃
3
新线程运行成功
4
新线程运行成功
5
新线程运行成功
6
新线程运行成功
7

但是如果我把函数“thread_func”改成下面这样,程序就崩溃了。

void thread_func(int i)
{
    if(i == 3)
    {
        int *p = 0;
        *p = 10;
    }
    cv::Mat img(100, 100, CV_8UC3); // newly inserted
    cv::resize(img, img, cv::Size(200, 200)); //newly inserted
    printf("A new thread ran successfully\n");
}

错误信息如下。

新线程运行成功
0
新线程运行成功
1
新线程运行成功
2
线程崩溃
在没有活动异常的情况下终止调用
已中止(核心已转储)

当然,我确定OpenCV模块没有问题。
你能帮我解决这个问题吗?
谢谢

简单的答案是你不能这样做:

void sigsegv_handler(int signum, siginfo_t *info, void *data)
{
    printf("The thread was crashed\n");
    pthread_exit(NULL);
}

首先,根据 7.1.4 Use of library functions, paragraph 4 of the C 11 standard:

The functions in the standard library are not guaranteed to be reentrant and may modify objects with static or thread storage duration.

或者,如 footnote 188 所总结:

Thus, a signal handler cannot, in general, call standard library functions.

因此,如果您的平台没有具体保证您可以可以从信号处理程序安全地调用哪些函数,您不能从信号处理程序中进行任何函数调用信号处理程序.

但是由于您正在调用 pthread_exit(),假设您使用的是 POSIX 系统,POSIX 确实提供 一些 保证您可以调用的函数,称为“异步信号安全,在 https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03. The Linux-specific list can be found at https://man7.org/linux/man-pages/man7/signal-safety.7.html

请注意,printf()pthread_exit() 都不在任何一个列表中

SIGSEGV 信号处理程序中调用 printf() 将是危险的 - 大多数 printf() 的实现将使用某种形式的 malloc()/free(),而 SIGSEGV 通常是 malloc()/new/free()/delete 操作遇到损坏的堆的结果。堆操作往往在某种锁下发生,以防止同时修改堆状态,因此在所有事物的 SIGSEGV 处理程序中调用 printf() 会产生巨大的死锁风险。

并且pthread_exit()也会导致巨大的问题——它不仅试图改变进程地址中的进程状态space,它还试图改变内核中的进程状态space.在信号处理程序中,这根本行不通。