Scaricare la presentazione
La presentazione è in caricamento. Aspetta per favore
PubblicatoLuigino Rossetti Modificato 11 anni fa
1
Gestione dei processi Un sistema operativo multitasking è in grado di gestire più processi Un processo corrisponde ad un programma in esecuzione. Un programma tipicamente alterna sequenze di istruzioni CPU ad operazioni di I/O Durante l’esecuzione di una operazione di I/O il programma non utilizza la CPU, che pertanto può essere assegnata dal sistema operativo ad un alto processo
2
Il contesto del processo
Rappresenta tutte le informazioni che descrivono la “fotografia” corrente del programma in esecuzione Alcune di queste informazioni sono: Il contenuto della memoria assegnata al processo Il contenuto attuale dei registri della CPU, incluso il Program Counter, che determina lo stato di avanzamento del programma Informazioni accessorie utilizzate dal sistema operativo per la gestione del processo, quali la priorità corrente Tali informazioni, escluso il contenuto della memoria sono memorizzate dal sistema operativo nel Process Control Block associato .
3
Gli stati di un processo
Definiscono se un processo è in esecuzione, o on attesa. Gli stati sono: New: processo creato, non ancora entrato in esecuzione Running: il processo sta utilizzando la CPU Waiting: il processo è in attesa della terminazione di un’operazione di I/O Ready: il processo potrebbe utilizzare la CPU, ma la CPU è assegnata ad un altro processo Halted: il processo ha finito la sua esecuzione o è stato stoppato dal sistema operativo (es. in conseguenza alla generazione di un’eccezione)
4
La sequenza degli stati
5
L’operazione di Context Switch
6
La schedulazione (scheduling) dei processi
Il sistema operativo ha generalmente più di un processo candidato per l’utilizzo della CPU (Ready) Tra le caratteristiche da tener conto nella determinazione di un efficiente algoritmo di scheduling sono le seguenti: Utilizzo della CPU: si cerca di massimizzare l’uso della CPU Throughput: massimizzazione dei processi eseguiti (terminati) per unità di tempo Tempo di risposta: minimizzazione del tempo di attesa in stato Ready per ogni processo
7
Prima implementazione: FIFO
L’utilizzo di una coda First-In-First out rappresenta l’implementazione più semplice, ma anche la meno efficiente. Si supponga, ad esempio, che nella coda siano tre processi P1, P2, P3, con il processo P1 inserito per primo nella coda. Si supponga inoltre che il processo P1 impiegherà la CPU per un tempo di 100ms, mentre gli altri due processi impiegheranno la CPU per un tempo di 1 ms. In questo caso il tempo medio di attesa risulterà pari a ( )/3 ms. Se invece venissero eseguiti prima P2 e P3, il tempo medio di attesa sarebbe, a parità di throughput: (1 + 2) /3 ms
8
Shortest Job First Con riferimento all’esempio precedente, si può intuire che l’organizzazione ideale assegnerebbe la CPU per prima al processo che la utilizzerà per il tempo minore. Tale approccio non è tuttavia realizzabile in pratica. E’ possibile dare una stima n+1 del tempo di esecuzione previsto dopo n passi a partire dalla stima precedentemente fatta (n) e dal tempo effettivamente impiegato al passo precedente tn: n+1 = tn +(1- )n
9
La priorità dei processi
In molte situazioni reali, esistono processi che hanno una “importanza” maggiore, e pertanto per tali processi risulta preferibile una maggiore prontezza del sistema operativo nell’assegnazione della CPU, a scapito di altri processi meno importanti Ciò si può ottenere assegnando una priorità ai processi. Il sistema operativo assegnerà la CPU al processo Ready di priorità più alta.
10
Priorità fisse e variabili
L’utilizzo di priorità fisse assegnante ai processi è adeguato per alcune classi di utilizzi, tra cui i sistemi real-time. In altri utilizzi, quali gestione multi-utente, tale politica non è ottimale in quanto rischia di creare lunghi tempi di attesa per alcuni processi. In un sistema time sharing si vuole invece che la CPU venga assegnata ai vari utenti in modo da minimizzare il tempo di risposta. Ciò può essere realizzato tramite l’utilizzo di priorità variabili: la priorità di processi che utilizzano pesantemente la CPU viene abbassata, mentre la priorità di processi che eseguono prevalentemente I/O viene elevata
11
La gestione della priorità variabile.
Il meccanismo di cambio della priorità avviene interrompendo periodicamente il processore, e verificando il processo che sta correntemente utilizzando la CPU. Ad ogni interruzione (tick) la priorità del processo corrente può essere decrementata, mentre la priorità degli altri processi può essere incrementata. In questa maniera si “premiano” i processi che eseguono prevalentemente I/O, con l’assunzione che tali processi utilizzeranno la CPU per un tempo breve prima di tornare nello stato wait
12
La gestione della priorità variabile (cont.)
Conseguentemente, lo scheduler gestirà più code FIFO di processi, una per ogni priorità. La CPU verrà assegnata al primo processo della coda corrispondente alla priorità massima Ad ogni tick, i processi possono cambiare coda, come conseguenza del loro “comportamento” Tale gestione pertanto fa si’ che il processo selezionato per l’utilizzo della CPU la impegnerà probabilmente per un tempo non più lungo rispetto agli altri processi che in quel momento sono ready.
13
Programmazione concorrente
La programmazione concorrente definisce due o più flussi di esecuzione tra loro indipendenti Le problematiche non cambiano anche considerando sistemi a processore singolo, ma sistema operativo multitasking La difficoltà nella dimostrazione della correttezza di tali algoritmi deriva dalla necessità di tenere in considerazione tutte le possibili combinazioni derivanti dalla esecuzione indipendente dei programmi. Gli errori dovuti alla mancata considerazione di particolari condizioni di esecuzione prendono il nome di “race conditions”, e sono in genere molto difficili da diagnosticare
14
Un esempio: l’implementazione di una sezione critica
Prende il nome di sezione critica un segmento di codice che viene eseguito da non più di un processo per volta L’uso delle sezioni critiche è necessario per una gestione opportuna delle risorse del sistema Molte risorse (ad esempio dispositivi di I/O) possono infatti essere utilizzate solo da un processo per volta Più in generale, quando più di un processo condivide delle strutture dati (ad esempio liste), ci si vuole assicurare che solo un processo per volta modifichi tali strutture per evitare situazioni di incosistenza Si consideri ad esempio l’utilizzo di una linked list condivisa: se un processo viene interrotto dal SO nel momento in cui sta modificando i riferimenti, altri processi vedrebbero la lista in uno stato inconsistente
15
Requisiti dell’algoritmo
L’algoritmo di mutua esclusione deve garantire i seguenti requisiti: Mutua esclusione: solo un processo per volta deve accedere alla sezione critica Progresso: se nessun processo sta accedendo alla sezione critica, l’assegnazione della sezione critica ad uno dei processi richiedenti deve avvenire in un tempo finito (non può essere postposta indefinitamente) Attesa finita(bounded waiting): deve esistere un limite finito al numero di volte in cui un processo in attesa per l’accesso alla sezione critica vede negato l’accesso a favore di un altro processo
16
Una prima implementazione della sezione critica
Si consideri il problema dell’implementazione della sezione critica per due processi, utilizzando variabili condivise tra i due processi. In questa implementazione la struttura condivisa è un array di due boolean: var flag: array[0..1] of boolean Il processo Pi esegue le seguenti istruzioni (j si riferisce all’indice dell’altro processo): while(flag[j]) {//Ripeti il test} flag[i] = true … //Sezione critica flag[i]= false
17
Analisi del primo algoritmo
Ad una prima analisi l’algoritmo sembrerebbe garantire la mutua esclusione: il processo Pi attende che il processo Pj sia uscito dalla sezione critica (ovvero che flag[j] diventi false) prima di entrare nella sezione critica. Una analisi più accurata rivela tuttavia che la mutua esclusione non viene garantita in questa sequenza (partendo da una condizione in cui nessun processo è nella sezione critica): 1) P0 entra nel ciclo while e trova flag[1] false 2) P1 entra nel ciclo while e trova flag[0] false 3) P1 pone flag[1] a true ed entra nella sezione critica 4) P0 pone flag[0] a true ed entra nella sezione critica
18
Secondo algoritmo Visto che la race condition derivava dal fatto che il processo Pi pone flag[i] = true dopo aver testato flag[j], allora si può pensare di invertire le due operazioni, ottenendo la sequenza: flag[i] = true while(flag[j]) {//Ripeti il test} … //Sezione critica flag[i]= false
19
Analisi del secondo algoritmo
In questo caso la mutua esclusione viene assicurata: infatti il processo i entra nella sezione critica solo se flag[i] == true e flag[j] == false. Ma la seconda condizione è verificata solamente se il secondo processo non è nella sezione critica Tuttavia il requisito di progresso non è soddisfatto dalla seguente possibile sequenza: 1) P0 pone flag[0] a true 2) P1 pone flag[1] a true In questa condizione i due processi si bloccano in un’attesa infinita: si è generata una situazione di deadlock
20
Algoritmo definitivo L’algoritmo di Peterson definisce una variabile booleana aggiuntiva:In questa implementazione la struttura condivisa è un array di due boolean: var flag: array[0..1] of boolean var turn: integer Il processo Pi esegue le seguenti istruzioni (j si riferisce all’indice dell’altro processo): flag[i] = true turn = j while(flag[j] and turn == j){} //Ripeti il test … //Sezione critica flag[i]= false
21
Analisi dell’algoritmo di Peterson
Mutua esclusione: quando Pi entra nella sezione critica o flag[j] == false o turn = i. Se entrambi i processi sono nella sezione critica, allora flag[0] e flag[i] sono true. Poiché la variabile turn non può essere contemporaneamente i e j, ciò significa che i due processi non possono essere entrati contemporaneamente nella sezione critica. Inoltre quando il primo dei due processi era entrato nella sezione critica, l’altro processo doveva aver già assegnato la variabile turn. A questo punto, tuttavia, quest’ultimo processo troverebbe la seconda condizione true pertanto deve necessariamente attendere che l’altro processo esca dalla sezione critica
22
Analisi algoritmo Peterson (Cont)
Progresso: il processo Pi può essere bloccato prima di entrare nella sezione critica solo e solo se flag[j] resta true e turn resta a j. Se Pj è al di fuori della sezione critica e non sta cercando di entrarci, allora flag[j] == false. Se Pj ha posto flag[j] a true, allora sta cercando di entrare nella sezione critica (eseguendo il loop while). La variabile turn può avere solo i valori i o j, e pertanto o il processo j o il processo i entrerà nella sezione critica (Progresso). Attesa finita: supponiamo che P1 e P0 continuino a richiedere l’accesso alla sezione critica. In questo flag[0] e flag[1] sono ripetutamente posti a true. Ciò significa che il processo Pi riesce a superare il ciclo while in base alla condizione turn == j. L’assegnazione della variabile turn prima del ciclo while assicura che Pi non dovrà attendere un numero indefinito di accessi di Pj prima di accedere a sua volta alla sezione critica.
Presentazioni simili
© 2024 SlidePlayer.it Inc.
All rights reserved.