1 I segnali. 2 Prima un po di teoria…... 3 Stati dei processi in UNIX Idle Sleeping Zombified...

Post on 01-May-2015

222 views 1 download

Transcript of 1 I segnali. 2 Prima un po di teoria…... 3 Stati dei processi in UNIX Idle Sleeping Zombified...

1

I segnali

2

I segnali

Prima un po’ di teoria…...

3

Stati dei processi in UNIX

Idle

Sleeping

Zombified

Runnable

Running

Forkiniziata

waitpid

Forkterminata

scheduling

Attesa diun evento

L’evento accade

exit

4

Segnali

• Sono ‘interruzioni’ software– comunicano al processo il verificarsi di un evento– ad ogni evento corrisponde un segnale numerato – un processo all’arrivo di un segnale di un certo tipo

può decidere di• ignorarlo

• lasciarlo gestire al kernel con l’azione di default definita per quel segnale

• specificare una funzione (signal handler) che viene mandata in esecuzione appena il segnale viene rilevato

5

Segnali (2)• Da chi sono inviati i segnali?

– da processo all’altro• usando la SC kill()• solo processi del gruppo (discendenti o antenati)

– dall’utente con particolari combinazioni di tasti (al processo in foregroud)

• Control-C corrisponde a SIGINT (ANSI)• Control-Z corresponde a SIGTSTP

– dall’utente con l’utility kill della shell

– dal SO per a comunicare al processo il verificarsi di particolari eventi (es. SIGFPE, errore floating-point, SIGSEGV, segmentation fault)

6

Segnali (3)

• Lo standard POSIX stabilisce un insieme di segnali riconosciuti in tutti i sistemi conformi– sono interi definiti come macro in /usr/include/bits/signum.h

– esempi:• SIGKILL (9) : il processo viene terminato (non può essere

intercettata) (quit)

• SIGALRM (14): è passato il tempo richiesto (quit)

7

Segnali (4)– Sono di uso comune anche segnali non POSIX:

• SIGINT (2) Control-C (ANSI) – richiesta di terminazione da tastiera (quit)

• SIGTSTP (POSIX) Control-Z– richiesta di sospensione da tastiera (suspend fino all’arrivo

SIGCONT)

• SIGFPE (8) (ANSI) – si è verificato un errore Floating Point (dump)

• SIGCHLD(17) (POSIX)– si è verificato un cambiamento di stato in un processo figlio

(ignore)

• SIGPIPE(13) (POSIX)– la pipe è stata chiusa in lettura (quit)

• …….

8

Segnali (5)• SD del kernel relative ai segnali

– signal handler array : descrive cosa fare quando arriva un segnale di un certo tipo

• ignorare, trattare + puntatore al codice della funzione da eseguire (handler)

– pending signal bitmap (signal mask): che contiene un bit per ogni tipo di segnale

• il bit X è a 1 se c’è un segnale pendente di tipo X

– ogni processo ha un signal handler array (nella user area) ed una pending signal bitmap (nella process table)

9

Segnali (6)• Cosa accade quando arriva un segnale?

– il processo che lo riceve viene interrotto– il kernel stabilisce quale comportamento adottare

controllando il contenuto del signal handler array – se deve essere eseguito un signal handler safun:

• lo stato del processo interrotto viene salvato• si esegue la funzione safun• il processo riprende l’esecuzione dallo stato in cui e’ stato

interrotto

10

Stati dei processi in UNIX (2)

Idle

Sleeping

Zombified

Runnable

Running

Forkiniziata

waitpidStopped

Forkterminata

scheduling

Attesa diun evento

L’evento accade

exitSegnaleSIGSTOP(CTRL Z)

SegnaleSIGCONT

11

SC per i segnali

alarm(), sigaction(),pause(),kill(),…...

12

Segnali: system call• Come si definisce un signal handler

personalizzato? – usando la SC sigaction()

• ci sono SC che permettono di inviare segnali– alarm(), kill()

• ci sono SC che permettono di mettersi in attesa dell’arrivo di un segnale– pause()

13

Segnali di sveglia: alarm() int alarm(unsigned int count);

– serve a implementare un timeout– invia un segnale SIGALRM al processo che l’ha

invocata dopo count secondi– se count è 0 non viene settato nessun allarme– in ogni caso tutte le richieste di allarme già settate

sono cancellate

– restituisce (0) se non c’erano allarmi settati oppure (x>0) se macavano x secondi allo scadere dell’ultimo allarme settato

14

Invio di una SIGALRM

int main (void) {

alarm(3); /* SIGALRM fra 3 secondi */

printf(”Ciclo infinito ….") ;

while (1) ; /* ciclo infinito */

printf(”Pippo") ; /* mai eseguita */

return 0 ;

}

15

Esempio : eseguiamo...

cosa accade se eseguiamo il codice dell’esempio :

$ a.out

Ciclo infinito ...

-- per (circa) 3 secondi non accade niente

16

Esempio : eseguiamo…(2)

cosa accade se eseguiamo il codice dell’esempio :

$ a.out

Ciclo infinito ...

Alarm clock -- arriva il segnale

-- processo terminato

$

17

Personalizzare la gestioneint sigaction(int signum,

const struct sigaction* act,

struct sigaction* oldact);– serve a definire un nuovo handler– signum : segnale da trattare– &act : struttura che definisce il nuovo trattamento

del segnale signum;– &oldact : (OUTPUT) ritorna il contenuto

precedente del signal handler array (può servire per ristabilire il comportamento precedente)

– ritorna (-1) se c’è stato errore

18

Personalizzare la gestione (2)struct sigaction { ...

void sa_handler (int); ...}

– sa_handler: indica come gestire il segnale può essere:•SIG_IGN ignora il segnale•SIG_DFL usare la funzione di gestione di default

• puntatore alla funzione da invocare all’arrivo del segnale

– gli altri campi di solito si lasciano invariati

19

Personalizzare la gestione (3)– SIGKILL e SIGSTP: non possono essere gestiti se

non con la procedura di default – il figlio eredita la gestione dei segnali dal padre– dopo la exec() le gestioni ritornano quelle di

default (ma i segnali ignorati continuano ad essere ignorati)

– i segnali SIGCHLD sono gli unici ad essere accumulati (stacked) negli altri casi se arriva un segnale dello stesso tipo di uno già settato nella signal mask viene perso

20

Personalizzare SIGALRM

void gestore (int sig) {/* numero segnale */

printf(”SIGALRM catturato\n") ;

}

int main (void) {

struct sigaction s ;

……

}

21

Personalizzare SIGALRM (2) int main (void) { …

/* inizializzo s con i valori correnti */

sigaction(SIGALRM,NULL,&s);

s.sa_handler=gestore; /* nuovo gestore */

/* installo nuovo gestore */ sigaction(SIGALRM,&s,NULL);

alarm(3); /* SIGALRM fra 3 secondi */

printf(”Ciclo infinito ….") ;

while (1) ; /* ciclo infinito */

printf(”Pippo") ; /* mai eseguita */

return 0 ;}

22

Esempio : eseguiamo...

cosa accade se eseguiamo il codice dell’esempio :

$ a.out

Ciclo infinito ...

-- per (circa) 3 secondi non accade niente

23

Esempio : eseguiamo…(2)

cosa accade se eseguiamo il codice dell’esempio :

$ a.out

Ciclo infinito ...

SIGALRM catturato -- arriva il segnale

-- il processo cicla indefinitamente …

24

Attesa di segnali: pause() int pause(void);

– sospende il processo fino all’arrivo di un segnale– serve a implementare l’attesa passiva di un segnale

– ritorna dopo che il segnale è stato catturato ed il gestore è stato eseguito, restituisce sempre (-1)

25

Attendere SIGALRM /* indica se è arrivato SIGALARM (=1) o no (=0) */

int sigalarm_flag = 0;

void gestore (int sig) {/* numero segnale */

sigalarm_flag = 1;

}

int main (void) {

struct sigaction s ;

……

}

26

Attendere SIGALRM (2)int main (void) {

sigaction(SIGALRM,NULL,&s);

s.sa_handler=gestore; /* nuovo gestore */

sigaction(SIGALRM,&s,NULL);

alarm(3); /* SIGALRM fra 3 secondi */

printf(”Ciclo fino a SIGALRM ….") ;

while (sigalarm_flag!= 1)

pause(); /* ciclo fino a SIGALRM */

/* serve a non sbloccarsi se arriva un altro segnale */

printf(”SIGALRM arrivato ...") ;

return 0 ;}

27

Esempio : eseguiamo...

cosa accade se eseguiamo il codice dell’esempio :

$ a.out

Ciclo fino a SIGALRM….

-- per (circa) 3 secondi non accade niente

28

Esempio : eseguiamo…(2)

cosa accade se eseguiamo il codice dell’esempio :

$ a.out

Ciclo infinito ...

SIGALRM arrivato -- arriva il segnale

-- processo terminato

$

29

Invio di segnali: kill() int kill(pid_t pid, int sig);

– invia un segnale di tipo sig a uno o più processi (dipende da pid)

– il segnale è inviato solo se • il processo che invia il segnale e chi lo riceve hanno lo

stesso owner

• il processo che invia il segnale è posseduto dal superutente (root)

– restituisce (0) se OK (-1) se si verifica un errore

30

Invio di segnali: kill()(2) int kill(pid_t pid, int sig);

– pid può essere • >0 , in questo caso è il pid del processo cui si deve inviare il

segnale

• =0, in questo caso il segnale è inviato a tutti i processi del gruppo del processo che esegue la kill

• -1, in questo caso il segnale è inviato a tutti i processi (tranne init) se il processo è di root, altrimenti come (pid=0)

• < -1, in questo caso il segnale è inviato a tutti i processi del gruppo a cui appartiene pid

31

Esempio : una shell con timeout/*istallazione gestore SIGCHLD*/

while (TRUE) { /*ciclo infinito*/

flag_sigchld = 0;

type_prompt(); /* stampa prompt*/

argv = read_comm(); /*legge command line*/

pid = fork();

if (pid) {/* codice padre */

Sleep(1); /* dopo 1 secondo si risveglia */

kill(pid,SIGKILL); /* e uccide il padre! */

while(!flag_sigchld)

pause();

} else {/*codice figlio*/

execvp(argv[0],argv); }

32

Esempio : una shell con timeout (2)void gestore_chld (int sig) {

int pid, stato;

pid = wait(&stato);

if (WIFEXITED(stato)) /* term con exit*/

printf(”%d: Terminato con exit %d”, pid,

WEXITSTATUS(stato));

else

printf(”%d: Terminato con kill %d”, pid,

WTERMSIG(stato));

flag_sigchld = 1;

}

33

Segnali e system call– Nello standard POSIX specifica che se arriva un

segnale mentre una SC (es. open(), read()) è in esecuzione la SC deve fallire con errore EINTR• si dovrebbe quindi testare EINTR dopo ogni SC e gestirlo

esplicitamente

• è possibile settare opportuni flags durante la sigaction() in modo da non essere interrotti

– Linux per default NON interrompe le SC all’arrivo di un segnale• Se vogliamo essere interrotti possiamo richiederlo

esplicitamente con una chiamata alla system call siginterrupt()

34

Race Condition?

Possiamo implementare sleep con alarm?

Nota: sleep e alarm si basano sul tempo di clock della macchina

35

In teoria si.../* def. Gestore alarm */

int sleep(int sec) {

/* installo gestore per SIGALRM */

alarm(sec);

/* SIGALRM fra sec secondi */

pause();

}

36

Problema• Il processo viene interrotto fra la

chiamata di alarm e pause

• Il tempo di attesa scade

• Il processo viene risvegliato, lo scheduler controlla la maschera dei segnali pendenti ed esegue il gestore che non fa nulla

• Il controllo passa al process che esegue pause!

• Potrebbe verificarsi un deadlock!

37

Come si risolve?

• Leggere la maschera dei segnali corrente e bloccare il segnale voluto (ad es. SIGALRM)

• Mandare il processo in attesa abilitando la ricezione del segnale voluto.

• Ripristinare la maschera dei segnali originaria.

38

Maschera dei segnali

• I moderni sistemi unix-like permettono di bloccare temporaneamente (o di eliminare completamente, impostando SIG_IGN come azione) la consegna dei segnali ad un processo.

• Si utilizza la maschera dei segnali (o signal mask) del processo cioè l'insieme dei segnali la cui consegna è bloccata.

• La signal mask viene ereditata dal padre alla creazione di un processo figlio

• Può essere modificata, durante l'esecuzione di un gestore, attraverso l'uso del campo sa_mask di sigaction.

39

Quali funzioni servono?#include <signal.h>

int sigemptyset(sigset_t *set)Crea una maschera vuota

int sigaddset(sigset_t *set, int signum) Aggiunge signum segnale alla maschera set

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)Cambia la maschera dei segnali del processo correntehow=SIG_BLOCK specifica segnali da bloccare

int sigsuspend(const sigset_t *mask)imposta la signal mask specificata, mettendo in attesa il processo.