Подтвердить что ты не робот

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

У меня есть приложение, которое я использую, чтобы поймать любую ошибку сегментации или 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() дает мне ошибки для освобождения уже освобожденной памяти. Я просто хочу освободить только один раз, но все равно не хочу выходить из приложения.

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

4b9b3361

Ответ 1

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

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

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

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

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

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

Ответ 2

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

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

Ответ 3

Я совсем не согласен с утверждением "Не поймайте 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;
}

Ответ 4

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

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

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

Ответ 5

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

Ответ 6

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

Ответ 7

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