Signál (informatika)
Úvod
Signály jsou jednoduché zprávy, které se posílají procesům. Signály slouží v Unix- like systémech k informování procesu o výskytu události. Pomocí signálů lze meziprocesově komunikovat a manipulovat s procesy (ukončovat, pozastavovat, atd.).
Příjemcem/odesílatelem signálu může být jen proces (v Unixech může být odesílatelem i jádro).
Jestliže proces obdrží signál, začne se ihned provádět příslušná akce, i když nebyla dokončena právě zpracovávaná funkce- mluvíme tzv. o asynchronních signálech. Po dokončení akce program pokračuje od místa přerušení (pokud nebyl ukončen)
Dělení signálů
Signály se dělí do dvou skupin:
- Signály, které se posílají při chybové události
(např. SIGILL- Illegal Instruction posílá jádro, jestliže se proces pokusí provést přednostní instrukci jádra) - Signály, vznikající mimo proces při asynchronní události
(např. SIGINT- Interrupt- posílá se procesu po stisknutí CTRL- C)
Druhy akcí
Signál je jen obyčejné celé číslo. Jestliže však o nich mluvíme, odvoláváme se na ně jmény, která jsou nadefinována v souboru.
Když proces dostane signál, pokaždé zareaguje nějakou akcí.
Tyto akce se dělí do tří skupin:
- Implicitní akce- každý signál má za následek provedení nějaké implicitní akce, která je provedena, pokud proces, pro který je signál určen, nevyžaduje jinou akci, tzv. neřekne nic jiného.
- Ignorování signálu- proces může na signály reagovat, tím pádem může jejich příchod ignorovat.
- Obsluha signálu- příchozí signál se obslouží pomocí uživatelsky definované funkce (handler). Jakmile se provede, proces pokračuje od místa, kde byl signálem přerušen.
Seznam implicitních akcí:
- exit - zrušení procesu
- core - zrušení procesu a uložení obsahu jeho paměti do souboru core- ten se využívá pro analyzování chyb
- ignore - ignorování signálu
- stop - pozastavení procesu
- continue - pokračování pozastaveného procesu
Dva signály provedou implicitní akci vždy. Je to SIGKILL- zruší proces a SIGSTOP- pozastaví proces.
Posílání signálů
Proces s UID (User ID) rovným nule může poslat signál libovolnému procesu. Proces, který má UID různé od nuly, může v linuxu poslat signál těm procesům, které mají stejné reálné nebo saved UID jako má on realné UID. Ve FreeBSD se musí UID procesů shodovat.
Signál se může poslat v shellu voláním:
#kill [-s signal] pid
Signály původně vznikly kvůli ukončování procesů, proto kill.
Pokud se neuvede číslo signálu, posílá se implicitně TERM. Programově se signál posílá voláním funkce:
int kill(pid_t pid, int sig); //(viz man 2 kill.)
Funkce pošle signál sig jednomu nebo skupině procesu (podel hodnoty pid). Je-li sid==O, tak se pouze zjistí, má-li proces oprávnění poslat signál. Jakým procesům se signál pošle, záleží jen pid:
- pid>0 - pošle se procesu s pid
- pid==0 - pošle se procesům ve stejné skupině
- pid==-1 - pošle se všem procesům kromě systémových
- pid<-1 - pošle se procesům ve skupině s číslem -pid
Obsluha signálů
Jak je psáno výše, jsou signály asynchronní. Při obsluze tedy není zřejmé, v jakém se proces nacházé stavu. Kvůli tomu by se měly v handlerech provádět jen bezpečné funkce (man sigaction). Výkon handleru pro obsluhu může být přerušen příchodem jiného signálu. Handler by měl vykonávat co nejméně operací- často jen zaznamená, že přišel signál a program poté mimo handler kontroluje (periodicky), zda přišel signál a provádí nějakou akci.
K nastavení obsluhy signálu slouží funkce:
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
Funkce nastaví obsluhu signálu sig podle act a do oldact uloží předchozí nastavení obsluhy.
Struktura sigaction je definovaná:
struct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void); }
kde void (*sa_handler)(int) specifikuje akci svázanou se signálem- buď adresa handleru nebo SIG_DFL- defaultní akce, SIG_IGN- ignorování signálu. Díky sigset_t sa_mask můžeme nastavit masku signálu, které budou blokovány v handleru. int sa_flags přetvářejí chování handleru (př. SA_RESTART- restartovat přerušená sytémová volání, SA_ONESHOT- po prvním obsloužení nastavit obsluhu na deaultní akci).
Podrobnější popis lze najít v manuálových stránkách- man sigaction.
Někdy je těžké zajistit, aby program správně obsloužil signál, který může kdykoliv přijít a přerušit běh programu. Jestliže se tomuto chceme vyhnout, použijeme mechanismus blokování signálu. Blokované signály jsou ignorovány až do jejich odblokování, poté jsou procesu doručeny. Narozdíl od ignorovaných, které jádro zahazuje a tak nejsou nikdy doručeny.
K nastavování blokovaných signálů se používá funkce:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
Funkce nastaví masku blokovaných signálů a vrátí starou masku. Chování se nastaví hodnotou how- SIG_BLOCK (blokují se stejné signály jako doposud a navíc ty definované argumentem set), SIG_UNBLOCK (signály v set jsou vyjmuty z blokovaných), SIG_SETMASK (blokovány signály definované v set).
Maska signálu se nastavuje funkcemi:
- int sigemptyset(sigset_t *set)- inicializuje množinu signálů danou set na prázdnou
- int sigfillset(sigset_t *set)- inicializuje množinu signálů danou set na všechny definované signály
- int sigaddset(sigset_t *set, int signum)- přidá do množiny signálu signál signum
- int sigdelset(sigset_t *set, int signum)- vymaže z množiny signálů signál signum
- int sigismember(const sigset_t *set, int signum)- zjistí,zda je signál v dané množině signálů
Pro získání signálů, které čekají na odblokování, se používá funkce int sigpending(sigset_t *set); Pokud je potřeba proces pozastavit, dokud nepřijde nějaký signál, využívá se funkce int pause().