7.6.1 I segnali

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). ________________________________________________________________

Alcune shell, come Bash, implementano un proprio comando interno kill che ha funzionalità analoghe, ma non identiche, a quelle di /usr/bin/kill (ad esempio non c’è l’opzione -p). Quindi, indicando sulla riga di comando semplicemente kill si lancia il comando interno di Bash (se si usa Bash come shell), mentre per utilizzare il comando di sistema è necessario specificare l’intero path, cioè /usr/bin/kill.

L’elenco dei segnali su di un elaboratore X386 è riportato nella tab. 7.10 (il prefisso SIG nei nomi dei segnali può essere omesso) e la legenda relativa alle azioni predefinite è riportata nella tab. 7.11.
|-|-----------------|---------------------------------------|-------|
N.-|Nome-------------|Descrizione-----------------------------|Azione-|
1 |SIGHUP           |Hang up -Terminazione del processo di controllo A   |
2 |SIGINT           |Interrupt- Interruzione del processo (^C)    |  A    |
34 |SSIIGGQIULILT          |QIluleigta-lI innstetrrurucztiioonne- dIestlr purozcioenseso no(^nY)permessa   |  AC    |
5 |SIGTRAP          |Trace trap - Notifica raggiungimento di break-|  C    |
| |                 |point                                   |       |
6 |SIGABRT|SIGIOT   |Abort-Generato dalla funzione abort()      |  C    |
7 |SIGBUS           |Bus error -Errore sul bus (bad memory access) C   |
8 |SIGFPE           |Floating-point exception-Errore aritmetico   |  C    |
9 |SIGKILL          |Kill-Terminazione immediata del processo    |  AX   |
10 |SIGUSR1          |User- defined signal 1                     |  A    |
11 |SIGSEGV          |Segment violation -Errorediaccessoallamemoria |  C    |
12 |SIGUSR2          |User- defined signal 2                     |  A    |
13 |SIGPIPE          |Broken pipe -Pipe interrotta (tentativo di aper- A    |
| |                 |tulertatu irna s)crittura di una pipe non ancora aperta in   |
14 |SIGALRM          |Alarm clock -Timer scaduto (funzione alarm())  A    |
15 |SIGTERM          |Termination -Terminazione del processo (^\)   |  A    |
16 |SIGSTKFLT         |Stack Fault                              |  C    |
17 |SIGCHLD|SIGCLD   |Child status  has changed  - Processo  figlio |   I   |
| |                 |terminato o sospeso                       |       |
18 |SIGCONT          |Continue- Riprende l’esecuzione del processo se     |
| |                 |sospeso                                  |       |
19 |SIGSTOP          |Stop (sospende il processo)                |  SX   |
20 |SIGTSTP          |Keyboard stop (^Z)                        |  S    |
21 |SIGTTIN          |Background read from tty -Tentativo di lettura  S    |
| |                 |dbelalockgsrtaonudnadrd input da parte di un processo in      |
22 |SIGTTOU          |Background write to tty -Tentativo di scrittura  S    |
| |                 |sullo standard output da parte di un processo in    |
| |                 |background                              |       |
23 |SIGURG           |Urgent condition on socket - Notifica di una |   I   |
| |                 |urgent condition su un socket              |       |
24 |SIGXCPU          |CPU limit exceeded -Ecceduto il limite del time C    |
| |                 |slice di CPU assegnato al processo           |       |
25 |SIGXFSZ          |File size limit exceeded - Ecceduto il limite|  C    |
| |                 |massimo della dimensione del file          |       |
26 |SIGVTALRM         |Virtual alarm clock-Allarme virtuale         |  A    |
2728 |SSIIGGPWRIONFCH         |PWriofinldinogw aslaizremcclhoacnkge-T-imFeirnedsetlraprrofiidliinmgenscsaiodnuattoa |  AI   |
| |                 |(interfaccia grafica)                        |       |
29 |SIGIO|SIGPOLL    |Input/Output now  possible -Notifica che si `e |  A    |
| |                 |pronti ad un’operazione di input/output     |       |
30 |SIGPWR           |Powerfailurerestart-Mancanzadialimentazione |  A    |
| |                 |elettrica                                 |       |
31 |SIGSYS|SIGUNUSED |System call error -Errore nella chiamata ad una C    |
---------------------subroutine----------------------------------------

Tabella 7.10: Elenco dei segnali inviabili ai processi.


|------------------------------------------------------|
Azione--Descrizione--------------------------------------|
|A     termina il processo                                |
| I    ignora il segnale                                  |
|CS     tseormspiennadiil ( pbrloocccesas)o i el pscrorciveis uson file core              |
|X     il segnale non pu`o essere ignorato o intercettato dal processo|
--------------------------------------------------------

Tabella 7.11: Legenda delle azioni predefinite dei processi.

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.

Per ogni segnale è associata dal kernel un’azione predefinita da intraprendere, ma i processi possono specificarne delle proprie. Dunque, per ogni segnale, un processo può attuare una delle seguenti politiche

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). ________________________________________________________________

7.6.1.1 Gestione

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, ...).

Nel caso in cui il processo voglia gestire un particolare segnale, esso comunica al kernel l’indirizzo di memoria a cui ha inizio la routine di gestione del segnale. Quindi, all’arrivo di un segnale, o meglio alla fine di una chiamata ad una system call, il kernel si accorge che a tale processo è stato inviato un segnale. Poiché il processo in esecuzione in questo momento sta girando in kernel space, il kernel non può lanciare direttamente l’esecuzione della routine di gestione del segnale, poiché si trova ad un indirizzo di memoria nello spazio di indirizzi del processo. Il kernel si limita a cambiare l’indirizzo della prossima istruzione di codice eseguita dal processo, in modo tale che al successivo time slice di CPU assegnato al processo, il processo inizi ad eseguire il codice relativo alla gestione del segnale arrivato.

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.