Home / Indice sezione | www.icosaedro.it | ![]() |
1. Eseguibili (o programmi) e processi
2. Scheduling dei processi
3. Struttura dati di un processo
4. Forking di processi
5. Exec
6. Visualizzare i processi
7. Segmentazione e memoria protetta
8. Eseguibili binari
9. Eseguibili script
10. Comunicare con i processi
Un processo è l'insieme delle informazioni di gestione, dei dati e del codice caricati in memoria e necessari per la sua esecuzione: tutta questa memoria occupata dal processo si chiama immagine del processo.
Ci possono essere anche parecchi processi che eseguono uno stesso programma: per esempio ci possono essere diversi utenti che eseguono /bin/bash e che stanno usando /bin/ls.
Il kernel esegue a turno tutti i processi che si trovano nello stato di "ready". Ogni processo viene eseguito tipicamente per 0.01 secondi, poi il kernel lo sospende, aggiorna lo scheduler e quindi invoca il processo che risulta averne diritto.
Stati di un processo. Su di un sistema a singolo processore, un solo processo alla volta si trova nello stato Running; scaduto il tempo ad esso concesso, il kernel lo interrompe e lo pone nello stato di Ready. I processi in attesa di eventi esterni, per esempio in attesa di dati dalla tastiera o in attesa di connessioni di rete, vengono posti nello stato di Waiting e risvegliati dal kernel solo quando l'evento arriva. In realtà esistono vari altri stati del processo che qui non abbiamo esaminato, ma si tratta di aspetti non fondamentali per la comprensione degli argomenti di questo corso. |
/home/pippo
cd
) e viene aggiunto al nome dei file
quando questo nome non è assoluto. In altri termini, nella comunicazione tra
processo e kernel si evita così di ripetere lunghe stringhe contenenti il
path della directory nella quale il processo sta lavorando.
E' ereditario.
umask
(v. man bash).
E' ereditario.
export NOMEVAR="stringa da assegnare"
Il comando export
è necessario perché in sua assenza
l'istruzione comporterebbe l'assegnamento di una semplice variabile
interna dell'interprete, che perciò non verrebbe ereditata dai processi
avviati. Il comando env
mostra tutte le variabili d'ambiente
correntmente assegnate.
E' ereditaria.
Alcune variabili di ambiente convenzionali riconosciute dai processi e il loro valore assunto per l'utente "pippo":
CLASSPATH=/usr/java/jdk1.3.1_01/jre/lib:.
Dir. delle classi Java (per la JVM).
DISPLAY=:0.0
Server X Window da usare (in questo caso il localhost, display 0, schermo 0). Dice ai processi client dove inviare i comandi di tracciamento e da dove ricevere l'input tastiera+mouse. Impostata per default all'avvio di X Window col comandostartx
.
EDITOR=/bin/vim
Text editor preferito. Alcuni programmi fanno riferimento al valore di questa variabile per eseguire il text editor preferito. Ad esempio, il comandocrontab -e
usa l'editor per editare il proprio file di configurazione.
HOME=/home/pippo
Home dir. dell'utente. Viene impostato dal processo di login.
HOSTDISPLAY=orso.casa.lan:0.0
Variante di DISPLAY.
PAGER=less --ignore-case
Informa il comandoless
che intendiamo eseguire le ricerche con/
ignorando la differenza tra lettere maiuscole e lettere minuscole, cosa spesso molto utile.
PATH=/home/pippo/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/sbin:/usr/sbin:/usr/java/jdk1.3.1_01/bin
Nelle directory indicate in questo elencobash
e altri comandi ricercano i comandi da avviare. Il carattere due-punti viene usato per separare gli elementi della lista, e quindi non può apparire nei nomi delle directory.
SHELL=/bin/bash
Il nome dello shell che stiamo usando.
USER=pippo
Il nome dell'utente che ha eseguito il login.
fork()
. Il kernel crea allora una copia esatta
del parent, incluso i dati, lo stack, l'heap, il program counter, ecc.
Naturalmente, il processo child ha un PID diverso, e il suo PPID è
quello del padre. Tutti i dati del processo padre visti nel paragrafo
precedente che sono ereditari, vengono duplicati nel child. Dunque il child
ha la stessa identità, la stessa dir. di lavoro, la stessa maschera dei
permessi e le stesse variabili di ambiente.
L'unica vera differenza è che la funzione fork()
ritorna
al padre il PID del figlio, mentre al figlio ritorna zero. L'esecuzione
del codice dei due processi prosegue quindi dal punto in cui è avvenuto il
forking.
La replicazione è l'unico modo in Unix per creare un nuovo processo, intendendo per nuovo un processo con un altro PID.
Esempio di frammento di programma in liguaggio in prosa che esegue la formattazione e la stampa di un documento. In questo caso l'utente deve aspettare che il programma abbia terminato il suo lavoro di stampa prima di poter proseguire l'editing del documento (pensiamo, ad esempio, che si tratti di un word processor):
In questo secondo esempio, lo stesso programma demanda il lavoro di formattazione e stampa a un processo child:procedura Stampa() { Formatta Invia alla stampante Ritorna }
Altro esempio tipico di forking: i daemon process che gestiscono servizi di rete: il padre aspetta la richiesta di connessione e, quando ne arriva una, crea un figlio al quale demanda il lavoro, mentre il padre si rimette subito in ascolto di nuove richieste di connessione (casi tipici: inetd, xinetd, sshd, Apache). In ambiente Unix questa è la strategia seguita da molti programmi server per poter rispondere a interrogazioni multiple contemporanee.procedura Stampa() { pid = fork() se pid > 0 io sono il padre: ritorna immediatamente altrimenti io sono il figlio: Formatta Invia alla stampante fine del programma e morte del processo }
Siccome il forking può richiedere un certo tempo per la duplicazione del processo, alcuni server di rete eseguono il pre-forking in base al carico di lavoro (come fa Apache): il processo padre esegue preventivamente il forking di un certo numero di processi figli, che sono pertanto già pronti a rispondere ad eventuali interrogazioni via rete.
exec(programma, parametri)
un processo viene sostituito dal programma indicato: il codice del nuovo
programma viene caricato in memoria ed eseguito partendo dall'inizio.
Ovviamente ciò comporta la cessazione del programma originario.Se un programma (come bash) vuole avviare un processo (come ls) senza però morire nell'intento, allora deve eseguire un forking seguito da un exec:
procedura EseguiLS() { pid = fork() se pid > 0 io sono il padre: 1) aspetta la terminazione del figlio, oppure 2) ritorna immediatamente se il processo è stato avviato con & altrimenti io sono il figlio: exec(/bin/ls, /) }
Dunque, quando da linea di comando diamo il comando ls
,
lo shell esegue le seguenti operazioni: genera un processo figlio
con fork()
; se il PID del processo ritornato dalla
funzione fork()
è positivo, io sono il padre (cioè
lo shell dal quale abbiamo lanciato il comando) e quindi rimango in
attesa della terminazione del figlio
oppure ritorno immediatamente (se il comando è stato dato
con l'&
finale); se invece il PID è zero,
io sono il figlio, e quindi eseguo con exec()
il comando
ls
.
Il comando$ ps ax PID TTY STAT TIME COMMAND 1 ? S 0:04 init [3] 2 ? SW 0:00 [keventd] 3 ? SW 0:00 [kapmd] 4 ? SWN 0:00 [ksoftirqd_CPU0] 5 ? SW 0:00 [kswapd] 6 ? SW 0:00 [bdflush] 7 ? SW 0:00 [kupdated] 8 ? SW 0:00 [mdrecoveryd] 82 ? SW 0:00 [khubd] 591 ? SW 0:00 [eth0] 666 ? S 0:00 syslogd -m 0 671 ? S 0:00 klogd -x 783 ? S 0:00 /usr/sbin/apmd -p 10 -w 5 -W -P /etc/sysconfig/apm-sc 838 ? S 0:00 /usr/sbin/sshd 871 ? S 0:00 xinetd -stayalive -reuse -pidfile /var/run/xinetd.pid 897 ? S 0:00 lpd Waiting 927 ? S 0:00 sendmail: accepting connections 951 ? S 0:00 /usr/sbin/httpd 952 ? S 0:00 /usr/sbin/httpd 970 ? S 0:00 gpm -t imps2 -m /dev/mouse 988 ? S 0:00 crond 1042 ? S 0:00 xfs -droppriv -daemon 1078 ? S 0:00 /usr/sbin/atd 1092 ? S 0:00 login -- salsi 1093 tty2 S 0:00 /sbin/mingetty tty2 1094 tty3 S 0:00 /sbin/mingetty tty3 1095 tty4 S 0:00 /sbin/mingetty tty4 1096 tty5 S 0:00 /sbin/mingetty tty5 1097 tty6 S 0:00 /sbin/mingetty tty6 1100 tty1 S 0:00 -bash 1149 tty1 S 0:00 /bin/sh /usr/X11R6/bin/startx 1161 tty1 S 0:00 xinit /home/salsi/.xinitrc -- 1162 ? S 0:09 X :0 1166 tty1 S 0:00 fvwm2 1174 tty1 S 0:00 /usr/X11R6/lib/X11/fvwm2/FvwmButtons 7 4 none 0 8 1176 tty1 S 0:00 netmon -i ppp0 -smooth 1177 tty1 S 0:00 wmmon 1178 tty1 S 0:00 xclock 1323 tty1 S 0:02 gnome-terminal 1330 tty1 S 0:00 gnome-pty-helper 1331 pts/1 S 0:00 bash 1386 pts/1 S 0:01 vim index.html 2494 tty1 S 0:00 gnome-terminal 2501 tty1 S 0:00 gnome-pty-helper 2502 pts/2 S 0:00 bash 4512 tty1 S 0:00 /bin/sh /usr/bin/opera 4513 tty1 S 0:04 /usr/lib/opera/6.02-20020701.3/opera 7081 pts/1 S 0:00 /bin/bash -c (ps ax) < /tmp/salsi/trash/v64790/6 >/tm 7088 pts/1 R 0:00 ps ax
pstree
mette in evidenza l'albero genealogico dei
processi, dal quale possimo dedurre chi ha avviato chi:
Il comando$ pstree -a -l -n -p -u init,1) |-(keventd,2) |-(kapmd,3) |-(ksoftirqd_CPU0,4) |-(kswapd,5) |-(bdflush,6) |-(kupdated,7) |-(mdrecoveryd,8) |-(khubd,82) |-(eth0,591) |-syslogd,666) -m 0 |-klogd,671) -x |-apmd,783) -p 10 -w 5 -W -P /etc/sysconfig/apm-scripts/apmscript |-sshd,838) |-xinetd,871) -stayalive -reuse -pidfile /var/run/xinetd.pid | |-in.ftpd,8302) | `-ipop3d,8416) |-lpd,897,lp) |-sendmail,927) |-httpd,951) | `-httpd,952,apache) |-gpm,970) -t imps2 -m /dev/mouse |-crond,988) |-xfs,1042,xfs) -droppriv -daemon |-atd,1078,daemon) |-login,1092) | `-bash,1100,salsi) | `-startx,1149) /usr/X11R6/bin/startx | `-xinit,1161) /home/salsi/.xinitrc -- | |-X,1162,root) :0 | `-fvwm2,1166) | |-FvwmButtons,1174) 7 4 none 0 8 | |-netmon,1176) -i ppp0 -smooth | |-wmmon,1177) | |-xclock,1178) | |-gnome-terminal,1323) | | |-gnome-pty-helpe,1330) | | `-bash,1331) | | `-vim,1386) index.html | | `-bash,7231) -c ... | | `-pstree,7238) -a -l -n -p -u | |-gnome-terminal,2494) | | |-gnome-pty-helpe,2501) | | `-bash,2502) | | `-less,7193) /usr/local/bin/mypstree | `-opera,4512) /usr/bin/opera | `-opera,4513) |-mingetty,1093) tty2 |-mingetty,1094) tty3 |-mingetty,1095) tty4 |-mingetty,1096) tty5 `-mingetty,1097) tty6
top
fornisce il quadro del carico della macchina, e
mostra in cima ("top") i processi più onerosi:
$ top 11:07am up 3:26, 4 users, load average: 0.00, 0.00, 0.00 53 processes: 51 sleeping, 2 running, 0 zombie, 0 stopped CPU states: 0.6% user, 0.6% system, 0.0% nice, 98.8% idle Mem: 255908K av, 241824K used, 14084K free, 0K shrd, 2684K buff Swap: 136512K av, 0K used, 136512K free 102548K cached PID USER PRI NI SIZE RSS SHARE STAT %CPU %MEM TIME COMMAND 1164 root 15 0 266M 10M 3684 S 0.8 4.2 0:44 X 1 root 15 0 480 480 420 S 0.0 0.1 0:04 init 2 root 15 0 0 0 0 SW 0.0 0.0 0:00 keventd 3 root 15 0 0 0 0 SW 0.0 0.0 0:00 kapmd 4 root 34 19 0 0 0 SWN 0.0 0.0 0:00 ksoftirqd_CPU0 5 root 15 0 0 0 0 SW 0.0 0.0 0:00 kswapd 6 root 25 0 0 0 0 SW 0.0 0.0 0:00 bdflush 7 root 15 0 0 0 0 SW 0.0 0.0 0:00 kupdated 8 root 25 0 0 0 0 SW 0.0 0.0 0:00 mdrecoveryd 82 root 15 0 0 0 0 SW 0.0 0.0 0:00 khubd 593 root 15 0 0 0 0 SW 0.0 0.0 0:00 eth0 668 root 15 0 560 560 472 S 0.0 0.2 0:00 syslogd 673 root 15 0 444 444 384 S 0.0 0.1 0:00 klogd 785 root 15 0 480 480 424 S 0.0 0.1 0:00 apmd 840 root 20 0 1228 1228 1020 S 0.0 0.4 0:00 sshd 873 root 18 0 916 916 724 S 0.0 0.3 0:00 xinetd 899 lp 15 0 1132 1132 976 S 0.0 0.4 0:00 lpd 929 root 15 0 1816 1816 1300 S 0.0 0.7 0:00 sendmail 953 root 15 0 1504 1504 1392 S 0.0 0.5 0:00 httpd 954 apache 18 0 1572 1572 1444 S 0.0 0.6 0:00 httpd 972 root 15 0 452 452 396 S 0.0 0.1 0:00 gpm 990 root 15 0 616 616 540 S 0.0 0.2 0:00 crond 1044 xfs 15 0 4376 4376 980 S 0.0 1.7 0:01 xfs 1080 daemon 15 0 524 524 460 S 0.0 0.2 0:00 atd 1094 root 15 0 1184 1184 960 S 0.0 0.4 0:00 login 1095 root 15 0 400 400 344 S 0.0 0.1 0:00 mingetty 1096 root 15 0 400 400 344 S 0.0 0.1 0:00 mingetty 1097 root 16 0 400 400 344 S 0.0 0.1 0:00 mingetty 1098 root 16 0 400 400 344 S 0.0 0.1 0:00 mingetty 1099 root 16 0 400 400 344 S 0.0 0.1 0:00 mingetty 1102 salsi 15 0 1272 1272 1000 S 0.0 0.4 0:00 bash 1144 salsi 16 0 1024 1024 872 S 0.0 0.4 0:00 x 1151 salsi 17 0 1020 1020 868 S 0.0 0.3 0:00 startx 1152 salsi 15 0 520 520 456 S 0.0 0.2 0:00 tee 1163 salsi 15 0 612 612 540 S 0.0 0.2 0:00 xinit 1168 salsi 15 0 2228 2228 1384 S 0.0 0.8 0:00 fvwm2 1176 salsi 15 0 1176 1176 1008 S 0.0 0.4 0:00 FvwmButtons 1177 salsi 15 0 2320 2320 1880 S 0.0 0.9 0:00 xterm 1178 salsi 15 0 656 656 580 S 0.0 0.2 0:00 netmon
I processore Intel 386 e seguenti, i processori Motorola PowerPC, i processori MIPS e altri ancora supportano il concetto di memoria protetta, di segmentazione, e di istruzioni privilegiate.
La memoria protetta è una soluzione hardware adottata dai processori per impedire ai processi di interferire tra di loro andando a scrivere su indirizzi di memoria arbitrari, con conseguenze fatali per il sistema.
La segmentazione è la tecnica hardware/software tipicamente adottata per mantenere traccia dello spazio di indirizzamento riservato a un processo. Per ogni processo, il sistema operativo mantiene una tabella delle aree di memoria cui esso ha accesso; per ogni istruzione eseguita dal processo, il processore verifica che l'accesso alla memoria rispetti la tabella dei segmenti assegnati. Se il processo, per errata programmazione o per deliberata intenzione del programmatore, tenta di accedere ad aree vietate della memoria, il processore interrompe il processo e genera un errore di segmentazione (SEGMENTATION FAULT).
Inoltre, il processore si può trovare di volta in volta in vari stati di esecuzione nei quali determinate istruzioni privilegiate possono o meno essere eseguite dal processo. I processori come l'i386 supportano fino a 4 modalità di privilegio, ma nei sistemi operativi come Unix, Linux e Windows ne vengono utilizzate solo due: la modalità kernel space e la modalità user space:
libc
ed è l'interfaccia tra i programmi che girano nello spazio utente e il
kernel.
Esempio di come si può produrre un eseguibile binario a partire da un sorgente C:
salsi@orso, ~ $ echo 'main() { printf("Ciao, mondo!"); }' > ciao.c salsi@orso, ~ $ cat ciao.c main() { printf("Ciao, mondo!"); } salsi@orso, ~ $ cc ciao.c -o ciao salsi@orso, ~ $ ls -l ciao* -rwxr-xr-x 1 salsi users 13507 Jul 29 08:50 ciao -rw-r--r-- 1 salsi users 35 Jul 29 08:49 ciao.c salsi@orso, ~ $ ./ciao Ciao, mondo! salsi@orso, ~ $ file ciao* ciao: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), not stripped ciao.c: ASCII text salsi@orso, ~ $ ldd ciao libc.so.6 => /lib/i686/libc.so.6 (0x42000000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) salsi@orso, ~ $
Esempio di come si può produrre uno script che visualizza il tradizionale saluto (Bash):
salsi@orso, ~ $ echo "#! /bin/bash" > hello salsi@orso, ~ $ echo 'echo Hello, world!' >> hello salsi@orso, ~ $ cat hello #! /bin/bash echo Ciao, mondo! salsi@orso, ~ $ chmod +x hello salsi@orso, ~ $ ls -l hello -rwxr-xr-x 1 salsi users 31 Jul 29 08:58 hello salsi@orso, ~ $ ./hello Hello, world!
Esempio di script PHP che fa la stessa cosa:
NOTA: 1 in quest'ultimo esempio in PHP, quando si esegue il programma l'interprete PHP genera in output anche l'header MIME per il protocollo HTTP, siccome per default il PHP suppone di stare rispondendo ad una richiesta CGI. Per evitare la generazione di queste righe basta aggiungere l'opzionesalsi@orso, ~ $ cat bye #! /usr/bin/php <? echo "Bye, bye..." ?> salsi@orso, ~ $ chmod +x bye salsi@orso, ~ $ ls -l bye -rwxr-xr-x 1 salsi users 41 Jul 31 08:19 bye salsi@orso, ~ $ ./bye X-Powered-By: PHP/4.1.2 Content-type: text/html Bye, bye...
-f
dopo il nome dell'interprete.
NOTA 2: è possibile scrivere i propri script anche su altri sistemi operativi e poi inviare lo script al sistema Unix, impostare i flag x e infine eseguirlo. Oltre che molto lento e scomodo, questo modo di operare richiede di fare attenzione alla diversa convenzione degli a-capo tra i diversi sistemi operativi:
Unix: LF (10)Cosa succede se si avvia uno script scritto in un file di testo realizzato su di un sistema "straniero" e che usa la convenzione di a-capo di quel sistema?
DOS, Windows: CR LF (10 13)
Macintosh pre-X: CR
dove#! /bin/bash<CR>
<CR>
è il codice ASCII CR, che per il kernel
di Unix non ha alcun significato particolare; il kernel tenta quindi
di eseguire un programma interprete con quel nome, ma, non trovandolo,
stampa un enigmatico
: bad interpreter: No such file or directory
Spiegazione: nel messaggio di errore non si legge il nome del "bad interpreter" perché il kernel ha inviato in stampa sul terminale la scritta "/bin/bash<CR>: bad interpreter"; purtroppo, il terminale esegue diligentemente il carriage return CR e riporta il cursore sul margine sinistro, sicché il resto della scritta copre il nome del bad interpreter.
kill
:
kill -1 7654
invia il segnale 1 (SIGHUP) al PID 7654. Si può omettere il numero di segnale,
nel qual caso viene inviato il 15 (SIGTERM). L'elenco di tutti i segnali
e il loro significato ed effetto si legge in man 7 signal
.
Per alcuni segnali il processo può intercettarli e comportarsi come
crede; se il processo non specifica diversamente al kernel, esiste una
azione default. Nel caso di altri segnali il processo non può ignorarli:
se lo fa il kernel lo termina. Per altri segnali ancora, il processo non
può intercettarli, e di solito il kernel termina il processo.
Le lettere nella colonna "Azione" hanno il seguente significato (le lettere maiuscole indicano che il processo non può intercettare il segnale, e l'azione indicata viene perciò eseguita d'ufficio dal kernel):Segnale Valore Azione Commento ------- ------ ------ ----------------------------------- SIGHUP 1 t Chiusura del terminale o morte del processo controllante SIGINT 2 t Interruzione da tastiera (CTRL-C) SIGQUIT 3 c Quit da tastiera (CTRL-\) SIGILL 4 c Istruzione illegale SIGKILL 9 K Kill signal SIGSEGV 11 c Violazione della segmentazione SIGTERM 15 t Richiesta di terminazione (default signal per kill) SIGUSR1 10 t User-defined signal 1 SIGUSR2 12 t User-defined signal 2 SIGCHLD 17 i Child fermato o ucciso SIGCONT 18 Continua se stoppato SIGSTOP 19 S Stoppa il processo SIGTSTP 20 s Stop da tastiera (CTRL-S) SIGBUS 7 c Bus error (bad memory access) SIGWINCH 28 i Window resize signal
t Default action is to terminate the process. i Default action is to ignore the signal. c Default action is to terminate the process and dump core. s Default action is to stop the process. S Stop the process. Restart with signal SIGCONT. K Kill the process.
Umberto Salsi | Commenti | Contatto | Mappa | Home / Indice sezione |
Still no comments to this page. Use the Comments link above to add your contribute.