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

Почему я не могу игнорировать сигнал SIGSEGV?

Вот мой код,

#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'. Правильно?

4b9b3361

Ответ 1

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

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

signal(SIGSEGV, SIG_IGN);

к этому

signal(SIGSEGV, sighandler);

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

В вашем случае команда оскорбления является той, которая пытается разыменовать указатель NULL.

printf("%d", *p);

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

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

movl    (%rax), %esi

с регистровым регистровым значением 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.

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

Ответ 2

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

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

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

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

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

Ответ 3

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

Ответ 4

ANS: Вы указали NULL на p-указатель

char *p = NULL;

Затем вы печатаете значение * p:

printf("%d",*p);

это означает, что вы хотите напечатать значение, расположенное на нулевом адресе (то есть 0 адресное место), что невозможно. Вот почему он дает ошибку сегментации. Ошибка сегментации в случае доступа к памяти, которая недоступна прямо сейчас программе.

ПОПРОБУЙТЕ ЭТО:

char *p = (char*)malloc(sizeof(char));
*p = '\0';
printf("%d",*p);

** ПРИМЕЧАНИЕ. ** Этот тип ошибки вызывается как ошибка оборванного указателя. Висячий указатель - это случай ошибки/исключения, когда синтаксис верен, но пользователь пытается получить доступ к значению из указателя, который уже указал на NULL.