La creazione e terminazione dei processi in Linux, così come in UNIX, avviene seguendo la regola dei ‘gradi di parentela’: qualsiasi processo (padre) può infatti generare altri processi (figli), identificati univocamente dal PID, process identifier, che non è altro che un numero intero assegnato in maniera progressiva quando il processo viene creato.
Occorrono di conseguenza delle regole univoche per la creazione, ma soprattutto per la terminazione dei processi, altrimenti viene messo a rischio sia il funzionamento del sistema operativo, sia, conseguentemente, l’esecuzione delle applicazioni.
La terminazione dei processi in Linux può essere normale oppure anomala: nel primo caso, vengono richiamate opportune funzioni di libreria e/o di sistema; nel secondo caso vengono svolte tutta una serie di operazioni finalizzate a liberare le risorse allocate ad un processo, che deve essere necessariamente ‘terminato’ in maniera forzata.
Entrando nel dettaglio, per terminare normalmente un programma o si sfrutta la funzione di libreria exit(), oppure viene richiamata la funzione di sistema _exit(), il tutto nel seguente modo:
void exit(int status)
oppure
void _exit(int status)
Come si evince dalla porzione di codice, entrambe le funzioni accettano un parametro, che rappresenta lo stato di uscita (exit status) del processo, che sarà poi passato al processo padre. Pur tuttavia, non è detto che un processo debba necessariamente ricevere immediatamente la notifica circa la terminazione di un figlio, ragion per cui viene conservato il descrittore nella tabella dei processi fino a quando non verrà eseguita la funzione wait().
L’esecuzione della funzione wait() è obbligatoria, altrimenti il rischio è quello di far crescere a dismisura la tabella dei processi e di esaurire i PID disponibili; basti pensare, a titolo di esempio, ad un Web server che genera un figlio per ogni richiesta proveniente da un client.
In attesa che un processo riceva la notifica circa la terminazione di un figlio, quest’ultimo rimane nello stato zombie, e si conserva, come accennato, il descrittore nella tabella dei processi finché non viene eseguita la funzione wait(), che restituisce il PID del figlio in caso di successo e -1 in caso di errore, nella seguente maniera:
pid_t wait(int *status)
Alla funzione abort() viene invece delegata la terminazione anomala di un processo Linux, con l’obiettivo di svolgere tutta una serie di operazioni che, per certi versi, trovano corrispondenza nell’invocazione della funzione _exit(). Con abort() infatti debbono essere liberate le risorse allocate al processo e debbono essere aggiornate correttamente tutte le strutture dati necessarie al funzionamento del sistema operativo.
di Filadelfo Scamporrino - Programmazione.it