1. Concetto di processo
• Processo: istanza di un programma in
esecuzione
– Codice del programma (sezione di testo)
– Stato di avanzamento
♦ Contatore di programma
♦ Pila (variabili locali, indirizzi di ritorno)
– Variabili locali (sezione dei dati)
1
2. Concetto di processo
• Programma e processo sono due entità
distinte
– Un programma è un'entità passiva
♦ File su disco (codice o eseguibile) immutato
♦ Non va in esecuzione da solo
– Un processo è un'entità attiva
♦ Struttura dati contenente puntatori a
– Codice
– Dati
– Stato del processo
2
3. Esecuzione di un processo
• Il gestore dei processi esegue, ad intervalli di
tempo più o meno regolari, una sequenza di
istruzioni pertinenenti al programma (trace)
• Un SO moderno, multiprogrammato, time-
sharing garantisce una esecuzione
sequenziale e concorrente (interleaved) di più
processi
• Compiti specifici del gestore dei processi:
– Garantire l'esecuzione sicura di una traccia
– Scegliere il prossimo processo di cui sarà
eseguita una traccia (scheduling)
– Gestire la commutazione periodica tra un
processo ed un altro (context switch)
3
5. Esecuzione interleaved
Memoria Indirizzi PC
principale
a+0
a+2
Gestore
processi a+6
Program
a + 10
Counter
Scade l'intervallo
Processo A
di tempo assegnato
al processo A
Processo B
Processo C
5
6. Esecuzione interleaved
Memoria Indirizzi PC
principale
g + 100
g + 102
Gestore
processi g + 106
Program
g + 110
Counter
g + 114
Processo A
Il gestore dei processi
sceglie come prossimo
processo da eseguire
Processo B
il processo B, e ne
prepara l'esecuzione
Processo C
6
7. Esecuzione interleaved
Memoria Indirizzi PC
principale
b + 32
b + 36
Gestore
processi b + 40
Program
Counter
Il processo B si
interrompe perché
Processo A
deve effettuare una
operazione di I/O
Processo B
Processo C
7
8. Esecuzione interleaved
Memoria Indirizzi PC
principale
g + 100
g + 102
Gestore
processi g + 106
Program
g + 110
Counter
g + 114
Processo A
Il gestore dei processi
sceglie come prossimo
processo da eseguire
Processo B
il processo C, e ne
prepara l'esecuzione
Processo C
8
9. Esecuzione interleaved
Memoria Indirizzi PC
principale
c + 248
c + 356
Gestore
processi c + 360
Program
c + 364
Counter
Il processo C termina
Processo A
la sua esecuzione
Processo B
Processo C
9
10. Esecuzione interleaved
Memoria Indirizzi PC
principale
g + 100
g + 102
Gestore
processi g + 106
Program
g + 110
Counter
g + 114
Processo A
Il gestore dei processi
sceglie come prossimo
processo da eseguire
Processo B
il processo A, e ne
prepara l'esecuzione
Processo C
10
11. Esecuzione interleaved
Memoria Indirizzi PC
principale
a + 14
a + 18
Gestore
processi a + 22
Program
a + 56
Counter
Il processo A si
Processo A
blocca perché richiede
una operazione di I/O
Processo B
Processo C
11
12. Rappresentazione di un processo
• L'esecuzione di tipo interleaved deve essere
modellata all'interno del gestore dei processi
• Sembrerebbe naturale introdurre un modello
del tipo DFA (Deterministic Finite Automata,
automa deterministico a stati finiti)
• Quanti stati utilizzo per il mio modello?
• Proviamo con il caso più semplice:
– Due stati: running, not running
– I processi non in esecuzione sono “accodati” di
fronte al processore, in attesa di ricevere tempo
di calcolo
– Tramite oppoerunte transizioni, un processo è
in grado di cambiare stato
12
13. Rappresentazione di un processo
Modello a due stati
blocking
creazione terminazione
not running running
dispatching
Diagramma di accodamento
creazione dispatching terminazione
CPU
coda processi pronti
13
14. Incompletezza del modello a 2 stati
• Il modello a due stati è incompleto
• Tutti i processi che non eseguono sono
equiparati a processi in qualche modo
“bloccati”
• In realtà, le cause del blocco possono essere
estremamente diverse:
– scadenza dell'intervallo di tempo di calcolo
assegnato
– comando di una operazione di I/O
– arrivo di una interruzione
• Per discriminare le diverse cause di blocco, è
necessario considerare un modello con
almeno tre stati
14
15. Modello a tre stati
• L'automa che descrive il ciclo di vita di un
processo è composto da tre stati:
– running: la CPU sta eseguendo una traccia del
processo
– blocked: la CPU ha comandato una istruzione
che richiede il verificarsi di un evento (di solito,
I/O) per conto del processo, che deve fermarsi
ed attendere la risposta
– ready: la CPU non sta eseguendo una traccia
del processo (quale che sia il motivo), tuttavia il
processo è nelle condizioni di essere
ripristinato
• Differenziazione esplicita degli stati di
“bloccato” e “pronto” 15
16. Modello a tre stati
Modello a tre stati
scadenza
intervallo
creazione terminazione
ready running
tempo
dispatching
completamento attesa
evento evento
blocked
Transizioni
ready => running: assegnazione CPU al processo
running => ready: il processo ha utilizzato il max.
tempo per esecuzione traccia
running => blocked: attesa completamento evento
blocked => ready: l'evento atteso si verifica 16
17. Stati aggiuntivi
• Il modello a tre stati è in grado di catturare le
diverse dinamiche di interruzione
dell'esecuzione di una traccia
• Vengono aggiunti alcuni stati aggiuntivi per la
gestione pulita dei meccanismi di creazione e
di terminazione del processo
• Due ulteriori stati:
– new: il sistema alloca ed inizializza le strutture
dati per la gestione dell'esecuzione del
processo
– exit: il sistema rilascia le strutture dati ed
alcune risorse allocate dal processo
17
18. Stati aggiuntivi
Stato aggiuntivo new
creazione admit terminazione
new ready
Strutture dati
per il processo
allocate ed
inizializzate
Stato aggiuntivo exit
terminazione
running exit
Rilascio strutture
dati allocate per il
processo
18
19. Modello a cinque stati
• Ad ogni processo è associato uno stato
• Durante l'esecuzione, un processo è soggetto
a cambiamenti di stato
• Stati:
– new: il processo è stato appena creato
– running: una unità di elaborazione esegue le
istruzioni del relativo programma
– blocked: il processo è in attesa del verificarsi di
un qualche evento
– ready: il processo attende di essere assegnato
ad una unità di elaborazione
– exit: il processo ha terminato la sua esecuzione
19
20. Modello a cinque stati
ammesso uscita
new exit
interruzione
ready running
completamento I/O attesa I/O
dispatching
verifica evento attesa evento
blocked
20
21. Modello a cinque stati
Diagramma di accodamento
CPU
Coda processi pronti
I/O Coda I/O Richiesta I/O
quanto di tempo
esaurito
processo figlio generazione
in esecuzione processo figlio
attesa
occorrenza
interruzione
interruzione
21
22. Cause di creazione di un processo
• Attivazione di un batch job
– Comando inviato tramite shell
• Login interattivo
– Accesso al sistema da parte di un utente
• Gestione di un servizio
– Un processo soddisfa una richiesta di servizio
– Il processo invocante non si interrompe
– Processi server
• Spawning
– Un processo soddisfa una richiesta di servizio
– Il processo invocante si interrompe
– Processi di login
22
23. Cause di terminazione di un processo
• Completamento della traccia di istruzioni
– Può essere comunicato esplicitamente
• Indisponibilità di memoria
– Il processo richiede troppa memoria
• Violazioni di limite
– Accesso a locazioni di memoria proibite
• Errore di protezione
– Accesso ad una risorsa protetta o inesistente
• Terminazione su richiesta
– Operata dal sistema o da un altro processo
• Errore aritmetico
– Divisione per zero, overflow
23
24. Il grado di multiprogrammazione
• Il processore è, solitamente, molto più veloce dei
sistemi di I/O
– La maggior parte dei processi residenti in memoria
è nello stato blocked, in attesa di completamento di
una richiesta di I/O
– Rischio di sottoutilizzo del processore
• Come si può prevenire questo problema?
– Si cerca di mantenere il maggior numero di processi
possibile negli stati running/ready
– Grado di multiprogrammazione: numero di processi
eseguibili (running + ready)
– Obiettivo: mantenere il più alto possibile il grado di
multiprogrammazione
24
25. Swapping
• Per superare il limite imposto dal vincolo di
memoria fisica disponibile, si introduce il
concetto di swapping nel modello
• Due stati aggiuntivi:
– Blocked-Suspended: il processo è bloccato (in
attesa di un evento) e la memoria che occupa è
stata liberata con un trasferimento dati su disco
(swap out)
– Ready-Suspended: il processo non è bloccato
(nessun evento, oppure attesa terminata) e
torna ad occupare memoria centrale con un
trasferimento dati da disco (swap in)
25
26. Modello a cinque stati con swapping
ammesso uscita
new exit
interruzione
Ripristino in
memoria (swap in)
ready running
completamento I/O
attesa I/O
dispatching
verifica evento
attesa evento
verifica evento
blocked
Rimozione dalla
ready memoria (swap out)
suspended blocked
suspended
verifica evento
26
27. Modello a cinque stati con swapping
Diagramma di accodamento
(swapping)
Processi parzialmente
swap in swap out
eseguiti e sottoposti
a swap
CPU
Coda processi pronti
I/O Coda I/O Richiesta I/O
27
28. Cause di sospensione di un processo
• Swapping
– Liberazione porzioni di memoria da parte del SO
– Obiettivo: aumento grado multiprogrammazione
• Richiesta
– Da parte di un utente interattivo
– Da parte di un altro processo (coordinamento
attività)
• Temporizzazione
– Gestione attività periodiche (monitoraggio)
• Recupero
– Ripristino da situazioni erronee/incoerenti
28
29. Gestione processi:
strutture di controllo
• Per gestire l'esecuzione dei processi, il SO
mantiene informazioni sui processi stessi e
sulle risorse utilizzate Immagine P 1
Risorse Strutture dati
di sistema di controllo
Tabella di
Memoria Tabella di memoria
memoria
Dispositivi ...
Tabella di
File ...
memoria
Immagine Pn
Tabella dei
Processi
processi
P1
Pn 29
30. Tabelle di memoria
• Contengono informazioni su:
– Allocazione memoria principale ai processi
– Allocazione memoria secondaria ai processi
– Modalità di protezione dei singoli segmenti di
memoria (read-only, read-write, execute)
– Informazioni necessarie a gestire la memoria
virtuale (nei sistemi in cui essa è supportata)
• Esempi:
– Pagine di memoria accessaibili al processo
– File aperti (e relativi descrittori)
30
31. Immagine di un processo
• L'immagine di un processo rappresenta
l'insieme di informazioni necessarie per poter
coordinare l'esecuzione di più processi
– Codice macchina del programma di cui il
processo risulta essere una istanza,
memorizzati in aree leggibili ed eseguibili
– Dati utilizzati dal programma, memorizzati in
aree di memoria leggibili e scrivibili
– Stack, utilizzato per gestire le chiamate di
funzione
– Process Control Block (PCB), una collezione di
attributi necessari al SO per controllare
l'esecuzione del processo stesso
31
32. Immagine di un processo
Indirizzi alti
Stack
Stack segment programma
Lo stack cresce verso il basso
“Buco” nello spazio
di indirizzamento
Heap
L'heap cresce verso alto
(malloc)
Data segments Variabili non inizializzate
BSS (vengono impostate a 0)
Variabili globali inizializzate
Data
Indirizzi bassi
Codice
Text segment eseguibile
32
33. Process Control Block
• Il SO associa a ciascun processo un
descrittore di processo (Process Control
Block)
– Identificatori: del processo, di altri processi
relazionati
– Stato del processo: running, ready, blocked,...
– Privilegi: per accesso a servizi e risorse
– Registri: salvataggio registri della CPU (registri
interni, program counter, stack pointer)
– Informazioni di scheduling: tutto ciò che il
sistema necessita di sapere per arbitrare la
assegnazione del processore ai processi in
stato ready (CPU scheduling)
– informazioni di stato: evento atteso 33
34. Process Control Block
Process Control Block
puntatore stato del processo
identificatore di processo (PID)
contatore di programma
registri
contatore di programma
elenco dei file aperti
...
34
35. Scheduling dei processi
• Obiettivo della multiprogrammazione:
– massimizzare l'utilizzo della CPU tramite
l'esecuzione contemporanea di più processi
– contemporaneo è da intendersi nel senso di
avanzamento alternato, non nel senso di
esecuzione contemporanea sulla stessa CPU
• Obiettivo del time sharing:
– Commutare l'uso della CPU fra processi così
velocemente da facilitare l'interattività con gli
utenti
35
36. Scheduling dei processi
• In un sistema con m CPU, al più m processi
possono essere in esecuzione alla volta
– Che succede se ho più di m processi in
memoria?
• L'esecuzione dei processi deve essere
schedulata sulle risorse di elaborazione a
disposizione
– Chi decide quale processo deve essere
ripristinato in esecuzione su quale CPU?
• Scheduler
36
37. Code di scheduling
• Per coordinare il passaggio di stato fra
processi, il SO fa uso massiccio di code di
scheduling
• Code utilizzate:
– Coda dei processi
♦ contiene tutti i processi del sistema
– Coda dei processi pronti (ready queue)
♦ contiene tutti i processi in stato di pronto
– Coda di dispositivo
♦ contiene tutti i processi in attesa per l'uso del
dispositivo
♦ una coda distinta per dispositivo
37
38. Code di scheduling
PCB7 PCB2
coda dei Primo elemento
processi
Ultimo elemento registri registri
pronti
... ...
coda Primo elemento
unità
Ultimo elemento
CDROM
PCB3 PCB14 PCB6
coda Primo elemento
unità
Ultimo elemento registri registri registri
dischi
... ... ...
coda PCB3
Primo elemento
terminale
Ultimo elemento registri
0
... 38
39. Scheduler
• Componente del SO che seleziona i processi
dalle code
• Due tipi di scheduling:
– Job scheduling
– CPU scheduling
39
40. Job scheduling
• Utilizzato nei sistemi batch
• L'insieme dei job da eseguire viene riversato
su disco (spooling)
• Lo scheduler dei job sceglie il prossimo job da
mandare in esecuzione
• Obiettivo: mantenere costante il numero di job
in memoria (grado di multiprogrammazione)
• Invocato con frequenza bassa
• Bilancio fra processi I/O-bound e CPU-bound
• Job scheduling non è integrato nel kernel dei
moderni sistemi UNIX
– Implementazioni applicative (stampa, posta)
40
41. CPU scheduling
• Utilizzato nei sistemi time sharing
• Seleziona il prossimo processo dalla coda di
pronto per l'esecuzione
• Obiettivi:
– Massimizzare numero di processi eseguiti
nell'unità di tempo (throughput)
– Minimizzare la latenza di processo
– Dare priorità ai processi più importanti
• Invocato con frequenza elevata (ogni 100
msec)
41
42. Context switch
• Quando la CPU interrompe l'esecuzione di un
processo per passare ad un altro:
1.Occorre salvare lo stato attuale del vecchio
processo
2.Occorre ripristinare lo stato precedente del
nuovo processo
• Le operazioni delineate in 1. e 2. prendono il
nome di context switch
• Contesto di un processo: contenuto del
descrittore di processo
– Registri, stato, info gestione memoria
• Durata context switch: 1usec – 1msec
– Dipende fortemente dalla architettura
42
43. Cambio modalità esecuzione
• I processi si alternano anche in diverse
modalità di esecuzione, caratterizzate da livelli
di privilegio diversi
– User mode, Kernel mode
• User mode: esecuzione applicativi
• Kernel mode: esecuzione servizi del kernel
– Accesso a privilegi di livello superiore
– Possibilità di esecuzione di istruzioni non
ammesse in user mode
– Caratterizzato (riconoscibile) da un settaggio di
un bit di stato del processore
43
44. Modalità esecuzione e context switch
Processo Processo
syscall()
Meccanismo per il User mode
cambio di modo Kernel mode
service_call() Scheduler
Invocazione
scheduler
Chiamata
bloccante
Hardware
44
45. Modi di esecuzione e context switch
• Cause di context switch
– Interruzione di clock (time sharing), viene
attivato lo scheduler per cedere il controllo ad
un altro processo
– Interruzione di I/O, con possibile riattivazione di
un processo a più alta priorità
– Nei sistemi basati su memoria virtuale, prelievo
di una pagina di memoria da swap (page fault)
• Cause di cambio modo di esecuzione
– Attivazione di una Interrupt Service Routine
– Context switch
– Chiamata di sistema
45
46. Context switch
Processo P0 Sistema operativo Processo P1
Salva lo stato in PCB0
Ripristina lo stato da PCB1
Salva lo stato in PCB1
Ripristina lo stato da PCB1
46
47. Context switch
Processo P0 Sistema operativo Processo P1
Interruzione o syscall bloccante
Salva lo stato in PCB0
Ripristina lo stato da PCB1
Salva lo stato in PCB1
Ripristina lo stato da PCB1
47
48. Context switch
Processo P0 Sistema operativo Processo P1
Interruzione o syscall bloccante
Salva lo stato in PCB0
Ripristina lo stato da PCB1
Salva lo stato in PCB1
Ripristina lo stato da PCB1
48
49. Context switch
Processo P0 Sistema operativo Processo P1
Interruzione o syscall bloccante
Salva lo stato in PCB0
Ripristina lo stato da PCB1
Salva lo stato in PCB1
Ripristina lo stato da PCB1
49
50. Context switch
Processo P0 Sistema operativo Processo P1
Interruzione o syscall bloccante
Salva lo stato in PCB0
Ripristina lo stato da PCB1
Interruzione o syscall bloccante
Salva lo stato in PCB1
Ripristina lo stato da PCB1
50
51. Context switch
Processo P0 Sistema operativo Processo P1
Interruzione o syscall bloccante
Salva lo stato in PCB0
Ripristina lo stato da PCB1
Interruzione o syscall bloccante
Salva lo stato in PCB1
Ripristina lo stato da PCB0
51
52. Modello processi UNIX
• Esecuzione dei processi in user mode (codice
applicativo) e kernel mode (servizi)
– Lo stato running deve essere suddiviso in due
stati: user running e kernel running
• Lo scheduler implementa un meccanismo di
prelazione per supportare la priorità
– Un processo che ritorna da una chiamata di
sistema (lasciando il kernel mode) può essere
messo in stato ready e lasciare il passo ad un
processo con più alta priorità
– Nuovo stato preempted che modella la
prelazione
52
53. Modello processi UNIX
• Modello gerarchico di processi: un processo
padre genera processi figli
• PID: process identifier (identifica il processo)
• Processo padre coordinatore aspetta che i
processi figli lavoratori terminino l'esecuzione
• Se al momento dell'uscita un processo figlio
non ha un processo padre che aspetta il suo
completamento, entra nello stato zombie
– Processo figlio morto
– Ha un PCB nella tabella dei processi, in modo
tale che il processo padre possa leggere il suo
stato di uscita
– Distinzione stati exited e zombie
53
54. Modello processi UNIX
preempted
new
User
mode
running
Ready
ready
Swapped out
Kernel
mode
running
Sleeping
Sleeping in
Swapped out
memory
exited zombie
54
55. Immagine di un processo
Testo
Dati
Contesto utente
Stack utente
Memoria condivisa
Program counter
Registro di stato del processore
Contesto registri
Stack pointer
Registri generali
Entry nella tabella dei processi
U area (area utente)
Contesto sistema
Tabella indirizzamento (memoria
virtuale)
Stack del kernel mode
55
56. Entry della tabella dei processi
Stato del processo
Puntatori alla U area
Dimensioni del processo
Identificatori d’utente (reale ed effettivo)
Identificatori di processi (pid, id del genitore)
Descrittore degli eventi (valido in stato sleeping)
Priorita’
Segnali (mandati al processi ma non ancora gestiti)
Timer (monitoring)
P_link (puntatore per la lista ready)
Stato della memoria (swap in/out)
56
57. User Area (U area)
Puntatore alla entry della tabella dei processi
Dimensioni del processo
Identificatori d’utente (effettivo)
Array per i gestori di segnali
Terminale
Campo di errore
Parametri di I/O (es. indirizzi dei buffer)
Timer (monitoring in modalita’ utente)
Valore di ritorno di system calls
Tabella dei descrittori di file
57
58. Modello di avvio processi UNIX
Kernel init
getty login shell
(PID=0) (PID=1)
getty login shell
Legge
/etc/inittab
getty login shell
• Shell tipiche:
– Bourne Again SHell
– Bourne Legge
–C /etc/passwd
– Korn
Comandi di shell: nome-comando [ arg1, ..., argn ]
58
59. Creazione di un processo UNIX
• Durante la propria esecuzione, un processo
può creare altri processi (fork)
– Processo creante: processo padre
– Processo creato: processo figlio
• Ciascun processo creato, a sua volta, può
creare altri processi
• Organizzazione dei processi ad albero
• Un processo può ereditare dal padre:
– Tutte le risorse
– Un sottoinsieme delle risorse
59
61. Creazione di un processo UNIX
• Esecuzione del processo padre dopo aver
creato un processo figlio:
– Il processo padre continua l'esecuzione in
maniera concorrente con i propri figli
– Il processo padre attende che alcuni o tutti i
suoi figli terminino l'esecuzione
• Esecuzione del processo figlio:
– Dallo stesso punto in cui si era interrotto il
padre prima di invocare la fork()
• Spazio di indirizzamento del processo figlio:
– Il processo figlio è un duplicato del padre
– Il processo figlio carica un programma
61
62. System call fork()
• In UNIX, ogni processo è caratterizzato da un
numero identificatore univoco (PID)
• Un nuovo processo viene creato per mezzo
della syscall fork()
• La syscall fork:
– Duplica il descrittore del processo padre nel
descrittore del processo figlio
– Genera un nuovo PID
– Inserisce il descrittore di processo del figlio
nella coda di pronto
• Valore di ritorno della fork:
– PID nel processo padre
– 0 nel processo figlio
62
63. System call fork()
• Signature: pid_t fork(void)
– Descrizione: crea un esatto duplicato del
processo chiamante
– Argomenti: nessuno
– Valore di ritorno:
♦ Processo padre -> PID del figlio
♦ Processo figlio -> 0
♦ -1 in caso di errore
Processo padre Processo figlio
stack stack
Entrambi i processi
fork() ripartono dall'istruzione
successiva alla fork()
heap heap
dati dati nel testo
testo testo
63
64. Utilizzi del meccanismo di forking
• Creazione di shell
comandi a catena fork() fork()
(pipeline) pipe()
exec() exec()
find wc
find . -name '*.c' -print | wc -l
output
• Gestione di servizi Server
richieste
process
(request serving) fork() fork()
– Web server Child Child
process process
– FTP server
– “XYZ” server
risposta risposta
64
65. Cattivi utilizzi del forking: fork bomb
• Una fork bomb è un particolare tipo di
processo che non fa altro che riprodurre
processi figli, I quali a loro volta si riproducono
• Meccanismo rozzo ma efficace di negazione
dei servizi (Denial of Service, DoS)
– Può essere accidentale
– Di solito, è intenzionale (malizioso)
• Una fork bomb fa esplicite assunzioni sul
comportamento del SO:
– Tabella dei processi di dimensioni limitate
(viene saturata, non si può più eseguire niente)
– Ciascun processo richiede risorse di calcolo (il
sistema rallenta considerevolmente)
65
67. Scheletro di una fork bomb
• Il tipico scheletro di una fork bomb è un ciclo
while che, per sempre, effettua una fork()
• Per peggiorare le cose, si può anche allocare
della memoria
– Memory leak: alloco memoria senza liberarla
– Porta molto rapidamente il sistema in swap
– Nei sistemi con Copy On Write, occorre scrivere
nella memoria per creare le pagine copia
while ( 1 ) {
x=malloc(1048576); /* 1MB */
*x = 0; /* forza il Copy On Write */
fork();
}
67
68. Prevenzione delle fork bomb
• Soluzioni comuni:
– Il SO imposta un limite al numero di processi
generati da un utente (ulimit)
– Uccisione di tutti i processi (tranne kill ed init)
kill -9 -1
– Modifiche al SO per impedire la diffusione del
bombing
68
69. vfork()
• Variante veloce della fork(), pensata per I
processi figli che caricano immediatamente
altre immagini
– Scenario ideale: shell (processo padre che
forka processi figli eseguenti comandi)
• Il kernel non copia la tabella di memoria del
padre nelle tabelle di memoria del figlio, ma le
fa condividere finché il processo figlio:
– non carica una immagine (exec())
– non esce (_exit())
• Nel frattempo, il processo padre si blocca
• Non esiste semantica Copy-On-Write: il figlio
può cambiare i valori delle variabili del padre
69
70. Gestione dei Process IDentifier
• Signature: pid_t getpid(void)
– Descrizione: ritorna il PID del processo
chiamante
– Argomenti: nessuno
– Valore di ritorno: il PID del processo chiamante
• Signature: pid_t getppid(void)
– Descrizione: ritorna il PID del processo padre di
quello chiamante
– Argomenti: nessuno
– Valore di ritorno: il PID del processo padre del
processo chiamante
70
71. Identificazione di processi
• Come si possono trovare I PID dei processi in
maniera estremamente rapida?
• Comando pgrep
– pgrep “stringa”
– Restituisce una lista di PID associati a processi
il cui nome contiene “stringa”
– pgrep init -> 1
71
72. Terminazione di un processo
• La syscall exit() viene utilizzata per terminare il
programma con un codice di uscita
• Alcune risorse allocate dal processo vengono
liberate dal SO
– Gli stream aperti vengono svuotati (flush)
– I file temporanei vengono rimossi
– Viene chiamata la funzione di chiusura _exit()
• Se un processo genitore termina, tutti i suoi
figli si chiamano orfani
– Gli orfani diventano immediatamente figli del
processo init (che ha PID=1)
72
73. Terminazione di un processo
• Variante: _exit()
• Il processo esce con un codice di uscita
• Non svuota gli stream aperti, non cancella i file
temporanei
• Chiude i descrittori di file aperti
• Quando si usa la _exit()?
– Processo figlio creato con vfork()
– Processo figlio che non ha caricato una
immagine con exec()
• Si impedisce che il figlio possa modificare le
risorse usate dal padre
73
74. Terminazione di un processo
• Signature: void exit(int status)
– Descrizione: termina l'esecuzione del processo
e fornisce un codice di uscita al processo
invocante
– Argomenti: il codice di uscita
♦ EXIT_SUCCESS (0): tutto OK
♦ EXIT_FAILURE (1): errore
– Valore di ritorno: nessuno
• Si possono usare anche altri codici di uscita >
0 per discriminare diverse cause di errore
74
75. Segnali
• Meccanismo di notifica asincrona di eventi ad
un processo
– Asincrona: non legata ad eventi interni al kernel
• Meccanismo primitivo di Inter Process
Communication (IPC)
• Un segnale è una interruzione software
(eccezione), inviata ad un processo
– Inviata tramite tastiera (CTRL-C da terminale)
– Inviata tramite il comando kill
– Inviata dal kernel (SIGSEGV)
75
76. Ciclo di vita di un segnale
• I segnali UNIX hanno un ciclo di vita preciso
• Step 1: un processo (o il kernel) genera un
segnale (raise, send, generate)
• Step 2: il kernel conserva il segnale fino a
quando non è in grado di consegnarlo
• Step 3: il kernel consegna il segnale al
processo destinatario
• Step 4: il kernel esegue una azione legata al
segnale
76
77. Azioni legate ad un segnale
• Quali sono le possibili azioni?
• Ignorare il segnale (ignore):
– Non viene intrapresa alcuna azione
– Due segnali (KILL, STOP) non possono essere
ignorati
• Gestire il segnale (catch and handle):
– Sospensione dell'esecuzione del processo
– Salto ad una funzione (handler) definita nel
codice del programma ed assegnata al segnale
– Ripristino dell'esecuzione del processo
• Eseguire una azione di default (default action):
– Il kernel esegue una azione preconfigurata (ad
es., uccisione del processo) 77
78. Identificatori di segnale
• I segnali sono identificati in maniera univoca
da numeri interi positivi, a partire dal numero 1
• A ciascun numero è associato una costante
simbolica avente prefisso SIG
• La definizione dell'intero insieme dei segnali è
definita nel file include <bits/signum.h>
• Per vedere la lista, si può utilizzare il comando
di gestione dei segnali kill:
kill -l
78
79. Alcuni segnali comuni
• SIGKILL (9):
– Termina istantaneamente il processo, senza
possibilità di effettuare operazioni di cleanup
– Non può essere gestito con un handler
• SIGTERM (15):
– termina istantaneamente il processo, con
possibilità di registrare un handler per
effettuare operazioni di cleanup
• SIGINT (2):
– Segnale di interruzione, inviato al processo
collegato ad un terminale, in seguito a CTRL-C
– Termina il processo
79
80. Alcuni segnali comuni
• SIGSEGV (11):
– Il kernel termina istantaneamente il processo, in
seguito ad un accesso invalido alla memoria
– Non può essere gestito con un handler
• SIGCHLD (15):
– Segnale inviato al processo padre quando un
processo figlio termina l'esecuzione
• SIGUSR1 (10), SIGUSR2 (12):
– Segnali configurabili tramite handler opportuni
– Azione di default: terminano il processo
• E tanti, tanti altri...
80
81. Invio di segnali: il comando kill
• Il comando kill permette di inviare segnali ai
processi
– kill -”id segnale” “pid processo
– kill -9 8732
– Occorre avere I privilegi opportuni!
• Variante pkill: identifica in maniera simbolica i
segnali ed i processi
– pkill -KILL program
• E da programma?
– Si utilizza la syscall kill()
81
82. Syscall kill()
• Signature: int kill(pid_t pid, int sig)
– Descrizione: invia il segnale identificato da sig
al processo identificato da pid
– Argomenti:
♦ IlPID del processo destinatario del segnale
♦ L'identificatore del segnale da inviare
– Valore di ritorno:
♦0 -> tutto OK
♦ -1: si è verificato un errore
82
83. Ricezione di segnali
• Si utilizza la syscall signal(), che permette di
associare ad un segnale una ben specifica
funzione handler
• Che prototipo deve avere la funzione handler?
– void sig_handler(int signo)
• Il file include <signal.h> definisce il tipo di dato
sighandler_t come puntatore ad una funzione
che vuole un intero e non ritorna niente (void)
– typedef void (*sighandler_t) (int);
83
84. Syscall signal()
• Signature: sighandler_t signal(int signo,
sighandler_t handler)
– Descrizione: sostituisce l'azione corrente del
segnale con l'azione definita dalla funzione
handler
– Argomenti:
♦ L'identificatore
del segnale da gestire
♦ Un puntatore alla funzione handler
– SIG_DFL: azione di default
– SIG_IGN: ignorare il segnale
– Puntatore ad una funzione effettiva
– Valore di ritorno:
♦ Ilpuntatore alla precedente funzione handler
♦ SIG_ERR in caso di errore 84
85. Handler di segnali: alcune note
• Quando il kernel consegna un segnale, il
processo può eseguire una qualunque
porzione di codice
– L'handler non può sapere quale
• E' importante che l'handler non esegua codice
“troppo complicato”
– Tipicamente, imposta dei flag “volatile”, libera
memoria, esce, non fa molto altro
• L'handler deve modificare variabili che il
processo non tocca MAI
• I segnali possono essere rientranti
– le syscall utilizzate all'interno dell'handler pure
– man 7 signal 85
86. Sincronizzazione processo padre-figlio
• Il processo padre può decidere di aspettare il
termine dell'esecuzione del processo figlio, per
mezzo della syscall wait()
• La syscall wait rimuove il descrittore del
processo padre dalla coda di pronto fino a che
il processo figlio non termina l'esecuzione
• Quando il processo figlio termina l'esecuzione,
ritorna un codice di uscita (exit status) al SO
– =0: tutto OK (EXIT_SUCCESS)
– =1: errore (EXIT_FAILURE)
• Il SO consegna il codice di uscita alla syscall
wait
86
87. Scenario wait() tempestiva
• Il processo padre crea Tabella
padre processi
un figlio con la fork() PID
fork()
Running
• Entrambi i processi PID
eseguono Running
figlio
concorrentemente
87
88. Scenario wait() tempestiva
• Il processo padre Tabella
wait() processi
invoca la wait() prima
padre
la morte del figlio, Blocked
tempestivamente Running
figlio
• Il processo padre
viene bloccato in
attesa che il figlio
termini la sua
esecuzione
88
89. Scenario wait() tempestiva
• Ad un certo punto, il Tabella
padre processi
processo figlio
termina la sua Running
X
esecuzione Zombie
figlio
• Il kernel imposta lo
stato del figlio a
Zombie
– Processo figlio morto a
tutti gli effetti
– Il PCB è ancora
presente nella tabella
dei processi
– PCB usato per fornire
al padre il valore di
uscita 89
90. Scenario wait() tempestiva
• Il kernel imposta a Tabella
processi
pronto il processo wait()
padre
padre, dal momento Running
PID,
che l'evento atteso si Running
codice
è verificato uscita
• Il kernel invia un
segnale SIGCHLD al
padre (di solito
ignorato)
• Il processo padre
ritorna ad eseguire
dopo la wait()
• Viene letto il codice di
uscita del figlio 90
91. Scenario wait() tardiva
• Il processo padre crea Tabella
padre processi
un figlio con la fork() PID
fork()
Running
• Entrambi i processi PID
eseguono Running
figlio
concorrentemente
91
92. Scenario wait() tardiva
• Ad un certo punto, il Tabella
padre processi
processo figlio
termina la sua Running
X
esecuzione Zombie
figlio
• Il kernel imposta lo
stato del figlio a
Zombie
– Processo figlio morto a
tutti gli effetti
– Il PCB è ancora
presente nella tabella
dei processi
– PCB usato per fornire
al padre il valore di
uscita 92
93. Scenario wait() tardiva
• Il processo padre Tabella
wait() processi
invoca la wait() dopo
padre
la morte del figlio, Running
PID,
X
tardivamente Zombie
codice
uscita
• Il kernel invia un
segnale SIGCHLD al
padre (di solito
ignorato)
• Viene usato il PCB del
figlio per comunicare
lo stato di uscita al
padre
• Viene eliminato il PCB
del figlio 93
94. Scenario wait() mai invocata
• Il processo padre crea Tabella
padre processi
un figlio con la fork() PID
fork()
Running
• Entrambi i processi PID
eseguono Running
figlio
concorrentemente
94
95. Scenario wait() mai invocata
• Ad un certo punto, il Tabella
padre processi
processo figlio
termina la sua Running
X
esecuzione Zombie
figlio
• Il kernel invia un
segnale SIGCHLD al
• Stato Zombie
padre (di solito
– Processo figlio morto a
ignorato)
tutti gli effetti
• Il kernel imposta lo – Il PCB è ancora
stato del figlio a presente nella tabella
Zombie dei processi
– PCB usato per fornire
al padre il valore di
uscita
95
96. Scenario wait() mai invocata
• Finché il padre Tabella
X
processi
esegue, esisterà
padre
X
sempre un PCB del Exiting
X
figlio in stato Zombie Zombie
• Quando il padre
termina l'esecuzione,
vengono ripuliti
entrambi i PCB
96
97. System call wait()
• Signature: pid_t wait(int *status)
– Descrizione: sospende l'esecuzione del
processo chiamante fino a quando uno dei suoi
processi figli termina
– Ingresso: puntatore ad un intero utilizzato per
memorizzare lo “stato” del processo
– Ritorno:
♦ Il PID del processo figlio terminato
♦ -1 in caso di errore
97
98. System call wait()
• Lo stato del processo può essere letto tramite
delle macro opportune
– WIFEXITED(status): restituisce 1 se il figlio è
terminato normalmente (exit()), 0 altrimenti
– WEXITSTATUS(status): restituisce il codice di
uscita del processo figlio (gli 8 bit meno significativi
di status: status & 255)
– WIFSIGNALED(status): restituisce 1 se il figlio è
stato terminato tramite un segnale, 0 altrimenti
– WIFSTOPPED(status): restituisce 1 se il figlio è
stato interrotto (SIGSTOP), 0 altrimenti
– WIFCONTINUED(status): restituisce 1 se il figlio è
stato ripristinato (SIGCONT), 0 altrimenti
– ... e tante altre (man wait)
98
99. System call waitpid()
• Signature: pid_t waitpid(pid_t pid, int *status,
int options)
– Descrizione: sospende l'esecuzione del
processo chiamante fino a quando uno dei suoi
processi figli cambia stato
– Ingresso:
♦ ilPID del processo (o dell'insieme di processi) da
aspettare
♦ puntatore ad un intero utilizzato per memorizzare
lo “stato” del processo
♦ maschera di bit contenente le opzioni di uso
– Ritorno:
♦ Il PID del processo figlio terminato
♦ -1 in caso di errore (errno=ECHILD) 99
100. System call waitpid()
• Il primo argomento pid può assumere i
seguenti valori:
– -1: aspetta un qualunque processo figlio
– 0: aspetta un qualunque processo figlio con
group ID pari a quello del processo chiamante
– >0: aspetta un qualunque processo figlio con
PID = pid
• Le opzioni sono le seguenti:
– WNOHANG: ritorna immediatamente se nessun
figlio è uscito (non aspetta)
♦ torna 0 se il padre ha figli, -1 altrimenti
– WUNTRACED: considera come cambio di stato
anche l'interruzione (SIGSTOP)
100
101. Caricamento di immagini
• UNIX mette a disposizione una famiglia di
funzioni (exec()) per sostituire l'immagine di un
processo padre con quella di un eseguibile
• La sostituzione è efficiente
– Non si copia l'intero contenuto della memoria
secondaria in memoria
– Si associano blocchi di memoria principale a
blocchi di memoria secondaria (memory
mapping)
– Si carica il blocco in memoria centrale solo
quando viene utilizzato dal processore
101
102. Caricamento di immagini
Spazio indirizzamento Memoria secondaria
processo (RAM) (periferica)
Pagina 1
Pagina 2
...
Pagina n Blocco
1 Blocco
2
Blocco
n
102
103. Caricamento di immagini
Spazio indirizzamento Memoria secondaria
processo (RAM) (periferica)
Blocco 1
Pagina 2
...
Pagina n Blocco
1 Blocco
2
Blocco
Sostituzione di codice: n
le pagine vengono
CPU sostituite quando richieste
dal processore.
103
104. Syscall execl
• Signature: int execl(const char *path, const
char *arg, ...)
– Descrizione: carica l'immagine eseguibile del
programma di nome path, passando i parametri
– Argomenti:
♦ Un puntatore alla stringa contenente il
percorso assoluto dell'eseguibile
♦ Una lista di stringhe (terminata con NULL),
che saranno assegnate ad argv[0], argv[1], ...
– Valore di ritorno:
♦ -1 in caso di errore
• execl() è una funzione variadica (ammette un
numero variabile di argomenti)
104
105. Famiglia funzioni exec()
• int execlp(const char *file, const char *arg, ...);
• int execle(const char *file, const char *arg, ...,
char * const envp[]);
• int execv(const char *file, const char *argv[]);
• int execvp(const char *file, const char *argv[]);
• int execle(const char *file, const char *arg[],
char * const envp[]);
105
106. Esecuzione semplificata
• Esiste una procedura estremamente
semplificata per l'esecuzione di processi
• Se si vuole eseguire un comando secco
(tipicamente, un comando di shell), senza
necessità di generare più figli, si può usare la
chiamata system()
• Cosa fa system()?
• Padre:
– fork() di un processo figlio
– wait() per aspettare l'uscita del figlio
• Figlio:
– execv() di una immagine
106
107. Funzione system()
• Signature: int system(const char *command)
– Descrizione: crea un processo figlio ed esegue
il comando “/bin/sh -c comando”, dove
comando è la stringa puntata da command
– Argomenti:
♦ Un puntatore alla stringa contenente il
comando
♦ Valore di ritorno:
♦ Il codice di stato della wait()
♦ -1 in caso di errore
• Esercizio: implementare la system() usando le
chiamate fork(), execv(), waitpid()
107
108. Una piccola avvertenza
• Durante l'esecuzione del comando tramite
system, nel processo figlio
– il segnale SIGCHLD è bloccato (sigprocmask)
– i segnali SIGINT e SIGQUIT sono ignorati
• Se il programma è lanciato da terminale, un
CTRL-C viene propagato a padre e figlio
– Ma non sempre il programma è lanciato da
terminale...
• Se la funzione system() è invocata all'interno di
un ciclo, bisogna esplicitamente controllare lo
stato di uscita del figlio
108
109. Utenti e gruppi: modello di protezione
• Il modello UNIX prevede l'attribuzione di
processi e file ad utenti e gruppi di lavoro
• Utente:
– Astrazione di una singola persona
– Può lanciare processi e gestire file
– Identificato da uno User Id (UID), numero intero
non negativo
– E' associato ad uno username e ad una
password per la verifica delle credenziali
– Database utenti memorizzato in /etc/passwd
– Un utente (superutente, root) ha pieni poteri di
accesso alle risorse del sistema (processi, file)
109
110. Utenti e gruppi: modello di protezione
• Il modello UNIX prevede l'attribuzione di
processi e file ad utenti e gruppi di lavoro
• Gruppo (di lavoro):
– Astrazione di un gruppo di persone che
lavorano per uno stesso obiettivo
– Ciascun utente può far parte di più gruppi, ma
ne usa uno alla volta (gruppo primario)
– Possono condividere accessi a file
– Identificato da un Group Id (GID), numero intero
non negativo
– E' associato ad un groupname e ad una
password per la verifica delle credenziali
– Database utenti memorizzato in /etc/group
110
111. Utenti e gruppi: permessi dei file
• Come impatta il modello di protezione sui file?
• Semplice schema a permessi
• Tre distinte categorie di utenti:
– User: UID dell'utente creatore di un file
– Group: GID primario dell'utente al momento
della creazione del file
– Others: il resto del mondo
• Tre distinte categorie di azioni su file:
– Read: è possibile leggere da file
– Write: è possibile scrivere su file
– eXecute: è possibile eseguire il file
111
112. Utenti e gruppi: permessi dei file
• A ciascun file è associata una maschera di bit
contenente le azioni permesse (r,w,x) per
ciascuna tipologia di utenti (u,g,o)
• La maschera di bit memorizza in maniera
compatta chi può accedere come al file
• Diversi tipi di rappresentazione:
– Stringa: rwxrwxr-x
– Ottale: 775
– Azione: ugo+rwx, o-w
112
113. Utenti e gruppi: diritti dei processi
• Come impatta il modello di protezione sui
processi in esecuzione sulla macchina?
• In un sistema UNIX, i diritti di un processo
coincidono molto spesso con i privilegi di
accesso ai file
– Filosofia UNIX: tutto ciò che si esegue è un
processo, tutto ciò che si rappresenta è un file
• Gli UID e GID di un processo sono quelli
utilizzati per il confronto con la maschera di bit
dei file
• Ma come fa un processo ad essere associato
ad un UID e ad un GID?
113
114. Utenti e gruppi: diritti dei processi
• Le cose, ovviamente, non sono così semplici
• Un processo UNIX si porta appresso quattro (!)
UID e quattro GID diversi per regolare i diritti di
acceso ai file da parte di un processo
– Filesystem User ID
– Real User ID
– Effective User ID
– Saved User ID
• La descrizione seguente vale non solo per gli
UID, ma anche per i GID!
114
115. Utenti e gruppi: Filesystem User ID
• Il Filesystem User ID (UID) è lo UID associato al
file su disco
• Dichiara solamente chi ha scritto
originariamente il file su disco, nient'altro!
– beh, dichiara anche I permessi di accesso...
– ... ma nient'altro che riguardi l'esecuzione dei
processi!
115
116. Utenti e gruppi: Real User ID
• Il Real User ID (UID) è lo UID dell'utente che ha
eseguito il processo
• Il Real User ID può essere (e solitamente è)
diverso dal Filesystem User ID
– Filesystem: chi ha scritto il file su disco
– Real: chi lo sta eseguendo ora
• Il Real User ID di un processo viene impostato
a quello del suo padre
– Shell: imposta il Real User ID a quello
dell'utente che la sta eseguendo
– Tutti i comandi (figli) eseguono con quell'UID
• Il superutente può impostare il Real User ID a
qualunque valore, gli utenti normali no 116
117. Utenti e gruppi: Effective User ID
• L'Effective User ID (EUID) è lo UID effettivo con
cui il processo sta eseguendo
• Tutti i controlli di accesso (a file) avvengono
tramite l'EUID
• L'EUID viene ereditato dal processo padre
• Un momento: ma non c'era già il Real User ID?
• Sì, ed in effetti, solitamente, Real User ID ed
Effective User ID coincidono
• E allora a che serve differenziare in Real User
ID ed Effective User ID?
– A fare cose paranormali con i poteri di un
utente normale
117
118. Utenti e gruppi: Effective User ID
• Ciascun file ha, in realtà, svariati altri bit nella
bitmask dei permessi
• Tali bit sono usati per specificare particolari
proprietà di un file o di una directory
• Una proprietà particolare è il Set User ID (SUID)
bit, impostabile alla stregua di un qualunque
permesso
– chmod +s nome_file, chmod 4xyz nome_file
• Quando viene avviato un eseguibile con SUID,
il kernel imposta l'EUID del file al filesystem
UID
– Esecuzione con i diritti del creatore, non
dell'utente! E se il creatore è root...
118
119. Utenti e gruppi: Effective User ID
• Gli utenti normali possono impostare il valore
di EUID al Real UID oppure al Saved UID
(introdotto a breve)
• Il superutente può impostare l'EUID al valore
che desidera
• Come trovare gli eseguibili SUID?
– find / -perm -4000 2>/dev/null
• Esempio: passwd
119
120. Utenti e gruppi: Saved User ID
• Il Saved User ID è una copia fedele
dell'Effective User ID di un processo, effettuata
nell'istante in cui questo effettua una exec()
• Utilizzato per tenere traccia del vecchio
Effective User ID prima di eseguire una
immagine (potenzialmente SUID)
• Il Saved User ID è inizialmente ereditato dal
processo padre
• Utenti non privilegiati non possono modificare
il Saved User ID
• Il superutente può impostare il Saved User ID
ad un qualunque valore
120
121. Utenti e gruppi: setuid()
• Signature: int setuid(uid_t uid)
Signature: int setgid(gid_t gid)
– Descrizione:
♦ Se EUID=0 (root), imposta Real, Effective e
Saved User Id a uid (qualunque)
♦ Se EUID>0 (non root), imposta Effective User
Id ad uid (uid=Real oppure uid=Saved)
– Argomenti:
♦ Il nuovo User ID (uid)
– Valore di ritorno:
♦ 0: tutto OK
♦ -1 in caso di errore
121
122. Utenti e gruppi: seteuid()
• Signature: int seteuid(uid_t euid)
Signature: int setegid(gid_t egid)
– Descrizione:
♦ Se EUID=0 (root), imposta l'Effective User Id a
euid (qualunque)
♦ Se EUID>0 (non root), imposta l'Effective
User Id ad uid (uid=Real oppure uid=Saved)
– Comportamento identico a setuid()
– Argomenti:
♦ Il nuovo Effective User ID (euid)
– Valore di ritorno:
♦ 0: tutto OK
♦ -1 in caso di errore
122
123. Utenti e gruppi: recupero UID
• Famiglia di funzioni get...id()
• Non danno mai un errore
• uid_t getuid(void);
• uid_t getgid(void);
• uid_t geteuid(void);
• uid_t getegid(void);
123
124. Una piccola applicazione
• Il SO Debian definisce il gruppo games come il
gruppo dei videogiocatori
– Tutti gli eseguibili dei videogiochi vengono
installati nella directory /usr/games
– Permessi: rwx r-x r-x (755)
– Utente: root
– Primary group:
♦ root (nel caso in cui non si scrivano file)
♦ games, con setgid (nel caso in cui si scrivano
file)
• In quest'ultimo caso, chiunque esegua un
videogioco prende il gruppo effettivo games
– L'user effettivo rimane sempre quello normale
124
125. Una piccola applicazione
• L'applicazione videogame scrive, come gruppo
games:
– Gli high score (platform, sparatutto)
– L'output delle partite (giochi da tavolo)
• L'applicazione vorrebbe scrivere gli altri file
col gruppo primario dell'utente che ha lanciato
l'eseguibile:
– Traccie di debug
• Come si ottiene questo?
125
126. Una piccola applicazione
• All'inizio del programma, vengono salvati i
Real ed Effective UID e GID:
ruid = getuid();
euid = geteuid();
rgid = getgid();
egid = getegid();
• Successivamente, si reimpostano UID e GID in
modo tale che l'utente possa scrivere file col
proprio gruppo primario:
setuid(ruid);
setgid(rgid);
• Qui, rgid è il GID dell'utente normale; ora sto
scrivendo I file come utente normale
126
127. Una piccola applicazione
• Prima di scrivere gli high score, imposto
setuid(euid);
setgid(egid);
• Ora, il GID è di nuovo quello effettivo (games):
write_high_scores(&players);
• Successivamente, reimposto UID e GID a quelli
“normali”
setuid(ruid);
setgid(rgid);
127
128. Job Control
• Task di una shell moderna:
– Lettura comando da terminale
– Creazione ed esecuzione dei relativi processi
• Spesso il comando immesso è complesso, e
consta di più “comandi”
(cd /src/dir && tar cf - . ) | ( cd /dst/dir && tar xf -)
• Servono meccanismi per operare su “gruppi”
di processi:
– Identificazione
– Interruzione, terminazione
– Sospensione, ripristino
• Modello di Job Control
128
129. Job Control
• Job Control: insieme di tutti i meccanismi
necessari per:
– identificare
– sospendere e riesumare
– eseguire attaccati ad un terminale
– eseguire staccati da un terminale
un insieme di processi
• Tale esigenza nasce dai vecchi terminali seriali
– Avevano un solo “schermo”
– Ci si poteva attaccare un solo comando per
volta (foreground)
– Gli altri comandi eseguivano staccati dal
terminale (background) 129
130. Job Control
• Job Control: insieme di tutti i meccanismi
necessari per:
– identificare
– sospendere e riesumare
– eseguire attaccati ad un terminale
– eseguire staccati da un terminale
un insieme di processi
• Tale esigenza nasce dai vecchi terminali seriali
– Avevano un solo “schermo”
– Ci si poteva attaccare un solo comando per
volta (foreground)
– Gli altri comandi eseguivano staccati dal
terminale (background) 130
131. Gruppi di processi
• I processi sono suddivisi in gruppi di processi
(process group)
• Un gruppo di processi è un insieme di processi
che possono essere segnalati tutti insieme
– Segnali: Terminazione, interruzione, ripristino
– Processi: i componenti di una pipeline
• Ciascun processo fa parte di un process group
• Ciascun process group:
– ha un processo detto “process group leader”; è
l'ultimo processo in una pipeline
– è identificato da un process group ID (PGID),
pari al PID del process group leader
– esiste fino a quando ha almeno un elemento 131
132. Sessioni
• Quando un utente effettua un login, il processo
login crea una nuova sessione
• Sessione: un insieme di process group
• All'inizio ho una solo process group di un solo
processo (la shell di login)
• Tale processo prende il nome di session leader
• Il PID del processo session leader è usato
come session ID (identifica la sessione)
• Ciascuna sessione ha associato un dispositivo
terminale (fisico o virtuale) di controllo, che
gestisce le operazioni di I/O da e verso la
sessione
132
133. Foreground e Background PG
• I process group all'interno di una sessione
sono suddivisi in due categorie:
– Un foreground process group
– Uno o più background process group
• Il foreground process group è l'unico gruppo
di processi ad essere attaccato direttamente al
terminale sia in ingresso che in uscita
– Si può interagire con i processi in esecuzione
tramite la tastiera
• I background process group sono attaccati al
terminale solamente in uscita
– Possono solo scrivere sul terminale, non
possono ricevere dati da terminale
133
134. Terminali
• Diversi tipi di terminale:
– Veri terminali fisici (VT100, VT102) attaccati via
connessioni seriali (/dev/ttyS0)
– Emulatore di terminale classico in testo, fornito
dal kernel (/dev/tty1) e prenotato da getty
♦ Alt-F1 + getty->login->bash
– Pseudo terminale in cui il kernel fornisce un
minimo supporto (/dev/pts/0) ed un processo
arbitrario si sostituisce all'emulatore di
terminale classico
♦ ssh -t, xterm, gnome-terminal, kterm
♦ Automazione domanda-risposta con uno
(pseudo) terminale: expect
– Chatscript di login del modem 134
135. Interazioni con il terminale
• In che modi si può interagire con il terminale,
tramite la tastiera?
– Terminazione dell'emulatore di terminale: viene
inviato un segnale SIGQUIT a tutti i processi
facenti parte del foreground process group
– Disconnessione rete/seriale terminale fisico:
viene inviato un segnale SIGHUP (Hangup) a
tutti i processi facenti parte del foreground
process group
♦ Window manager testuale screen: intercetta
il segnale e conserva lo stato delle finestre
– Interruzione pipeline (CTRL-C): viene inviato un
segnale SIGINT a tutti i processi facenti parte
del foreground process group
135
136. Interazioni con il terminale
• In che modi si può interagire con il terminale,
tramite la tastiera?
– Sospensione pipeline (CTRL-Z): viene inviato
un segnale SIGTSTOP a tutti i processi facenti
parte del foreground process group
– Ripristino pipeline in foreground (%, fg): viene
inviato un segnale SIGCONT a tutti i processi
facenti parte del foreground process group
sospeso
136
137. p
Un modello a sessioni e gruppi
login
Session
Session Terminale
leader Login shell di controllo
stdout
stderr
BG PG PG FG PG PG
BG PG
leader leader
stdin stdout
PG stderr
leader
stdout
stderr 137
138. Gestione dei gruppi e delle sessioni
• Esecuzione di un process group in foreground
– Si lancia una pipeline normalmente
ls -lR / | grep passwd | less -Mr
• Esecuzione di un process group in
background
– Si termina una pipeline col simbolo &
ls -lR / | grep passwd > output.txt 2>&1 &
– Viene stampata una riga del tipo
[1] 25647
– 1: ID del job inviato
– 25647: process group ID (PiD del processo
leader del gruppo, ossia l'ultimo della pipeline,
grep)
138
139. Gestione dei gruppi e delle sessioni
• Visione dello stato dei process group di una
sessione: comando jobs
– jobs -l: elenco completo dei process group, con
process group ID relativi e stato di esecuzione
– jobs -p: elenco dei process group ID
• Sospensione del process group in foreground:
– CTRL-Z
• Ripristino in background di un process group
sospeso:
– bg %job_id (bg in per un process group unico)
• Ripristino in foreground di un process group
sospeso:
– fg %job_id (fg o % per un process group unico) 139
140. Creazione di una sessione
• Signature: pid_t setsid(void)
– Descrizione:
♦ Crea una nuova sessione
♦ Crea un nuovo process group all'interno
della sessione
♦ Imposta il processo chiamante a session e a
group leader
♦ Utilizzato dalle shell e dai demoni
– Argomenti: nessuno
– Valore di ritorno:
♦ Il session ID della sessione, se tutto OK
♦ -1 in caso di errore
• Comando setsid 140
141. Ottenimento session ID
• Signature: pid_t getsid(pid_t pid)
– Descrizione: ritorna il session ID della sessione
in cui si trova il processo identificato da pid
– Argomenti:
♦ Il pid del processo (0: processo chiamante)
– Valore di ritorno:
♦ Il session ID della sessione, se tutto OK
♦ -1 in caso di errore
141
142. Impostazione di un process group ID
• Signature: int setpgid(pid_t pid, pid_t pgid)
– Descrizione: imposta il process group ID del
processo identificato da pid a pgid
♦ Pid deve riferirsi al processo invocante o ad un
suo figlio che non ha invocato exec()
♦ Pid deve essere nella stessa sessione del
processo invocante
♦ Pid non deve riferirsi ad un session leader
– Argomenti:
♦ Il pid del processo considerato
♦ Il pgid considerato
– Valore di ritorno:
♦ 0: tutto OK
♦ -1 in caso di errore 142
143. Ottenimento session ID
• Signature: pid_t getpgid(pid_t pid)
– Descrizione: ritorna il process group ID del
processo identificato da pid
– Argomenti:
♦ Il pid del processo (0: processo chiamante)
– Valore di ritorno:
♦ Il processo group ID della sessione, se tutto
OK
♦ -1 in caso di errore
143
144. Demoni UNIX
• Il demone (daemon) è un processo che:
– esegue come figlio diretto di init
– non è connesso ad alcun terminale
– esegue in background
• Etimologia: Maxwell's demon (1867)
http://en.wikipedia.org/wiki/Maxwell%27s_demon
• Scopi:
– Gestione task di sistema
– Processo server: ascolta richieste, genera figli
che producono risposte
• Solitamente, un demone è avviato da root o da
un utente dedicato (apache)
144
145. Passi di creazione di un demone
• Step 1: il processo invoca una fork()
– Viene creato un nuovo processo figlio
– Tale processo diventerà il demone
• Step 2: nel padre, si invoca la exit()
– Il padre esce e rende orfano il figlio
– Il figlio diventa figlio di init
– Il figlio non è un process group leader
• Step 3: il processo figlio invoca setsid()
– Il figlio diventa un session leader (non essendo
un process group leader, può diventarlo)
– Viene anche creato un process group di cui il
processo figlio è leader
– Il figlio non ha un terminale di controllo 145
146. Passi di creazione di un demone
• Step 4: il figlio invoca chdir(“/”)
– Viene impostata la directory di lavoro a /
– Altrimenti la directory ereditata dal padre
potrebbe essere dovunque (dipende da dove è
stato invocato il processo padre)
– Tale directory non potrebbe essere più
smontata, in quanto in uso (reference!) da parte
del demone
– Il demone tende a durare per tutto il ciclo di vita
della macchina
– O ammazzo il demone, o non smonto il
filesystem contenente la directory da cui è stato
invocato il demone
146
147. Passi di creazione di un demone
• Step 5: il figlio imposta la maschera dei
permessi a 0 (opzionale)
• Step 6: il figlio chiude tutti i descrittori di file
– Non servono
– stdin, stdout, stderr non servono perché il
demone non è connesso ad alcun terminale
– Gli altri descrittori non servono perché il
demone, solitamente, non fa I/O, bensì crea
processi figli per la gestione di richieste
• Step 7: i descrittori stdin, stdout, stderr
vengono redirezionati a /dev/null (o a qualche
logfile)
147