420 likes | 647 Views
Creazione e terminazione dei processi. Tommaso Motta www.webalice.it/motta.tommaso. 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)
E N D
Creazione e terminazione dei processi Tommaso Motta www.webalice.it/motta.tommaso Generazione e terminazione processi
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 Generazione e terminazione processi
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 Generazione e terminazione processi
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 Generazione e terminazione processi
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 Generazione e terminazione processi
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 Generazione e terminazione processi
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) Generazione e terminazione processi
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 Generazione e terminazione processi
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...) Generazione e terminazione processi
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 Generazione e terminazione processi
Creazione e terminazione processi in Linux Tommaso Motta www.webalice.it/motta.tommaso Generazione e terminazione processi
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 Generazione e terminazione processi
Gerarchia di processi Generazione e terminazione processi
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 Generazione e terminazione processi
Fork ( ) • I due processi hanno descrittori (PCB) e immaginidistinte • 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 Generazione e terminazione processi
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 Generazione e terminazione processi
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 ... Generazione e terminazione processi
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 Generazione e terminazione processi
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) Generazione e terminazione processi
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); } Generazione e terminazione processi
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 Generazione e terminazione processi
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) Generazione e terminazione processi
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);} } Generazione e terminazione processi
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);} } Generazione e terminazione processi
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 Generazione e terminazione processi
Esempio 3: generazione di 2 figli void 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); } } Generazione e terminazione processi
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 Generazione e terminazione processi
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);} } } Generazione e terminazione processi
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 Generazione e terminazione processi
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 Generazione e terminazione processi
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). Generazione e terminazione processi
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);} } Generazione e terminazione processi
Attesa terminazione figlio: waitpid • La funzione waitpid • Sospende l’esecuzione del processo padre ed attende la terminazione del processo figlio di cui vienefornito 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). Generazione e terminazione processi
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);} } Generazione e terminazione processi
Sostituzione del programma in esecuzione: exec • La funzione exec • sostituisce il segmento codice e il segmento datidel 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 stessoe 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 Generazione e terminazione processi
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 Generazione e terminazione processi
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”); } Generazione e terminazione processi
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 Generazione e terminazione processi
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 Generazione e terminazione processi
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); } } Generazione e terminazione processi
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 Generazione e terminazione processi