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 przez 0 ), 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 lub raise , 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 .
  • 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:
C11
      /* quick_exit is safe to call */
      quick_exit(EXIT_FAILURE);
C11
      /* 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.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow