Почему я не могу игнорировать сигнал 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'. Правильно?
Ответы
Ответ 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.