Cos’è un segnale
Un segnale è un messaggio di una parola che il kernel consegna a un processo: «il terminale si è chiuso», «hai toccato memoria non valida», «per favore fermati». Il processo può installare un handler per la maggior parte di essi, ignorarli, o accettare l’azione predefinita, che di solito è terminare (a volte con un core dump). Due non possono essere catturati né ignorati: SIGKILL termina il processo sul posto e SIGSTOP lo congela.
TERM contro KILL
kill PID invia SIGTERM: una richiesta. Un programma ben scritto lo cattura, finisce la richiesta corrente, svuota i file, chiude le connessioni ed esce; è ciò che systemd, Docker e Kubernetes inviano per primo. kill -9 invia SIGKILL: il kernel rimuove il processo senza eseguire alcun suo codice, quindi i lock restano presi, i file temporanei restano, e le scritture in corso vanno perse. Usalo solo dopo che a TERM è stato dato tempo (systemd aspetta TimeoutStopSec, Docker 10 secondi di default) e ha fallito.
I segnali come API
- SIGHUP: i demoni ricaricano la configurazione (nginx, sshd, HAProxy con semantica
-sftramite reload di systemd). Per un processo in primo piano significa che il terminale è sparito;nohuplo ignora. - SIGUSR1 / SIGUSR2: definiti dal programma. nginx riapre i log su USR1 e aggiorna il binario su USR2;
ddstampa l’avanzamento su USR1; Gunicorn e Unicorn li usano per i riavvii graceful. - SIGPIPE: scrittura su una pipe il cui lettore è uscito. La sua azione predefinita, terminare, è il motivo per cui
yes | headfinisce e per cui i server devono ignorarlo o gestire gli errori di scrittura. - SIGCHLD: un figlio è uscito; i padri raccolgono i figli con
wait(), e un padre che non lo fa lascia zombie. - SIGWINCH: il terminale è stato ridimensionato; editor e multiplexer ridisegnano.
Inviare segnali
kill -TERM 1234 # per nome (o kill -15 1234)
kill -s USR1 $(pidof nginx)
pkill -HUP sshd # per nome di processo
killall -9 runaway # tutti i processi con quel nome
systemctl kill -s HUP myservice
docker kill --signal=SIGUSR1 container I numeri 1–15 sono gli stessi su ogni Unix; oltre differiscono per architettura (SIGUSR1 è 10 su x86 e 30 su SPARC/MIPS), ed è per questo che gli script dovrebbero usare i nomi.
Domande
Che differenza c’è tra SIGTERM e SIGKILL?
SIGTERM chiede al processo di uscire e può essere gestito; SIGKILL lo termina immediatamente e non può essere catturato. Prova sempre prima TERM; KILL è per i processi bloccati.
Cos’è il segnale 11 / SIGSEGV?
Un segmentation fault: il programma ha letto o scritto memoria che non gli appartiene, di solito un puntatore nullo o pendente in codice C/C++ o un bug in un’estensione nativa. La shell riporta il codice di uscita 139.
Perché il mio processo è uscito con 137 se nessuno ha eseguito kill -9?
L’ha fatto il kernel: l’out-of-memory killer invia SIGKILL al processo più grande quando la memoria finisce, e i runtime dei container fanno lo stesso quando viene raggiunto un limite di memoria. Controlla dmesg o il flag OOMKilled del container.
Posso ignorare SIGINT così Ctrl-C non ferma il mio script?
Sì: trap "" INT in bash, o signal.signal(SIGINT, SIG_IGN) in Python. Di solito è meglio catturarlo e fare pulizia prima di uscire, così l’utente può comunque fermare lo script.