Your SlideShare is downloading. ×
Sistemi Operativi: Processi - Lezione 07
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Saving this for later?

Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime - even offline.

Text the download link to your phone

Standard text messaging rates apply

Sistemi Operativi: Processi - Lezione 07

1,788
views

Published on

Published in: Technology

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,788
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
50
Comments
0
Likes
1
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 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
  • 4. Esecuzione interleaved Memoria principale Gestore processi Program Counter Processo A Processo B Processo C 4
  • 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
  • 60. Albero di processi UNIX kernel page daemon swapper init utente 1 utente 2 utente 3 60
  • 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
  • 66. Cattivi utilizzi del forking: fork bomb bomb ... bomb bomb ... bomb bomb ... ... bomb bomb bomb bomb ... ... ... ... 66
  • 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