C Language
Obsługa sygnałów
Szukaj…
Składnia
- void (* signal (int sig, void (* func) (int))) (int);
Parametry
Parametr | Detale |
---|---|
sig | Sygnał do ustawienia procedury obsługi sygnału na jedną z SIGABRT , SIGFPE , SIGILL , SIGTERM , SIGINT , SIGSEGV lub SIGILL wartość zdefiniowaną w implementacji |
func | Procedura obsługi sygnału, która jest jedną z następujących opcji: SIG_DFL , dla domyślnej procedury obsługi, SIG_IGN aby zignorować sygnał, lub wskaźnik funkcji z podpisem void foo(int sig); . |
Uwagi
Korzystanie z procedur obsługi sygnałów tylko z gwarancjami standardu C nakłada różne ograniczenia na to, co można lub nie można zrobić w zdefiniowanej przez użytkownika funkcji obsługi sygnału.
Jeśli funkcja zdefiniowana przez użytkownika powróci podczas obsługi
SIGSEGV
,SIGFPE
,SIGILL
lub dowolnego innego przerwania sprzętowego zdefiniowanego przez implementację, zachowanie nie jest zdefiniowane przez standard C. Wynika to z faktu, że interfejs C nie daje środków do zmiany błędnego stanu (np. Po dzieleniu przez0
), a zatem po powrocie program jest w dokładnie tym samym błędnym stanie, co przed wystąpieniem przerwania sprzętowego.Jeśli funkcja zdefiniowana przez użytkownika została wywołana w wyniku wywołania
abort
lubraise
, moduł obsługi sygnału nie może ponownie wywołaćraise
.Sygnały mogą nadejść w trakcie każdej operacji, dlatego też niepodzielności operacji nie można zasadniczo zagwarantować, ani obsługa sygnałów nie działa dobrze z optymalizacją. Dlatego wszystkie modyfikacje danych w module obsługi sygnału muszą dotyczyć zmiennych
- typu
sig_atomic_t
(wszystkie wersje) lub bezatomowy typ atomowy (od C11, opcjonalnie) - które mają kwalifikacje
volatile
.
- typu
Inne funkcje ze standardowej biblioteki C zwykle nie będą przestrzegać tych ograniczeń, ponieważ mogą one zmieniać zmienne w globalnym stanie programu. Standard C zapewnia jedynie gwarancje
abort
,_Exit
(od C99),quick_exit
(od C11),signal
(dla tego samego numeru sygnału) i niektórych operacji atomowych (od C11).
Zachowanie nie jest zdefiniowane przez standard C, jeśli którakolwiek z powyższych zasad zostanie naruszona. Platformy mogą mieć określone rozszerzenia, ale na ogół nie można ich przenosić poza tę platformę.
Zwykle systemy mają własną listę funkcji bezpiecznych dla sygnału asynchronicznego , czyli funkcji biblioteki C, z których można korzystać z procedury obsługi sygnału. Np. Często
printf
jest jedną z tych funkcji.W szczególności standard C nie definiuje wiele na temat interakcji z interfejsem wątków (od C11) lub bibliotek specyficznych dla platformy, takich jak wątki POSIX. Takie platformy muszą same określać interakcję takich bibliotek wątków z sygnałami.
Obsługa sygnału za pomocą „signal ()”
Numery sygnałów mogą być synchroniczne (jak SIGSEGV
- błąd segmentacji), gdy są wyzwalane przez nieprawidłowe działanie samego programu lub asynchroniczne (jak SIGINT
- interaktywna uwaga), gdy są inicjowane spoza programu, np. Przez naciśnięcie klawisza jak Cntrl-C
.
Funkcja signal()
jest częścią standardu ISO C i można jej użyć do przypisania funkcji do obsługi określonego sygnału
#include <stdio.h> /* printf() */
#include <stdlib.h> /* abort() */
#include <signal.h> /* signal() */
void handler_nonportable(int sig)
{
/* undefined behavior, maybe fine on specific platform */
printf("Catched: %d\n", sig);
/* abort is safe to call */
abort();
}
sig_atomic_t volatile finished = 0;
void handler(int sig)
{
switch (sig) {
/* hardware interrupts should not return */
case SIGSEGV:
case SIGFPE:
case SIGILL:
/* quick_exit is safe to call */
quick_exit(EXIT_FAILURE);
/* use _Exit in pre-C11 */
_Exit(EXIT_FAILURE);
default:
/* Reset the signal to the default handler,
so we will not be called again if things go
wrong on return. */
signal(sig, SIG_DFL);
/* let everybody know that we are finished */
finished = sig;
return;
}
}
int main(void)
{
/* Catch the SIGSEGV signal, raised on segmentation faults (i.e NULL ptr access */
if (signal(SIGSEGV, &handler) == SIG_ERR) {
perror("could not establish handler for SIGSEGV");
return EXIT_FAILURE;
}
/* Catch the SIGTERM signal, termination request */
if (signal(SIGTERM, &handler) == SIG_ERR) {
perror("could not establish handler for SIGTERM");
return EXIT_FAILURE;
}
/* Ignore the SIGINT signal, by setting the handler to `SIG_IGN`. */
signal(SIGINT, SIG_IGN);
/* Do something that takes some time here, and leaves
the time to terminate the program from the keyboard. */
/* Then: */
if (finished) {
fprintf(stderr, "we have been terminated by signal %d\n", (int)finished);
return EXIT_FAILURE;
}
/* Try to force a segmentation fault, and raise a SIGSEGV */
{
char* ptr = 0;
*ptr = 0;
}
/* This should never be executed */
return EXIT_SUCCESS;
}
Użycie signal()
nakłada ważne ograniczenia na to, co wolno robić w modułach obsługi sygnałów, dodatkowe uwagi można znaleźć w uwagach.
POSIX zaleca stosowanie sigaction()
zamiast signal()
, ze względu na jego nieokreślone zachowanie i znaczne różnice w implementacji. POSIX definiuje także o wiele więcej sygnałów niż standard ISO C, w tym SIGUSR1
i SIGUSR2
, które mogą być dowolnie używane przez programistę do dowolnego celu.