Esercitazione n. 8 Processi (in ambiente GNU/Linux) dott. Carlo Todeschini tode@cremona.polimi.it Politecnico di Milano A.A. 2010/2011 Queste slide sono distribuite con licenza Creative Commons Attribuzione-Non commerciale-condividi allo stesso modo 2.5 Italia
Gestione dei processi: ripasso... In computing, a process is an instance of a computer program that is being sequentially executed by a computer system that has the ability to run several computer programs concurrently. A computer program itself is just a passive collection of instructions, while a process is the actual execution of those instructions. Several processes may be associated with the same program. A process may split itself into multiple daughter subprocesses or threads that execute in parallel, running different instructions on much of the same resources and data or the same instructions on logically different resources and data. Fonte: Wikipedia Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 2
Gestione dei processi: ripasso... Stati di un processo: Esempi? Fonte: Wikipedia Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 3
Gestione dei processi: ripasso... Alcuni comandi utili (shell in ambiente Linux): ps report a snapshot of the current processes es: $ ps aux top display Linux tasks es: $ top ('q' o <CTRL C> per uscire) es: $ top p <PID1> p <PID2> p <PID3> es: premere 'H' per visualizzare i thread pstree display a tree of processes es: $ pstree a kill send a signal to a process es: $ kill 9 <PID> Esempi? Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 4
Gestione dei processi: ripasso... Per creare un processo figlio identico al processo padre si utilizza la funzione fork: #include <sys/types.h> #include <unistd.h> pid_t fork(void); Tutti i segmenti del padre sono duplicati nel figlio al momento della fork Nel processo padre la funzione fork ritorna come valore il PID del figlio mentre nel processo figlio la funzione ritorna 0; in caso di errore ritorna -1 Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 5
Gestione dei processi: ripasso... Un processo termina la propria esecuzione con la funzione exit: #include <stdlib.h> void exit(int status); Ritorna al processo chiamante il valore status che, per essere correttamente visualizzato/utilizzato, deve essere diviso per 256. Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 6
Gestione dei processi: ripasso... #include <stdio.h> #include <stdlib.h> main( ) pid_t pid; pid = fork (); Start #include <stdio.h> #include <stdlib.h> main( ) pid_t pid; pid = fork (); if ( pid == 0 ) printf ("Sono il processo figlio\n"); exit (0); else printf ("Sono il processo padre\n"); exit (0); /* non necessaria */ if ( pid == 0 ) printf ("Sono il processo figlio\n"); exit (0); else printf ("Sono il processo padre\n"); exit (0); /* non necessaria */ Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 7
C/Processi e file Problema Si vuole realizzare un programma in linguaggio C in grado di: Generare due processi padre/figlio in cui: Il processo padre scrive la sequenza di caratteri [A-Z][a-z] in un file di testo (aperto prima di eseguire la fork) e successivamente chiude e rimuove il file creato Il processo figlio attende 5 secondi (tramite la funzione sleep (num_secs) ) e stampa il contenuto del file precedentemente gestito dal processo padre Evidenziare le problematiche di una gestione di questo tipo... Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 8
C/Processi e file Codice sorgente #include <stdio.h> #include <stdlib.h> main () pid_t pid; int i; char c; FILE *textfile; // si apre il file in scrittura textfile = fopen ( "test.txt", "w+" ); if ( textfile!= NULL ) // si richiama la funzione fork per creare un figlio pid = fork (); es_processi_e_file.c Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 9
C/Processi e file Codice sorgente if ( pid == 0 ) // quello che segue è il codice eseguito dal figlio // si attendono alcuni secondi per dare tempo al processo // padre di terminare la propria esecuzione printf ("[figlio] Attendo 5 secondi...\n"); sleep ( 5 ); // si stampa la posizione attuale del cursore sul file // si noterà che è posizionato alla fine del file... printf ("\n[figlio] Posizione attuale puntatore file: %ld\n", ftell(textfile) ); // si riporta all'inizio il cursore rewind ( textfile ); // si stampa il contenuto testuale del file, carattere // per carattere printf ("[figlio] Stampo contenuto del file di testo:\n"); c = fgetc ( textfile ); while (! feof ( textfile ) ) printf ("%c", c); c = fgetc ( textfile ); printf ("\n"); // si chiude il file aperto e si esce dal processo printf ("[figlio] Chiudo il file ed esco.\n"); fclose ( textfile ); exit ( 0 ); // si nota che, nonostante il processo padre abbia già // cancellato il file, il processo figlio è comunque in // grado di leggerne il contenuto! Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 10
C/Processi e file Codice sorgente else // segue il codice eseguito dal processo padre // si inseriscono alcuni caratteri nel file di testo printf ("[padre] Inserimento dei caratteri [A-Z][a-z] nel file\n"); for ( i=65 ; i<=90 ; i++ ) fputc ( i, textfile ); for ( i=97 ; i<=122 ; i++ ) fputc ( i, textfile ); // si stampa la posizione attuale del cursore sul file printf ("[padre] Posizione attuale puntatore file: %ld\n", ftell(textfile) ); // si chiude il file aperto e lo si cancella dal filesystem printf ("[padre] Chiudo il file e lo cancello\n"); fclose ( textfile ); remove ( "test.txt" ); else printf ("Errore! Non è stato possibile aprire il file.\n"); Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 11
Gestione dei processi: ripasso... Per attendere la terminazione di un qualsiasi processo figlio, il processo chiamante utilizza la funzione wait: #include <sys/types.h> #include <sys/wait.h> pid_t wait(int *status); Il processo chiamante riceve il valore del PID del processo figlio terminato. La variabile status contiene il valore di terminazione impostato dalla funzione exit() del figlio. Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 12
Gestione dei processi: ripasso... E' possibile attendere la terminazione di un determinato processo, conoscendone il PID, tramite la funzione waitpid: #include <sys/types.h> #include <sys/wait.h> pid_t waitpid (pid_t pid, int *status, int options); Il processo chiamante riceve il valore del PID del processo figlio terminato e in status il valore di terminazione del figlio. Con il parametro opzioni si può specializzare la funzione (v. man waitpid ). Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 13
Gestione dei processi: ripasso... E' possibile ottenere il proprio PID con la funzione getpid e il PID del processo chiamante con la funzione getppid: #include <sys/types.h> #include <unistd.h> pid_t getpid(void); pid_t getppid(void); Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 14
C/Processi in attesa Problema Si vuole realizzare un programma in linguaggio C in grado di: Generare due processi padre/figlio in cui: Il processo padre attende la terminazione dei compiti assegnati al processo figlio Il processo figlio stampa a standard output il proprio PID e il PID del padre Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 15
C/Processi in attesa Codice sorgente /* Questo programma deve: - generare due processi padre/figlio in cui: - il processo padre attende la terminazione dei compiti assegnati al processo figlio - il processo figlio stampa a standard output il proprio PID e il PID del padre */ #include <stdio.h> #include <stdlib.h> main () pid_t pid; int status; // si richiama la funzione fork per creare un figlio pid = fork (); es_processi_e_attese.c Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 16
C/Processi in attesa Codice sorgente if ( pid == 0 ) // quello che segue è il codice eseguito dal figlio // si stampano a STDOUT le informazioni richieste printf ("[figlio] Il mio PID è %d e quello del processo che mi ha invocato è %d\n", getpid(), getppid() ); // si produce un codice di ritorno exit ( 24 ); else // segue il codice eseguito dal processo padre printf ("[padre] In attesa della terminazione del processo figlio...\n"); // si attende che il processo figlio termini la computazione pid = wait ( &status ); // OK: si sblocca il padre perchè il figlio ha terminato printf ("[padre] Il figlio, con PID %d, ha terminato il lavoro con il seguente codice di ritorno: %d.\n", pid, status/256 ); Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 17
Gestione dei processi: ripasso... Per mutare il codice di un processo si utilizza la funzione execl: #include <unistd.h> int execl(const char *path, const char *arg0,, const char *argn, NULL); path è il percorso completo del programma (file eseguibile) che deve sostituire il codice del processo che invoca l'istruzione execl; seguono una serie di parametri che vengono passati al programma da eseguire. Nota: vengono sostituiti dal nuovo programma i segmenti codice e dati mentre rimane invariato il segmento di sistema; il processo mantiene il proprio PID. Per approfondimenti v. man exec Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 18
C/Processi in attesa random Problema Si vuole realizzare un programma in linguaggio C in grado di: Generare quattro processi in cui: Il processo padre crea due figli e poi si mette in attesa della terminazione di entrambi; prima di uscire stampa i PID dei processi terminati, nell'ordine in cui questi terminano il primo processo figlio muta il proprio codice ed esegue il programma "es_processi_e_attese_random_nuovo_prog"che stampa a standard output i tre parametri ricevuti in input. Il sorgente di tale programma è "es_processi_e_attese_random_nuovo_prog.c" e va compilato prima Il secondo processo figlio crea a sua volta un processo figlio, il terzo, e: Il secondo processo figlio termina subito, senza attendere la terminazione del processo generato Il terzo processo figlio aspetta un numero casuale di secondi (da 0 a 5) prima di terminare (per attendere K secondi si usa la funzione sleep (K) e per generare numeri casuali tra 0 e N-1 si utilizza rand ()%N ) Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 19
C/Processi in attesa random Codice sorgente #include <stdio.h> #include <stdlib.h> #include <unistd.h> main () pid_t pid1, pid2, pid3, term1, term2; int stato1, stato2; es_processi_e_attese_random.c es_processi_e_attese_random_nuovo _prog.c // si richiama la funzione fork per creare il primo figlio pid1 = fork (); if ( pid1 == 0 ) // quello che segue è il codice eseguito dal primo figlio printf ("[Primo figlio - PID: %d] Lancio il programma \"es_processi_e_attese_random_nuovo_prog\"\n", getpid()); execl ("es_processi_e_attese_random_nuovo_prog", "es_processi_e_attese_random_nuovo_prog", "Parametro_#1", "Parametro_#2", Parametro_#3", NULL); exit ( -1 ); // il padre procede subito senza aspettare la terminazione del figlio Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 20
C/Processi in attesa random Codice sorgente // si chiama ancora la funzione fork per creare il secondo figlio pid2 = fork (); if ( pid2 == 0 ) // quello che segue è il codice eseguito dal secondo figlio printf ("[Secondo figlio - PID: %d] Creo una altro figlio, il terzo\n", getpid()); pid3 = fork (); if (pid3 == 0 ) // quello che segue è il codice eseguito dal terzo figlio printf ("[Terzo figlio - PID: %d] Vado a dormire...\n", getpid()); sleep (rand ()%5); printf ("[Terzo figlio] Svegliato! Termino subito.\n"); exit ( 2 ); exit ( 1 ); // si attende la terminazione dei primi due figli, non si sa in quale ordine term1 = wait ( &stato1 ); term2 = wait ( &stato2 ); // probabilmente terminano i primi due figli, perchè il terzo dorme un po'... printf ("Ordine di terminazione dei primi due figli: 1: %d - 2: %d\n", term1, term2); exit ( 0 ); Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 21
C/Processi in attesa random Codice sorgente /* */ es_processi_e_attese_random_nuovo_prog.c Questo programma riceve tre parametri in input da linea di comando e li stampa a STDOUT. #include <stdio.h> main ( int argc, char *argv[] ) if ( argc == 4 ) printf ( "Nome del programma: %s\n", argv[0] ); printf ( "Parametro n. 1: %s\n", argv[1] ); printf ( "Parametro n. 2: %s\n", argv[2] ); printf ( "Parametro n. 3: %s\n", argv[3] ); else /* Errore: parametri di input errati */ printf ( "\nutilizzo: %s Par_1 Par_2 Par_3\n\n", argv[0] ); Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 22
C/Processi per cracker ;-) Problema Si vuole realizzare un programma in linguaggio C che tenti di individuare la password di un utente (password utente di un sistema Unix) sfruttando un dizionario di parole comuni (formato testo, in italiano); deve quindi generare una serie di processi in cui: il processo padre: chiede all'utente di fornire la password cifrata invoca i processi figli assegnando loro un dizionario da caricare (filename) e una parte del dizionario da testare (lettera iniziale) attende la terminazione di tutti i processi figli e stampa l'esito della computazione i processi figli eseguono una mutazione di codice invocando il programma es_password_cracking_crypt passando come parametri: la password criptata, il filename del dizionario, la lettera di competenza. es_password_cracking_crypt stampa un messaggio in caso individui la password originale Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 23
C/Processi per cracker ;-) Nota Esempio di file di testo contenente password criptate (/etc/passwd): [ ] tode:cadhajq99wurg:1000:1000:carlo Todeschini,,,:/home/tode:/bin/bash gigi:zuc6b2fgsuy7w:1004:1004:luigi Rossi:/home/gigi:/bin/bash [ ] Nota: oggi non si usa più questa codifica e si utilizza un file accessorio, "/etc/shadow", leggibile solo dall'utente root Es. di dizionario (fonte: http://linguistico.sourceforge.net/): [ ] primitivo primizia primiziale primo primogenito primogenitore primogenitrice primogenitura primola primordiale [ ] Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 24
C/Processi per cracker ;-) Nota Per semplificare il problema si assume che le password da individuare siano state criptate con l'algoritmo DES, utilizzato fino a pochi anni fa sui sistemi UNIX. In particolare la password si ottiene con la seguente istruzione: cryptedpassword = crypt ( clearpassword, salt ) Dove crypt è il comando di sistema UNIX (v. man crypt) e salt è rappresentato dalla prime due lettere della password in chiaro. Es: crypt ( zufolando, zu ) => zuc6b2fgsuy7w crypt ( Carlo, Ca ) => CaDHaJq99wurg Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 25
C/Processi per cracker ;-) Codice sorgente #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #define MAX_CHARS 50 #define NUM_CHARS 52 es_password_cracking.c es_password_cracking_crypt.c main () pid_t pid[num_chars], term[num_chars]; int exit_state[num_chars], n; char charlist[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", chartotry[2], cryptedpassword[max_chars], asciifilehandler[max_chars], dictionaryfilename[] = "es_password_cracking_it_dictionary.txt"; /* Inizializzazione di alcune variabili */ n = 0; chartotry[1] = '\000'; Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 26
C/Processi per cracker ;-) Codice sorgente /* Si chiede all'utente di inserire la password in forma criptata. L'algoritmo utilizzato è il "crypt" di Unix (v. slide) */ printf ("Inserire la password criptata: "); scanf ("%s", cryptedpassword); while (n < NUM_CHARS) pid[n] = fork (); if ( pid[n] == 0 ) /* figlio: stampa un messaggio informativo e subito esegue una mutazione di codice lanciando il programma "es_password_cracking_crypt"; questo programma è specializzato nell'operazione di generare una serie di password criptate a partire da un dizionario di termini comuni in italiano per verificare se una di queste è la password cercata. Si lanciano tanti processi quante sono le lettere dell'alfabeto che compongono l'iniziale delle parole del dizionario. */ printf ("\nfiglio #%d (con PID = %d) incaricato delle parole che iniziano per '%c'\n", n, getpid(), charlist[n]); chartotry[0] = charlist[n]; execl ("es_password_cracking_crypt", "es_password_cracking_crypt", cryptedpassword, chartotry, dictionaryfilename, NULL); exit ( -1 ); Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 27
C/Processi per cracker ;-) Codice sorgente else // padre n++; /* Si attende che tutti i processi figli creati portino a termine il compito assegnato; si utilizza la funzione wait e si memorizza il PID del processo terminato e il relativo codice di ritorno. */ for (n = 0; n < NUM_CHARS; n++) term [n] = wait (&exit_state [n]); printf ("\ntutti i processi figli sono terminati!\n\n"); for (n = 0; n < NUM_CHARS; n++) if ((exit_state [n] / 256) <= 3 ) printf ("PID = %d - Valore di ritorno = %d \n", term [n], exit_state [n] / 256); else printf ("Il processo con PID = %d ha trovato che la password inizia per '%c' \n", term [n], exit_state [n] / 256); exit ( 0 ); Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 28
C/Processi per cracker ;-) Codice sorgente /* es_password_cracking_crypt.c Questo programma riceve in input: - password criptata (v. slide) - iniziale della passwrd da provare - filename del dizionario italiano da caricare Nota: per la compilazione utilizzare gcc -lcrypt -o es_password_cracking_crypt es_password_cracking_crypt.c */ #include <stdio.h> #include <string.h> #include <crypt.h> #include <stdlib.h> #define MAX_STRING 50 int main( int argc, char *argv[] ) char passwordsalt[3], generatedpasswordtotest [MAX_STRING], dictionarypasswordtotest [MAX_STRING], secredpass[max_string], initialchar; FILE *dictionaryfile; Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 29
C/Processi per cracker ;-) Codice sorgente if ( argc == 4 ) /* Ok, ci sono tutti i parametri necessari */ /* si apre il file del dizionario */ dictionaryfile = fopen ( argv[3], "r" ); if ( dictionaryfile!= NULL ) /* si inizializzano alcune variabili */ strcpy ( secredpass, argv[1] ); initialchar = *argv[2]; passwordsalt[2] = '\000'; /* lettura dell'intero file delle parole del dizionario */ while (! feof ( dictionaryfile ) ) /* lettura del file riga per riga */ fgets ( dictionarypasswordtotest, MAX_STRING, dictionaryfile ); /* controllo se l'iniziale della parola è quella da gestire */ if ( dictionarypasswordtotest[0] == initialchar ) /* si toglie il carattere di 'a capo' sovrascrivendolo con il carattere 'terminatore di stringa' */ if (dictionarypasswordtotest[strlen (dictionarypasswordtotest) - 1] == '\n') dictionarypasswordtotest[strlen (dictionarypasswordtotest) - 1] = '\000'; Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 30
C/Processi per cracker ;-) Codice sorgente /* si crea il "salt" necessario per l'algoritmo di creazione della password criptata e la si genera */ passwordsalt[0] = dictionarypasswordtotest[0]; passwordsalt[1] = dictionarypasswordtotest[1]; strcpy ( generatedpasswordtotest, crypt (dictionarypasswordtotest, passwordsalt) ); /* si controlla se la password appena generata coincide con quella passata in input */ if (! strcmp ( secredpass, generatedpasswordtotest ) ) printf ("\n******************************************\n"); printf ("Password TROVATA!: '%s' (criptata = %s)\n", dictionarypasswordtotest, generatedpasswordtotest ); printf ("******************************************\n"); close (dictionaryfile); /* il codice di ritorno è il codice ASCII della lettera iniziale */ exit ( initialchar ); /* il controllo è terminato e non è stata trovata nessuna password che combaci */ printf ("\nla password non è stata trovata con le parole che iniziano con la lettera '%c'\n", initialchar); close (dictionaryfile); exit ( 1 ); Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 31
C/Processi per cracker ;-) Codice sorgente else printf ("\nerrore nella lettura del file! Exit.\n"); exit ( 2 ); else /* Errore: parametri di input errati */ printf ( "\nutilizzo: %s password_criptata lettera_iniziale descrittore_file_dizionario\n\n", argv[0] ); exit ( 3 ); Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 32
C/Analisi di processi Problema Il programma che segue lancia inizialmente un processo P, che crea due processi figli F1 e F2; a sua volta il processo F1 crea un processo figlio N. Nel programma sono indicati tre punti di esecuzione, marcati con le etichette T1, T2 e T3. Queste etichette indicano gli istanti di tempo in cui l esecuzione del programma raggiunge tali punti. Si chiede di riempire le tabelle seguenti, che indicano i valori assunti dalle diverse copie delle variabili del programma negli istanti di tempo T1, T2 e T3, a seconda dei contesti dei processi che vengono via via creati. Fonte: prof. Luca Breveglieri Politecnico di Milano Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 33
C/Analisi di processi Codice sorgente T3 main () int status; pid_t pid_f1, pid_f2, pid_n; pid_f1 = fork (); if (pid_f1 == 0) T1 pid_n = fork (); if (pid_n == 0) execl ("nipote", NULL); exit (-1); wait (&status); exit (pid_n); T2 wait (&status); pid_f2 = fork (); if (pid_f2 == 0) execl ("figlio", NULL); exit (-1); // biforcazione // biforcazione // mutazione codice // terminazione // sincronizzazione // terminazione // sincronizzazione // biforcazione // mutazione codice // terminazione Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 34
C/Analisi di processi Tabelle Contesto di P Istante di tempo Var T1 T2 T3 pid_f1 pid_f2 pid_n status Contesto di F2 Istante di tempo Var T1 T2 T3 pid_f1 pid_f2 pid_n status Contesto di F1 Istante di tempo Var T1 T2 T3 pid_f1 pid_f2 pid_n status Contesto di N Istante di tempo Var T1 T2 T3 pid_f1 pid_f2 pid_n status NE = al momento indicato la variabile non esiste U = non si può dire con certezza se la variabile esiste o quale sia il suo valore <VAL> = la variabile esiste e se ne riporta il valore Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 35
C/Analisi di processi Codice sorgente T3 main () int status; pid_t pid_f1, pid_f2, pid_n; pid_f1 = fork (); if (pid_f1 == 0) T1 pid_n = fork (); if (pid_n == 0) execl ("nipote", NULL); exit (-1); wait (&status); exit (pid_n); T2 wait (&status); pid_f2 = fork (); if (pid_f2 == 0) execl ("figlio", NULL); exit (-1); Contesto di P // biforcazione // biforcazione // mutazione codice // terminazione // sincronizzazione // terminazione // sincronizzazione // biforcazione // mutazione codice // terminazione Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 36
C/Analisi di processi Codice sorgente T3 main () int status; pid_t pid_f1, pid_f2, pid_n; pid_f1 = fork (); if (pid_f1 == 0) T1 pid_n = fork (); if (pid_n == 0) execl ("nipote", NULL); exit (-1); wait (&status); exit (pid_n); T2 wait (&status); pid_f2 = fork (); if (pid_f2 == 0) execl ("figlio", NULL); exit (-1); Contesto di F1 // biforcazione // biforcazione // mutazione codice // terminazione // sincronizzazione // terminazione // sincronizzazione // biforcazione // mutazione codice // terminazione Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 37
C/Analisi di processi Codice sorgente T3 main () int status; pid_t pid_f1, pid_f2, pid_n; pid_f1 = fork (); if (pid_f1 == 0) T1 pid_n = fork (); if (pid_n == 0) execl ("nipote", NULL); exit (-1); wait (&status); exit (pid_n); T2 wait (&status); pid_f2 = fork (); if (pid_f2 == 0) execl ("figlio", NULL); exit (-1); Contesto di F2 // biforcazione // biforcazione // mutazione codice // terminazione // sincronizzazione // terminazione // sincronizzazione // biforcazione // mutazione codice // terminazione Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 38
C/Analisi di processi Codice sorgente T3 main () int status; pid_t pid_f1, pid_f2, pid_n; pid_f1 = fork (); if (pid_f1 == 0) T1 pid_n = fork (); if (pid_n == 0) execl ("nipote", NULL); exit (-1); wait (&status); exit (pid_n); T2 wait (&status); pid_f2 = fork (); if (pid_f2 == 0) execl ("figlio", NULL); exit (-1); Contesto di N // biforcazione // biforcazione // mutazione codice // terminazione // sincronizzazione // terminazione // sincronizzazione // biforcazione // mutazione codice // terminazione Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 39
C/Analisi di processi Soluzione Contesto di P Istante di tempo Var T1 T2 T3 pid_f1 PID F1 PID F1 U (3) pid_f2 U (1) U (1) U (3) pid_n U (1) U (1) U (3) status U (1) U (1) U (3) Contesto di F2 Istante di tempo Var T1 T2 T3 pid_f1 NE NE PID F1 pid_f2 NE NE 0 pid_n NE NE U (1) status NE NE PID N Contesto di F1 Istante di tempo Var T1 T2 T3 pid_f1 0 0 NE pid_f2 U (1) U (1) NE pid_n U (1) PID N NE status U (1) U (1) NE Contesto di N Istante di tempo Var T1 T2 T3 pid_f1 NE U (2) NE pid_f2 NE U (2) NE pid_n NE U (2) NE status NE U (2) NE (1) la variabile esiste ma non è ancora stata inizializzata (2) il processo a cui la variabile appartiene ha mutato codice (3) il processo cui la variabile appartiene potrebbe essere esistente, già terminato o non ancora iniziato. Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 40
C: T h e E N D! (quasi...) /* codice sorgente artistico... */ void fareesame () if ((libro_di_testo == capito_tutto) && (dispense_docente == tutto_chiaro) && (esercitazioni == capite_e_metabolizzate) && (laboratori == fatti_tanti_esercizi)) esamepassato = TRUE; else sleep (NUM); fareesame (); In-bocca-al-lupo per l'esame! Esercitazioni di Fondamenti di Informatica Politecnico di Milano sede di Cremona A.A. 2010/2011 Carlo Todeschini tode@cremona.polimi.it 41