Sistemi Operativi: Processi - Lezione 07

2,179 views

Published on

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,179
On SlideShare
0
From Embeds
0
Number of Embeds
10
Actions
Shares
0
Downloads
58
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Sistemi Operativi: Processi - Lezione 07

  1. 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. 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. 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. 4. Esecuzione interleaved Memoria principale Gestore processi Program Counter Processo A Processo B Processo C 4
  5. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 39. Scheduler • Componente del SO che seleziona i processi dalle code • Due tipi di scheduling: – Job scheduling – CPU scheduling 39
  40. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 60. Albero di processi UNIX kernel page daemon swapper init utente 1 utente 2 utente 3 60
  61. 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. 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. 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. 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. 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. 66. Cattivi utilizzi del forking: fork bomb bomb ... bomb bomb ... bomb bomb ... ... bomb bomb bomb bomb ... ... ... ... 66
  67. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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

×