WyjątekWyjątek (ang. exception) - mechanizm przepływu sterowania używany w procesorach oraz współczesnych językach programowania do obsługi zdarzeń wyjątkowych, a w szczególności błędów, których wystąpienie zmienia prawidłowy przebieg wykonywania programu. W momencie zajścia niespodziewanego zdarzenia generowany jest wyjątek, który musi zostać obsłużony poprzez zapamiętanie bieżącego stanu programu i przejście do procedury jego obsługi. W niektórych sytuacjach po obsłużeniu wyjątku można powrócić do wykonywania przerwanego kodu, korzystając z zapamiętanych informacji stanu. Przykładowo obsługa błędu braku strony pamięci polega najczęściej na pobraniu brakującej strony z pliku wymiany, co umożliwia kontynuowanie pracy programu, natomiast błąd dzielenia przez zero powoduje, że wykonywanie dalszych obliczeń nie ma sensu i musi zostać definitywnie przerwane. Wyjątki w procesorachWyjątki w procesorach są zdarzeniami, których wynikiem jest przerwanie wykonania bieżącego strumienia instrukcji i przekazanie sterowania do oprogramowania systemowego w celu programowej reakcji na zdarzenie. Wyjątki dzielą się na synchroniczne, obsługiwane bezpośrednio po wystąpieniu, oraz asynchroniczne, których obsługa może, w zależności od bieżącego priorytetu procesora, zostać opóźniona. Do wyjątków asynchronicznych należą przerwania. Wyjątki synchroniczne - to pułapki (traps) i błędy (faults, aborts, errors). Przerwania mogą być generowane sprzętowo przez sterowniki urządzeń zewnętrznych oraz - w nowocześniejszych architekturach (np. ARM) - programowo przez procesor. Pułapki są generowane przez jednostkę wykonawczą procesora w wyniku wykonania instrukcji, na końcu jej wykonania. Pułapki służą do trzech celów:
Przy wystąpieniu pułapki wykonanie instrukcji, która ją spowodowała, zostaje normalnie zakończone. Błędy mogą być generowane przez procesor lub jego otoczenie. Charakterystyczną cechą błędów jest to, że uniemożliwiają one zakończenie wykonania instrukcji, podczas której wystąpiły. Są to np:
Wyjątki w językach programowaniaW językach programowania wsparcie dla wyjątków realizowane jest na poziomie składni i semantyki danego języka. Zgłoszenie sytuacji wyjątkowej możliwe jest w dowolnym miejscu kodu poprzez instrukcje zwane raise lub throw. Od ich angielskich nazw w języku polskim proces ten nazywany jest podnoszeniem lub rzucaniem wyjątku. Dla dowolnej partii kodu możliwe jest zdefiniowanie bloku obsługi, który przechwytuje (ang. catch) określone rodzaje wyjątków. Poniżej widoczna jest typowa realizacja w pseudokodzie: operacje programu try operacje programu jeśli wystąpiła sytuacja wyjątkowa: throw wyjątek operacje programu catch wyjątek obsłuż wyjątek end operacje programu W momencie wykonania instrukcji throw sterowanie przekazywane jest do bloku catch, w którym powinien być zawarty kod obsługi danego rodzaju wyjątku. Po obsłużeniu, sterowanie nie powraca już do bloku try – program wykonuje się dalej od instrukcji end, zatem dalsze operacje wewnątrz tego bloku nie będą wykonywane. Zezwala się na rzucanie wyjątków z wnętrza funkcji, a także na zagnieżdżanie bloków try. W momencie wystąpienia wyjątku sterowanie jest przekazywane do pierwszego z nich, który potrafi go obsłużyć. Blok finallyIstotnym problemem w obsłudze wyjątków jest to, że wewnątrz bloku try mogły zostać tymczasowo zaalokowane jakieś zasoby, które po zakończeniu wykonywania powinny zostać zwolnione. Jeśli rzucanie i przechwytywanie wyjątku zachodzi w obrębie tej samej funkcji, odpowiedni kod można umieścić za sekcją try ... catch, lecz funkcja rzuca wyjątek, który powinien przechwycić kod wywołujący, programista sam musi zadbać, by zwolnić wszystkie tymczasowe zasoby przed jego rzuceniem. Dlatego w niektórych językach wprowadzony jest dodatkowy, opcjonalny blok finally, który musi się wykonać niezależnie od tego, czy wewnątrz try został rzucony wyjątek, czy nie. Poniżej przedstawiony jest przykład w pseudokodzie ilustrujący zagadnienie: procedura foo() try zaalokuj zasób X operacje programu jeśli wystąpiła sytuacja wyjątkowa: throw wyjątek operacje programu finally zwolnij zasób X end koniec try wywołaj foo() catch wyjątek obsłuż wyjątek end Na samym początku procedury Typy wyjątkówReprezentacja wyjątków jest zależna od konkretnego języka programowania. Przykładowo, w C++ wyjątkiem może być wartość dowolnego typu: try
{
throw 20;
}
catch(int x)
{
cout << "Wystąpił wyjątek o kodzie " << x;
}
W Javie wyjątki mogą być wyłącznie obiektami klas rozszerzających klasę try
{
throw new Exception("Informacja o błędzie");
}
catch(Exception x)
{
System.err.println(x.getMessage());
}
Bezpieczna obsługa wyjątkówSkuteczność obsługi błędów zależy od przyjętej strategii obsługi wyjątków. Jednym z największych wyzwań jest konieczność przekazywania informacji o wyjątkach między systemami. Aplikacje biznesowe mogą składać się z wielu niewielkich programów oraz być rozproszone pomiędzy kilka maszyn, co wymaga podjęcia decyzji czy dany wyjątek powinien być obsługiwany w ramach aktualnego procesu czy przekazany do innej części systemu. Odporność na wyjątkiO kodzie powiemy, że jest bezpieczny dla wyjątków (ang. exception-safe), jeśli rzucenie wyjątku w jego obrębie nie produkuje niepożądanych skutków ubocznych takich, jak wycieki pamięci, generowanie nieprawidłowego wyniku czy pozostawienie systemu w stanie niespójnym. Kod bezpieczny dla wyjątków musi spełniać niezmienniki nawet w przypadku wystąpienia błędu. Wyróżniamy kilka poziomów bezpieczeństwa[1]:
Zobacz teżPrzypisy
|