|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [IT] I segnali in C | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
I segnali in C Per descrivere i segnali, si rende necessario introdurre il concetto di evento. Un evento,
in pratica, e' un fatto accaduto all'interno del sistema. Puo' essere generato
dalle periferiche (interrupt, operazioni di I/O), da processi (fra cui
quelli di sistema), ecc.
La generazione di un evento asincrono (cioe' che
puo' arrivare in qualsiasi momento) puo' avvenire tramite un segnale rivolto
ad un processo.
Un segnale puo' essere inviato da un processo ad un altro processo, dal kernel ad un processo oppure da un processo a se' stesso (funzione "alarm"). In ogni caso, l'invio del segnale passa per il kernel. Infatti, il processo non riceve subito il segnale. Se il processo si trova in modalita' utente (esecuzione), il kernel lo "passa" subito. Se, invece, il processo si trova in modo kernel (in attesa di essere scelto per l'esecuzione da pare dello scheduler dei processi), gli viene "passato" al momento della riattivazione da parte dello scheduler. Vediamo adesso come si puo' inviare e catturare
un segnale.
Alarm Come gia' accennato, la funzione "alarm"
si usa per far inviare un segnale al processo che la invoca. Piu' precisamente,
"alarm"
permette l'invio del segnale "SIGALRM"
dopo un certo tempo t
(specificato come parametro).
Nel frammento di codice mostrato potrebbe mancare una cosa: il processo deve essere in grado di catturare il segnale per poter avviare una routine utente (catcher). Questa routine puo' essere attivata tramite la funzione "signal" che modifica il comportamento di default del segnale.#include <signal.h> ... unsigned t; //tempo che deve passare ... //A partire da questo momento, il processo //ricevera' un segnale di allarme dopo t secondi. alarm(t); ... La funzione "signal" verra' descritta piu' avanti. Un'ultima nota riguardante "alarm": usando il valore 0 come tempo, si ottiene l'azzeramento del conteggio eventualmente gia' avviato. Kill "Kill"
si occupa di inviare un segnale ad un processo di cui si conosca il PID
(Process IDentification number), o ad un gruppo di processi con lo stesso
PGID
(Progress
Group IDentification number).
Tenendo conto che un segnale e' identificato da
un numero, i parametri accettati da "kill"
sono due: il PID del processo che deve ricevere il segnale ed il numero
del segnale stesso, entrambi interi.
Il segnale SIGSEGV (vedi tabella, oppure il file "/usr/include/bits/signum.h") invia, al processo il cui PID e' 40 (numero inventato sul momento), l'indicazione di violazione di segmento (di memoria).#include <signal.h> ... int errore=0; ... //invia il segnale e riporta l'eventuale errore. errore=kill(40,SIGSEGV); ... Nella variabile "errore" ci sara' "0" se il segnale e' stato inviato correttamente, "-1" altrimenti (nella variabile "errno" ci sara' il codice di errore: vedi "gestione degli errori". Il valore del PID passato alla funzione "alarm" puo' essere anche diverso da un numero positivo: PID=0 significa che il segnale viene inviato a tutti i processi con PGID uguale a quello del mittente; PID=-1 significa che il segnale e' inviato a tutti i processi (eccetto quelli numero "0=init" e "1=scheduler") in ordine decrescente di PID (i PID vengono letti dalla tabella dei processi); PID<-1 significa che il segnale viene inviato a tutti i processi con PGID uguale al valore assoluto del PID (es. PID=-100, PGID=100). Signal Con la funzione "signal"
e' possibile catturare i segnali che arrivano ad un processo.
Questo piccolo pseudo-programma cattura il segnale
di uccisione del processo e lo gestisce in qualche modo con la funzione
"gestoreKill()".
La struttura mostrata, non soddisfa, pero', tutte le esigenze: infatti, dopo che un segnale viene catturato, l'azione (meglio, la funzione) ad esso associata, viene riportata a quella di default. Cioe', nel programmino precedente, il segnale viene catturato solo la prima volta. Si puo' ovviare a questo inconveniente inserendo come prima istruzione, nella funzione "gestoreKill()", di nuovo una chiamata a "signal":
Vi e' comunque un difetto non eliminabile completamente:
il processo termina (azione di default) se interviene un SIGKILL
appena prima della chiamata a "signal"
nella funzione "gestoreKill()".
I segnali in arrivo possono essere anche ignorati. Per ignorare un segnale si puo' usare Per ripristinare il segnale, gli si puo' assegnare una funzione particolare (come visto sopra) oppure si puo' ripristinare il comportamento di default:signal(segnale,SIG_IGN); "SIG_IGN" e "SIG_DFL" sono due particolari funzioni. La prima compie un'azione nulla. La seconda invece contiene le azioni di default per i segnali.signal(segnale,SIG_DFL); Il segnale "SIGKILL" non puo' essere ignorato.
La seguente tabella contiene un elenco
dei segnali usabili (tratta dal file "/usr/include/bits/signum.h").
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(c) 1999-2006
|