Introduzione ai sistemi operativi con UNIX
Prec Succ

5. I processi

  1. fork
  2. execl, execve
Nei paragrafi precedenti ho fatto attenzione ad utilizzare correttamente le parole programma e processo. Anche se spesso questi termini vengono confusi, non andrebbero usati in modo intercambiabile, per mettere in evidenza una differenza importante.

Un programma eseguibile è composto da codice macchina direttamente eseguibile oppure codice sorgente che sarà interpretato. Solitamente risiede permanentemente sotto forma di file su disco. Un programma indica in modo non ambiguo e comprensibile al calcolatore le azioni da intraprendere.

Un processo o task o anche job è un programma in esecuzione. Si dice che il processo viene eseguito sequenzialmente, se in un certo istante è in esecuzione una sola istruzione del processo oppure nessuna (ricordarsi dell'interleaving).

Se ad esempio due utenti diversi di uno stesso sistema UNIX si collegano per leggere la posta ed entrambi utilizzano il noto programma Pine, vi saranno due processi pine presenti in memoria contemporaneamente. Tuttavia UNIX non duplicherà lo spazio codice, ma solo lo spazio dati. Si dice che questi due processi sono entrambi istanze dello stesso programma (Pine).

I processi presenti nel sistema non sono solo quelli che sono attivati dagli utenti: esistono alcuni processi di sistema sempre presenti che svolgono funzioni del sistema operativo stesso. Questi processi non sono connessi al terminale perchè non hanno bisogno di interagire con l'utente come fa ad esempio Pine. Essi possono essere connessi ad un terminale, possono stampare eventuali messaggi sul terminale stesso, ma poi se ne disconnettono e continuano il loro lavoro silenziosamente (vedere daemon(3)). Vengono detti in gergo demoni e in molti casi si segue la convenzione di far terminare il nome di questi programmi con una d per ricordare che sono demoni. ps(1) richiede l'opzione -x per visualizzare informazioni anche su questi processi che per default non visualizza. Vedremo a breve scrivere dei demoni nel momento in cui parleremo della creazione dei processi tramite fork(2).

Alcuni esempi: httpd è il demone del web server Apache che risponde alle richieste web remote, a cui abbiamo già accennato prima. Il processo con pid 0 in FreeBSD è lo swapper, ossia lo schedulatore. Il processo con pid 1 è il processo di inizializzazione init(8) che è l'antenato di tutti gli altri processi del sistemi. Il processo 2 è il demone di paginazione (pagedaemon). Strettamente parlando httpd(8) non fa parte del sistema operativo e non è un processo speciale. Al contrario invece gli altri citati come esempio.

Ogni processo ha associata un'area dati in cui vengono memorizzate le variabili globali, ha un suo stack che viene usato per quelle automatiche, ha suoi valori per i registri di macchina, incluso il puntatore alla prossima istruzione da eseguire (o program counter), una lista delle risorse assegnategli, come i file aperti come abbiamo visto parlando delle system call per l'I/O, le informazioni riguardanti i limiti di memoria e altri dati importanti tra i quali la directory corrente, le informazioni sui permessi che il processo ha, la riga di comando che ha avviato il processo, il PID del processo padre, il terminale di controllo, ecc.

In UNIX ogni processo in esecuzione viene identificato univocamente da un numero intero positivo o nullo che viene detto ID di processo (process ID, process identifier o in breve pid) e varia da 0 a 99999. Ribadisco che viene garantita l'unicità del pid: non saranno mai presenti contemporaneamente nel sistema due processi con lo stesso pid. Quando il sistema raggiunge il limite di numerazione 99999 ricomincia a riutilizzare i numeri pid che si sono liberati partendo dall'inizio (wrap-around). Il pid viene usato dall'utente per riferirsi al processo quando invoca alcuni comandi di shell che agiscono sui processi come kill(1), o dal programmatore per riferirsi ad un processo in una system call, oppure dal kernel stesso per identificare un processo.

Da chi vengono creati i nuovi processi? Dai processi stessi tramite la system call fork(2). Oltre la fork(2) non esiste in UNIX altro meccanismo per la creazione di nuovi processi, eccetto quello della creazione del processo radice init(8) durante il booting. A breve descriveremo meglio come funziona il meccanismo di forking dei processi.

Per ora basti sapere che un processo "padre" attivo che invoca la fork(2) crea quindi un processo che è detto suo "figlio". Infatti vi è una relazione di parentela tra i processi in UNIX. Essi sono organizzati in un albero genealogico. Ogni processo sa chi è il suo padre, nel senso che nel descrittore di un processo c'è un campo detto ID del processo padre (Parent process ID o in breve ppid). Questo valore corrisponde al pid del processo creatore, per lo meno inizialmente. Infatti è possibile che un processo padre termini tramite exit(3), lasciando dei processi figli attivi (processi orfani). Il ppid di questi processi verrà cambiato dal sistema operativo con l'ID del processo root di sistema sempre presente, l'init(8), che ha pid sempre pari ad 1. init(8) è un processo speciale, un po' come la directory root del filesystem (/). Se tutti i processi sono creati da un unico processo genitore, chi creerà init(8)? La risposta è ovvia e la abbiamo accennata nel paragrafo precedente: init(8) viene creato dal kernel durante la fase di avvio del sistema ed è il primo processo ad essere creato, ecco perchè il suo pid è sempre 1.

Queste due semplici system call:

pid_t
     getpid(void);

pid_t
     getppid(void);
restituiscono rispettivamente il pid e il ppid del processo stesso che le invoca.

Il programma pstree(1), che non fa parte del sistema base di FreeBSD ma può essere installato a parte come pacchetto oppure tramite la collezione delle port (/usr/ports/sysutils/pstree), visualizza l'albero genealogico dei processi ed opera semplicemente riarrangiando l'output di ps(1).

Una informazione di schedulazione importante di un processo, che è presente nel descrittore, è lo stato del processo stesso. Semplificando, e generalmente parlando, gli stati possibili sono fondamentalmente tre. Non tutte le transizioni tra questi tre stati dei processi sono possibili: tra sei transizioni complessive, due non esistono.

diagramma degli stati
gli stati principali di un processo

Dovete immaginare che ogni processo abbia un diagramma di questo tipo associato e che in ogni istante lo stato corrente sia indicato da uno di quegli ellissi riempito con un colore.

Lo stato run (o meglio in inglese running) rappresenta un processo in esecuzione, cioè che sta usando la CPU. Se vi è una sola CPU, in ogni momento vi sarà un solo processo in stato di esecuzione: tutti gli altri saranno o nello stato ready o wait.

Lo stato ready rappresenta un processo pronto ma non in esecuzione. È pronto ad andare in esecuzione ma non può perchè non ci sono attualmente CPU disponibili (o non c'è più la CPU nel caso ve ne sia una sola).

Lo stato wait (o waiting) rappresenta un processo bloccato in attesa di un evento esterno, ad esempio il completamento di una richiesta di I/O. Anche se una CPU fosse libera, il processo non può proseguire l'esecuzione se prima non si verifica l'evento che lo ha fatto bloccare.

Le quattro transizioni tra questi tre stati avvengono nelle seguenti circostanze:

  1. avviene tipicamente quando questo processo fa una richiesta su un dispositivo di I/O lento; in generale ogni volta che il processo richiede una risorsa che può essere usata solo in modo mutuamente esclusivo ed è occupata da un altro processo al momento della richiesta.
  2. in in sistema time-sharing avviene perchè è scaduto il quanto di tempo assegnato al processo che stiamo considerando; in generale avviene quando lo schedulatore sospende l'esecuzione di questo processo e ne sceglie un altro da mandare in esecuzione.
  3. avviene quando lo scheduler ha scelto proprio questo processo tra quelli pronti da rimandare in esecuzione, eventualmente in base a schemi di priorità.
  4. avviene quando i dati di cui il processo considerato era in attesa sono pronti; generalmente parlando avviene quando la risorsa occupata in attesa della quale il processo si era bloccato si è resa di nuovo libera.

I processi che sono bloccati oppure che sono pronti non è detto che risiedano in memoria principale, ma la loro immagine potrebbe essere salvata sul disco dal sistema operativo per liberare temporaneamente della RAM necessaria ad altri processi. Questa tecnica viene detta swapping, e come l'interleaving o l'overlapping, avviene in modo trasparante al processo, senza che il programmatore deva minimamente preoccuparsi di gestire la cosa. È tutto gestito dal sistema operativo.

Quali sono le informazioni sui permessi di un processo in UNIX? Si tratta di sei campi di tipo ID. Come un file, ogni processo ha associati un possessore e un gruppo dell'utente che realmente lo ha avviato, detti rispettivamente Real User ID (ruid) e Real Group ID (rgid). Il gruppo coincide col gruppo principale dell'utente proprietario del processo padre; infatti il ruid e il rgid di un processo figlio vengono ereditati dal processo padre che ha chiamato la fork(2) e non vengono solitamente cambiati (comunque solo un processo del superutente può cambiare ruid e rgid, vedasi setruid(2) e setrgid(2)).

ruid e rgid non sono i soli dati di permesso associati ad un processo. Poichè un utente UNIX, come abbiamo visto, può essere membro di uno o più gruppi oltre al gruppo principale, ad ogni processo è associata anche associata la lista dei gruppi di accesso (vedere getgroups(2)), che viene utilizzata per determinare l'accessibilità alle risorse.

Precedentemente abbiamo visto che esistono anche due permessi che si applicano agli eseguibili che abbiamo chiamato sid (set-user-ID) e gid (set-group-ID). È venuto il momento di spiegare come il kernel opera in presenza di questi permessi.

Supponiamo che un eseguibile abbia il permesso sid (detto anche suid). Un esempio di tale eseguibile è il comando passwd(1) che permette agli utenti di cambiarsi la propria password (e solo quella), senza richiedere l'intervento dell'amministratore.

$ ls -l /usr/bin/passwd 
-r-sr-xr-x  2 root  wheel  28828 Apr  3 10:55 /usr/bin/passwd
supponete poi che l'utente joe utilizzi con successo il comando passwd(1) per cambiarsi la sua password, creando il relativo processo figlio della sua shell di login:
$ id -p
uid     joe
groups  joe
$ passwd
Changing local password for joe.
Old password:
New password:
Retype new password:
passwd: updating the database...
passwd: done
La domanda che sorge a questo punto è come è possibile che l'utente joe sia riuscito a modificare il database degli utenti dove non ha permesso di scrittura? Infatti è possibile verificare che joe non può scrivere sul database:
$ ls -l /etc/spwd.db
-rw-------  1 root  wheel  40960 May 17 23:23 /etc/spwd.db
del resto se joe potesse scrivere su questo db potrebbe anche cambiare la password di root e guadagnare facilmente accesso di root alla macchina, rendendo vano ogni altro meccanismo di protezione.

Eppure passwd(1) è riuscita a modificare questo database. Il meccanismo è il seguente: quando joe esegue passwd, il ruid e il rgid del processo passwd sono quelli rispettivamente dell'utente joe e del gruppo joe. Il ruid e il rgid non vengono cambiati. Tuttavia esiste un altro campo di permessi nel descrittore di un processo, detto Effective User Id o eid o euid, che identifica l'utente effettivo e inizialmente ha lo stesso valore di ruid. Nel nostro caso euid verrà cambiato dal valore dell'id di joe nel valore dell'id del root, che è 0, dalle funzioni di tipo exec(3) eseguite dal processo figlio forkato dalla shell che si trasformerà nel processo passwd(1). Ne riparleremo meglio in seguito quando tratteremo la execve(2).

Sono proprio questi euid e egid, insieme alla lista dei gruppi di accesso, che determinano i permessi che il processo ha sulle altre risorse del sistema, ad es. permettono di stabilire quali file o dispositivi il processo può aprire.

In questo modo solo il comando passwd(1) può accedere in scrittura al database degli utenti e solo in modo controllato. Ovviamente chi ha scritto il comando passwd(1) ha dovuto stare molto attento a fare tutti i controlli possibili per evitare che passwd(1) possare essere usato in modo improprio.

In generale quando si scrivono programmi suid, è bene impostare l'euid sull'id privilegiato del possessore del file solo appena prima delle sezioni del codice che hanno bisogno di tali permessi e riportare l'euid al valore dell'uid dell'utente reale non appena finisce una tale sezione, usando la system call seteuid(2). In altri termini le applicazioni seteuid dovrebbero rilasciare ogni volta i loro privilegi non appena non servono più.

Discorso analogo vale per il sgid: i file eseguibili che hanno settato il bit setgid gireranno coll'egid settato al valore del gid del file. Inoltre per sicurezza un utente può settare tramite chmod(2) il suid bit o il sgid bit solo su un file di cui è possessore.

Altri due campi di permesso sono il Saved Set User ID (svuid) e il Saved Set Group ID (svgid) e servono per semplificare il toggling dell'euid o del egid tra i valori di ruid,rgid e quelli set-user-ID, set-group-ID. In questi campi le funzioni di tipo exec() salvano i valori dell'euid e del egid dopo averli eventualmente cambiati. Se il programma cambia il suo euid e il suo egid, e poi successivamente decide di voler tornare ai valori di euid e egid che aveva appena è stato caricato dalla exec(), potrà utilizzare i valori salvati del svuid e il svgid e rimetterli rispettivamente nell'euid e nell'egid.

Una dimostrazione pratica con ps(1) (UID corrisponde ad euid):

$ ps -caxo ruid,rgid,uid,svuid,svgid,command
 RUID  RGID   UID SVUID SVGID COMMAND
    0     0     0     0     0 swapper
    0     0     0     0     0 init
    0     0     0     0     0 pagedaemon
    0     0     0     0     0 vmdaemon
    0     0     0     0     0 bufdaemon
    0     0     0     0     0 vnlru
    0     0     0     0     0 syncer
    0     0     0     0     0 adjkerntz
    0     0     0     0     0 usbd
    0     0     0     0     0 moused
    0     0     0     0     0 httpd
   88    88    88    88    88 mysqld
   80    80    80    80    80 httpd
   80    80    80    80    80 httpd
   80    80    80    80    80 httpd
   80    80    80    80    80 httpd
   80    80    80    80    80 httpd
 1001  1001     0     0     0 XFree86
 1001  1001     0     0  1001 xterm
 1001  1001  1001  1001  1001 bash
 1001  1001  1001  1001  1001 ps
    0  1001     0     0  1001 login
 1001  1001  1001  1001  1001 sh
 1001  1001  1001  1001  1001 xinit
 1001  1001  1001  1001  1001 blackbox
    0     0     0     0     0 getty
    0     0     0     0     0 getty
    0     0     0     0     0 getty
    0     0     0     0     0 getty
    0     0     0     0     0 getty
    0     0     0     0     0 getty
    0     0     0     0     0 getty
    0     0     0     0     0 sh
possiamo anche stampare il nome utente corrispondente a ruid e quello corrispondente a uid:
$ ps -caxo ruser,rgid,user,svuid,svgid,command
RUSER  RGID USER  SVUID SVGID COMMAND
root      0 root      0     0 swapper
root      0 root      0     0 init
root      0 root      0     0 pagedaemon
root      0 root      0     0 vmdaemon
root      0 root      0     0 bufdaemon
root      0 root      0     0 vnlru
root      0 root      0     0 syncer
root      0 root      0     0 adjkerntz
root      0 root      0     0 usbd
root      0 root      0     0 moused
root      0 root      0     0 httpd
mysql    88 mysql    88    88 mysqld
www      80 www      80    80 httpd
www      80 www      80    80 httpd
www      80 www      80    80 httpd
www      80 www      80    80 httpd
www      80 www      80    80 httpd
ant    1001 root      0     0 XFree86
ant    1001 root      0  1001 xterm
ant    1001 ant    1001  1001 bash
ant    1001 ant    1001  1001 ps
root   1001 root      0  1001 login
ant    1001 ant    1001  1001 sh
ant    1001 ant    1001  1001 xinit
ant    1001 ant    1001  1001 blackbox
root      0 root      0     0 getty
root      0 root      0     0 getty
root      0 root      0     0 getty
root      0 root      0     0 getty
root      0 root      0     0 getty
root      0 root      0     0 getty
root      0 root      0     0 getty
root      0 root      0     0 sh
altro esempio: supponiamo che l'utente joe (uid 1002, gid 1002) abbia lanciato passwd(1); ecco cosa visualizzerebbe il ps(1) di prima lanciato da un altro utente o dallo stesso joe ad esempio da un altra consolle o dopo aver sospeso il processo passwd:
$ ps -caxo ruid,rgid,uid,svuid,svgid,command
 RUID  RGID   UID SVUID SVGID COMMAND
 ....  ....   ... ..... ..... .......
 1002  1002     0     0  1002 passwd
 ....  ....   ... ..... ..... .......
$ ps -caxo ruser,rgid,user,svuid,svgid,command
RUSER  RGID USER  SVUID SVGID COMMAND
.....  .... ....  ..... ..... .......
joe    1002 root      0  1002 passwd
.....  .... ....  ..... ..... .......
$ ps -caxo ruser,user,command
RUSER USER  COMMAND
..... ....  .......
joe   root  passwd
..... ....  .......
in particolare l'ultimo comando ps mette in evidenza che l'utente reale del processo passwd è joe, ma quello effettivo è root, perchè passwd è un eseguibile set-user-ID che appartiene al root.

L'amministratore di sistema può voler ottenere una lista dei file che hanno il set-user-ID o il set-group-ID nell'intero filesystem, con indicazioni anche di chi è il proprietario e il gruppo proprietario di questi file e i permessi che il file ha. Ci viene in aiuto il comando find(1):

# find / -perm +06000 -ls
oppure
# find / -perm +ug+s -ls
173076  544 -r-sr-xr-x    1 root             wheel              251444 Apr  3 10:53 /bin/rcp
 21641  132 -r-xr-sr-x    1 root             kmem                66248 Apr  3 10:54 /sbin/ccdconfig
 21688  448 -r-sr-xr-x    1 root             wheel              203868 Apr  3 10:54 /sbin/ping
 21689  448 -r-sr-xr-x    1 root             wheel              206472 Apr  3 10:54 /sbin/ping6
 21698  332 -r-sr-x---    1 root             operator           168672 Apr  3 10:54 /sbin/shutdown
151427  244 -r-sr-sr-x    1 uucp             dialer             123160 Apr  3 10:53 /usr/bin/cu
151428  172 -r-sr-xr-x    1 uucp             wheel               87276 Apr  3 10:53 /usr/bin/uucp
151430   72 -r-sr-xr-x    1 uucp             wheel               36852 Apr  3 10:53 /usr/bin/uuname
151433  188 -r-sr-sr-x    1 uucp             dialer              95820 Apr  3 10:53 /usr/bin/uustat
151435  172 -r-sr-xr-x    1 uucp             wheel               87996 Apr  3 10:53 /usr/bin/uux
151486   60 -r-sr-xr-x    1 man              wheel               29752 Apr  3 10:54 /usr/bin/man
151534   40 -r-sr-xr-x    4 root             wheel               19616 Apr  3 10:55 /usr/bin/at
151534   40 -r-sr-xr-x    4 root             wheel               19616 Apr  3 10:55 /usr/bin/atq
151534   40 -r-sr-xr-x    4 root             wheel               19616 Apr  3 10:55 /usr/bin/atrm
151534   40 -r-sr-xr-x    4 root             wheel               19616 Apr  3 10:55 /usr/bin/batch
151549   68 -r-sr-xr-x    6 root             wheel               32884 Apr  3 10:55 /usr/bin/chpass
151549   68 -r-sr-xr-x    6 root             wheel               32884 Apr  3 10:55 /usr/bin/chfn
151549   68 -r-sr-xr-x    6 root             wheel               32884 Apr  3 10:55 /usr/bin/chsh
151549   68 -r-sr-xr-x    6 root             wheel               32884 Apr  3 10:55 /usr/bin/ypchpass
151549   68 -r-sr-xr-x    6 root             wheel               32884 Apr  3 10:55 /usr/bin/ypchfn
151549   68 -r-sr-xr-x    6 root             wheel               32884 Apr  3 10:55 /usr/bin/ypchsh
151577   28 -r-xr-sr-x    1 root             kmem                13088 Apr  3 10:55 /usr/bin/fstat
151591   20 -r-xr-sr-x    1 root             kmem                 9832 Apr  3 10:55 /usr/bin/ipcs
151597    8 -r-sr-xr-x    1 root             wheel                3648 Apr  3 10:55 /usr/bin/keyinfo
151598   16 -r-sr-xr-x    1 root             wheel                7508 Apr  3 10:55 /usr/bin/keyinit
151615   16 -r-sr-xr-x    1 root             wheel                7232 Apr  3 10:55 /usr/bin/lock
151618   44 -r-sr-xr-x    1 root             wheel               21824 Apr  3 11:00 /usr/bin/login
151636  180 -r-xr-sr-x    1 root             kmem                92088 Apr  3 10:55 /usr/bin/netstat
151643    8 -r-sr-xr-x    1 root             wheel                4028 Apr  3 10:55 /usr/bin/opieinfo
151646   24 -r-sr-xr-x    1 root             wheel               10520 Apr  3 10:55 /usr/bin/opiepasswd
151648   60 -r-sr-xr-x    2 root             wheel               28828 Apr  3 10:55 /usr/bin/passwd
151648   60 -r-sr-xr-x    2 root             wheel               28828 Apr  3 10:55 /usr/bin/yppasswd
151653   24 -r-sr-xr-x    1 root             wheel               10648 Apr  3 10:55 /usr/bin/quota
151657   20 -r-sr-xr-x    1 root             wheel               10184 Apr  3 10:55 /usr/bin/rlogin
151661   16 -r-sr-xr-x    1 root             wheel                7980 Apr  3 10:55 /usr/bin/rsh
151672   20 -r-sr-xr-x    1 root             wheel                8200 Apr  3 10:55 /usr/bin/su
151675  112 -r-xr-sr-x    1 root             kmem                56260 Apr  3 10:55 /usr/bin/systat
151683   64 -r-xr-sr-x    1 root             kmem                32360 Apr  3 10:55 /usr/bin/top
151709   32 -r-xr-sr-x    1 root             kmem                16196 Apr  3 10:55 /usr/bin/vmstat
151711   20 -r-xr-sr-x    1 root             tty                  9988 Apr  3 10:55 /usr/bin/wall
151719   16 -r-xr-sr-x    1 root             tty                  7524 Apr  3 10:55 /usr/bin/write
151745   52 -r-sr-xr-x    1 root             wheel               24892 Apr  3 10:55 /usr/bin/crontab
151750   48 -r-sr-sr-x    1 root             daemon              23700 Apr  3 10:55 /usr/bin/lpq
151751   56 -r-sr-sr-x    1 root             daemon              27220 Apr  3 10:55 /usr/bin/lpr
151752   48 -r-sr-sr-x    1 root             daemon              22832 Apr  3 10:55 /usr/bin/lprm
154634 1184 -r-xr-sr-x    1 root             smmsp              585240 Apr  3 10:55 /usr/libexec/sendmail/sendmail
154637  480 -r-sr-sr-x    1 uucp             dialer             219332 Apr  3 10:53 /usr/libexec/uucp/uucico
154638  196 -r-sr-s---    1 uucp             uucp                98604 Apr  3 10:53 /usr/libexec/uucp/uuxqt
200474   20 -r-xr-sr-x    1 root             kmem                 9816 Mar 23 04:38 /usr/local/bin/libgtop_server2
238183   12 -r-sr-xr-x    1 root             wheel                5388 Mar 22 17:44 /usr/local/sbin/eject
173137   20 -r-xr-sr-x    1 root             kmem                 9704 Apr  3 10:55 /usr/sbin/iostat
173152   32 -r-sr-xr-x    1 root             wheel               16320 Apr  3 10:55 /usr/sbin/mrinfo
173154   60 -r-sr-xr-x    1 root             wheel               29928 Apr  3 10:55 /usr/sbin/mtrace
173187   32 -r-xr-sr-x    2 root             kmem                14760 Apr  3 10:55 /usr/sbin/pstat
173187   32 -r-xr-sr-x    2 root             kmem                14760 Apr  3 10:55 /usr/sbin/swapinfo
173212   24 -r-sr-x---    1 root             network             11080 Apr  3 10:55 /usr/sbin/sliplogin
173220   32 -r-sr-xr-x    1 root             wheel               15444 Apr  3 10:55 /usr/sbin/timedc
173221   28 -r-sr-xr-x    1 root             wheel               13700 Apr  3 10:55 /usr/sbin/traceroute
173222   32 -r-sr-xr-x    1 root             wheel               15752 Apr  3 10:55 /usr/sbin/traceroute6
173223   16 -r-xr-sr-x    1 root             kmem                 8072 Apr  3 10:55 /usr/sbin/trpt
173252   88 -r-xr-sr-x    1 root             daemon              43168 Apr  3 10:55 /usr/sbin/lpc
173300  704 -r-sr-xr--    1 root             network            333100 Apr  3 10:55 /usr/sbin/ppp
173301  188 -r-sr-x---    1 root             dialer              95376 Apr  3 10:55 /usr/sbin/pppd
180854   16 -r-xr-sr-x    1 root             games                7180 Apr  3 10:53 /usr/games/dm
197755  512 -rws--x--x    1 root             wheel              232916 Mar 23 13:07 /usr/X11R6/bin/xterm
  3031   12 -r-sr-sr-x    1 root             wheel                4804 Mar 23 18:10 /usr/X11R6/bin/Xwrapper-4
 25970   20 -r-sr-xr-x    1 root             wheel                8896 Mar 23 19:48 /usr/X11R6/bin/gnome-pty-helper
220271   56 -r-xr-sr-x    1 root             games               26716 Mar 29 13:04 /usr/X11R6/bin/glines
220274  140 -r-xr-sr-x    1 root             games               70712 Mar 29 13:04 /usr/X11R6/bin/gnibbles
220275  148 -r-xr-sr-x    1 root             games               75676 Mar 29 13:04 /usr/X11R6/bin/gnobots2
220277  112 -r-xr-sr-x    1 root             games               56696 Mar 29 13:04 /usr/X11R6/bin/gnome-stones
220278   88 -r-xr-sr-x    1 root             games               43168 Mar 29 13:04 /usr/X11R6/bin/gnome-xbill
220280   88 -r-xr-sr-x    1 root             games               43576 Mar 29 13:04 /usr/X11R6/bin/gnometris
220281   80 -r-xr-sr-x    1 root             games               39456 Mar 29 13:04 /usr/X11R6/bin/gnomine
220283   60 -r-xr-sr-x    1 root             games               29336 Mar 29 13:04 /usr/X11R6/bin/gnotravex
220284   48 -r-xr-sr-x    1 root             games               23940 Mar 29 13:04 /usr/X11R6/bin/gnotski
220286  512 -r-xr-sr-x    1 root             games              238712 Mar 29 13:04 /usr/X11R6/bin/gtali
220287   96 -r-xr-sr-x    1 root             games               47404 Mar 29 13:04 /usr/X11R6/bin/iagno
220289  104 -r-xr-sr-x    1 root             games               53072 Mar 29 13:04 /usr/X11R6/bin/mahjongg
220290   44 -r-xr-sr-x    1 root             games               20764 Mar 29 13:04 /usr/X11R6/bin/same-gnome
220688  448 -rwsr-xr-x    1 root             wheel              197516 Mar 23 23:49 /usr/X11R6/bin/xscreensaver
 51315 4384 ---s--x--x    1 root             wheel             2214504 Mar 23 12:00 /usr/X11R6/bin/xlock
 68277  164 -rws--x--x    1 root             wheel               83160 Mar 23 10:15 /usr/X11R6/bin/rxvt
  4960   20 -r-sr-xr-x    1 root             wheel                8736 Mar 29 11:38 /usr/X11R6/libexec/gnome-pty-helper
182099   20 -r-sr-xr-x    1 root             wheel                8672 Mar 29 11:27 /usr/X11R6/libexec/libzvt-2.0/gnome-pty-helper
Il comando ps(1) quando viene usato senza altri argomenti mostra solo le informazioni sui processi il cui utente effettivo coincide con l'utente reale che invoca il processo ps(1) e che hanno un terminale di controllo. Ad esempio:
$ ps
  PID  TT  STAT      TIME COMMAND
  186  p0  Rs     0:00.05 -bash (bash)
  204  p0  R+     0:00.00 ps
  197  p1  Ss+    0:00.38 aumix -C ansi
  154  v0  I+     0:00.04 /bin/sh /usr/X11R6/bin/startx
  173  v0  I+     0:00.02 xinit /home/ant/.xinitrc -- -nolisten tcp
  179  v0  I      0:00.34 blackbox
L'output è composto da una riga per processo, esclusa la prima riga che è di intestazione. Per ogni processo, da sinistra verso destra le informazioni di default sono: l'ID del processo (il PID), il terminale di controllo, lo stato del processo, il tempo di CPU consumato, somma del tempo di esecuzione in modo utente più il tempo di esecuzione in modo kernel (di solito TIME è piuttosto inferiore rispetto al tempo trascorso dal momento in cui è stato avviato il programma), e infine l'intera linea di comando associata al processo.

I processi mostrati sono la shell bash(1) (GNU Bourne-Again SHell), il processo ps(1) stesso, il mixer audio aumix(), lo script di shell startx(1) che inizializza le sessioni grafiche X, il quale funziona come front-end facile da usare ad xinit(1) (difatti startx è stato lanciato senza argomenti), e infine il window manager blackbox(1), che preferisco perchè è veloce e funzionale.

TT rappresenta una abbreviazione del percorso del dispositivo terminale. Ad esempio p0 è una abbreviazione di /dev/ttyp0 (ho premesso il prefisso /dev/tty a p0), v0 sta per /dev/ttyv0, ecc. /dev/ttyv0 è la prima consolle virtuale in modo testo. L'utente si è loggato a questa consolle e poi ha lanciato tramite startx(1) il sistema grafico X Window. Il comando tty(1) ritorna il nome del terminale connesso al suo standard input e output:

$ tty
/dev/ttyp1
/dev/ttyp0 è il primo pseudo-terminale usato ad esempio dagli emulatori di terminale per X, come xterm(1), /dev/ttyp1 è il secondo ecc.

Notate che il comando relativo alla shell bash(1) è stato scritto come "-bash (bash)". Il vero nome del comando è sempre bash, non esiste un comando chiamato -bash su disco, quel meno indica che la shell è stata invocata come shell di login. Per maggiori informazioni sulla differenza tra i due diversi modi di invocare la shell, come shell di login o meno vedere la sezione INVOCATION della manpage bash(1). In breve la differenza tra le shell di login e quelle che non sono di login sta semplicemente in quali file di inizializzazione vengono letti (vedere a proposito anche la sezione FILES di bash(1)). Questa suddivisione della configurazione risulta a volte comoda, perchè l'utente potrebbe volere che alcuni comandi di shell siano eseguiti automaticamente solo al login (per esempio il comando fortune(6)), ma non ad ogni successiva invocazione della shell.

Usando l'opzione -x si rilevano altri processi appartenenti all'utente considerato che non lavorano col terminale: xmms(1) (X Multimedia System) è un famoso riproduttore audio per la X, mentre dillo è un web browser grafico piccolo e leggero scritto in linguaggio C:

$ ps -x
  PID  TT  STAT      TIME COMMAND
  187  ??  Ss     0:25.93 xmms
  198  ??  Is     0:00.30 dillo
  186  p0  Ss     0:00.05 -bash (bash)
  205  p0  R+     0:00.00 ps -x
  197  p1  Ss+    0:00.38 aumix -C ansi
  154  v0  I+     0:00.04 /bin/sh /usr/X11R6/bin/startx
  173  v0  I+     0:00.02 xinit /home/ant/.xinitrc -- -nolisten tcp
  179  v0  S      0:00.34 blackbox

Notare che l'output di ps(1) per default è ordinato in base al campo TT e a parità di TT in base al campo PID, entrambi in senso crescente.

Per mettere meglio in evidenza la relazione tra i processi possiamo chiedere a ps(1) di aggiungere dopo il pid la colonna ppid:

$ ps -O ppid
  PID  PPID  TT  STAT      TIME COMMAND
  171   170  p0  Ss     0:00.07 -bash (bash)
  238   171  p0  R+     0:00.00 ps -O ppid
  217   208  p1  Ss+    0:00.53 aumix -C ansi
  148   116  v0  I+     0:00.04 /bin/sh /usr/X11R6/bin/startx
  158   148  v0  I+     0:00.02 xinit /home/ant/.xinitrc -- -nolisten tcp
  164   158  v0  S      0:00.41 blackbox
oppure possiamo usare pstree:
$ pstree -h    
pstree $Revision: 2.17 $ by Fred Hucht (C) 1993-2002
EMail:fred@thp.Uni-Duisburg.de

Usage: pstree [-f file] [-g] [-u user] [-U] [-s string] [-p pid] [-w] [pid ...]
   -f file   read input from <file> (- is stdin) instead of running
             "ps -axo "user pid ppid command""
   -g n      use graphics chars for tree. n=1: IBM-850, n=2: VT100
   -u user   show only branches containing processes of <user>
   -U        don't show branches containing only root processes
   -s string show only branches containing process with <string> in commandline
   -p pid    show only branches containing process <pid>
   -w        wide output, not truncated to window width
   pid ...   process ids to start from, default is 1 (init)
             use 0 to also show kernel processes
$ pstree -u $USER
-+- 00001 root /sbin/init --
 \-+- 00116 root login [pam] (login)
   \-+- 00148 ant /bin/sh /usr/X11R6/bin/startx
     \-+- 00158 ant xinit /home/ant/.xinitrc -- -nolisten tcp
       |--- 00159 root /usr/X11R6/bin/XFree86 :0 -nolisten tcp
       \-+- 00164 ant blackbox
         |-+- 00170 root xterm -ls
         | \-+- 00171 ant -bash (bash)
         |   \-+- 00267 ant pstree -u ant
         |     \-+- 00268 ant sh -c ps -axo "user pid ppid command"
         |       \--- 00269 ant ps -axo user pid ppid command
         |--- 00207 ant dillo
         |-+- 00208 root /usr/X11R6/bin/xterm -rv +sb -geometry 79x13 -T aumix 
         | \--- 00217 ant aumix -C ansi
         \--- 00218 ant xmms
$ pstree 148
-+- 00148 ant /bin/sh /usr/X11R6/bin/startx
 \-+- 00158 ant xinit /home/ant/.xinitrc -- -nolisten tcp
   |--- 00159 root /usr/X11R6/bin/XFree86 :0 -nolisten tcp
   \-+- 00164 ant blackbox
     |-+- 00170 root xterm -ls
     | \-+- 00171 ant -bash (bash)
     |   \-+- 00253 ant pstree 148
     |     \-+- 00254 ant sh -c ps -axo "user pid ppid command"
     |       \--- 00255 ant ps -axo user pid ppid command
     |--- 00207 ant dillo
     |-+- 00208 root /usr/X11R6/bin/xterm -rv +sb -geometry 79x13 -T aumix -n a
     | \--- 00217 ant aumix -C ansi
     \--- 00218 ant xmms

Lo stato di un processo è indicato con una sequenza di lettere; la prima rappresenta lo stato di esecuzione, secondo la seguente tabella, le altre lettere rappresentano informazioni di stato addizionali (vedere ps(1)):

lettera | significato
--------+-----------------------------------------------
I       | processo inattivo (idle) per più di circa 20 secondi
R       | processo eseguibile (runnable)
S       | processo dormiente (sleeping) da meno di 20 secondi circa
D       | processo in attesa non interrompibile (disk wait)
T       | processo sospeso (stopped)
Z       | processo morto (zombie)
J       | processo in prigione (jail(2))

Notare che R significa che il processo si trova nella memoria RAM ed è pronto per l'esecuzione. Nell'esempio di ps(1) visto sopra sia la bash(1) che ps(1) erano nello stato R, ma ovviamente solo uno dei due, stava realmente eseguendo una istruzione in un'istante.

ps(1) ha molte altre opzioni per cui si rimanda alla manpage ps(1). Ad esempio per aggiungere l'informazione della data ed ora in cui un processo ha iniziato l'esecuzione usare:

$ ps -O lstart

Altre opzioni comuni sono -a, che abbiamo già usato e che fa sì che siano visualizzate anche le informazioni sui processi degli altri utenti oltre a quelli dell'utente che ha invocato ps(1), -c che fa sì che la colonna command contenga solo il nome dell'eseguibile anziché l'intero comando con tutti i parametri.

procfs(5) è il filesystem dei processi, un filesystem virtuale di solito montato su /proc (come si rileva dal file /etc/fstab(5)), che implementa una vista sotto forma di file e directory sulla tabella dei processi mantenuta dal kernel. /proc contiene una directory per ogni processo, nominata con il suo PID e dentro ogni directory parecchi file, ciascuno contenente una particolare categoria di informazioni. Il link simbolico /proc/curproc è uno speciale link dinamico che punta sempre al processo che vi si riferisce:

$ file /proc/curproc
/proc/curproc: symbolic link to 293
$ file /proc/curproc
/proc/curproc: symbolic link to 294
$ file /proc/curproc
/proc/curproc: symbolic link to 295
Ad esempio il file cmdline contiene il nome dell'eseguibile e tutti gli argomenti sulla riga di comando:
$ cat /proc/curproc/cmdline 
cat/proc/curproc/cmdline$
status contiene lo stato corrente di un processo:
$ cat /proc/curproc/status 
cat 326 175 326 175 5,1 ctty 1053372390,342672 0,0 0,1558 nochan 1001 1001 1001,1001,1001,0 -
una spiegazione di alcune, non tutte le informazioni mostrate: cat è il nome del processo, 326 è il suo PID, 175 il suo PPID, "1053372390,342672" rappresenta la data e l'ora in cui il processo è partito, con la precisione dei microsecondi. La data "00:00:00 GMT, Jan. 1, 1970" rappresenta per convenzione l'inizio dell'Epoca UNIX e il tempo viene misurato in termini di secondi (e sottomultipli) trascorsi da quella data. Così 1053372390,342672 rappresenta circa "21:26:30 CEST, May 19, 2003". Il primo 1001 rappresenta l'euid del processo (effective user id) e per un processo che ha acquisito i privilegi di root tramite il meccanismo del suid sarà 0.

Per gli altri file vedere procfs(5). procfs(5) è utile a scopi di debugging e fornisce alcune informazioni estese a comandi quali ps(1), ma non è obbligatorio montarlo e se ne può fare a meno. L'amministratore potrebbe decidere di non montare procfs e di impostare la variabile del kernel kern.ps_showallprocs a 0, per impedire, per ragioni di sicurezza, ad un utente qualsiasi di usare l'opzione -a di ps(1) od ottenere tramite /proc informazioni sui processi che non sono i suoi.


Prec Indice Succ
Utenti di UNIX e permessi fork