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