Sygnały systemowe

W języku C jest możliwość przechwycenia różnych sygnałów systemowych. Działa to podobnie jak obsługa wyjątków w innych językach. Sporo informacji można znaleźć na stronie:

http://www.yolinux.com/TUTORIALS/C++Signals.html

Każdy sygnał ma swój konkretny numer w systemie. Np. gdy kończymy program wciskając Ctrl+C, to zostaje wysłane przerwanie SIGINT o numerze 2. W samym systemie mamy do dyspozycji kilka zdefiniowanych przerwań, które wykonują coś innego.

SIGINT - Proces został przerwany;

SIGQUIT - Ktoś poprosił proces o zatrzymanie i zrobienie zrzutu pamięci do pliku systemowego;

SIGFPE - Błąd operacji zmiennoprzecinkowej;

SIGTRAP - Debugger pyta, gdzie jest proces;

SIGSEGV - Proces próbował uzyskać dostęp do niewłaściwego miejsca w pamięci;

SIGWINCH - Zmieniono wielkość okna terminala;

SIGTERM - Ktoś własnie poprosił jądro systemu o skasowanie procesu;

SIGPIPE - Proces zapisał coś do potoku, którego nikt nie odczytuje.

Zdefiniowanych przerwań jest o wiele więcej i można o nich poczytać na stronie zamieszczonej powyżej. Niestety różne systemy operacyjne mogą się różnić liczbą dostępnych sygnałów, a nawet nazwą. Sygnały zaprezentowane na stronie dostępne są w systemach UNIX. Dla przykładu UNIXOWY SIGALRM jest nie dostępny w Windowsie (jest to sygnał z listy POSIX), a w Linuxie ten sygnał może nazywać się SIGVTALRM. Aby wyświetlić listę sygnałów można użyć polecenia

kill -l

Dla Windowsa XP i Linuxa (Weezy Raspian) uzyskamy coś takiego:

Oczywiście na Windowsie można zainstalować CygWin, który będzie naśladował Linuxa, ale osobiście wole instalację w wirtualnej maszynie lub na innej partycji. Ale z ciekawości możecie zainstalować. Cały projekt można pobrać ze strony:

http://cygwin.com/install.html

Otwierając bibliotekę signal.h można zobaczyć numery dostępnych sygnałów oraz zobaczyć które z tych sygnałów są UNIXOWE (POSIX):

#define NSIG 23

#define SIGINT 2
#define SIGILL 4
#define SIGABRT_COMPAT 6
#define SIGFPE 8
#define SIGSEGV 11
#define SIGTERM 15
#define SIGBREAK 21
#define SIGABRT 22       /* used by abort, replace SIGIOT in the future */
#define SIGABRT2 22

#ifdef _POSIX
#define    SIGHUP    1    /* hangup */
#define    SIGQUIT    3    /* quit */
#define    SIGTRAP    5    /* trace trap (not reset when caught) */
#define SIGIOT  6       /* IOT instruction */
#define    SIGEMT    7    /* EMT instruction */
#define    SIGKILL    9    /* kill (cannot be caught or ignored) */
#define    SIGBUS    10    /* bus error */
#define    SIGSYS    12    /* bad argument to system call */
#define    SIGPIPE    13    /* write on a pipe with no one to read it */
#ifdef __USE_MINGW_ALARM
#define    SIGALRM    14    /* alarm clock */
#endif
#endif

Wszystkie sygnały poniżej słowa POSIX dostępne są tylko w systemie UNIX lub LINUX. W bibilotece signal.h zdefiniowane są także specjalne symbole takie jak SIG_DFL oraz SIG_IGN, które mogą służyć np. do obsłużenia sygnału w standardowy sposób lub do ignorowania sygnałów. Aby zignorować sygnały CTRL+C wystarczy wpisać coś takiego:

signal(SIGINT,SIG_IGN)

W ten sposób nie będzie można wymusić zamknięcia programu za pomocą ctrl+c.

Najprostszy przykład obsługi sygnału, który będzie działał wszędzie zaprezentowałem poniżej:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

// funkcja zostanie wywolana, gdy nacisniemy ctrl-c (SIGINT)
void
signal_callback_handler(int signum)
{
 printf("Przechwycony signal %d\n",signum);
 // Cleanup and close up stuff here

 // Terminate program
 exit(signum);
}

int main()
{
 // Rejestracja signalu i funkcji która ma się wywołac
 signal(SIGINT, signal_callback_handler);

 while(1)
 {
 printf("Program pracuje.\n");
 sleep(1);
 }
 return EXIT_SUCCESS;
}

Program będzie co 1 sekundę wyświetlał napis "Program pracuje.", a gdy naciśniemy ctrl+c, to wyświetli się napis "Przechwycony signal  2" i program zakończy działanie.

W systemach UNIX i LINUX można korzystać ze struktury sigaction() dzięki której można obsłużyć sygnały posix.

Czasem chcemy wywołać jakieś przerwanie samemu w kodzie wtedy można zastosować funkcje raise() tak jak poniżej.

raise(SIGINT);

Dzięki tej funkcji można tworzyć unikalne zachowania oparte na sygnałach. Do dyspozycji mamy także funkcje alarm() i sleep(), które korzystają z zegara procesora. Najlepiej nie używać tych funkcji w tym samym czasie.