terminate вызывается без активного исключения при вызове 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 нет проблем.
Не могли бы вы помочь мне решить эту проблему?
благодаря
1 ответ
Простой ответ: вы не можете этого сделать:
void sigsegv_handler(int signum, siginfo_t *info, void *data)
{
printf("The thread was crashed\n");
pthread_exit(NULL);
}
Во-первых, согласно 7.1.4 Использование библиотечных функций, параграф 4 стандарта C 11:
Не гарантируется, что функции в стандартной библиотеке будут реентерабельными, и они могут изменять объекты со статической продолжительностью хранения или хранением потоков.
Или, как указано в сноске 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()
также вызовет огромные проблемы - он не только пытается изменить состояние процесса в адресном пространстве процесса, но и пытается внести изменения в состояние процесса в пространстве ядра. Изнутри обработчика сигнала это просто не сработает.