Title: I segnali
1I segnali
2I segnali
3Stati dei processi in UNIX
Fork terminata
Idle
Runnable
Fork iniziata
Levento accade
scheduling
Sleeping
Running
Attesa di un evento
exit
waitpid
Zombified
4Segnali
- Sono interruzioni software
- comunicano al processo il verificarsi di un
evento - ad ogni evento corrisponde un segnale numerato
- un processo allarrivo di un segnale di un certo
tipo può decidere di - ignorarlo
- lasciarlo gestire al kernel con lazione di
default definita per quel segnale - specificare una funzione (signal handler) che
viene mandata in esecuzione appena il segnale
viene rilevato
5Segnali (2)
- Da chi sono inviati i segnali?
- da processo allaltro
- usando la SC kill()
- solo processi del gruppo (discendenti o antenati)
- dallutente con particolari combinazioni di tasti
(al processo in foregroud) - Control-C corrisponde a SIGINT (ANSI)
- Control-Z corresponde a SIGTSTP
- dallutente con lutility kill della shell
- dal SO per a comunicare al processo il
verificarsi di particolari eventi (es. SIGFPE,
errore floating-point, SIGSEGV, segmentation
fault)
6Segnali (3)
- Lo standard POSIX stabilisce un insieme di
segnali riconosciuti in tutti i sistemi conformi - sono interi definiti come macro in
/usr/include/bits/signum.h - esempi
- SIGKILL (9) il processo viene terminato (non
può essere intercettata) (quit) - SIGALRM (14) è passato il tempo richiesto (quit)
7Segnali (4)
- Sono di uso comune anche segnali non POSIX
- SIGINT (2) Control-C (ANSI)
- richiesta di terminazione da tastiera (quit)
- SIGTSTP (POSIX) Control-Z
- richiesta di sospensione da tastiera (suspend
fino allarrivo SIGCONT) - SIGFPE (8) (ANSI)
- si è verificato un errore Floating Point (dump)
- SIGCHLD(17) (POSIX)
- si è verificato un cambiamento di stato in un
processo figlio (ignore) - SIGPIPE(13) (POSIX)
- la pipe è stata chiusa in lettura (quit)
- .
8Segnali (5)
- SD del kernel relative ai segnali
- signal handler array descrive cosa fare quando
arriva un segnale di un certo tipo - ignorare, trattare puntatore al codice della
funzione da eseguire (handler) - pending signal bitmap (signal mask) che contiene
un bit per ogni tipo di segnale - il bit X è a 1 se cè un segnale pendente di tipo
X - ogni processo ha un signal handler array (nella
user area) ed una pending signal bitmap (nella
process table)
9Segnali (6)
- Cosa accade quando arriva un segnale?
- il processo che lo riceve viene interrotto
- il kernel stabilisce quale comportamento adottare
controllando il contenuto del signal handler
array - se deve essere eseguito un signal handler safun
- lo stato del processo interrotto viene salvato
- si esegue la funzione safun
- il processo riprende lesecuzione dallo stato in
cui e stato interrotto
10Stati dei processi in UNIX (2)
Fork terminata
Idle
Runnable
Fork iniziata
Levento accade
scheduling
Sleeping
Running
Attesa di un evento
Segnale SIGCONT
exit
Segnale SIGSTOP (CTRL Z)
Stopped
waitpid
Zombified
11SC per i segnali
- alarm(), sigaction(),pause(),kill(),...
12Segnali system call
- Come si definisce un signal handler
personalizzato? - usando la SC sigaction()
- ci sono SC che permettono di inviare segnali
- alarm(), kill()
- ci sono SC che permettono di mettersi in attesa
dellarrivo di un segnale - pause()
13Segnali di sveglia alarm()
- int alarm(unsigned int count)
- serve a implementare un timeout
- invia un segnale SIGALRM al processo che lha
invocata dopo count secondi - se count è 0 non viene settato nessun allarme
- in ogni caso tutte le richieste di allarme già
settate sono cancellate - restituisce (0) se non cerano allarmi settati
oppure (xgt0) se macavano x secondi allo scadere
dellultimo allarme settato
14Invio di una SIGALRM
- int main (void)
- alarm(3) / SIGALRM fra 3 secondi /
- printf(Ciclo infinito .")
- while (1) / ciclo infinito /
- printf(Pippo") / mai eseguita /
- return 0
-
15Esempio eseguiamo...
- cosa accade se eseguiamo il codice dellesempio
- a.out
- Ciclo infinito ...
- -- per (circa) 3 secondi non accade niente
-
-
16Esempio eseguiamo(2)
- cosa accade se eseguiamo il codice dellesempio
- a.out
- Ciclo infinito ...
- Alarm clock -- arriva il segnale
- -- processo terminato
-
-
-
17Personalizzare la gestione
- int sigaction(int signum,
- const struct sigaction act,
- struct sigaction oldact)
- serve a definire un nuovo handler
- signum segnale da trattare
- act struttura che definisce il nuovo
trattamento del segnale signum - oldact (OUTPUT) ritorna il contenuto
precedente del signal handler array (può servire
per ristabilire il comportamento precedente) - ritorna (-1) se cè stato errore
18Personalizzare la gestione (2)
- struct sigaction
- ...
- void sa_handler (int)
- ...
-
- sa_handler indica come gestire il segnale può
essere - SIG_IGN ignora il segnale
- SIG_DFL usare la funzione di gestione di default
- puntatore alla funzione da invocare allarrivo
del segnale - gli altri campi di solito si lasciano invariati
19Personalizzare la gestione (3)
- SIGKILL e SIGSTP non possono essere gestiti se
non con la procedura di default - il figlio eredita la gestione dei segnali dal
padre - dopo la exec() le gestioni ritornano quelle di
default (ma i segnali ignorati continuano ad
essere ignorati) - i segnali SIGCHLD sono gli unici ad essere
accumulati (stacked) negli altri casi se arriva
un segnale dello stesso tipo di uno già settato
nella signal mask viene perso
20Personalizzare SIGALRM
- void gestore (int sig) / numero segnale /
- printf(SIGALRM catturato\n")
-
-
- int main (void)
- struct sigaction s
-
-
21Personalizzare SIGALRM (2)
- int main (void)
- / inizializzo s con i valori correnti /
- sigaction(SIGALRM,NULL,s)
- s.sa_handlergestore / nuovo gestore /
- / installo nuovo gestore /
sigaction(SIGALRM,s,NULL) - alarm(3) / SIGALRM fra 3 secondi /
- printf(Ciclo infinito .")
- while (1) / ciclo infinito /
- printf(Pippo") / mai eseguita /
- return 0
22Esempio eseguiamo...
- cosa accade se eseguiamo il codice dellesempio
- a.out
- Ciclo infinito ...
- -- per (circa) 3 secondi non accade niente
-
-
23Esempio eseguiamo(2)
- cosa accade se eseguiamo il codice dellesempio
- a.out
- Ciclo infinito ...
- SIGALRM catturato -- arriva il segnale
- -- il processo cicla indefinitamente
-
-
-
24Attesa di segnali pause()
- int pause(void)
- sospende il processo fino allarrivo di un
segnale - serve a implementare lattesa passiva di un
segnale - ritorna dopo che il segnale è stato catturato ed
il gestore è stato eseguito, restituisce sempre
(-1)
25Attendere SIGALRM
- / indica se è arrivato SIGALARM (1) o no (0)
/ - int sigalarm_flag 0
-
- void gestore (int sig) / numero segnale /
- sigalarm_flag 1
-
- int main (void)
- struct sigaction s
-
-
26Attendere SIGALRM (2)
- int main (void)
- sigaction(SIGALRM,NULL,s)
- s.sa_handlergestore / nuovo gestore /
- sigaction(SIGALRM,s,NULL)
- alarm(3) / SIGALRM fra 3 secondi /
- printf(Ciclo fino a SIGALRM .")
- while (sigalarm_flag! 1)
- pause() / ciclo fino a SIGALRM /
- / serve a non sbloccarsi se arriva un altro
segnale / - printf(SIGALRM arrivato ...")
- return 0
27Esempio eseguiamo...
- cosa accade se eseguiamo il codice dellesempio
- a.out
- Ciclo fino a SIGALRM.
- -- per (circa) 3 secondi non accade niente
-
-
28Esempio eseguiamo(2)
- cosa accade se eseguiamo il codice dellesempio
- a.out
- Ciclo infinito ...
- SIGALRM arrivato -- arriva il segnale
- -- processo terminato
-
-
-
29Invio di segnali kill()
- int kill(pid_t pid, int sig)
- invia un segnale di tipo sig a uno o più processi
(dipende da pid) - il segnale è inviato solo se
- il processo che invia il segnale e chi lo riceve
hanno lo stesso owner - il processo che invia il segnale è posseduto dal
superutente (root) - restituisce (0) se OK (-1) se si verifica un
errore
30Invio di segnali kill()(2)
- int kill(pid_t pid, int sig)
- pid può essere
- gt0 , in questo caso è il pid del processo cui si
deve inviare il segnale - 0, in questo caso il segnale è inviato a tutti i
processi del gruppo del processo che esegue la
kill - -1, in questo caso il segnale è inviato a tutti i
processi (tranne init) se il processo è di root,
altrimenti come (pid0) - lt -1, in questo caso il segnale è inviato a tutti
i processi del gruppo a cui appartiene pid
31Esempio una shell con timeout
- /istallazione gestore SIGCHLD/
- while (TRUE) /ciclo infinito/
- flag_sigchld 0
- type_prompt() / stampa prompt/
- argv read_comm() /legge command line/
- pid fork()
- if (pid) / codice padre /
- Sleep(1) / dopo 1 secondo si risveglia /
- kill(pid,SIGKILL) / e uccide il padre! /
- while(!flag_sigchld)
- pause()
- else /codice figlio/
- execvp(argv0,argv)
32Esempio una shell con timeout (2)
- void gestore_chld (int sig)
- int pid, stato
-
- pid wait(stato)
- if (WIFEXITED(stato)) / term con exit/
- printf(d Terminato con exit d, pid,
- WEXITSTATUS(stato))
- else
- printf(d Terminato con kill d, pid,
- WTERMSIG(stato))
- flag_sigchld 1
33Segnali e system call
- Nello standard POSIX specifica che se arriva un
segnale mentre una SC (es. open(), read()) è in
esecuzione la SC deve fallire con errore EINTR - si dovrebbe quindi testare EINTR dopo ogni SC e
gestirlo esplicitamente - è possibile settare opportuni flags durante la
sigaction() in modo da non essere interrotti - Linux per default NON interrompe le SC allarrivo
di un segnale - Se vogliamo essere interrotti possiamo
richiederlo esplicitamente con una chiamata alla
system call siginterrupt()
34Race Condition?
- Possiamo implementare sleep con alarm?
- Nota sleep e alarm si basano sul tempo di clock
della macchina
35In teoria si...
- / def. Gestore alarm /
- int sleep(int sec)
- / installo gestore per SIGALRM /
- alarm(sec)
- / SIGALRM fra sec secondi /
- pause()
-
36Problema
- Il processo viene interrotto fra la chiamata di
alarm e pause - Il tempo di attesa scade
- Il processo viene risvegliato, lo scheduler
controlla la maschera dei segnali pendenti ed
esegue il gestore che non fa nulla - Il controllo passa al process che esegue pause!
- Potrebbe verificarsi un deadlock!
37Come si risolve?
- Leggere la maschera dei segnali corrente e
bloccare il segnale voluto (ad es. SIGALRM) - Mandare il processo in attesa abilitando la
ricezione del segnale voluto. - Ripristinare la maschera dei segnali originaria.
38Maschera dei segnali
- I moderni sistemi unix-like permettono di
bloccare temporaneamente (o di eliminare
completamente, impostando SIG_IGN come azione) la
consegna dei segnali ad un processo. - Si utilizza la maschera dei segnali (o signal
mask) del processo cioè l'insieme dei segnali la
cui consegna è bloccata. - La signal mask viene ereditata dal padre alla
creazione di un processo figlio - Può essere modificata, durante l'esecuzione di un
gestore, attraverso l'uso del campo sa_mask di
sigaction.
39Quali funzioni servono?
include ltsignal.hgt int sigemptyset(sigset_t
set) Crea una maschera vuota int
sigaddset(sigset_t set, int signum) Aggiunge
signum segnale alla maschera set int
sigprocmask(int how, const sigset_t set,
sigset_t oldset) Cambia la maschera dei segnali
del processo corrente howSIG_BLOCK specifica
segnali da bloccare int sigsuspend(const
sigset_t mask) imposta la signal mask
specificata, mettendo in attesa il processo.