Jak używać obsługi sygnałów w języku C?

Jak używać obsługi sygnałów w języku C?
W tym artykule pokażemy, jak używać obsługi sygnałów w Linux za pomocą języka C. Ale najpierw omówimy sygnał, w jaki sposób wygeneruje niektóre wspólne sygnały, których możesz użyć w swoim programie, a następnie sprawdzimy, w jaki sposób różne sygnały mogą być obsługiwane przez program, podczas gdy program wykonuje program. A więc zacznijmy.

Sygnał

Sygnał to zdarzenie, które jest generowane w celu powiadomienia procesu lub wątku, że nadeszła jakaś ważna sytuacja. Gdy proces lub wątek otrzymają sygnał, proces lub wątek zatrzymają to, co robi i podejmie jakieś działania. Sygnał może być przydatny do komunikacji między procesami.

Standardowe sygnały

Sygnały są zdefiniowane w pliku nagłówka sygnał.H Jako stała makro. Nazwa sygnału rozpoczęła się od „SIG”, a następnie krótki opis sygnału. Tak więc każdy sygnał ma unikalną wartość liczbową. Twój program powinien zawsze używać nazwy sygnałów, a nie numeru sygnałów. Przyczyną jest to, że liczba sygnału może się różnić w zależności od systemu, ale znaczenie nazw będzie standardem.

Makro NSIG jest całkowitą liczbą zdefiniowanego sygnału. Wartość NSIG jest jednym większym niż całkowita liczba zdefiniowanych sygnału (wszystkie liczby sygnałów są przydzielane kolejno).

Poniżej znajdują się standardowe sygnały:

Nazwa sygnału Opis
Wzdychanie Rozłączyć się proces. Sygnał westchnienia służy do zgłaszania odłączenia terminali użytkownika, być może dlatego, że zdalne połączenie jest utracone lub rozłącza się.
Sigint Przerwać proces. Gdy użytkownik wpisuje znak Intr (zwykle Ctrl + C) Sygnał SIGINT jest wysyłany.
Sigquit Wyrzuć proces. Gdy użytkownik wpisuje znak rezygnacji (zwykle Ctrl + \), wysyłany jest sygnał sigquit.
Sigill Nielegalne instrukcje. Gdy podjęto próbę wykonania śmieci lub uprzywilejowanej instrukcji, generowany jest sygnał Sigill. Również Sigill można wygenerować, gdy stos jest przepełniony lub gdy system ma problemy z uruchomieniem modułu obsługi sygnału.
Sigtrrap TRAP TRAP. Instrukcja przerwania i inna instrukcja pułapki wygenerują sygnał SIGTRAP. Debugger używa tego sygnału.
Sigabrt Anulować. Sygnał sigabrt jest generowany, gdy funkcja abort () jest wywoływana. Ten sygnał wskazuje błąd wykryty przez sam program i zgłaszany przez wywołanie funkcji abort ().
Sigfpe Wyjątek zmiennoprzecinkowy. Gdy wystąpił śmiertelny błąd arytmetyczny, generowany jest sygnał SIGFPE.
Sigusr1 i Sigusr2 Sygnały sigusr1 i sigusr2 mogą być używane według własnego uznania. Przydatne jest napisanie dla nich obsługi sygnału w programie, który odbiera sygnał do prostej komunikacji międzyprocesowej.

Domyślne działanie sygnałów

Każdy sygnał ma domyślne działanie, jeden z poniższych:

Termin: Proces zakończy się.
Rdzeń: Proces zakończy się i wytworzy plik zrzutu podstawowego.
IGN: Proces zignoruje sygnał.
Zatrzymywać się: Proces zatrzyma się.
Cd: Proces będzie kontynuowany od zatrzymania.

Domyślne działanie można zmienić za pomocą funkcji Handler. Nie można zmienić domyślnego działania niektórych sygnału. Sigkill I Sigabrt Domyślne działania sygnału nie można zmienić ani zignorować.

Obsługa sygnału

Jeśli proces odbiera sygnał, proces ma wybór działania dla tego rodzaju sygnału. Proces może zignorować sygnał, może określić funkcję obsługi lub zaakceptować domyślne działanie dla tego rodzaju sygnału.

  • Jeśli określone działanie sygnału jest ignorowane, sygnał jest odrzucany natychmiast.
  • Program może zarejestrować funkcję obsługi za pomocą funkcji, takiej jak sygnał Lub sigaction. Nazywa się to moduł obsługi łapie sygnał.
  • Jeśli sygnał nie został ani obsługiwany ani zignorowany, jego domyślne działanie ma miejsce.

Możemy obsłużyć sygnał za pomocą sygnał Lub sigaction funkcjonować. Tutaj widzimy, jak najprostsze sygnał() Funkcja służy do obsługi sygnałów.

int sygnał () (int signum, void (*func) (int))

sygnał() zadzwoni do Func funkcja, jeśli proces odbiera sygnał sformułowanie. sygnał() zwraca wskaźnik do funkcjonowania Func Jeśli się powiedzie lub zwraca błąd do Errno i -1 w przeciwnym razie.

Func Wskaźnik może mieć trzy wartości:

  1. Sig_dfl: Jest to wskaźnik do funkcji domyślnej systemu Sig_dfl (), zadeklarowane w H plik nagłówka. Służy do podejmowania domyślnego działania sygnału.
  2. Sig_ign: Jest to wskaźnik do systemu ignoruj ​​funkcję Sig_ign (),zadeklarowane w H plik nagłówka.
  3. Wskaźnik funkcji obsługi definiowanej przez użytkownika: Typ funkcji zdefiniowanego przez użytkownika jest void (*) (int), oznacza, że ​​typ powrotu jest nieważny i jeden argument typu int.

Podstawowy przykład obsługi sygnału

#włączać
#włączać
#włączać
void sig_handler (int signum)
// Rodzaj zwrotu funkcji obsługi powinien być nieważny
printf („\ Ninside Function \ n”);

int main ()
sygnał (sigint, sig_handler); // rejestruj moduł obsługi sygnału
for (int i = 1 ;; i ++) // infinite pętla
printf („%d: wewnętrzna funkcja główna \ n”, i);
sen (1); // Opóźnienie na 1 sekundę

powrót 0;

Na zrzucie ekranu wyjścia z przykładu1.c, widzimy, że w funkcji głównej wykonywanie Infinite Loop. Gdy użytkownik wpisał Ctrl+C, główna funkcja wykonania i wywoływana jest funkcja obsługi sygnału. Po zakończeniu funkcji obsługi, wykonanie funkcji głównej wznowiło. Gdy typ użytkownika wpisał ctrl+\, proces jest rezygnacji.

Zignoruj ​​przykład sygnałów

#włączać
#włączać
#włączać
int main ()
sygnał (sigint, sig_ign); // zarejestruj obsługę sygnału do ignorowania sygnału
for (int i = 1 ;; i ++) // infinite pętla
printf („%d: wewnętrzna funkcja główna \ n”, i);
sen (1); // Opóźnienie na 1 sekundę

powrót 0;

Tutaj funkcja obsługi jest rejestracja Sig_ign () funkcja ignorowania działania sygnału. Tak więc, gdy użytkownik wpisał Ctrl+C, Sigint Sygnał generuje, ale akcja jest ignorowana.

Przykład reżyser

#włączać
#włączać
#włączać
void sig_handler (int signum)
printf („\ Ninside Function \ n”);
sygnał (sigint, sig_dfl); // rejestruj moduł obsługi sygnału do akcji domyślnej

int main ()
sygnał (sigint, sig_handler); // rejestruj moduł obsługi sygnału
for (int i = 1 ;; i ++) // infinite pętla
printf („%d: wewnętrzna funkcja główna \ n”, i);
sen (1); // Opóźnienie na 1 sekundę

powrót 0;

Na zrzucie ekranu wyjścia z przykładu3.c, widzimy, że gdy użytkownik po raz pierwszy wpisał Ctrl+C, funkcja obsługi wywołała. W funkcji obsługi obsługi sygnał rejestruje się do rejestru Sig_dfl dla domyślnego działania sygnału. Gdy użytkownik wpisał Ctrl+C po raz drugi, proces jest zakończony, co jest domyślnym działaniem Sigint sygnał.

Wysyłanie sygnałów:

Proces może również wyraźnie wysyłać sygnały do ​​siebie lub do innego procesu. Do wysyłania sygnałów może być używana funkcja Raise () i Kill (). Obie funkcje są zadeklarowane sygnałem.H Plik nagłówka.

Int Raise (int signum)

Funkcja Raise () używana do wysyłania sygnału sformułowanie do procesu wywołania (samego). Zwraca zero, jeśli się powiedzie, i niezerowa wartość, jeśli się nie powiedzie.

Int Kill (PID_T PID, int signum)

Funkcja zabijania używana do wysyłania sygnału sformułowanie do procesu lub grupy procesowej określonej przez pid.

Przykład obsługi sygnału SIGUSR1

#włączać
#włączać
void sig_handler (int signum)
printf („Funkcja Handler Inside \ n”);

int main ()
sygnał (sigusr1, sig_handler); // rejestruj moduł obsługi sygnału
printf („Wewnątrz głównej funkcji \ n”);
Raise (sigusr1);
printf („Wewnątrz głównej funkcji \ n”);
powrót 0;

Tutaj proces wysyła sygnał Sigusr1 do siebie za pomocą funkcji Racid ().

Program przykładowy z przykładem z zabójstwem

#włączać
#włączać
#włączać
void sig_handler (int signum)
printf („Funkcja Handler Inside \ n”);

int main ()
pid_t pid;
sygnał (sigusr1, sig_handler); // rejestruj moduł obsługi sygnału
printf („Wewnątrz głównej funkcji \ n”);
PID = getpid (); // PROCES ID OF SEBY
Kill (PID, Sigusr1); // Wyślij Sigusr1 do siebie
printf („Wewnątrz głównej funkcji \ n”);
powrót 0;

Tutaj proces wysyła Sigusr1 sygnał do siebie zabić() funkcjonować. getPid () służy do uzyskania samego siebie identyfikatora procesu.

W następnym przykładzie zobaczymy, w jaki sposób procesy rodzica i dzieci komunikuje się (komunikacja między procesami) za pomocą zabić() i funkcja sygnału.

Komunikacja dla dzieci z sygnałami

#włączać
#włączać
#włączać
#włączać
void sig_handler_parent (int signum)
printf („rodzic: otrzymałem sygnał odpowiedzi od dziecka \ n”);

void sig_handler_child (int signum)
printf („dziecko: otrzymałem sygnał od rodzica \ n”);
sen (1);
Kill (getppid (), sigusr1);

int main ()
pid_t pid;
if ((pid = widelc ())<0)
printf („widelec nie powiódł się \ n”);
wyjście (1);

/ * Proces dziecięcy */
else if (pid == 0)
sygnał (sigusr1, sig_handler_child); // rejestruj moduł obsługi sygnału
printf („Dziecko: czekam na sygnał \ n”);
pauza();

/ * Proces nadrzędny */
w przeciwnym razie
sygnał (sigusr1, sig_handler_parent); // rejestruj moduł obsługi sygnału
sen (1);
printf („Parent: Signing Signal to Child \ n”);
Kill (PID, Sigusr1);
printf („rodzic: oczekiwanie na odpowiedź \ n”);
pauza();

powrót 0;

Tutaj, widelec() Funkcja tworzy proces dzieci i zwraca zero do procesu dziecka i identyfikatora procesu dziecięcego do procesu nadrzędnego. Tak więc PID został sprawdzony, aby zdecydować o procesie rodzica i dzieci. W procesie nadrzędnym spał przez 1 sekundę, aby proces dzieci mógł zarejestrować funkcję obsługi sygnału i czekać na sygnał z nadrzędnego. Po 1 sekundowej procesie nadrzędnej wyślij Sigusr1 sygnał do procesu dziecka i poczekaj na sygnał odpowiedzi od dziecka. W procesie dziecka najpierw czeka na sygnał z nadrzędnego i po odbieraniu sygnału, wywoływana jest funkcja modułu obsługi. Z funkcji obsługi procesu dziecięcego wysyła kolejne Sigusr1 sygnał do rodzica. Tutaj getppid () Funkcja służy do uzyskania identyfikatora procesu nadrzędnego.

Wniosek

Sygnał w Linux to duży temat. W tym artykule widzieliśmy, jak obsługiwać sygnał z bardzo podstawowego, a także uzyskać wiedzę, w jaki sposób sygnał generuje, w jaki sposób proces może wysyłać sygnał do siebie i innego procesu, w jaki sposób sygnał można wykorzystać do komunikacji międzyprocesowej.