Signály jsou vlastně softwarová přerušení. Každý netriviální program má co do činění se signály. Signály se objevily už ve Verzi 7, ale až s příchodem SVR3 se staly efektivní.
Každý signál má své jméno. Jméno každého signálu začíná na SIG. Verze 7 měla 15 různých signálů. SVR4 a BSD mají 31 různých signálů. Jména těchto signálů jsou definována v souboru <signal.h>. Žádný z těchto signálů nemá hodnotu 0, POSIX.1 totiž definuje signál null.
Signály mohou být generovány různými událostmi:
Signály jsou klasickou asynchronní záležitostí. Reakce na ně mohou být následující:
Tabulka 13 uvádí přehled signálů dle různých norem.
Jméno Popis ANSI C POSIX.1
Pozn. Norm. akce SIGABRT
Abnormální ukončení (abort)
Ukon.+core SIGALRM
Budík
Ukončení SIGBUS
Chyba sběrnice
Ukončení SIGCHLD
Potomek zastaven či ukončen
Ignorace SIGCONT
Pokračování po SIGSTOP
Pokračování SIGEMP
Instrukce EMP
Ukon.+core SIGFPE
Aritmetická vyjímka
Ukon.+core SIGHUP
Zavěšení
Ukončení SIGILL
Ilegální instrukce
Ukončení SIGINFO
Požadavek o info z terminálu
BSD
Ignorace SIGINT
Přerušení z terminálu
Ukončení SIGIO
Asynchronní I/O
Ukon./Ign. SIGIOT
Hardwarová chyba (IOT)
Ukon.+core SIGKILL
Ukončení (nejde chytit)
Ukončení SIGPIPE
Zápis do roury bez čtenářů
Ukončení SIGPOLL
Sdílená událost
Sys.V
Ukončení SIGPROF
Budík profile (settimer)
Ukončení SIGPWR
Chyba napájení/restart
Sys.V
Ignorace SIGQUIT
Ukončení z terminálu
Ukon.+core SIGSEGV
Vadný odkaz do paměti
Ukon.+core SIGSTOP
Zastavení (nejde chytit)
Zastavení SIGSYS
Chybné volání systému
Ukon.+core SIGTERM
Ukončení
Ukončení SIGTRAP
Trasování (hw vyjímka)
Ukon.+core SIGTSTP
Zastavení z terminálu
Zastavení SIGTTIN
Proces v pozadí chce číst tty
Zastavení SIGTTOU
Pr. v pozadí chce psát do tty
Zastavení SIGURG
Urgentní podmínka na soket
Ignorace SIGUSR1
Uživatelský signál 1
Ukončení SIGUSR2
Uživatelský signál 2
Ukončení SIGVTALRM
Virtuální hodiny (settimer)
Ukončení SIGWINCH
Změna velikosti okna terminálu
Ignorace SIGXCPU
Překročen časový limit CPU
Ukon.+core SIGXFSZ
Překr. limit velikost souboru
Ukon.+core
Nejjednodušším prostředníkem k signálům je funkce signal.
| #include <signal.h> |
| void (*signal (int signo, void (*func)(int)))(int); |
| Vrací: předchozí obsluhu signálu |
Za argument signo lze dosadit název z tabulky 13. Argument func může nabývat následujících hodnot:
Příklad:
#include <signal.h>
static void sig_usr(int); /* one handler for both signals */
int
main(void)
{
if (signal(SIGUSR1, sig_usr) == SIG_ERR)
err_sys("can't catch SIGUSR1");
if (signal(SIGUSR2, sig_usr) == SIG_ERR)
err_sys("can't catch SIGUSR2");
for ( ; ; )
pause();
}
static void
sig_usr(int signo) /* argument is signal number */
{
if (signo == SIGUSR1)
printf("received SIGUSR1\n");
else if (signo == SIGUSR2)
printf("received SIGUSR2\n");
else
err_dump("received signal %d\n", signo);
return;
}
Jestliže jsme schopni signály zpracovávat, musíme je umět i zasílat. To nám umožňuje funkce kill nebo raise. Funkce kill umožňuje zaslání signálu procesu nebo skupině procesů. raise umožňuje poslat signál procesu samotnému.
| #include <sys/types.h> #include <signal.h> |
| int kill (pid_t pid, int signo); int raise (int signo); |
| Vrací: 0 když OK, -1 při chybě |
Je několik rozdílných podmínek pro argument pid funkce kill.
| pid > 0 | Signál je posílán procesu s daným PID |
| pid == 0 | Signál pro všechny procesy ze skupiny vysílajícího |
| pid < 0 | Signál pro všechny procesy ze skupiny abs(pid) |
| pid == -1 | Nespecifikováno v POSIX.1 |
POSIX.1 rezervuje signál 0 jako speciální null signál. Tento signál se často používá pro zjištění, zda daný proces existuje. Jestliže pošleme signál null neexistujícímu procesu, errno bude ESRCH. Pozor, nezapomínejte, že unix po určité době recykluje identifikační čísla procesů.
Funkce alarm nám umožňuje nastavit budík na předem stanovený čas. Zazvonění budíku je v unixu reprezentováno generováním signálu SIGALRM. Jestli bude signál ignorován nebo zpracován, je již věcí daného procesu.
| #include <unistd.h> |
| unsigned int alarm (unsigned int seconds); |
| Vrací: 0 nebo počet sekund předchozího alarmu |
Argument sice specifikuje čas, ale musíte počítat s dodatečnou režií systému. Jeden proces může mít nastaven maximálně jeden budík.
Doplněk k této funkci tvoří funkce pause. Po vyvolání této funkce přejde proces do stavu pozdržení (suspend) až do příchodu signálu.
| #include <unistd.h> |
| int pause (void); |
| Vrací: -1 s errno nastavenou na EINTR |
Použití funkce ilustruje nejlépe příklad.
Příklad:
#include <signal.h>
#include <unistd.h>
static void
sig_alrm(int signo)
{
return; /* nothing to do, just return to wake up the pause */
}
unsigned int
sleep1(unsigned int nsecs)
{
if (signal(SIGALRM, sig_alrm) == SIG_ERR)
return(nsecs);
alarm(nsecs); /* start the timer */
pause(); /* next caught signal wakes us up */
return( alarm(0) ); /* turn off timer, return unslept time */
}
Tato funkce vypadá jako funkce sleep (viz funkci sleep). Implementace však přináší problémy:
Standardním použitím alarm je implementace tzv. hlídacího psa (watch dog). Je to vlastně nastavení maximální doby trvání nějaké operace. Není-li operace hotova do určité doby, došlo patrně k chybě.
Příklad:
#include <signal.h>
static void sig_alrm(int);
int
main(void)
{
int n;
char line[MAXLINE];
if (signal(SIGALRM, sig_alrm) == SIG_ERR)
err_sys("signal(SIGALRM) error");
alarm(10);
if ( (n = read(STDIN_FILENO, line, MAXLINE)) < 0)
err_sys("read error");
alarm(0);
write(STDOUT_FILENO, line, n);
exit(0);
}
static void
sig_alrm(int signo)
{
return; /* nothing to do, just return to interrupt the read */
}
Pomocí signálů můžeme implementovat také funkce TELL_CHILD, ... z kap. "Podmínky závodu". Popis funkcí, které jsou použity v tomto příkladu a nejsou popsány v této práci, lze nalézt např. v manuálových stránkách.
Příklad:
#include <signal.h>
static volatile sig_atomic_t sigflag;
/* set nonzero by signal handler */
static sigset_t newmask, oldmask, zeromask;
static void
sig_usr(int signo)
/* one signal handler for SIGUSR1 and SIGUSR2 */
{
sigflag = 1;
return;
}
void
TELL_WAIT()
{
if (signal(SIGUSR1, sig_usr) == SIG_ERR)
err_sys("signal(SIGINT) error");
if (signal(SIGUSR2, sig_usr) == SIG_ERR)
err_sys("signal(SIGQUIT) error");
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGUSR1);
sigaddset(&newmask, SIGUSR2);
/* block SIGUSR1 and SIGUSR2, and save current signal mask */
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
err_sys("SIG_BLOCK error");
}
void
TELL_PARENT(pid_t pid)
{
kill(pid, SIGUSR2); /* tell parent we're done */
}
void
WAIT_PARENT(void)
{
while (sigflag == 0)
sigsuspend(&zeromask); /* and wait for parent */
sigflag = 0;
/* reset signal mask to original value */
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error");
}
void
TELL_CHILD(pid_t pid)
{
kill(pid, SIGUSR1); /* tell child we're done */
}
void
WAIT_CHILD(void)
{
while (sigflag == 0)
sigsuspend(&zeromask); /* and wait for child */
sigflag = 0;
/* reset signal mask to original value */
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error");
}
Tato funkce slouží k předčasnému ukončení programu. Pošle signál SIGABRT procesu.
| #include <stdlib.h> |
| void abort (void); |
Procesy by neměly signál SIGABRT ignorovat.
Funkce sleep je v textu mnohokrát použita. Jde vlastně jen o uspání procesu na příslušnou dobu.
| #include <stdlib.h> |
| unsigned int sleep (unsigned int seconds); |
| Vrací: 0 nebo počet nedospalých sekund |
Funkce převede proces do stavu pozastavení (suspend) do té doby, než:
Ladislav Dobias