Почему я не могу игнорировать сигнал SIGSEGV? - PullRequest
16 голосов
/ 10 декабря 2011

Вот мой код,

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

int main(int argc,char ** argv)
   {
     char *p=NULL;
     signal(SIGSEGV,SIG_IGN); //Ignoring the Signal
     printf("%d",*p);
     printf("Stack Overflow"); //This has to be printed. Right?
   return 0;
    }

При выполнении кода я получаю ошибку сегментации. Я проигнорировал сигнал, используя SIG_IGN. Так что я не должен получить ошибку сегментации. Правильно? Затем оператор printf() после вывода значения '* p' также должен быть выполнен. Правильно?

Ответы [ 4 ]

20 голосов
/ 10 декабря 2011

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

Если вы собираетесь поймать сигнал, измените это

signal(SIGSEGV, SIG_IGN);

к этому

signal(SIGSEGV, sighandler);

Вы, вероятно, должны также использовать sigaction() вместо signal(). Смотрите соответствующие справочные страницы.

В вашем случае нарушающая инструкция пытается разыменовать нулевой указатель.

printf("%d", *p);

То, что следует, полностью зависит от вашей платформы.

Вы можете использовать gdb, чтобы установить, какая конкретная инструкция по сборке запускает сигнал. Если ваша платформа похожа на мою, вы найдете инструкцию

movl    (%rax), %esi

с регистром rax, имеющим значение 0, т.е. NULL. Один (непереносимый!) Способ исправить это в вашем обработчике сигналов - использовать третий аргумент сигнала, который получает ваш обработчик, то есть пользовательский контекст. Вот пример:

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

#define __USE_GNU
#include <ucontext.h>

int *p = NULL;
int n = 100;

void sighandler(int signo, siginfo_t *si, ucontext_t* context)
{
  printf("Handler executed for signal %d\n", signo);
  context->uc_mcontext.gregs[REG_RAX] = &n;
}

int main(int argc,char ** argv)
{
  signal(SIGSEGV, sighandler);
  printf("%d\n", *p); // ... movl (%rax), %esi ...
  return 0;
}

Эта программа отображает:

Handler executed for signal 11
100

Сначала он запускает обработчик, пытаясь разыменовать NULL-адрес. Затем обработчик устраняет проблему, устанавливая rax по адресу переменной n. Как только обработчик возвращается, система повторяет ошибочную инструкцию, и это время успешно выполняется. printf() получает 100 в качестве второго аргумента.

Я настоятельно рекомендую не использовать такие непереносимые решения в своих программах.

14 голосов
/ 10 декабря 2011

Вы можете игнорировать сигнал, но вы должны что-то с этим сделать.Я полагаю, что вы делаете в опубликованном коде (игнорирование SIGSEGV через SIG_IGN) не будет работать вообще по причинам, которые станут очевидными после прочтения жирным шрифтом.

Когда вы делаете что-то, что заставляет ядро ​​отправить вам SIGSEGV:

  • Если у вас нет обработчика сигнала, ядро ​​убивает процесс, и это то, что
  • Если вы делаетеесть обработчик сигнала
    • Ваш обработчик вызывается
    • Ядро перезапускает ошибочную операцию

Так что если выне делайте ничего, чтобы упираться в это, оно будет непрерывно повторяться.Если вы do перехватываете SIGSEGV и не выходите, тем самым мешая нормальному потоку, вы должны:

  • исправить все так, чтобы нарушающая операция не перезапускаласьили
  • исправить макет памяти так, чтобы то, что было обидно, было исправно при следующем запуске
10 голосов
/ 11 декабря 2011

Другой вариант - заключить в скобки рискованную операцию с помощью setjmp / longjmp, то есть

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

static jmp_buf jbuf;
static void catch_segv()
{
    longjmp(jbuf, 1);
}

int main()
{
    int *p = NULL;

    signal(SIGSEGV, catch_segv);
    if (setjmp(jbuf) == 0) {
        printf("%d\n", *p);
    } else {
        printf("Ouch! I crashed!\n");
    }
    return 0;
}

Шаблон setjmp / longjmp здесь аналогичен блоку try / catch.Это очень рискованно, и не спасет вас, если ваша рискованная функция заполняет стек или распределяет ресурсы, но вылетает, прежде чем они освобождаются.Лучше проверить свои указатели, а не косвенные через плохие.

1 голос
/ 07 ноября 2018

Пытаться игнорировать или обрабатывать SIGSEGV - неправильный подход.SIGSEGV, запускаемый вашей программой всегда , указывает на ошибку.Либо в вашем коде, либо в коде, которому вы делегируете.Как только у вас возникла ошибка, может произойти все что угодно .Нет никакого разумного действия "очистки" или исправления, которое может выполнить обработчик сигнала, потому что он не может знать, где сигнал был запущен или какое действие выполнить.Лучшее, что вы можете сделать, - это позволить программе быстро завершиться сбоем, чтобы у программиста была возможность отладить ее, когда она все еще находится в состоянии немедленного сбоя, вместо того, чтобы она (вероятно) потерпела неудачу позже, когда причина сбоя была устранена.затемняется.И вы можете вызвать быстрый сбой программы, не пытаясь игнорировать или обрабатывать сигнал.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...