Ловля сегфо в С - PullRequest
       34

Ловля сегфо в С

9 голосов
/ 16 февраля 2009

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

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

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

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

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

Ответы [ 8 ]

21 голосов
/ 16 февраля 2009

Ну, SIGSEGV является trappable, и это POSIX, поэтому он переносим в этом смысле.

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

12 голосов
/ 16 февраля 2009

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

   #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);
}
9 голосов
/ 16 февраля 2009

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

5 голосов
/ 16 февраля 2009

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

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

1 голос
/ 16 февраля 2009

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

хорошо, вот фрагмент кода для начала:

#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 () обратно в место сохранения в основном файле.

привет

1 голос
/ 16 февраля 2009

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

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

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

1 голос
/ 16 февраля 2009

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

1 голос
/ 16 февраля 2009

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

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

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

...