c++ - 在分段错误处理程序中调用 pthread_exit 时终止调用而没有活动异常
问题描述
你好吗?
我将在 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 库函数的使用,C 11 标准的第 4 段:
标准库中的函数不保证是可重入的,并且可能会修改具有静态或线程存储持续时间的对象。
或者,如脚注 188 所述:
因此,信号处理程序通常不能调用标准库函数。
因此,如果您的平台没有具体保证您可以从信号处理程序中安全调用哪些函数,您 就不能从信号处理程序中进行任何函数调用。
但是由于您正在调用pthread_exit()
,假设您使用的是 POSIX 系统,POSIX 确实提供了一些关于您可以调用哪些函数的保证,称为“异步信号安全”,位于https://pubs.opengroup.org/onlinepubs/9699919799/ functions/V2_chap02.html#tag_15_04_03 . Linux 特定列表可以在https://man7.org/linux/man-pages/man7/signal-safety.7.html找到
请注意,这两个列表中都printf()
没有pthread_exit()
。
printf()
从信号处理程序中调用SIGSEGV
将是危险的 - 大多数实现printf()
将使用某种形式的malloc()
/ free()
,并且SIGSEGV
通常是malloc()
/ new
/ free()
/delete
操作遇到损坏的堆的结果。堆操作往往在某种锁下发生,以防止同时修改堆状态,因此调用所有事物printf()
的SIGSEGV
处理程序会产生巨大的死锁风险。
而且pthread_exit()
还会引起巨大的问题——它不仅试图改变进程地址空间中的进程状态,还试图改变内核空间中的进程状态。在信号处理程序中,这根本行不通。
推荐阅读
- php - 如果具有相同的 id 和相同的值或值在 PHP 中为空,则合并行
- r - 根据其他列组中的值改变列组
- android - 有没有办法通过火基地添加七段数字识别是Android ML套件?
- python - python文件名清理
- java - 尝试使用 Java SDK 将 Amazon Transcribe 与 ColdFusion 结合使用时遇到问题
- mysql - 删除重复数据
- android - 如何将旋转滚动监听器附加到 Android Wear numberPicker?
- ruby - 通过命令行输入使用 ruby 读取文件输入
- java - 如何在MVC中连接到java(jsp,servlet)中的不同表?
- reactjs - ComponentDidMount 不工作