I segnali sono uno dei meccanismi di comunicazione più vecchio utilizzato sui sistemi Unix-like, per segnalare in maniera asincrona l’occorrenza di un evento ad un processo. I segnali sono sostanzialmente degli interrupt software.
Un segnale può essere inviato (generato) tramite la tastiera o direttamente dal sistema, come nel caso in cui un processo tenti di accedere ad una locazione di memoria al di fuori della sua memoria virtuale.
È bene chiarire il fatto che i segnali sono generati o inviati da un processo che sarà indicato come mittente e sono notificati, consegnati (delivered) ad un processo che sarà indicato come destinatario. In particolare un segnale è detto consegnato (delivered) al processo, quando questo viene effettivamente preso in considerazione dal processo stesso, mentre per tutto il tempo in cui il segnale è inviato (o generato) ma non ancora consegnato, si parla di segnale pendente (pending). Questa è soltanto un’astrazione. Non vi è nessun messaggio che viaggia da un processo all’altro, ma il processo destinatario si accorge soltanto che gli è stato notificato un segnale senza poter neanche sapere chi glielo ha inviato.
L’elenco dei segnali che possono essere inviati ad un processo può essere ottenuto per mezzo del comando kill (man page kill(1)), che è il comando utilizzato per inviare i segnali ai processi.
____________________________________________________________________
Comando: kill
Path: /usr/bin/kill
SINTASSI
$ kill [option] [process ...]
DESCRIZIONE
Se il sengale da inviare non viene specificato, viene inviato il segnale SIGTERM (15). ________________________________________________________________
![]()
|
![]()
|
|
Il numero massimo dei segnali supportati è vincolato dalla dimensione del data bus del sistema: le CPU con 32 bit di data bus, possono avere fino a 32 segnali, mentre CPU a 64 bit (come gli Alpha) possono gestire fino a 64 segnali diversi. |
dipendentemente da com’è scritto il programma relativo.
In particolare, i segnali SIGSTOP e SIGKILL non possono essere né ignorati né gestiti autonomamente da un processo, ma la loro gestione è effettuata direttamente dal kernel, ovvero viene intrapresa l’azione predefinita: in corrispondenza della consegna di un segnale SIGSTOP, il processo destinatario viene sospeso, mentre alla consegna del segnale SIGTERM il processo destinatario viene immediatamente terminato.
I processi accettano implicitamente l’azione predefinita del kernel per la gestione dei segnali, ma nella scrittura del programma può essere specificata una politica diversa. Questo avviene definendo nel programma un signal handler, ovvero una specifica routine di gestione del segnale. In tal caso si parla di intercettazione del segnale.
In genere per i segnali che rappresentano eventi relativi ad errori di un processo (divisione per zero o violazioni di accesso, come ad esempio per il segnale SIGFPE) è definita un’azione predefinita che implica la scrittura di un file core (core dump) che annota lo stato del processo (ed in particolare della memoria ad esso relativa) prima di far terminare il processo stesso. Tale file può essere esaminato in seguito con un debugger (un apposito programma per l’analisi del funzionameno di altri programmi) per investigare sulla causa dell’errore.
L’ordine di consegna dei segnali non è determinato dall’ordine in cui sono stati generati, ma può darsi che sia consegnato prima quello che è stato generato dopo.
Non tutti i processi hanno la possibilità di inviare segnali ad altri processi, ma i segnali possono essere inviati a processi che hanno gli stessi effective UID e GID del processo mittente oppure il suo stesso PGID. In particolare i processi che girano con priorità di superuser (UID = 0) possono inviare segnali a qualunque processo.
Esiste anche il comando killall (man page killall(1) per inviare un segnale a più processi.
____________________________________________________________________
Comando: killall
Path: /usr/bin/killall
SINTASSI
$ killall [option] [name ...]
DESCRIZIONE
Se il sengale da inviare non viene specificato, viene inviato il segnale SIGTERM (15). ________________________________________________________________
GNU/Linux implementa i segnali sfruttando le informazioni memorizzate nella struttura dati task_struct38 del processo. I segnali pendenti per un determinato processo, vengono tenuti nel campo signal della struttura task_struct segnati come bloccati (ad eccezione dei segnali SIGSTOP e SIGKILL che non possono essere bloccati).
Il sistema tiene traccia di come ogni processo gestisce i vari segnali nel vettore sigaction puntato dalla struttura task_struct, accessibile dal processo mediante opportune system call.
I segnali sono recapitati impostando il relativo bit nel campo signal della struttura task_struct del processo di destinazione.
Se il processo di destinazione non ha bloccato il segnale ed è nello stato interruptible, allora viene attivato cambiando il suo stato in running in modo tale che lo scheduler possa considerarlo uno dei candidati per l’assegnamento del prossimo time slice di CPU.
I segnali non sono presentati al processo destinatario nel momento che vengono generati, ma devono attendere che il processo sia nello stato running.
Ogni volta che un processo esce da una system call, i suoi campi signal e blocked sono controllati e quelli non bloccati gli vengono segnalati, ovvero viene intrapresa l’azione specificata in corrispondenza di tali eventi, che può essere quella specificata dal processo oppure, in sua assenza, quella di default del kernel. Potrebbe sembrare un meccanismo inaffidabile, ma i processi generalmente chiamano le system call, anche soltanto per scrivere dei caratteri sullo schermo.
|
In realtà è possibile che i segnali vengano controllati anche all’inizio di alcune system call, dipendentemente dal fatto se queste sono considerate lente o veloci dal sistema. Quelle veloci (fast) sono eseguite in maniera praticamente immediata, mentre quelle lente (slow) sono quelle per cui l’esecuzione potrebbe richiedere un tempo indefinito (lettura/scrittura di pipe, file di disposistivo, socket, ...). |
Mentre un processo sta eseguendo la routine di gestione di un segnale, il kernel impedisce la gestione di altri segnali, bloccandoli, che comunque rimangono in coda. Quando il processo termina la rouine di gestione di un segnale, il kernel provvede a sbloccare la notifica dei segnali arrivati, riabilitando il processo a gestire eventuali segnali arrivati nel frattempo.
Poiché i segnali sono asincroni è possibile incorrere in eventuali race condition39.