Обработка ошибок сегментации

У меня есть приложение, которое я использую для обнаружения ошибок сегментации или Ctrl-C. Используя приведенный ниже код, я могу поймать ошибку сегментации, но обработчик вызывается снова и снова. Как я могу их остановить. К вашему сведению, я не хочу выходить из своего приложения. Я просто могу освободить все поврежденные буферы.

Является ли это возможным?

void SignalInit(void )
{

struct sigaction sigIntHandler;

sigIntHandler.sa_handler = mysighandler;
sigemptyset(&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;
sigaction(SIGINT, &sigIntHandler, NULL);
sigaction(SIGSEGV, &sigIntHandler, NULL);

}

и обработчик идет так.

void mysighandler()
{
MyfreeBuffers(); /*related to my applciation*/
}

Здесь для сигнала ошибки сегментации обработчик вызывается несколько раз, и, как очевидно, MyfreeBuffers () выдает мне ошибки для освобождения уже освобожденной памяти. Я просто хочу освободить только один раз, но все еще не хочу выходить из приложения.

Пожалуйста помоги.


person user1225606    schedule 18.04.2012    source источник


Ответы (7)


Действие по умолчанию для таких вещей, как SIGSEGV, - завершить ваш процесс, но после того, как вы установили для него обработчик, он вызовет ваш обработчик, переопределив поведение по умолчанию. Но проблема в том, что инструкция segfaulting может быть повторена после того, как ваш обработчик завершит работу, и если вы не приняли меры для исправления первой ошибки seg, повторная инструкция снова вызовет ошибку, и она будет продолжаться и повторяться.

Итак, сначала найдите инструкцию, которая привела к SIGSEGV, и попытайтесь ее исправить (вы можете вызвать что-то вроде backtrace() в обработчике и сами посмотреть, что пошло не так)

Кроме того, в стандарте POSIX говорится, что:

Поведение процесса не определено после обычного возврата из функции перехвата сигналов для сигналов [XSI] SIGBUS, SIGFPE, SIGILL или SIGSEGV, которые не были сгенерированы с помощью kill (), [RTS] sigqueue () или raise ( ).

Итак, идеальный вариант - это в первую очередь исправить свой segfault. Обработчик segfault не предназначен для обхода основного условия ошибки

Так что лучшим предложением будет- Не ловите SIGSEGV. Пусть дамп ядра. Проанализируйте ядро. Исправьте неверную ссылку на память, и готово!

person Pavan Manjunath    schedule 18.04.2012
comment
Выявление нарушений сегментации иногда бывает полезно, потому что тогда программист может сообщить, где произошла ошибка, в stderr, файле журнала или на удаленном сервере, прежде чем сбой программы. Я представляю себе это так: программист хранит глобальные переменные, сохраняя имя текущей обрабатываемой функции (разные вещи для многопоточных программ) и текущий номер строки (операторы обновления переменной номера строки (за счет вычислительной мощности) будут вставляется автоматически в каждую строку). Когда происходит segfault, программист может сообщить о SIGSEGV в tois () @ main.c: 492 для регистрации. - person sijanec; 13.10.2020

Я совершенно не согласен с утверждением «Не ловите SIGSEGV».

Это довольно хороший способ справиться с неожиданными условиями. И гораздо проще справляться с указателями NULL (которые задаются ошибками malloc) с сигнальным механизмом, связанным с setjmp/longjmp, чем распределять управление состояниями ошибок по всему вашему коду.

Однако обратите внимание, что если вы используете «sigaction» в SEGV, вы не должны забывать сказать SA_NODEFER в sa_flags - или найти другой способ справиться с тем фактом, что SEGV вызовет ваш обработчик только один раз.

#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>

static void do_segv()
{
  int *segv;

  segv = 0; /* malloc(a_huge_amount); */

  *segv = 1;
}

sigjmp_buf point;

static void handler(int sig, siginfo_t *dont_care, void *dont_care_either)
{
   longjmp(point, 1);
}

int main()
{
  struct sigaction sa;

  memset(&sa, 0, sizeof(sigaction));
  sigemptyset(&sa.sa_mask);

  sa.sa_flags     = SA_NODEFER;
  sa.sa_sigaction = handler;

  sigaction(SIGSEGV, &sa, NULL); /* ignore whether it works or not */ 

  if (setjmp(point) == 0)
   do_segv();

  else
    fprintf(stderr, "rather unexpected error\n");

  return 0;
}
person Champignac    schedule 26.09.2015
comment
Это хорошее решение. Мне не особо понравилось то, что он вылетел, а потом проанализировал дамп ядра. - person dturvene; 04.11.2017
comment
Обратите внимание, что вам необходимо по-прежнему распространять проверки ошибок с этим стилем, но другим способом. Здесь вызывающий код должен знать, как очистить любые ресурсы для всего кода, который он вызвал в случае ошибки. В традиционном стиле каждый фрагмент кода проверяет наличие ошибок, а затем очищает только свои собственные ресурсы. ИМО, последнее предпочтительнее, поскольку каждая часть должна меньше знать о других. - person gntskn; 20.07.2020
comment
Супер полезный ответ, спасибо. См. Также раздел «Стандартные сигналы» здесь ... Похоже, мне нужно будет вручную указать кучу сигналов, если я хочу все уловить ... - person Andrew; 10.11.2020
comment
Хорошо, если вы это сделаете, вы можете использовать strsignal, чтобы получить строковое представление сигнал, а затем вы можете распечатать / зарегистрировать эту строку и сам sig int в целом. Затем вы можете повторно использовать этот код для множества сигналов, используя только 1 handler() функцию! - person Andrew; 10.11.2020

Если SIGSEGV срабатывает снова, очевидный вывод состоит в том, что вызов MyfreeBuffers(); не устраняет основную проблему (и если эта функция действительно выполняет только free() выделенную память, я не уверен, почему вы могли подумать было бы).

Грубо говоря, SIGSEGV срабатывает при попытке доступа к недоступному адресу памяти. Если вы не собираетесь выходить из приложения, вам нужно либо сделать этот адрес памяти доступным, либо изменить путь выполнения с помощью longjmp().

person caf    schedule 18.04.2012

Не пытайтесь продолжить после SIG_SEGV. По сути, это означает, что среда вашего приложения каким-то образом повреждена. Возможно, вы только что разыменовали нулевой указатель, или может быть, какая-то ошибка привела к тому, что ваша программа повредила свой стек, или кучу, или некоторую переменную указателя, вы просто не знаете. Единственное безопасное решение - закрыть программу.

Совершенно законно использовать control-C. Многие приложения делают это, но вы должны быть очень осторожны в том, что вы делаете в своем обработчике сигналов. Вы не можете вызвать любую функцию, которая не является реентерабельной. Это означает, что если ваш MyFreeBuffers() вызывает функцию stdlib free(), вы, вероятно, облажались. Если пользователь нажимает Ctrl-C, когда программа находится в середине malloc() или free() и, таким образом, на полпути к манипулированию структурами данных, которые они используют для отслеживания распределения кучи, вы почти наверняка повредите кучу, если вызовете malloc() или free() в сигнале обработчик.

Единственная безопасная вещь, которую вы можете сделать в обработчике сигнала, - это установить флаг, чтобы сказать, что вы поймали сигнал. Затем ваше приложение может опрашивать флаг через определенные промежутки времени, чтобы решить, нужно ли ему выполнить какое-либо действие.

person JeremyP    schedule 18.04.2012

Ну, вы можете установить переменную состояния и только освободить память, если она не установлена. Обработчик сигнала будет вызываться каждый раз, вы не можете контролировать это AFAIK.

person g13n    schedule 18.04.2012

Я вижу в случае восстановления после SIG_SEGV, если ваша обработка событий в цикле и одно из этих событий вызывает нарушение сегментации, тогда вам нужно только пропустить это событие, продолжить обработку оставшихся событий. На мой взгляд, SIG_SEGV похож на NullPointerException в Java. Да, после любого из них состояние будет непоследовательным и неизвестным, однако в некоторых случаях вам нужно уладить ситуацию и продолжить. Например, в алгоритмической торговле вы должны приостановить выполнение ордера и позволить трейдеру вручную взять его на себя, без сбоя всей системы и разрушения всех других ордеров.

person newlogic    schedule 22.07.2014

Похоже, по крайней мере, в Linux решением может быть трюк с опцией -fnon-call-exceptions. Это даст возможность преобразовать сигнал в общее исключение C ++ и обработать его обычным способом. Посмотрите linux3 / gcc46: -fnon-call-exceptions , какие сигналы являются инструкциями по захвату? например.

person Ivan Uskov    schedule 14.11.2016