Nauč se Python > Kurzy > Linuxová administrace > Procesy & Signály > Signály

Signály #

Signál (angl. signal) je způsob, jak poslat „upozornění“ běžícímu procesu. I když ten proces zrovna dělá něco jiného, může na signál zareagovat. Vlastně, jak si ukážeme, na signál zareagovat musí.

Signály jsou docela minimalistické: obsahují jen jedno malé číslo, druh signálu. Větší „zprávy“ se musí procesům posílat jiným způsobem (třeba souborem, rourou nebo přes síť), ale i v těchto případech se dá signálem upozornit že si proces má nějakou větší zprávu přečíst. Typickým příkladem je ukončení probíhajícího procesu pomocí Ctrl+C, kdy pošleme procesu signál žádající o jeho ukončení.

Přijetí signálu #

Když procesu pošleš signál, tak ten proces přeruší to co zrovna dělá a signál zpracuje. Co přesně se stane závisí na druhu signálu; možnosti jsou tyto:

  • Ukončení (angl. terminate): proces se ukončí.
  • Ignorování (angl. ignore): nic se nestane.
  • Ukončení s chybou (angl. core dump): proces se ukončí. Pokud je na to systém nastaven, stav paměti ukončeného procesu se zapíše do speciálního souboru, aby bylo jednodušší zjistit co se v momentě ukončení zrovna dělo.
  • Pozastavení (angl. stop): Proces se pozastaví. V shellu tohle dělá zkratka Ctrl+Z
  • Obnovení (angl. continue): Pozastavený proces se znovu spustí.
  • Zachycení (angl. catch): Po přijetí signálu se zavolá funkce, kterou proces nastavil.

Když se proces na signály předem nepřipraví, provede se nějaká výchozí akce. Ta závisí na druhu signálu, ale nejčastější jsou první dvě: proces se ukončí.

Druhy signálů #

Každý druh signálu má své číslo a zkratkovité jméno. Jejich seznam najdeš v manuálové stránce signal(7), pomocí příkazu:

$ man 7 signal

Pozor, man signal zobrazí stránku signal(2) – nápovědu pro funkci signal, nikoli signály obecně. To číslo je číslo „sekce“ manuálu; seznam sekcí najdeš v man man.

Každý signál má i své číslo, které se ale může na různých systémech a procesorech lišit. Lepší je proto používat jména – snad kromě čísla 9 pro SIGKILL.

Posílání signálů #

Příkaz na posílání signálů už znáš: je to kill. Přepínačem pojmenovaným podle signálu můžeš vybrat, který signál pošleš.

Zkus si to!

V jedom terminálu zadej:

$ sleep 1000

A ve druhém třeba:

$ ls -a | grep sleep
 12345678 pts/3   00:00:00 sleep
$ kill -SIGABRT 12345678

Signál SIGABRT proces dostane když se snaží provést neplatnou operaci – instrukci, které procesor nerozumí. To je většinou hodně závažná chyba. Kdybys při instalaci systému zadala „automatické hlášení chyb“, už by k vývojářům Fedory putovalo hlášení o tom, že v /usr/bin/sleep z balíčku coreutils je chyba. Pravděpodobně takhle ale virtuálku nastavenou nemáš, ale můžeš se aspoň pomocí abrt-cli list podívat, že chyba byla zaznamenána.

Podobně si můžeš vyzkoušet jiné signály, třeba:

  • SIGINT se posílá když zmáčkneš Ctrl+C.
  • SIGTERM posílá kill když mu nezadáš jiný signál.
  • SIGKILL (9) se posílá když zmáčkneš Ctrl+\; proces se „natvrdo“ ukončí. U tohoto signálu si procesy nemůžou nastavit jiné chování: proces se vždy ukončí.
  • SIGSTOP se posílá když zmáčkneš Ctrl+z; proces se pozastaví. Podobně jako u SIGKILL si i u tohoto signálu procesy nemůžou nastavit jiné chování.
  • SIGCONT posílá příkaz fg; pozastavený proces se obnoví.

Signály v Pythonu #

To, že se přijetí signálu přeruší cokoli co proces zrovna dělá, se dá přirovnat k výjimkám v Pythonu. Koneckonců, výjimka KeyboardInterrupt je jen reakce Pythonu na signál SIGINT: vzniká tak, že so Python na SIGINT připravil: řekl operačnímu systému že pro SIGINT nemá ukončit proces, ale má se zavolat funkce která způsobí výjimku.

Program v Pythonu můžeš „připravit“ na výjimky pomocí try a except. Na to, aby se program připravil na přijetí signálu, slouží funkce signal z modulu signal:

import signal
import os
import time

print('Moje PID:', os.getpid())

def handler(sig_number, frame):
    print(f'Dostal jsem signál {sig_number}: {signal.strsignal(sig_number)}')

signal.signal(signal.SIGUSR1, handler)
signal.signal(signal.SIGUSR2, handler)
signal.signal(signal.SIGINT, handler)

while True:
    time.sleep(10)

Tento program reaguje na SIGUSR1 a SIGUSR2, tedy např. kill -SIGUSR1 $pid.

Můžeš si ověřit, že pro SIGKILL a SIGSTOP funkce signal.signal selže. A taky že předefinování SIGINT přenastaví chování Ctrl+C – právě pro takové situace se hodí SIGKILL.


Toto je stránka lekce z kurzu, který probíhá nebo proběhl naživo s instruktorem.