Ogni processo Unix e' contraddistinto da un identificatore di processo
(process ID) indicato con pid e da un identificatore
del processo padre che viene indicato con ppid
(parent process ID).
Il processo padre (parent process) e' il processo che
genera un altro processo, detto processo figlio
(child process).
Piu' propriamente il processo padre dovrebbe essere chiamato
processo genitore dall'inglese (parent=genitore),
ma il termine padre si adatta ugualmente bene allo scopo.
Il pid e' un numero univoco in un determinato istante, fornito dal
sistema operativo e necessario ad identificare un processo in "corso"
sul sistema.
Il ppid e' l'identificativo del padre del processo che si sta'
esaminando. In altri termini il ppid e' il pid del processo
che ha generato il processo considerato.
Un processo si puo' trovare in uno dei seguenti stati:
Ready
E' pronto per essere eseguito.
Running
E' in esecuzione su una CPU del sistema.
Sleeping
Attende un evento.
Swapped
Parte del processo e' stato trasferito su disco, per liberare
la memoria per altri processi.
Terminated
Il processo e' terminato. Invio del segnale SIGCHLD al parent.
Zombie
Il processo ha terminato la sua esecuzione, ma il parent non ha
raccolto il segnale SIGCHLD. Il processo mantiene ancora allocate
delle risorse.
Il processo padre termina prima del processo figlio.
In questa situazione il child process rimane orfano del
padre e viene adottato dal processo init che per
definizione e' il padre di tutti i processi.
Il processo figlio termina, ma il padre non rileva il suo
termine.
Quando cio' si verifica, il processo figlio e' definito
defunto oppure zombie e rimane in tale stato finche'
o il padre non ha rilevato la sua terminazione, oppure fino a quando
anche il padre termina; al termine del padre il processo figlio
viene ereditato dal processo init che ne rileva la sua
terminazione.
Un processo zombie mantiene allocate le risorse fino quando non sia
stato rilevato il suo stato di terminazione o dal processo padre
o dal processo init.
Nella situazione di normalita', il padre deve quindi rilevare la
terminazione del figlio tramite la system call wait(). Il figlio
puo' cosi' rilasciare ogni risorsa impegnata.
Legenda della numerazione posta a fianco del flusso di processo:
E' in esecuzione il solo processo padre.
Il processo padre invoca la funzione fork().
Viene generato il processo figlio.
Il processo padre e' eseguito in concorrenza con il
processo figlio (oltre agli altri processi running sul
sistema).
Se il padre invoca la funzione wait(), il padre attende
la terminazione del figlio.
Il processo figlio e' eseguito in concorrenza con il
processo padre (oltre agli altri processi running sul
sistema).
Il figlio ha eseguito la terminazione del programma
invocando la funzione _exit() o exit().
Al padre e' inviato il segnale SIGCHLD.
Il padre ha rilevato la terminazione del figlio tramite
la funzione wait() e le risorse impegnate dal figlio vengono
liberate.
Il processo padre riprende l'esecuzione fino alla sua
terminazione.
Al termine della gestione della pipe, viene atteso il processo
figlio tramite la system call wait()
Termine del processo padre.
In alternativa un processo padre puo' anche:
Chiamare la funzione popen() che
provvede a creare un nuovo processo eseguendo il comando passato in
argomento alla popen() stessa.
Il processo viene collegato da una pipe generata anch'essa da popen().
Gestire la comunicazione in input o in output con il processo,
tramite la pipe creata.
Chiamare la funzione pclose() che attende il processo generato dalla popen().
Un processo consumatore (lettore della pipe):
Utilizza il file descriptor di input della pipe.
Impiega la system call read() per leggere i dati dalla pipe.
Puo' utilizzare le funzioni fscanf(), fread(), fgetc(), fgets()
previo impiego della fuzione fdopen() necessaria per associare
uno stream al file descriptor della pipe.
Gli viene restituito EOF quando i dati da leggere sulla pipe
sono terminati e il processo produttore ha chiuso il file
descriptor di output sulla pipe.
Impiega la system call close() per chiudere il file descriptor
della pipe.
In alternativa puo' essere impiegata la funzione fclose() se in
precedenza era stata chiamata la funzione fdopen().
Un processo produttore (scrittore della pipe):
Utilizza il file descriptor di output della pipe.
Impiega la system call write() per scrivere i dati sulla pipe.
Puo' utilizzare le funzioni fprintf(), fwrite(), fputc(), fputs()
previo impiego della fuzione fdopen() necessaria per associare
uno stream al file descriptor della pipe.
Riceve il segnale SIGPIPE
nel caso che la pipe non possa piu' venire letta;
tipicamente questa situazione si verifica quando il processo
consumatore termina per un qualsiasi motivo o, piu' in generale,
quando il processo consumatore chiude prematuramente il file
descriptor della pipe (cioe' senza attendere che gli venga
restituito EOF). In altri termini
SIGPIPE segnala una condizione
di anomalia sulla pipe.
Impiega la system call close() per chiudere il file descriptor
della pipe.
In alternativa puo' essere impiegata la funzione fclose() se in
precedenza era stata chiamata la funzione fdopen().
Le funzioni per la gestione dei processi non sono definite da una
libreria specifica, ma fanno riferimento a piu' librerie standard;
pertanto per il loro utilizzo e' necessario
includere gli headers
appropriati come descritto nella sinopsi di ciascuna funzione. Es.: