Tohle jsou jen poznámky, ne materiály k samostudiu.
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.
Například httpd
čte konfiguraci ze spousty souborů,
ale signálem se dá přimět, aby je všechny načetl znova.
Příkaz systemctl reload httpd
dělá právě tohle: pošle HTTP serveru signál.
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:
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čí.
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
.
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 si procesy nemůžou nastavit
jiné chování.SIGCONT
posílá příkaz fg
; pozastavený proces se obnoví.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
.
Démoni systémových služeb často reagují na signály.
Když se podíváš na systemctl cat httpd
a systemctl cat sshd
,
uvidíš řádky jako:
KillSignal=SIGWINCH
– služba se ukončuje (stop
) pomocí signálu
SIGWINCH
, po kterém se server ukončí „elegantně“: přestane přijímat
nová spojení, ta co jsou otevřená zpracuje, a až potom se ukončí.ExecReload=/usr/sbin/httpd $OPTIONS -k graceful
– tady se pro reload
volá speciální příkaz, který ale ve výsledku nedělá nic moc jiného než
poslání signálu SIGUSR1
.
Když server tenhle signál dostane, dokončí otevřená spojení, ale mezitím
znovu načte konfiguraci a nová spojení začne zpracovávat s novým nastavením.ExecReload=/bin/kill -HUP $MAINPID
– pro reload
tohoto serveru se pošle
signál SIGHUP
, který server interpretuje jako žádost o to, aby znovu načetl
nastavení.Co dělá který signál, to se dozvíš v dokumentaci konkrétního serveru.
Signálů je málo, a tak se často „zneužívají“ – třeba SIGWINCH
by se formálně
měl posílat když se změní velikost terminálu; programy jako nano
nebo
top
si po jeho přijetí zjistí novou velikost a překreslí výstup.
Ale Apache httpd
ho používá pro ukončení.