Вопрос

У меня есть программа, которая иногда отказывается от арифметики указателей.Я знаю, что это случается, но я не могу легко проверить заранее, есть ли сбой сегмента или нет - либо я могу «предварительно просканировать» входные данные, чтобы увидеть, вызовет ли это сбой сегмента (что может быть невозможно определить), или я могу переоборудовать его, чтобы не использовать арифметику указателей, что потребует значительно большего объема работы, или я могу попытаться поймать ошибку сегмента.Итак, мой вопрос:

1) Как в C можно поймать сегфолт?Я знаю что-нибудь в ОС вызывает сбой, но что может сделать программа C в случае сбоя, чтобы умереть немного более изящно, чем просто Segmentation fault?

2) Насколько это портативно?

Я полагаю, что это крайне непереносимое поведение, поэтому, если вы опубликуете какой-либо код для обнаружения сбоя, пожалуйста, расскажите мне, над чем он работает.Я использую Mac OS X, но мне бы хотелось, чтобы моя программа работала на как можно большем количестве платформ, и я хочу посмотреть, какие у меня есть варианты.

И не волнуйтесь: по сути, все, что я хочу сделать, это распечатать более удобное для пользователя сообщение об ошибке и освободить немного malloc()потеряешь память, а потом умрешь.Я не планирую просто игнорировать все полученные сегменты и идти вперед.

Это было полезно?

Решение

Вам необходимо определить обработчик сигнала.В системах Unix это делается с помощью функции sigaction.Я сделал это с тем же кодом в 64- и 32-разрядной версии Fedora и в Sun Solaris.

Другие советы

Что ж, SIGSEGV можно перехватывать, а это POSIX, поэтому в этом смысле он переносим.

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

Вы можете использовать функцию сигнал чтобы установить новый обработчик сигнала:

   #include <signal.h>
   void (*signal(int signum, void (*sighandler)(int)))(int);

Что-то вроде следующего кода:

signal(SIGINT , clean_exit_on_sig);
signal(SIGABRT , clean_exit_on_sig);
signal(SIGILL , clean_exit_on_sig);
signal(SIGFPE , clean_exit_on_sig);
signal(SIGSEGV, clean_exit_on_sig); // <-- this one is for segmentation fault
signal(SIGTERM , clean_exit_on_sig);

void 
clean_exit_on_sig(int sig_num)
{
        printf ("\n Signal %d received",sig_num);
}

Безопасные действия в обработчике сигнала: очень ограниченное.Небезопасно вызывать любую библиотечную функцию, о которой не известно, что она является реентерабельной, что исключает, например, free() и printf().Лучше всего установить переменную и вернуть ее, но это вам не очень поможет.Также безопасно использовать системные вызовы, такие как write().

Обратите внимание, что в двух приведенных здесь примерах обратной трассировки backtrace_symbols_fd() функция будет безопасной, поскольку она напрямую использует необработанный fd, но вызов fprintf() неверно, и его следует заменить использованием write().

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

Я бы посоветовал вам переосмыслить свою стратегию относительно ввода:Почему его невозможно продезинфицировать?Самое важное — это проверка размера, для этого в стандартной библиотеке C есть соответствующие функции.Тогда, конечно, вам придется проверить достоверность введенных данных относительно содержимого.Да, это, вероятно, потребует много работы, но это единственный способ написать надежную программу.

РЕДАКТИРОВАТЬ: Я не большой эксперт по C и не знал, что даже ошибка сегментации может быть обработана обработчиком сигнала.Тем не менее, я думаю, что это неправильный путь по причинам, упомянутым выше.

Вам нужно будет предоставить обработчик SIGSEGV, этот выглядит вполне прилично.

обработка сигналов (относительно) переносима на машины Unix (включая Mac и Linux).Большие различия заключаются в деталях исключений, которые передаются в качестве аргумента процедуре обработки сигналов.Извините, но для этого вам, вероятно, понадобится куча #ifdef, если вы хотите печатать более разумные сообщения об ошибках (например, где и по какому адресу произошла ошибка)...

ок, вот фрагмент кода, с которого вы можете начать:

#include <signal.h>

/* reached when a segv occurrs */
void
SEGVFunction( SIGARGS )
{
     ...
}

...
main(...) {
    signal(SIGSEGV, SEGVFunction); /* tell the OS, where to go in case... */
    ...
    ... do your work ...
}

Ваша задача:

  • проверьте, что такое SIGARGS (зависит от ОС, поэтому используйте ifdef)
  • Узнайте, как извлечь адрес неисправности и компьютер из информации об исключении в sigArgs.
  • напечатать разумное сообщение
  • Выход

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

с уважением

Здесь приведен пример того, как перехватить SIGSEGV и распечатать трассировку стека с помощью функции backtrace() glibc:

как сгенерировать трассировку стека при сбое моего приложения на C++

Вы можете использовать это, чтобы отловить сбой и очистить его, но будьте осторожны:вам не следует делать слишком много вещей в обработчике сигналов, особенно в том, что связано с вызовами типа malloc().Существует множество вызовов, которые небезопасны для сигнала, и вы можете в конечном итоге выстрелить себе в ногу, если, скажем, вызовите malloc из malloc.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top