Introduzione ai sistemi operativi con UNIX | ||
---|---|---|
Prec | Succ |
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.
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:
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/passwdsupponete 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: doneLa 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.dbdel 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 shpossiamo 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 shaltro 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-helperIl 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 blackboxL'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 blackboxoppure 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 295Ad 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 |