Gestione dei processi

Loris Tissino

25 giugno 2009

Gestione dei processi

Definizione di processo

Un processo è, essenzialmente, un programma in esecuzione.

Ad ogni processo vengono associati:

PID (Process Identifier)

Ad ogni processo è associato un identificativo numerico, chiamato PID.

Per visualizzare il PID della propria shell Bash, si può impartire il comando ps:

$ ps
  PID TTY          TIME CMD
18945 pts/3    00:00:00 bash
19019 pts/3    00:00:00 ps

... oppure sfruttare la variabile $$:

$ echo $$
18945

PPID

Nei sistemi Un*x, quando un processo ne crea un altro, "padre" e "figlio" rimangono in qualche modo associati. Per ogni processo, infatti, è noto qual è il processo padre (detto PPID, Parent PID).

Il rapporto gerarchico può essere osservato lanciando una nuova shell dalla shell corrente:

$ bash
$ ps
  PID TTY          TIME CMD
18945 pts/3    00:00:00 bash
19058 pts/3    00:00:00 bash
19074 pts/3    00:00:00 ps
$ ps --ppid 18945   # elenca i processi che hanno come padre il processo 18945
  PID TTY          TIME CMD
19058 pts/3    00:00:00 bash

Fork

La creazione di un nuovo processo da parte del processo padre viene effettuata con la chiamata di sistema fork.

Vediamo un semplice esempio in C.

#include <stdio.h>

int main(int argc, char **argv)
{
int child_pid = fork();
if (child_pid == -1) {
       printf("fork() fallita\n");
       return 1;
    }
else if (child_pid) {
       printf("Sono il padre, il PID di mio figlio è %d.\n", child_pid);
    }
else {
       printf("Sono il figlio.\n");
    }
}

Le chiamate della famiglia exec

Spesso il processo figlio ha bisogno di un codice eseguibile diverso da quello del processo padre. Eseguirà allora una chiamata di sistema dela famiglia exec, con la quale il sistema sostituirà l'intera immagine di memoria con il file nominato nel suo primo parametro.

Un semplice esempio:

#include <stdio.h>

int main(int argc, char **argv, char **envp)
{
int child_pid = fork();
if (child_pid) {
       printf("Sono il padre, il PID di mio figlio è %d.\n", child_pid);
    }
else {
       printf("Dovrei eseguire cal\n");
       static char *arguments[]={"cal"};
       execve("/usr/bin/cal", arguments, envp);
       printf("Questa istruzione non verrà eseguita\n");
    }
}    

I processi adottati da Init

Se un processo fa un fork e termina prima del processo figlio generato, il processo figlio viene "adottato" dal processo init (l'antenato comune di tutti i processi, che ha PID 1).

Lo schema è il seguente.

PPPPPPPPPPPPP
         FFFFAAAAAAAAAAAAAAAAA

P = processo padre
F = processo figlio
A = processo figlio adottato da init quando il padre termina

I processi zombie

Se un figlio termina il lavoro prima del processo padre, rimane nello stato c.d. zombie fino al termine di quest'ultimo.

Lo schema è il seguente.

PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
         FFFFFFFFZZZZZZZZZZZZZZZZZZZZZZZZZ

P = processo padre
F = processo figlio
Z = processo figlio che diventa zombie al suo termine

L'attesa da parte del processo padre

Più correttamente, il processo padre dovrebbe porsi in attesa del processo figlio, con una chiamata di sistema waitpid.

Il caso, a seconda di chi tra padre e figlio finisce prima il proprio lavoro, potrebbe essere uno dei seguenti:

a)
PPPPPPPPPPPPPPPPWWWWWWWWWWWWWWWWWWWWWWWWWP
         FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

b)
PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPWPPPPPPPPPPP
         FFFFFFFZZZZZZZZZZZZZZZZZZZZZZZ

P = processo padre
F = processo figlio
W = processo padre in attesa

Pstree

La gerarchia dei processi può essere visualizzata con il comando pstree.

$ pstree
init---NetworkManager---{NetworkManager}
     +-firefox---run-mozilla.sh---firefox-bin---10*[{firefox-bin}]
     +-gdm---gdm-+-Xorg---Xorg
     +-5*[getty]
     +-gnome-terminal-+-bash---ssh
     |                +-bash
     |                +-bash---cat
     |                +-bash---pstree
     |                +-{gnome-terminal}
     +-quanta
     +-wpa_supplicant
(output ridotto)

Con il comando pstree -p si può visualizzare il PID di ogni processo.

La directory speciale /proc

I sistemi Linux mettono a disposizione una speciale directory, denominata /proc, contenente informazioni, in forma di file, sui processi.

Per ogni processo, nella directory /proc/PID, si hanno ad esempio:

Si veda proc(5) per ulteriori informazioni.

Informazioni sui processi: ps

Il programma ps è lo strumento principale di visualizzazione delle informazioni relative ai processi.

Il comando ps funziona con opzioni espresse in tre forme diverse:

Le opzioni hanno significati diversi in Unix98 e BSD. Ad esempio:

$ ps a
  PID TTY      STAT   TIME COMMAND
 4449 tty4     Ss+    0:00 /sbin/getty 38400 tty4
...
 5570 tty7     SL+    0:00 /usr/bin/X :0 -br -audit 0 -auth /var/lib/gdm/:0.Xaut
 5825 pts/0    Ss+    0:00 bash
 6611 pts/1    Ss     0:00 bash
 7328 pts/1    R+     0:00 ps a
$ ps -a
  PID TTY          TIME CMD
 5570 tty7     00:00:00 Xorg
 7329 pts/1    00:00:00 ps

Ps: opzioni utili

Alcune opzioni frequentemente utilizzate sono:

Notare la quasi completa equivalenza di ps aux e ps -ef.

Esecuzione in background e foreground

Un'intera pipeline può essere eseguita in background posponendo un ampersand alla fine del comando.

Un lavoro (job) in background può essere riportato in foreground con il comando fg.

Il lavoro in foreground può essere sospeso premendo ctrl-Z (^Z).

$ yes > /dev/null &
[1] 7489
$ fg %1
yes > /dev/null

(ctrl-Z)

[1]+  Stopped                 yes > /dev/null
$ bg %1
[1]+ yes > /dev/null &

Gli stati di un processo

Un processo può trovarsi in uno dei seguenti stati:

   D    Uninterruptible sleep (usually IO)
   R    Running or runnable (on run queue)
   S    Interruptible sleep (waiting for an event to complete)
   T    Stopped, either by a job control signal or because it is being
        traced.
   W    paging (not valid since the 2.6.xx kernel)
   X    dead (should never be seen)
   Z    Defunct ("zombie") process, terminated but not reaped by its
        parent.

(da ps(1))

Invio di segnali ai processi

Un meccanismo fondamentale nella comunicazione tra processi consiste nell'invio di segnali. I segnali possono essere inviati:

Per inviare il segnale SIGHUP al processo 9876 si può digitare:

kill -SIGHUP 9876
kill -HUP 9876
kill -1 9876

Segnali comuni

L'elenco completo dei segnali inviabili può essere visualizzato con il comando kill -l. I segnali più importanti sono:

Programmi utili per la gestione dei processi

Alcuni programmi che possono essere utili:

Gestione delle priorità

Ad ogni processo è associato un livello di priorità.

-20 (alta)                 0 (normale)               19 (bassa)
  +------------------------+--------------------------+

I programmi possono essere avviati con bassa priorità (da tutti) o con alta priorità (da root) tramite il comando nice

Ai processi può essere cambiata la priorità con il comando renice.

Gestione interattiva dei processi

Per una gestione interattiva dei processi, può essere utile il programma top.

Alcune opzioni interessanti possono essere attivate con la pressione di:

Esercizi