Signály je mechanizmus pro komunikaci a manipulaci s procesy.
Signál je speciální zpráva zaslaná procesu. Signály jsou asynchronní, tj.
když jej proces obdrží, ihned ho obslouží, bez dokončení aktuální funkce (ta
se dokončí až pak). Každý signál je specifikován svým číslem, v programech
se však používají jména definovaná v <bits/signum.h>
.
Všechny signály jsou pak popsány v signal(7).
raise(3) |
#include <signal.h> |
int raise (int sig); |
|
Př. Vytvořte program, který si pošle SIGTERM a tím se tedy ukončí. Program nemažte, budete ho rozšiřovat o další probírané funkce!
Proces může poslat signál jinému procesu. Např. ho ukončit zasláním SIGTERM nebo SIGKILL.
OTÁZKA: Jaký je rozdíl mezi SIGTERM a SIGKILL?
kill(1) |
kill [signal] pid ... |
|
Př.
Spusťte si program top
a zabijte ho pomocí SIGKILL.
kill(2) |
#include <sys/types.h> #include <signal.h>
|
int kill(pid_t pid, int sig); |
|
Př.
Nahraďte funkci raise
funkcí kill
.
Pro zaslání příkazu procesu jsou rezervovány dva uživatelské signály, SIGUSR1 a SIGUSR2. Někdy se pro tento účel používá i SIGHUP, na "probuzení" nebo "restart" programu.
strsignal(3) |
#define _GNU_SOURCE #include <string.h>
|
char *strsignal(int sig); |
|
psignal(3) |
#include <signal.h> |
void psignal(int sig, const char *s); |
|
pause(2) |
#include <unistd.h> |
int pause(void); |
|
Př. Uspěte program a z jiného terminálu mu pošlete SIGCONT.
Co proces udělá po obdržení signálu záleží na určení signálu. Každý signál
má defaultní určení, která určuje, co se stane, když program nestanovuje
jinak. Pro většinu signálů jej může program ignorovat nebo zavolat speciální
funkci (signal-handler). Při volání handleru se program
zastaví, vykoná se
handler a pak program pokračuje.
Např. SIGBUS (bus error), SIGSEGV (segmentation violation) a SIGFPE
(floating point exception) jsou zaslány procesu, když se pokouší provést
nepřípustnou akci. Defaultní určení signálu je ukončení procesu a vytvoření
core souboru.
signal(2) |
#define _GNU_SOURCE #include <signal.h>
|
typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t action);
|
|
Př. Napište signal-handler, který bude bude počítat, kolik signálů SIGTERM program dostane. Při SIGQUIT se ukončí a vypíše počet obdržených signálů SIGTERM. Pošlete programu pár signálů SIGTERM a pak SIGQUIT.
Protože signály jsou asynchronní, program by mohl být při zpracovávání
signálu v nekonzistentním stavu a proto by se v signal-handleru neměly volat
knihovní a systémové funkce a vykonávat I/O operace. signal-handler by měl
vykonat jen práci nezbytně nutnou k ošetření signálu.
Je možné, že signal-handler bude přerušen obdržením jiného signálu. Pokud se
to stane, je velmi těžké to debugovat, pokud je tam chyba, proto by se měl
dávat velký pozor na to, co se v signal-handleru provádí.
Nebezpečné je dokonce i přiřazení do globální proměnné, protože může
probíhat ve více instrukcích, a druhý signál se může vyskytnout mezi nimi.
Tato globální proměnná by měla být typu sig_atomic_t, do kterého se
přiřazuje v jedné instrukci (protože je to int).
#include <signal.h> |
typedef int __sig_atomic_t; typedef __sig_atomic_t sig_atomic_t;
|
Př. Proměnnou, ve které počítáte počet signálů SIGTERM, změňte na typ sig_atomic_t.
alarm(2) |
#include <unistd.h> |
unsigned int alarm(unsigned int seconds); |
|
Př. Zařiďte, aby se program sám ukončil po 10 sekundách běhu.
Proces může skončit abnormálně, jako odpověď na signál. Např. SIGBUS,
SIGSEGV a SIGFPE způsobí ukončení procesu. Další signály slouží k
explicitnímu ukončení. SIGINT je procesu zaslán, když se jej uživatel snaží
ukončit pomocí C-C. SIGTERM posílá kill
. Při volání
funkce abort si proces
sám pošle SIGABRT, což jej také ukončí s core souborem. Nejmocnější je
SIGKILL, který jej okamžitě ukončí a program tomu nemůže nijak zabránit
(blokovat, ingnorovat, ošetřit).
Čekání na potomka pomocí wait
proces blokuje, dokud se
potomek neukončí.
Většinou ale chceme, aby i rodič pokračovat dál. Jak se ale postarat o
potomka ihned po jeho ukončení, tak, aby nezůstávali zombie?
Jedna možnost je periodicky volat wait3
nebo
wait4
, které lze volat i v
neblokujícím módu.
Mnohem lepší je ale rodiče informovat o ukončení potomka, pomocí signálu.
Když se ukončí potomek, systém pošle rodiči signál SIGCHLD, který defaultně
nemá žádné určení. Funkci wait tedy zavoláme v signal-handleru signálu
SIGCHLD.
Další funkce týkající se signálů jsou: