(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Обработка Segmentation Fault в C++

Вводная

 C++ является "небезопасным" ("unmanaged") языком, поэтому программы могут "вылетать" - аварийно завершать работу без сохранения данных пользователя, сообщения об ошибке и т.п. - стоит только, например, залезть в не инициализированную память. Например:
void fall()
{
  char * s = "short_text";
  sprintf(s,"This is very long text");
}

 или
void fall()
{
  int * pointer = NULL;
  *pointer = 13;
}

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

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

Способ 1: SEH

 Если Вы используете OS Windows в качестве целевой ОС и Visual C++ в качестве компилятора, то Вы можете использовать Structured Exception Handling - расширение языка С++ от Microsoft, позволяющее отлавливать любые исключения, происходящие в программе.

 Общий синтаксис обработки исключений выглядит следующим образом:

__try
{
  segfault1();
}
__except( condition1 )
{
  // обработка исключения, если condition1 == EXCEPTION_EXECUTE_HANDLER.
  // в condition1 может (должен) быть вызов метода, проверяющего
  //    тип исключения, и возвращающего EXCEPTION_EXECUTE_HANDLER
  //    если тип исключения соответствует тому, что мы хотим обработать
}
__except( condition2 )
{
  // еще один обработчик
}
__finally
{
  // то, что выполнится если ни один из обработчиков не почешется
}

 Вот "работающий пример" - "скопируй и вставь в Visual Studio"
#include <stdio.h>
#include <windows.h>
#include <excpt.h>

int memento() // обработка Segfault
{
    MessageBoxA(NULL,"Memento Mori","Exception catched!",NULL);
    return 0;
}

void fall() // генерация segfault
{
      int* p = 0x00000000;  
      *p = 13;
}

int main(int argc, char *argv[])
{
    __try
    {
        fall();
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        memento();
    }
}

 Мне лично не удалось заставить заработать __finally (поэтому я и написал __except с кодом проверки, который всегда работает), но это, возможно, кривизна моих рук.

 Данная методика, при всей ее привлекательности, имеет ряд минусов:

Один компилятор. Одна ОС. Не "чистый С++". Если Вы хотите работать без средств MS - Вы не сможете использовать эту методику
Один поток - одна таблица. Если Вы напишете конструкцию из __try… __except, внутри __try запустите другой поток и, не выходя из __try второй поток вызовет segfault, то… ничего не произойдет, программа упадет "как обычно". Потому, что на каждый поток нужно писать отдельный обработчик SEH.

 Минусов оказалось настолько много, что приходится искать второе решение.

Способ 2: POSIX - сигналы

 Способ рассчитан на то, что в момент падения программа получает POSIX-сообщение SIGSEGV. Это безусловно так во всех UNIX-системах, но это фактически так (хотя никто не гарантировал, windows - не posix-совместима) и в windows тоже.

 Методика простая - мы должны написать обработчик сообщения SIGSEGV, в котором программа совершит "прощальные действия" и, наконец, упадет:
void posix_death_signal(int signum)
{
    memento(); // прощальные действия
        signal(signum, SIG_DFL); // перепосылка сигнала
    exit(3); //выход из программы. Если не сделать этого, то обработчик будет вызываться бесконечно.
}

 после чего мы должны зарегистрировать этот обработчик:
signal(SIGSEGV, posix_death_signal);

 Вот готовый пример:
#include <stdio.h>
#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <signal.h>

int memento()
{
    int a=0;
    MessageBoxA(NULL,"Memento mori","POSIX Signal",NULL);
    return 0;
}
void fall()
{
      int* p = 0x00000000;
      *p = 13;
}
void posix_death_signal(int signum)
{
    memento();
    signal(signum, SIG_DFL);
    exit(3);
}

int main(int argc, char *argv[])
{
    signal(SIGSEGV, posix_death_signal);
    fall();
}

 В отличие от SEH, это работает всегда: решение "многопоточное" (вы можете уронить программу в любом потоке, обработчик запустится в любом случае) и "кроссплатформенное" - работает под любым компилятором, и под любой POSIX-совместимой ОС.

Ссылки по теме


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 01.11.2011 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Delphi Professional Named User
Enterprise Connectors (1 Year term)
Oracle Database Personal Edition Named User Plus Software Update License & Support
VMware Horizon Apps Standard, v7 : 10 Pack (Named User)
Rational ClearCase Multisite Floating User License
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Программирование на Microsoft Access
CASE-технологии
OS Linux для начинающих. Новости + статьи + обзоры + ссылки
СУБД Oracle "с нуля"
Компьютерная библиотека: книги, статьи, полезные ссылки
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100