Sistemi operativi
Transcript
Sistemi operativi
Program m azione di Sist em a – 4 Lucidi per il corso di Laboratorio di Sistemi Operativi tenuto da Paolo Baldan presso l'Università Ca' Foscari di Venezia, anno accademico 2004/ 2005. Parte di questo materiale è rielaborato dai lucidi del Corso di Laboratorio di Sistemi Operativi tenuto da Rosario Pugliese presso l'Università di Firenze, anno accademico 2001/ 02. Gest ione dei Segnali Segnali I processi t alvolt a devono gest ire event i inaspet t at i o im predicibili quali errori run-t im e (es. divisione per 0) m ancanza di corrent e elet t rica t erm inazione di un processo figlio richiest a di t erm inazione da part e di un ut ent e (<Ctrl-c>) richiest a di sospensione da part e di un ut ent e (<Ctrl-z>) Per la gest ione di event i asincroni di quest o t ipo Unix offre il m eccanism o dei segnali. Sono chiam at i int errupt soft ware: la loro elaborazione può com port are l'int erruzione del flusso regolare del processo. 3 Segnali: Gest ione Ogni segnale ha un ident ificat ore num erico signal.h definisce delle cost ant i sim boliche SIGxxx che ident ificano i vari segnali. es. SIGCHLD rappresent a la t erm inazione di un figlio il num ero di segnali predefinit i varia da 15 a 35 a seconda della versione di Unix Quando Unix si rende cont o che si è verificat o l'event o associat o ad un segnale SIGxxx invia il segnale in quest ione al processo corrispondent e. Il descrit t ore di un processo (PCB) nella process t able include un bit m ap, con una ent ry per ogni segnale, che m em orizza i segnali pendent i. L'invio del segnale consist e nell'at t ivazione del bit corrispondent e nel bit m ap. 4 Segnali: Gest ione Il kernel verifica la presenza di segnali pendent i per un processo quando quest o passa da kernel a user m ode (es. rit orno da sys call). abbandona lo st at o sleep o vi ent ra. Debolezze dei segnali un segnale può essere gest it o con un cert o rit ardo (problem a per applicazioni real-t im e) non si t iene cont o del num ero di segnali dello st esso t ipo pendent i (se vari SIGINT arrivano in successione rapida, può accadere che uno solo di quest i venga rilevat o). 5 Segnali: Handler Quando un processo “ riceve” un segnale può reagire in uno dei seguent i m odi Ignore: il segnale viene ignorat o e non ha alcun effet t o SIGKILL e SIGSTOP non possono essere ignorat i quest o perm et t e al superuser di t erm inare processi. Cat ch: esegue un signal handler, una funzione che gest isce l'event o associat o al segnale sospende il flusso di esecuzione corrent e esegue il signal handler riprende il flusso di cont rollo originario quando il signal handler t erm ina. 6 Segnali: Handler La reazione ad un segnale può essere definit a dal program m at ore o può essere quella di default st abilit a dal kernel. La reazione di default , in genere, consist e in una delle azioni seguent i t erm ina il processo generando un file core (dum p) senza generare un file core (quit ) ignora il segnale e lo cancella (ignore) sospende il processo (suspend) riprende l'esecuzione del processo (resum e). 7 Condizioni che generano un segnale Pressione di t ast i speciali sul t erm inale <Ctrl-c> invia il segnale SIGINT Eccezioni hardware l 'int errupt hardware viene cat t urat o dal kernel che invia un segnale corrispondent e al processo divisione per 0 (SIGFPE) riferim ent o non valido alla m em oria (SIGSEGV) Syst em call kill un processo può spedire un segnale ad alt ri processi deve aver il dirit t o di farlo: l'uid del processo che esegue kill deve essere lo st esso del processo a cui si spedisce il segnale l'uid di root (0) 8 Alcuni segnali predefinit i Macro SIGHUP SIGINT SIGQUIT SIGILL SIGTRAP SIGABRT SIGEMT SIGFPE SIGKILL SIGBUS SIGSEGV SIGSYS SIGPIPE SIGALRM SIGTERM # 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Default quit quit dump dump dump dump dump dump quit dump dump dump quit quit quit Description hang up interrupt quit illegal instruction trace trap abort emulator trap instruction arithmetic execption kill (cannot be caught, blocked or ignored) bus error (bad format address) segmentation violation (out-of-range address) bad argument to system call write on a pipe/socket with no one to read it alarm clock software termination signal 9 Alcuni segnali predefinit i Macro SIGUSR1 SIGUSR2 SIGCHLD SIGPWR SIGWINCH SIGURG SIGPOLL SIGSTOP SIGSTP SIGCONT SIGTTIN SIGTTOU SIGVTALRM SIGPROF SIGXCPU SIGXFSZ # Default Description 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 quit quit ignore ignore ignore ignore exit quit quit ignore quit quit quit quit dump dump user signal 1 user signal 2 child status changed power fail or restart window size change urgent socket condition pollable event stopped (signal) stopped (user) continued stopped (tty input) stopped (tty output) virtual timer expired profiling timer expired CPU time limit exceeded file size limit exceeded 10 Segnali da t ast iera È possibile inviare un segnale ad un processo in foreground prem endo <Ctrl-c> o <Ctrl-z> dalla t ast iera. Quando il driver di un t erm inale riconosce che è st at o prem ut o <Ctrl-c> (<Ctrl-z>) invia un segnale SIGINT (SIGSTP) a t ut t i i processi nel gruppo del processo in foreground. Alcuni segnali collegat i SIGCHLD: inviat o al padre da un figlio che t erm ina; SIGSTOP: sospensione da dent ro un program m a; SIGCONT: riprende l'esecuzione di un program m a dopo una sospensione. 11 Richiedere un segnale di allarm e: alarm () unsigned int alarm (unsigned int count) Ist ruisce il nucleo a spedire il segnale SIGALRM al processo invocant e dopo count secondi. Un event uale alarm()già schedulat o viene sovrascrit t o col nuovo. Se count è 0, non schedula nessun nuovo alarm() (e cancella quello event ualm ent e già schedulat o). Rest it uisce il num ero di secondi rim anent i prim a dell'invio dell'allarm e, oppure 0 se non è schedulat o nessun alarm(). Not a: l'allarm e è inviat o dopo alm eno count secondi, m a il m eccanism o di scheduling può rit ardare ult eriorm ent e la ricezione del segnale. 12 Gest ire i segnali: signal() int (*signal(int signum, void (*handler)(int)))(void) oppure typedef void (*sighandler_t)(int) sighandler_t signal(int signum, sighandler_t handler) Inst alla un nuovo signal handler, handler, per il segnale con num ero signum. Rest it uisce il precedent e signal handler associat o a signum, se ha successo; alt rim ent i, -1. 13 Gest ire i segnali: signal() L'handler può essere indirizzo di una funzione handler definit a dall'ut ent e La funzione handler ha un argom ent o int ero che rappresent a il num ero del segnale. Quest o perm et t e di ut ilizzare lo st esso handler per segnali different i. uno dei seguent i valori SIGN_IGN: indica che il segnale dev'essere ignorat o; SIGN_DFL: indica che deve essere usat o l'handler di default fornit o dal nucleo. Not a Il nom e di una funzione è un valore punt at ore a funzione. Una funzione in una dichiarazione di param et ro viene int erpret at a dal com pilat ore com e un punt at ore. Quindi nel prot ot ipo di signal() si può t ogliere “ * ” . 14 Gest ire i segnali: signal() I segnali SIGKILL e SIGSTP non sono riprogram m abili. Con la creazione di nuovi processi ... Dopo una fork(), il processo figlio eredit a le polit iche di gest ione dei segnali del padre. Se il figlio esegue una exec() i segnali precedent em ent e ignorat i cont inuano ad essere ignorat i i segnali i cui handler erano st at i ridefinit i sono ora gest it i dagli handler di default . Ad eccezione di SIGCHLD, i segnali non sono accodat i quando più segnali dello st esso t ipo arrivano “ cont em poraneam ent e” , solo uno viene t rat t at o. 15 Esem pio: crit ical.c Il program m a critical.c suggerisce com e si possono prot eggere pezzi di codice “ crit ici” da int erruzioni dovut e a <Ctrl-c> (segnale SIGINT) o ad alt ri segnali sim ili che possono essere ignorat i. Procede nel m odo seguent e salva il precedent e valore dell'handler, indicando che il segnale deve essere ignorat o; esegue la regione di codice prot et t a; riprist ina il valore originale dell'handler. 16 Esem pio: crit ical.c #include <stdio.h> #include <signal.h> int main (void) { void (*oldHandler) (int); /* To hold old handler value */ printf ("I can be Control-C'ed\n"); sleep (3); oldHandler = signal (SIGINT, SIG_IGN); /* Ignore Control-C */ printf ("I'm protected from Control-C now\n"); sleep (3); signal (SIGINT, oldHandler); /* Restore old handler */ printf ("I can be Control-C'ed again\n"); sleep (3); printf ("Bye!\n"); return 0; } 17 Esem pio: crit ical.c $ critical I can be Control-C'ed I'm protected from Control-C now I can be Control-C'ed again Bye! $ 18 Prot ezione dai segnali: Mascheram ent o Ignorare i segnali non è sem pre una buona soluzione... possono port are inform azioni rilevant i. Un processo può “ bloccare” la ricezione di segnali (la cui azione di default non sia ignore). Un segnale bloccat o che venga inviat o ad un processo rim ane pendent e fino a che il processo sblocca il segnale; il processo cam bia l'azione associat a in “ ignore” . I segnali pendent i possono essere analizzat i... Syst em call: sigprocmask, sigpending 19 Inviare segnali: kill() int kill(pid_t pid, int signum) spedisce il segnale con valore signum al processo con PID pid. Perm essi è possibile inviare il segnale nei seguent i casi i processi m it t ent e e dest inat ario hanno lo st esso propriet ario; più precisam ent e real o effect ive uid del m it t ent e coincide con real o effect ive uid del dest inat ario. il processo m it t ent e ha com e propriet ario il superuser. 20 Inviare segnali: kill() Il com port am ent o di kill varia a seconda di pid pid > 0 il segnale è inviat o al processo pid; pid = 0 invia il segnale a t ut ti i processi nel gruppo del m itt ente; pid = -1 se il m it tente ha per propriet ario il superuser, invia il segnale a tutt i i processi, m it tent e incluso; se il m it tente non ha per proprietario un superuser, invia il segnale a tutt i i processi nello st esso gruppo del m ittent e, con esclusione del m it tente; pid < -1 invia il segnale a t ut ti i processi nel gruppo |pid|. Rest it uisce valore 0, se invia con successo alm eno un segnale; alt rim ent i, rest it uisce -1. 21 Esem pio: lim it .c (t erm inazione figlio) Il program m a limit.c perm et t e all'ut ent e di lim it are il t em po im piegat o da un com ando per l'esecuzione. limit nsec cmd args esegue il com ando cmd con gli argom ent i args indicat i, dedicandovi al m assim o nsec secondi. Il program m a definisce un handler per il segnale SIGCHLD. 22 Esem pio: lim it .c (t erm inazione figlio) #include <stdio.h> #include <signal.h> int delay; void childHandler (int); /* death-of-child handler (see later) */ int main (int argc, char *argv[]) { int pid; signal (SIGCHLD, childHandler); /* Install death-of-child handler */ pid = fork (); /* Duplicate */ if (pid == 0) { /* Child */ execvp (argv[2], &argv[2]); /* Execute command */ perror ("limit"); /* Should never execute */ } else { /* Parent */ sscanf (argv[1], "%d", &delay); /* Read delay from command line */ sleep (delay); /* Sleep for the specified number of seconds */ printf ("Child %d exceeded limit and is being killed\n", pid); kill (pid, SIGINT); /* Kill the child */ } return 0; 23 } Esem pio: lim it .c (t erm inazione figlio) void childHandler (int sig) { /* Executed if the child dies */ int childPid, childStat; /* before the parent */ childPid = wait (&childStat); /* Accept child's termination code */ printf ("Child %d terminated within %d seconds\n", childPid, delay); exit (/* EXITSUCCESS */ 0); } $ limit 3 find / -name filechenonce find: /root/.links: Permission denied find: /root/.ssh: Permission denied ... Child 828 exceeded limit and is being killed $ limit 1 ls count.c myexec.c redirect.c limit.c limit myfork.c Child 828 terminated within 0 seconds $ critical.c lez7.ps 24 Esem pio: pulse.c (sospensione& ripresa) Il program m a pulse.c crea due figli che ent rano in un ciclo infinit o e m ost rano un m essaggio ogni secondo. Il padre aspet t a 2 secondi e quindi sospende il prim o figlio, m ent re il secondo figlio cont inua l'esecuzione. Dopo alt ri 2 secondi il padre riat t iva il prim o figlio, aspet t a alt ri 2 secondi e quindi t erm ina ent ram bi i figli. 25 Esem pio: pulse.c (sospensione& ripresa) #include <signal.h> #include <stdio.h> int main (void) { int pid1; int pid2; pid1 = fork (); if (pid1 == 0) { /* First child */ while (1) { /* Infinite loop */ printf ("pid1 is alive\n"); sleep (1); } } pid2 = fork (); if (pid2 == 0) { /* Second child */ while (1) { /* Infinite loop */ printf ("pid2 is alive\n"); sleep (1); } } 26 Esem pio: pulse.c (sospensione& ripresa) /* ... continue ... */ sleep (2); kill (pid1, sleep (2); kill (pid1, sleep (2); kill (pid1, kill (pid2, return 0; SIGSTOP); /* Suspend first child */ SIGCONT); /* Resume first child */ SIGINT); /* Kill first child */ SIGINT); /* Kill second child */ } 27 Esem pio: pulse.c (sospensione& ripresa) ... funzionam ent o ... $ pulse pid1 is pid2 is pid1 is pid2 is pid2 is pid2 is pid1 is pid2 is pid1 is pid2 is pid1 is pid2 is $ alive alive alive alive alive alive alive alive alive alive alive alive 28 Gruppi di processi Ogni processo è m em bro di un gruppo di processi. Un gruppo di processi ha associat o un ident ificat ore pgid – process group id (da non confondere con il gid). Un processo figlio eredit a il gruppo di appart enenza dal padre. La syst em call setpgid() perm et t e al processo invocant e di cam biare gruppo di appart enenza (a se st esso o ad alt ri). 29 Gruppi di processi e t erm inale di cont rollo Ad ogni processo può essere associat o un t erm inale di cont rollo è t ipicam ent e il t erm inale da cui il processo è lanciat o; i figli eredit ano il t erm inale di cont rollo del padre; se un processo esegue una exec(), il t erm inale di cont rollo non cam bia. Ad ogni t erm inale è associat o un processo di cont rollo se il t erm inale individua un m et acarat t ere com e <Ctrlc> spedisce il segnale appropriat o a t ut t i i processi nel gruppo del processo di cont rollo; se un processo t ent a di leggere dal suo t erm inale di cont rollo e non è m em bro del gruppo del processo di cont rollo di quel t erm inale, il processo riceve un segnale SIGTTIN che, norm alm ent e, lo sospende. 30 Gruppi e t erm inale: uso nella shell All'avvio di una shell int erat t iva la shell è il processo di controllo del t erm inale da cui è lanciata il term inale in questione è il t erm inale di cont rollo della shell. Se la shell esegue un com ando in foreground la shell figlia si m ett e in un diverso gruppo, assum e il cont rollo del t erm inale, esegue il com ando così ogni segnale generat o dal t erm inale viene indirizzat o al com ando e non alla shell originaria. quando il com ando t erm ina, la shell originaria riprende il cont rollo del t erm inale. Se la shell esegue un com ando in background la shell figlia si m ett e in un diverso gruppo ed esegue il com ando, m a non non assum e il controllo del t erm inale. così ogni segnale generat o dal t erm inale continua ad essere indirizzat o alla shell originaria. se il com ando in background tent a di leggere dal suo term inale di 31 cont rollo, viene sospeso da un segnale SIGTTIN. Cam biare il gruppo: set pgid() int setpgid(pid_t pid, pid_t pgrpId) assegna valore pgrpId al process group id del processo con PID pid. Se pid è 0, cam bia il valore del process group id del processo invocant e. Se pgrpId è 0, assegna il process group ID del processo con PID pid al processo invocant e. Ha successo se il processo invocant e ed il processo specificat o com e prim o argom ent o hanno lo st esso (effect ive) uid/gid; il propriet ario del processo invocant e è il superuser. Rest it uisce 0 se ha successo; -1 alt rim ent i. 32 Cam biare il gruppo: set gpid() Quando un processo vuole creare un proprio gruppo di processi, dist int o dagli alt ri gruppi del sist em a, t ipicam ent e passa il proprio PID com e argom ent o per setgpid() setgpid(0,getpid()) 33 Ot t enere il gruppo: get gpid() pid_t getpgid(pid_t pid) Rest it uisce il gruppo di processi a cui appart iene il processo con PID pid. Se pid è 0, rest it uisce il gruppo di processi a cui appart iene il processo invocant e. Non fallisce m ai. 34 Esem pio: pgrp1.c Il program m a pgrp1.c m ost ra com e un t erm inale invia i segnali ad ogni processo appart enent e al gruppo di processi del suo processo di cont rollo. Poiché un figlio eredit a il gruppo di processi del padre, padre e figlio cat t urano il segnale SIGINT. 35 Esem pio: pgrp1.c #include <signal.h> #include <stdio.h> void sigHandler (int sig) { printf ("Process %d got a %d signal \n", getpid (), sig); } int main (void) { signal (SIGINT, sigHandler); /* Handle Control-C */ if (fork () == 0) printf ("Child PID %d PGRP %d waits\n", getpid (), getpgid (0)); else printf ("Parent PID %d PGRP %d waits\n", getpid (), getpgid (0)); pause (); /* Wait for a signal */ } 36 Esem pio: pgrp1.c $ pgrpl Parent PID 24444 PGRP 24444 waits Child PID 24445 PGRP 24444 waits <^C> Process 24445 got a 2 signal Process 24444 got a 2 signal $ pause() sospende il processo invocant e fino all'arrivo di un segnale che ne causa la t erm inazione che causa l'esecuzione di un signal handler. 37 Esem pio: pgrp2.c Il program m a pgrp2.c m ost ra che se un processo lascia il gruppo del processo di cont rollo del t erm inale, non riceve più segnali dal t erm inale. 38 Esem pio: pgrp2.c #include <signal.h> #include <stdio.h> void sigHandler (int sig) { printf ("Process %d got a SIGINT\n", getpid ()); exit (1); } int main (void) { int i; signal (SIGINT, sigHandler); /* Install signal handler */ if (fork () == 0) setpgid (0, getpid ()); /* Place child in its own process group */ printf ("Process PID %d PGRP %d waits\n", getpid (), getpgid (0)); for (i = 1; i <= 3; i++) { /* Loop three times */ printf ("Process %d is alive\n", getpid ()); sleep(2); } return 0; } 39 Esem pio: pgrp2.c esecuzione... $ pgrp2 Process PID 24535 PGRP 24535 waits Process 24535 is alive Process PID 24536 PGRP 24536 waits Process 24536 is alive <^C> Process 24535 got a SIGINT $ Process 24536 is alive Process 24536 is alive <return> $ 40 Esem pio: sigt t in.c Il program m a sigt t in.c m ost ra che se un processo lascia il gruppo del processo di cont rollo del t erm inale e quindi t ent a di leggere dal t erm inale st esso, riceve un segnale SIGTTIN che ne provoca la sospensione. Nell'esem pio l'handler per SIGTTIN (21) viene riprogram m at o. 41 Esem pio: sigt t in.c #include <signal.h> #include <stdio.h> #include <sys/termio.h> #include <fcntl.h> void sigHandler (int sig) { printf ("%d Inappropriate read from control terminal\n", sig); exit (1); } int main (void) { int status; char str [100]; if (fork () == 0) { /* Child */ signal (SIGTTIN, sigHandler); /* Install handler */ setpgid (0, getpid ()); /* Place myself in a new process group */ printf ("Enter a string: "); scanf ("%s", str); /* Try to read from control terminal */ printf ("You entered %s\n", str); } else { /* Parent */ wait (&status); /* Wait for child to terminate */ } 42 } Esem pio: sigt t in.c $ pgrp3 Enter a string: 21 Inappropriate read from control terminal $ 43 Segnali per IPC I segnali possono essere ut ilizzat i per (form e lim it at e di) IPC. Esem pio il processo P apre un file si duplica, creando un figlio Q P scrive un int ero, sem pre all'inizio del file Q legge un int ero, sem pre all'inizio del file Funziona se P e Q si sincronizzano t ram it e segnali P scrive ed invia un segnale a Q, quindi at t ende un segnale Q aspet t a un segnale prim a di leggere, legge ed invia un segnale a P 44 Esem pio: com m Sig.c #include #include #include #include #include #include <stdio.h> <signal.h> <stdlib.h> <sys/types.h> <sys/stat.h> <fcntl.h> int usr1rcv = 0; /* var per comunicazione tra handler e figlio : segnala la ricezione di un segnale SIGUSR1 */ void usr1Handler(int sig) { /* handler per SIGUSR1 */ usr1rcv = (sig == SIGUSR1); } int main (void) { int fdTmp; /* file descriptor comune */ pid_t father, son; char tmpFile[7] = "XXXXXX"; int elem; fdTmp = mkstemp(tmpFile); /* Crea un file temporaneo */ unlink(tmpFile); signal(SIGUSR1,usr1Handler); /* Installa l'handler */ 45 Esem pio: com m Sig.c if ((son = fork()) != 0) { /* processo padre */ for(elem=0; elem<10; elem++) { sleep(1); /* scrive un valore per il figlio */ lseek(fdTmp, 0, SEEK_SET); write(fdTmp, &elem, sizeof(elem)); printf("Padre: Inviato il valore %d\n", elem); /* segnala la presenza del valore */ kill(son, SIGUSR1); /* attende che il figlio legga il valore */ while (!(usr1rcv)) pause(); usr1rcv = 0; } } 46 Esem pio: com m Sig.c else { /* processo figlio */ father = getppid(); while (1) { /* attende che il padre spedisca un valore */ while (!(usr1rcv)) pause(); lseek(fdTmp, 0, SEEK_SET); read(fdTmp, &elem, sizeof(elem)); printf("Figlio: Ricevuto il valore %d\n", elem); usr1rcv = 0; /* segnala al padre che ha letto il valore */ kill(father, SIGUSR1); } } } 47