370 likes | 485 Views
SC che operano su processi. Getpid, fork, exec, wait, waitpid, exit, dup, dup2. Process identifier: getpid,getppid. pid indice del processo all’interno della tabella dei processi ppid indice del processo padre all’interno della tabella dei processi il kernel ha pid 0 e ini t ha pid 1
E N D
SC che operano su processi Getpid, fork, exec, wait, waitpid, exit, dup, dup2
Process identifier: getpid,getppid • pid indice del processo all’interno della tabella dei processi • ppid indice del processo padre all’interno della tabella dei processi • il kernel ha pid 0 e init ha pid 1 • si possono ottenere con pid_t getpid(void) pid_t getppid(void)
PID: getpid,getppid (2) /* frammento che stampa il pid del processo in esecuzione e quello del padre usando la macro WRITE di stampa (in sysmacro.h) … */ char buf[N]; /* stringa da stampare */ … sprintf(buf,”Processo %d, mio padre e’ %d\n.”, getpid(),getppid()); WRITELN(buf); …
PID: getpid,getppid (2.1) /* da sysmacro.h … */ #define STIN 0 #define STDOUT 1 #define STDERR 2 #define WRITE(m) \ IFERROR(write(STDOUT,m,strlen(m)),m) #define WRITELN(m) WRITE(m); WRITE(m);
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 • padre e figlio hanno due tabelle dei descrittori di file diverse (il figlio ha una copia di quella del padre) • MA …. condividono la tabella dei file aperti (e quindi anche il puntatore alla locazione corrente di ogni file)
Creazione : fork() (2) pid_t fork(void); • 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 ...
Creazione di processi (2) copia • Spazio di indirizzamento di padre e figlio dopo una fork terminata con successo 232 - 1 232 - 1 Stack Stack Area vuota Area vuota heap heap Data Data Text Text 0 0 SI figlio SI padre
Creazione di processi (3) • Come prosegue l’esecuzione nei processi padre e figlio 232 - 1 232 - 1 Stack Stack &x &x 45 0 Area vuota Area vuota PC = istruzione successiva a fork heap heap Data Data Text Text 0 0 SI padre (pid=34) SI figlio (pid = 45)
Creazione di processi (4) /* frammento che crea un nuovo processo */ int pid; /* pid del processo creato */ … IFERROR( pid = fork(),”main: creazione”); if ( pid ) { /* siamo nel padre */ sprintf(buf,”Processo %d, ho creato %d\n.”, getpid(),pid); WRITELN(buf); } else { /* siamo nel figlio */ sprintf(buf,”Processo %d, mio padre e’ %d\n.”, getpid(),getppid()); WRITELN(buf); }
Terminazione : exit() void exit(int status); • chiude tutti i descrittori di file, • libera lo spazio di indirizzamento, • invia un segnale SIGCHLD al padre • salva il primo byte (0-255) di status nella tabella dei processi in attesa che il padre lo accetti (con la wait(), waitpid()) • se il processo padre è terminato, il processo ‘orfano’ viene adottato da init (cioè ppid viene settato a 1) • se eseguita nel main è equivalente ad una return
Attesa di terminazione del figlio pid_t wait(int *status) pid_t waitpid(pid_t pid,int *status,int options) • restituisce informazioni sul tipo di terminazione del figlio e il byte meno significativo ritornato con la exit() • per leggere queste informazioni c’è bisogno di opportune maschere WIFEXITED(status) WEXITSTATUS(status) • in caso di errore (es non ci sono figli …) viene ritornato il valore -1
Attesa di terminazione ...(2) pid_t wait(int *status) pid_t waitpid(pid_t pid,int *status,int options) • la waitpid permette di fare attese non bloccanti • WNOHANGspecificato nelle opzioni indica di non attendere se nessun figlio è ancora terminato • permette di attendere un figlio con un pid specifico (pid)
Esempio : wait() ed exit() int status ; /* conterra’ lo stato */ IFERROR( pid = fork(),”main: creazione”); if ( pid ) { /* siamo nel padre */ sleep(20); /* aspetta 10 secondi */ pid = wait(&status); if (WIFEXITED(status)) { /*!=0 se il figlio e’terminato normalmente, (exit o return) non ucciso da signal */ printf(“stato %d\n”, WEXITSTATUS(status)); } else { /* siamo nel figlio */ printf(“Processo %d, figlio.\n”,getpid()); exit(17); /*termina con stato 17 */ }
Esempio : wait() ed exit() (2) cosa accade se eseguiamo un main contenente il codice dell’esempio : $ a.out & -- avvio l’esecuzione in bg Processo 1246, figlio. -- stampato dal figlio
Esempio : wait() ed exit() (3) prima che i 20 secoondi siano finiti ... $ a.out & -- avvio l’esecuzione in bg Processo 1246, figlio. -- stampato dal figlio $ ps -l … S UID PID PPID ………… CMD … Z 501 1246 1245 …………… a.out -- il figlio e’ un processo zombie
Esempio : wait() ed exit() (4) quando il padre si risveglia ed esegue la wait() ... $ a.out & -- avvio l’esecuzione in bg Processo 1246, figlio. -- stampato dal figlio $ ps -l … S UID PID PPID ………… CMD … Z 501 1246 1245 …………… a.out -- il figlio e’ un processo zombie (Z) $ Stato 17. -- stampato dal padre $
Differenziazione : le exec() • execve • è l’unica chiamata di sistema vera • execl, execlp,execle,execv, execvp • sono funzioni di libreria con differenze sul tipo di parametri • alla fine invocano la execve • tutte le exec*() • differenziano un processo rimpiazzando il suo spazio di indirizzamento con quello di un file eseguibile passato come parametro
Differenziazione : le exec() (2) • execl, execlp,execle,execv, execvp, execve • è possibile richiedere che la exec() cerchi il file nelle directory specificate dalla variabile di ambiente PATH è (p nel nome) • è possible passare un array di argomenti secondo il formato di argv[] (v nel nome) • è possible passare un array di stringhe che descrivono l’environment (e nel nome) • è possibile passare gli argomenti o l’environment come lista (terminato da NULL) (l nel nome)
Differenziazione : le exec() (3) • execl, execlp,execle,execv, execvp, execve • le exec() non ritornano in caso di successo!!! • Restituiscono -1 in caso di fallimento • non trova il file, il file non è eseguibile etc... • ATTENZIONE 1: le exec non creano nuovi processi!! • ATTENZIONE 2: padre e figlio continuano a condividere la tabella dei file aperti ….
Differenziazione : le exec() (4) Numero che contraddistingue il file come eseguibile • Formato di un file eseguibile • risultato di compilazione, linking etc ... Magic number Ampiezza area di memoria occupata dalle variabili globali NON inizializzate Altre info Ampiezza BSS Variabili globali inizializzate I-Data segment Text segment Codice del programma (assemblato)
Differenziazione : le exec() (5) Variabili di ambiente (envp) 232 - 1 env • (1) il contenuto del file eseguibile viene usato per sovrascrivere lo spazio di indirizzamento del processo che la invoca Agomenti (argv) argv Stack FRAME per la funzione main Area vuota BSS-segment Data Ampiezza BSS I-Data segment I-Data segment Text segment Text 0 File eseguibile
Differenziazione : le exec() (6) 232 - 1 env • (2) si carica in PC l’indirizzo iniziale X argv Indirizzo della prima istruzione compilata di main() Stack Area vuota BSS-segment X Ampiezza BSS I-Data segment I-Data segment Text segment Text X 0
Esempio : una shell semplificata int pid, status; char * argv[]; while (TRUE) { /*ciclo infinito*/ type_prompt(); /* stampa prompt*/ argv = read_comm(); /*legge command line*/ IFERROR3(pid = fork(),”Nella fork”,continue); if (pid) {/* codice padre */ wait(&status); if (WIFEXITED(status)) …/*gest. stato*/ } else {/*codice figlio*/ IFERROR(execvp(argv[0],argv),”nella execvp”); }
Duplicazione dei descrittori di file: dup() e dup2() int dup(int oldfd); int dup2(int oldfd,int newfd); • creano entrambi una copia del descrittore di file oldfd, • entrambi i descrittori puntano alla stassa locazione della tabella dei file aperti e possono essere utilizzati per lavorare sullo stesso file • dup cerca la prima posizione libera • dup2 chiude prima newfd (se aperto) e poi ci copia oldfd
Es: la redirezione • La shell permette di ridirigere stdin/out/error • Es. $ ls indirizzi.pdf k.c s.doc
Es: la redirezione • La shell permette di ridirigere stdin/out/error • Es. $ ls indirizzi.pdf k.c s.doc $ ls > pippo
Es: la redirezione • La shell permette di ridirigere stdin/out/error • Es. $ ls indirizzi.pdf k.c s.doc $ ls > pippo $ more pippo indirizzi.pdf k.c s.doc
Es: la redirezione (2) • La shell permette di ridirigere stdin/out/error. Es (2) $ more pippo i.pdf s.doc k.c $ sort < pippo k.c i.pdf s.doc
Es: la redirezione (3) • Redirezione dello stdout ed error su file diversi $ more pippo.c pluto.c 1> out 2> log
Es: la redirezione (3) • Redirezione dello stdout ed error su file diversi $ more pippo.c pluto.c > out &> log
Es: la redirezione (3) • Redirezione dello stdout ed error su file diversi $ more pippo.c pluto.c 1> out 2> log $ more log pippo.c: No such file or directory
Es: la redirezione (3) • Redirezione dello stdout ed error su file diversi $ more pippo.c pluto.c 1> out 2> log $ more log pippo.c: No such file or directory $ more out /* frammento che stampa il pid del processo in esecuzione e quello del padre usando la macro WRITE di stampa (in sysmacro.h) … */ char buf[N]; /* stringa da stampare */ … sprintf(buf,”Processo %d, mio padre e’ %d\n.”, getpid(),getppid());
Es: la redirezione (3.1) • Attenzione: out e log vengono sovrascritti!!!!! • Per appendere usare ‘>>’ $ more pippo.c pluto.c 1>> out 2>> log
Es: la redirezione (3.1) • Attenzione: out e log vengono sovrascritti!!!!! • Per appendere usare ‘>>’ $ more pippo.c pluto.c 1>> out 2>> log $ more log pippo.c: No such file or directory pippo.c: No such file or directory
Es: redirezione con dup() e dup2() • Es. voglio ridirigere lo standard output (file descriptor 1) su un file pippo int fd; ... IFERROR(fd=open(“pippo”,O_WRONLY|O_TRUNC|O_CREAT,0644),”nella open”); dup2(fd,STDOUT); /* duplica fd sullo standard output*/ close(fd); /* fd non serve piu’ */ printf(“Questo viene scritto in pippo!”); ...
Come la shell implementa la redirezione ... • Es. $ ls -l > pippo • Il processo shell si duplica con una fork()e si mette in attesa della terminazione del figlio con unawait • Il figlio apre in scrittura il file pippo (creandolo o troncandolo) • Il figlio duplica il descrittore di pippo con la dup2 sullo stdout e chiude il descrittore originario • Il figlio invoca una exec di ls -l, la quale conserva i descrittori dei file, e quindi va a scrivere in pippo ogni volta che usa il file descriptor 1
Come la shell implementa la redirezione … (2) • Es. $ ls -l > pippo(cont.) • Quando il figlio termina, il padre riprende la computazione con i sui descrittori di file invariati. • (padre e figlio hanno ognuno la sua tabella dei descrittori e casomai puntano alla stessa locazione della tabella dei file aperti)