Impareremo ora a gestire i processi. Prima di procedere, consigliamo la lettura del precedente capitolo sul background che ne introduce l’uso e richiama alcuni concetti di base.
Cosa è un processo?
Il processo è originato da una sequenza di istruzioni ospitate su uno storage in forma di file eseguibile o script. Una volta lanciato questo adopera potenza di calcolo e RAM per eseguirle. A ogni processo è associato un identificativo numerico sequenziale univoco, il Process ID Number (PID). Esistono due distinte tipologie di processi. I processi utente e quelli di sistema, anche chiamati demoni, che invece affronteremo in un capitolo specifico. I processi utente sono in generale associati a una qualche forma di interazione con chi li ha lanciati manualmente per guidarne l’esecuzione. Un processo di sistema, al contrario, quasi sempre è più autonomo e non interagisce con il principale utilizzatore in modo diretto ma si limita, invece, a rispondere a specifiche sollecitazioni. Illustriamo questa differenza con un esempio. Sulla vostra macchina sta operando un browser web che vi consente di visualizzare questo testo, lo avete lanciato consapevolmente generando un nuovo processo utente che ha richiesto il contenuto e lo ha reso visibile. Un altro dispositivo, raggiunto attraverso internet, sta esponendo un servizio web che agisce come processo di sistema quindi risponde autonomamente a questo genere di richieste veicolando verso il vostro browser proprio ciò che state leggendo.
Ereditarietà dei processi
A ogni processo è altresì associato il Parent Process ID Number (PPID), ulteriore identificativo numerico non univoco, che tiene traccia del processo progenitore. Caratteristica fondamentale dei processi linux è l’ereditarietà. Ogni processo ha infatti facoltà di lanciare ulteriori processi. Un processo può essere quindi parent o child nei confronti di un altro processo in conseguenza del fatto che questo sia il processo genitore oppure il processo generato. Di fatto ciò crea una struttura ad albero. Ovviamente il primo processo lanciato dal boot sarà il kernel con PID 0 seguito dai processi di inizializzazione a partire dal PID 1. Viene da se che ogni processo potrà avere più figli ma uno e un solo padre. Ogni processo, inoltre, ha una vita: quando viene lanciato nasce, quando è in esecuzione o in attesa vive e, infine, quando viene terminato muore. Un processo figlio, per mantenere coerenza, viene automaticamente terminato quando il processo padre che lo ha direttamente originato termina.
Andiamo al terminale, lanciando il comando pstree generiamo un processo che sarà figlio di bash (l’interprete dei comandi) nonché nipote di sshd perché eseguito attraverso ssh su una macchina remota. Il motivo per cui abbiamo lanciato proprio questo comando è che esso ci offre una rappresentazione grafica di quanto abbiamo appena descritto.
fprincipe@html52:~$ pstree
systemd─┬─accounts-daemon───2*[{accounts-daemon}]
├─atd
[...]
├─sshd─┬─2*[sshd───sshd───bash]
│ └─sshd───sshd───bash───pstree
[...]
└─systemd-udevd
Raggruppamenti di processi
Ovviamente la sola relazione padre-figlio non è in grado di fotografare la varietà di rapporti fra processi che possono instaurarsi in funzione dell’attuale complessità di un sistema operativo linux. Esistono altri raggruppamenti possibili. Ne elencheremo un paio assai importanti e di uso comune.
Immaginate un utente che si collega a una macchina condivisa. Questo utente lancia da terminale alcuni processi. In condizioni normali, a meno che il sistema operativo non sia instruito a fare diversamente, i processi lanciati dovranno morire quando l’utente si disconnette per mantenere un corretto management delle risorse. Tutti i processi che hanno questo genere di relazione appartengono a una specifica session e condividono lo stesso Session ID (SID) che sarà uguale al PID del processo che ha originato la session. Quest’ultimo sarà considerato Session Leader e quindi condizionerà la vita degli altri processi della session.
In altri casi, invece, determinati processi sono in relazione fra loro poiché interdipendenti. Ciò accade a esempio quando lo input di uno è lo output di un altro come può avvenire utilizzando la pipe “|”. In questo specifico caso, infatti, il sistema deve poter tracciare questa relazione fra i processi per terminarli o sospenderli in modo opportuno in funzione di ciò che accade al Group Leader. Per ogni processo è quindi possibile assegnare anche un valore di Process Group ID (PGID) che identifica il PID del Group Leader.
Come avete visto la topologia dei processi linux e il loro schema relazionale può essere molto complesso. Nel prossimo capitolo introdurremo alcuni utili comandi per monitorare e gestire i processi.