T. Motta Generazione e terminazione processi
1
Creazione e terminazione dei processi
Tommaso Motta
www.webalice.it/motta.tommaso
T. Motta Generazione e terminazione processi
2
Introduzione
Sistema multitasking: più processi in esecuzione in contemporanea
ho parallelismo reale solo se c’è un processore per ogni processo, altrimenti i processi avanzano alternati (time-sharing)
2 tipologie di processi
processi di sistema operativo: realizzano servizi e gestiscono le risorse del sistema
processi utente: sono mandati in esecuzione dall’utente
T. Motta Generazione e terminazione processi
3
Processi permanenti e temporanei
I processi possono essere:
permanenti: vengono creati all’avvio del sistema operativo e continuano fino alla chiusura del s.o.
temporanei: vengono creati quando servono e poi terminano
necessitano di meccanismi per la generazione e la terminazione dei processi
T. Motta Generazione e terminazione processi
4
Generazione dei processi
I processi temporanei possono essere generati da
altri processi (permanenti o temporanei)
o dalla richiesta di un nuovo processo da parte di un utente interattivo (ad. es.: attivazione di un’icona)
Ogni creazione di un processo corrisponde ad una chiamata di sistema (system call)
Quando un processo richiede la generazione di un processo (processo figlio) deve indicare il programma (codice) da associare al processo che viene generato
T. Motta Generazione e terminazione processi
5
Generazione dei processi
Dopo la generazione del processo il padre può:
sospendersi in attesa che il figlio termini... oppure...
...continuare ad evolversi in concorrenza con il figlio
Un processo può suddividersi in un numero n di attività che avanzano parallelamente
T. Motta Generazione e terminazione processi
6
Attività per la generazione di un processo
Per generare un processo il S.O. deve:
verificare la disponibilità di spazio in memoria RAM per il processo e per il suo PCB
se non è disponibile la memoria bisogna fare swapping (memoria virtuale)
assegnare la memoria al processo
creare il PCB
T. Motta Generazione e terminazione processi
7
Terminazione di processi
I processi temporanei possono
terminare spontaneamente (normale conclusione o condizioni di errori)
terminare involontariamente (errori “fatali” o fatti terminare da altri processi o da un utente)
T. Motta Generazione e terminazione processi
8
Terminazione dei processi
In un sistema sono necessari
meccanismi di terminazione utilizzabili dal processo che intende terminare
meccanismi di soppressione con il quale un processo può causare la terminazione di un altro processo temporaneo
i meccanismi di soppressione possono essere invocati solo da:
processi antenati (padre) o processi permanenti
utente che ha mandato in esecuzione il processo o amministratore
T. Motta Generazione e terminazione processi
9
Terminazione dei processi
Quando un processo termina invia al processo padre un segnale
Alla terminazione o soppressione di un processo è necessario: verificare la legittimità dell’operazione
(l’utente/processo può sopprimere questo processo?)
rilasciare la memoria e tutte le risorse del processo
notificare la terminazione al processo padre (se non è già terminato prima...)
T. Motta Generazione e terminazione processi
10
Windows: API per generazione e terminazione di processi
Windows NON ha il concetto di gerarchia di processi
API utili per creare e terminare i processi:
CreateProcess(): crea un processo
CreateThread(): crea un thread in un processo esistente
ExitThread(): termina il thread corrente
ExitProcess(): termina il processo e tutti i suoi threa
TerminateProcess(): permette a un processo di far terminare un altro processo
T. Motta Generazione e terminazione processi
11
Creazione e terminazione processi in Linux
Tommaso Motta
www.webalice.it/motta.tommaso
T. Motta Generazione e terminazione processi
12
Gerarchia di processi
Ogni processo deriva dalla duplicazione di un processo esistente
l’unico processo che non deriva da duplicazione è il processo iniziale (init)
Un processo permanente o temporaneo può generare processi figli temporanei
Tutti i processi sono legati in una struttura ad ALBERO
Il comando pstree mostra i processi eidenziando la struttura gerarchica
T. Motta Generazione e terminazione processi
13
Gerarchia di processi
T. Motta Generazione e terminazione processi
14
Creazione di processi: fork
Il comando fork indica che in quel punto deve essere attivato un nuovo processo
I due processi evolvono indipendentemente l’esecuzione viene suddivisa in due esecuzioni distinte
Il comando crea un processo figlio che è la COPIA ESATTA del processo padre al padre viene restituito il PID del figlio (valore
negativo se non ha funzionato)
al figlio viene restituito il valore 0
T. Motta Generazione e terminazione processi
15
Fork ( )
I due processi hanno descrittori (PCB) e immagini distinte
Il processo figlio eredita da padre il programma e l’immagine nel processore
Dopo la generazione i due processi avanzano indipendentemente eseguendo lo stesso programma
T. Motta Generazione e terminazione processi
16
Fork e S.O.
Quando si usa la fork il S.O. deve:
allocare un nuovo PCB e inserirlo nella tabella dei processi
assegnare al processo un PID
fare una copia logica di tutti i dati del processo padre
in realtà molte aree di memoria rimangono condivise, vengono duplicate solo le aree su cui uno dei due fa una modifica
restituire il PID del figlio al padre e 0 al figlio
collocare il nuovo processo nella lista dei processi pronti
T. Motta Generazione e terminazione processi
17
Creazione: fork()
pid_t fork(void);
crea un nuovo processo con indice pid
lo spazio di indirizzamento del nuovo processo è un duplicato di quello del padre
restituisce 0 al figlio e pid al padre
oppure -1 (solo al padre) in caso di fallimento
es. la tabella dei processi non ha più spazio ...
T. Motta Generazione e terminazione processi
18
Terminazione dei processi
Un processo termina eseguendo la chiamata di sistema exit che libera tutte le risorse
“ripudia” i figli => diventano di proprietà di init e quando muoiono vengono da lui eliminati
Il processo che esegue la exit diventa uno zombie i processi genitori devono eliminare gli zombie
un processo può sopprimere un altro processo con la chiamata di sitema kill
T. Motta Generazione e terminazione processi
19
Terminazione: exit()
void exit(int status);
il codice di terminazione della exit (cioè l’intero passato come parametro) viene “restituito" al padre. Se il processo che termina non ha più un processo padre (è
già terminato) il valore viene restituito all’interprete comandi del S.O.
chiude tutti i descrittori di file,
libera lo spazio di indirizzamento,
invia un segnale SIGCHLD al padre
se il processo padre è terminato, il processo ‘orfano’ viene adottato da init (cioè il ppid viene settato a 1)
T. Motta Generazione e terminazione processi
20
Esempio creazione di processi
/* frammento che crea un nuovo processo */
int pid; /* pid del processo creato */
pid = fork();
if ( pid == 0 )
{ /* siamo nel figlio */
printf(“Processo figlio”);
exit(1);
}
else
{ /* siamo nel padre */
printf(“Processo padre”);
exit(1);
}
T. Motta Generazione e terminazione processi
21
PID del processo: getpid()
La funzione getpid
consente ad un processo di conoscere il valore del proprio pid
Prototipo getpid:
pid_t getpid(void)
il kernel ha pid 0
init ha pid 1
T. Motta Generazione e terminazione processi
22
PID del padre: getppid()
La funzione getppid
consente ad un processo di conoscere il valore del pid del processo padre
Prototipo getppid:
pid_t getppid(void)
T. Motta Generazione e terminazione processi
23
Esempio 1
#include <stdio.h>
#include <sys/types.h>
void main(int argc, char * argv[])
{ pid_t pid;
int retstatus=0;
pid = fork();
if (pid==0) /* sono nel figlio*/
{printf (“sono il processo figlio\n”);
retstatus=1;
exit(retstatus);}
else /* pid != 0, sono nel padre */
{printf (“sono il processo padre\n”);
exit(retstatus);}
}
T. Motta Generazione e terminazione processi
24
Esempio 2: getpid
#include <stdio.h>
#include <sys/types.h>
void main()
{ pid_t pid;
printf (“Prima della fork: PID = %d\n”, getpid());
pid = fork();
if (pid==0) /* PROCESSO FIGLIO*/
{printf (“FIGLIO: PID = %d\n”, getpid());
exit(0);}
else /* PROCESSO PADRE */
{printf (“PADRE: PID = %d\n”, getpid());
printf (“PADRE: PID DEL FIGLIO = %d\n”, pid);
exit(0);}
}
T. Motta Generazione e terminazione processi
25
Esempio 2: esecuzione getpid
Prima della fork: PID = 3375
FIGLIO: PID = 3399
PADRE: PID = 3375
PADRE: PID DEL FIGLIO = 3399
Dal risultato dell’esecuzione si deduce che l’ordine di esecuzione dei processi è stato: figlio e padre
Nota bene:
quando la fork è stata eseguita, è stato creato il secondo processo e l’esecuzione può proseguire o con il processo padre per primo oppure con il processo figlio per primo
T. Motta Generazione e terminazione processi
26
Esempio 3: generazione di 2 figlivoid main()
{ pid_t pid1, pid2;
pid1 = fork();
if (pid1==0) /* PRIMO PROCESSO FIGLIO*/
{printf (“FIGLIO 1: PID = %d\n”, getpid());
printf (“FIGLIO 1: eseguo exit\n”);
exit(0);}
else /* PROCESSO PADRE */
{pid2 = fork();
if (pid2==0) /* SECONDO PROCESSO FIGLIO*/
{printf (“FIGLIO 2: PID = %d\n”, getpid());
printf (“FIGLIO 2: eseguo exit\n”);
exit(0);}
else /* PROCESSO PADRE */
{printf (“PADRE: PID = %d\n”, getpid());
printf (“PADRE: PID DEL FIGLIO 1 = %d\n”, pid1);
printf (“PADRE: PID DEL FIGLIO 2 = %d\n”, pid2);
exit(0); }
}
T. Motta Generazione e terminazione processi
27
Esempio 3: esecuzione
Nell’ipotesi che l’ordine di esecuzione sia figlio1, padre, figlio2, padre
FIGLIO 1: PID = 4300
FIGLIO 1: eseguo exit
FIGLIO 2: PID = 4335
FIGLIO 2: eseguo exit
PADRE: PID = 3375
PADRE: PID DEL FIGLIO 1 = 4300
PADRE: PID DEL FIGLIO 2 = 4335
T. Motta Generazione e terminazione processi
28
Esempio 4: ancora 2 figli...void main()
{ pid_t pid;
pid = fork();
if (pid==0) /* PRIMO PROCESSO FIGLIO*/
{printf (“(1)sono il primo figlio con pid: = %d\n”,getpid());
exit(0);}
else /* PROCESSO PADRE */
{printf (“(2)sono il processo padre\n”);
printf (“(3)ho creato un primo figlio con pid: = %d\n”,pid);
printf (“(4)il mio pid e’: = %d\n”,getpid());
pid = fork();
if (pid==0) /* SECONDO PROCESSO FIGLIO*/
{printf (“(5)sono il secondo figlio con pid: = %d\n”,getpid());
exit(0);}
else /* PROCESSO PADRE */
{printf (“(6)sono il processo padre\n”);
printf (“(7)ho creato un secondo figlio con pid: =%d\n”,pid);
exit(0);}
}
}
T. Motta Generazione e terminazione processi
29
Esempio 4: esecuzione
2)sono il processo padre
1)sono il primo figlio con pid: = 695
3)ho creato un primo figlio con pid: = 695
4)il mio pid e’: = 694
6)sono il processo padre
5)sono il secondo figlio con pid: = 696
7)ho creato un secondo figlio con pid: 696
T. Motta Generazione e terminazione processi
30
Attesa di terminazione del figlio
Il processo padre, dopo aver generato il figlio può sospendersi in attesa della sua terminazione:
invoca la chiamata di sistema wait che sincronizza il padre con l’evento relativo alla terminazione del figlio
La wait permette di riunificare due o più processi concorrenti in un unico processo
Il processo che chiama la wait rimane bloccato fino a quando non è terminato il processo figlio
T. Motta Generazione e terminazione processi
31
Attesa terminazione figlio: wait
La funzione wait
sospende l’esecuzione del processo padre che la esegue ed attende la terminazione di un qualsiasi processo figlio;
se il figlio termina prima che il padre esegua la wait, l’esecuzione della wait nel padre termina istantaneamente.
Prototipo wait:
pid_t wait (int*)
il valore restituito dalla funzione (di tipo pid_t) è il valore del pid del figlio terminato.
il parametro passato per indirizzo assume il valore del codice di terminazione del figlio (e cioè il valore del parametro della exit eseguita dal figlio per terminare).
T. Motta Generazione e terminazione processi
32
Esempio: wait
#include . . . .
void main(int argc, char * argv[])
{ pid_t pid;
int stato_exit, stato_wait;
pid = fork();
if (pid==0)
{printf (“sono il processo figlio\n”);
printf(“il mio pid e’: %d \n”,getpid( ));
stato_exit=5;
exit(stato_exit);}
else
{printf("Ho generato il processo figlio con pid %d\n",pid)
pid = wait(&stato_wait);
printf("E’ terminato il processo %d con esito %d\n",pid, stato_wait/256);}
}
T. Motta Generazione e terminazione processi
33
Attesa terminazione figlio: waitpid La funzione waitpid
Sospende l’esecuzione del processo padre ed attende la terminazione del processo figlio di cui viene fornito il pid;
se il figlio termina prima che il padre esegua la waitpid, l’esecuzione della waitpid nel padre termina istantaneamente.
Prototipo waitpid
pid_t waitpid(pid_t pid, int *status, int options);
nel padre:
il valore resitituito assume il valore del pid del figlio terminato;
status assume il valore del codice di terminazione del processo figlio;
options specifica ulteriori opzioni (ipotizziamo > 0).
T. Motta Generazione e terminazione processi
34
Esempio: waitpid
#include . . .
void main(int argc, char * argv[])
{ pid_t pid, my_pid;
int status;
pid = fork();
if (pid==0)
{/* CODICE DEL FIGLIO */ }
else /* pid != 0, sono nel padre */
{printf ("Ho generato il processo figlio con pid %d\n",pid);
printf("Attendo la terminazione del figlio con pid %d\n",pid);
my_pid = waitpid(pid, &status, 1);
printf("E’ terminato il processo %d con esito %d\n",my_pid, status);}
}
T. Motta Generazione e terminazione processi
35
Sostituzione del programma in esecuzione: exec
La funzione exec
sostituisce il segmento codice e il segmento dati del processo corrente con il codice e i dati di un programma contenuto in un file eseguibile specificato.
Il segmento di sistema non viene sostituito (file e socket rimangono aperti e disponibili)
il processo rimane lo stesso e quindi mantiene lo stesso pid
la funzione exec passa dei parametri al programma che viene eseguito, tramite il meccanismo di passaggio dei parametri al main argc e argv
T. Motta Generazione e terminazione processi
36
Sintassi exec
Sintassi:
int execl(char *path_programma, char *arg0, char *arg1,..char *argn);
path_programma: path completo del file eseguibile del nuovo programma da lanciare
arg0,arg1, … argn: puntatori a stringhe che verranno passate come parametri al main del nuovo programma
arg0 deve essere il nome del programma
argn in chiamata deve essere il valore NULL
il valore restituito è: 0 se l’operazione è stata eseguita correttamente;
-1 se c’è stato un errore e l’operazione di sostituzione del codice è fallita.
Al momento dell’esecuzione del main del nuovo programma - void main (int argc, char *argv[]) - arg0, arg1 .. vengono resi accessibili tramite l’array di puntatori argv
T. Motta Generazione e terminazione processi
37
Esempio: exec
/* programma main1 */
#include <stdio.h>
void main(int argc, char * argv[])
{int i;
printf (“programma main1 in esecuzione\n”);
printf (“ho ricevuto %d parametri\n”, argc);
for (i=0; i<argc; i++)
printf(“il parametro %d e’:
%s \n”,argv[i]);
}
/* programma exec1 */
#include <stdio.h>
#include <sys/types.h>
void main(int argc, char * argv[])
{char PO[]=“main1”;
char P1[]=“parametro1”;
char P2[]=“parametro2”;
printf (“programma exec1 in esecuzione\n”);
execl(“/antola/esempi/main1”,P0,P1,P2,NULL);
printf (“errore di exec”);
}
T. Motta Generazione e terminazione processi
38
Esempio: esecuzione
Eseguo il programma exec1:
programma exec1 in esecuzione
programma main1 in esecuzione
ho ricevuto 3 parametri
il parametro 0 e’:main1
il parametro 1 e’:parametro1
il parametro 2 e’:parametro2
T. Motta Generazione e terminazione processi
39
Utilizzo fork-exec
La sostituzione di codice non implica necessariamente la generazione di un figlio
in questo caso, quando il programma che è stato lanciato in esecuzione tramite la execl termina, termina anche il processo che lo ha lanciato (sono lo stesso processo!!)
E’ necessario creare un nuovo processo, che effettua la sostituzione di codice (utilizzo di fork- exec), quando è necessario “mantenere in vita” il processo di partenza, dopo l’esecuzione del codice sostituito
spesso questo implica che il padre attenda la terminazione del programma lanciato con mutazione di codice
T. Motta Generazione e terminazione processi
40
Esempio: fork-exec
void main()
{ pid_t pid, chpid;
pid = fork();
if (pid==0)
/* PROCESSO FIGLIO*/
{printf (“FIGLIO: prima del cambio di codiced\n”)
printf (“FIGLIO: PID = %d\n”, getpid());
execl(“./prog”, “prog”, NULL);
printf (“FIGLIO: errore nel cambio di codice\n”);
exit(1);}
else
/* PROCESSO PADRE */
{printf (“PADRE: wait\n”);
chpid = wait (NULL);
printf (“PADRE: PID DEL FIGLIO = %d\n”, chpid);
exit(0); }
}
T. Motta Generazione e terminazione processi
41
Esempio: fork-exec (...continuo)
/* Programma prog*/
void main (int argc, char * argv[])
{
printf (“PROG: PID = %d\n”, getpid());
printf (“PROG: exit\n”);
}
ESECUZIONE nell’ipotesi che venga eseguito prima il padre e poi il figlio
PADRE: wait
FIGLIO: prima del cambio del codice
FIGLIO: PID = 4995
PROG: PID = 4995
PROG: exit
PADRE: PID DEL FIGLIO = 4995