Docsity
Docsity

Prepara i tuoi esami
Prepara i tuoi esami

Studia grazie alle numerose risorse presenti su Docsity


Ottieni i punti per scaricare
Ottieni i punti per scaricare

Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium


Guide e consigli
Guide e consigli


Algoritmi di Scheduling: FIFO, SJF, Round Robin e MFQ, Appunti di Sistemi Operativi

Libro di Michael Dahlin, Thomas Anderson tradotto in italiano (13 capitoli).

Tipologia: Appunti

2017/2018

In vendita dal 09/09/2018

maDave
maDave 🇮🇹

12 documenti

1 / 11

Toggle sidebar

Questa pagina non è visibile nell’anteprima

Non perderti parti importanti!

bg1
Capitolo 7 (Professore)
DEF: Lo scheduling sono le regole/politiche di ordinamento delle richieste da parte di utenti vari su varie
risorse. Quindi non si fa solo per il processore (ES: Scheduling dei pacchetti, scheduling di richiesta risorse
etc..).
Possiamo avere sistemi uniprocessore e multiprocessore:
Uniprocessore
I problemi dello scheduling possono essere risolti in tempo polinomiale
Multiprocessore
Alcuni problemi sono NP completi
L’ottimizzazione globale quindi è su più funzioni obiettivo quindi molto complessi => li approssimiamo.
Definizioni per lo scheduling:
Task/Job: In origine lo scheduling dei lavori veniva eseguito dai lavoratori. Task (Thread) Job
(Processo)
Tempo di risposta: Quanto ci mette un task/job per essere completato?
Throughput: Task/Job eseguiti nell’unità di tempo
Overhead: Tutte le volte devo applicare un algoritmo di scheduling ed eseguirlo => overhead
+ overhead - throughput
Fairness: Ogni thread/processo deve essere proporzionale al suo contesto, e al contesto degli altri
thread/processi.
Predicibilità
Workload: Un insieme di task eseguite dal sistema
Scheduler con prerilascio: Considereremo solo questo
Work-conserving: Se ho del lavoro da fare lo faccio, altrimenti si ferma.
Se lo scheduler non è con prerilascio il work-conserving non è efficiente
Algoritmo di scheduling
FIFO
Vengono schedulati in ordine di creazione dei thread. Lo scheduling agisce solo nella coda dei thread pronti.
Fin quando non finisce l’esecuzione del thread, quest’ultimo tiene il processore per tutto il tempo della sua
esecuzione.
Metrica dove funziona male: Tempo medio di elaborazione (Latenza)
Shortest Job First (SJF)
Per sistema uniprocessori è ottima per il ritardo medio.
Eseguo per primo il task più veloce (il tempo di
completamento minore).
Questo funziona se non vengono mai bloccati.
pf3
pf4
pf5
pf8
pf9
pfa

Anteprima parziale del testo

Scarica Algoritmi di Scheduling: FIFO, SJF, Round Robin e MFQ e più Appunti in PDF di Sistemi Operativi solo su Docsity!

Capitolo 7 (Professore)

DEF: Lo scheduling sono le regole/politiche di ordinamento delle richieste da parte di utenti vari su varie risorse. Quindi non si fa solo per il processore (ES: Scheduling dei pacchetti, scheduling di richiesta risorse etc..).

Possiamo avere sistemi uniprocessore e multiprocessore:

  • Uniprocessore
    • (^) I problemi dello scheduling possono essere risolti in tempo polinomiale
  • Multiprocessore
    • Alcuni problemi sono NP completi

L’ottimizzazione globale quindi è su più funzioni obiettivo quindi molto complessi => li approssimiamo.

Definizioni per lo scheduling:

  • (^) Task/Job : In origine lo scheduling dei lavori veniva eseguito dai lavoratori. Task (Thread) Job (Processo)
  • Tempo di risposta : Quanto ci mette un task/job per essere completato?
  • Throughput : Task/Job eseguiti nell’unità di tempo
  • Overhead : Tutte le volte devo applicare un algoritmo di scheduling ed eseguirlo => overhead
    • (^) + overhead - throughput
  • Fairness : Ogni thread/processo deve essere proporzionale al suo contesto, e al contesto degli altri thread/processi.
  • Predicibilità
  • Workload : Un insieme di task eseguite dal sistema
  • (^) Scheduler con prerilascio : Considereremo solo questo
  • Work-conserving: Se ho del lavoro da fare lo faccio, altrimenti si ferma.
    • Se lo scheduler non è con prerilascio il work-conserving non è efficiente
  • Algoritmo di scheduling

FIFO

Vengono schedulati in ordine di creazione dei thread. Lo scheduling agisce solo nella coda dei thread pronti. Fin quando non finisce l’esecuzione del thread, quest’ultimo tiene il processore per tutto il tempo della sua esecuzione.

Metrica dove funziona male: Tempo medio di elaborazione (Latenza)

Shortest Job First (SJF)

Per sistema uniprocessori è ottima per il ritardo medio.

Eseguo per primo il task più veloce (il tempo di

completamento minore).

Questo funziona se non vengono mai bloccati.

Figura a lato: 5 thread che arrivano uno di seguito

l’altro. Sia FIFO che SJF hanno stesso throughput

anche se eseguiti in modo diverso.

Metrica dove funziona male: Throughput, non è equo poiché potrebbero arrivare sempre thread corti e non viene eseguito mai quello più lungo ( Starvation ).

Come si può essere un po’ più equi? Si fa a turno

Round Robin

Ogni task (thread) prende la risorsa per un certo QDT (quanto di tempo = scatta da timer ), se viene bloccato prima allora la ottiene fino a quel momento, e metto in esecuzione qualcun altro della coda Round Robin dei pronti mettendo in fondo il thread a cui ho appena tolto la risorsa.

Quanto deve essere grande il QDT? Non è facile stabilire quanto deve essere grande.

Il tempo speso in generale nel sistema è proporzionale a quanti thread ho e il loro QDT quindi è molto equo.

Tempo di risposta: #thread pronti * QDT

I sistemi che usano Round Robin vengono

chiamati sistemi Time Sharing.

Tipicamente è tra i 20-120 msec.

Nella round robin tendo ad avere tanti

cambi di contesto. Per esempio

se i processi non si bloccano mai nella fifo

non ho preemptive avendo così il cambio di

il loro QDT totalmente

la priorità si abbassa, altrimenti

rimane dov’è.

Quelli in fondo potrebbero

entrare in starvation => Per ognuno dei thread presenti in una coda, controllo se è fermo per X msec => aumento la priorità del thread che sto considerando.

CAPITOLO 7 DAL LIBRO

La miglior performance è la transizione dallo stato di not-working a quello di working. Questo è uno speedup infinito. – John Ousterhout

Quando ci sono molte cose da fare, come facciamo a sapere quale sia la prima? Nello scorso capitolo, abbiamo descritto come creare i thread, cambiarli tra di loro, e sincronizzare il loro accesso alle strutture dati condivise. In ogni momento, alcuni thread sono in esecuzione nel processore. Altri stanno aspettando invece il loro turno. Altri ancora sono bloccati per completare un’azione di I/O, o che su una variabile condizione venga fatta una “signal()”, o che una lock sia rilasciata. Quando c’è più di un thread che è in esecuzione, lo politica di scheduling del processore determina quale thread andrà ad essere eseguito.

Stai probabilmente pensando che rispondere a questa domanda è facile: fare il lavoro in ordine di arrivo. Dopo tutto, questo è il modo più “fair” che noi conosciamo.

Stai anche probabilmente pensando che la risposta a questa domanda è poco importante. Con l’avanzare della tecnologia vengono prodotto processori sempre più efficienti e quindi non importano le considerazioni fatte in precedenza. Non siamo d’accordo! I sistemi operativi dei server in particolare hanno tantissimo overloaded. Applicazioni parallele possono creare molto lavoro rispetto ai reali processori, e se non ci prendessimo cura di creare un design dettagliato di una politica di scheduling, le performance potrebbero degenerare. Ci sono relazioni sottili tra le politiche di scheduling e la gestione della durata dei device come laptop o smartphone. Queste domande si applicano se il conflitto all’origine è il processore, memoria, disco, o rete, e vedremo tutto ciò nel resto del libro.

Le politiche di scheduling non sono un rimedio universale. Senza però la capacità, la performance, potrebbe essere lenta tra i thread che sono in esecuzione. In questo capitolo, discuteremo le condizioni di overload e come adattarlo ad esso.

Ognuno che aspetta in fila si chiede quale sia la linea da seguire in modo tale da scorrere più veloce;

Non esiste nessuna risposta giusta; piuttosto, ogni strategia di scheduling pone un insieme completo di tradeoff tra varie proprietà desiderabili. In questo capitolo, descriveremo di alcuni trade-off e proveremo ad illustrare come costruire un approccio al problema di selezionare una politica di scheduling.

Consideriamo cosa potrebbe accadere se stessi eseguendo il sito web per una compagnia che stia diventando il prossimo Facebook. Basandoci sulla storia, ti chiederai di quanti server tu abbia bisogno per poter rispondere alle richieste in real-time. Cosa succede se il tuo sito finisse su https://www.slashdot.org e molti utenti iniziano ad accendere di colpo? Se non stessimo attenti, ognuno penserebbe che il sito sarebbe estremamente lento, e ciascun utente andrebbe via senza farne più ritorno. Google, Amazon, e Yahoo hanno stimato che perdono il 5-10% dei loro clienti se il tempo di risposta 100 millisecondi.

  • Vorresti implementare facilmente differenti politica di scheduling o sbatterci contro?
  • Come peggiorerebbe la tua performance se gli utenti raddoppiassero?
  • Cacci alcuni utenti per mantenere le tue performance?
  • (^) Quali utenti sarebbero da cacciare?
  • Se comprassi un server di quanto la performance migliorerebbe?
  • Le risposte potrebbero cambiare se venisse attaccato il sistema?

In questo capitolo, proveremo a darti gli strumenti analitici e concettuali per aiutarti con le tue domande.

I punti chiave in questo capitolo sono:

  • Scheduling Uniprocessore. Come fanno le politiche di scheduling ad essere eque, in real-time ed efficienti?
  • Scheduling Multiprocessore. Come fanno a cambiare le politiche di scheduling quando abbiamo processori multi-core?
  • Efficienza energetica e scadenza scheduling. Molti pc permettono di risparmiare energia a patto di avere prestazioni più basse. Com’è possibile realizzare questo tradeoff, minimizzando l’impatto di risposta all’utente? Più in generale, come siamo sicuro che il nostro lavoro venga finito in tempo?
  • Teoria delle code
  • (^) Controllo overload. Come possiamo mantenere il sistema “response” se il sistema è in overload?

7.1 Scheduling Uniprocessore

Partiremo prendendo in considerazione un solo processore, generalizzando il tutto alle politiche multiprocessori nelle prossime sezioni. Inizieremo come 3 semplici politiche – FIFO, SJF, e Round Robin - per descrivere i concetti dello scheduling.

Prima di procedere, abbiamo bisogno di definire pochi termini. Un workload è un insieme di task per qualche sistema, definisce quando un compito arriva e quanto tempo richiede per essere completato. In altri parole, il workload definisce l’input per un algoritmo di scheduling. Dato un workload, lo scheduler del processore decide quando ogni task deve essere assegnato al processore.

Siamo interessati agli algoritmi di scheduling i quali lavorano bene con vari ambienti, perché i workload possono variare da sistema a sistema e da utente ad utente. Alcuni task sono computer-bound e usano solo il processore. Altri, come il compilatore o il web browser, mischiano I/O e computazioni. Altri ancori, come BitTorrent, sono I/O bound , i quali utilizzano la maggior parte del loro tempo per I/O e solo per brevi periodi. Nella discussione, inizieremo con workload compute-bound semplici e li generalizzeremo per includere mix di task differenti per procedere.

Alcune politiche che delineeremo sono le migliori in particolare nella metrica e workload, e qualche altre saranno le peggiori. Discuteremo dell’ottimo e del pessimo, comparando le politiche work-conserving : ovvero che il processore non sarà mai in attesa se c’è qualche lavoro da fare.

Nella nostra discussione assumeremo anche che lo scheduler ha la capacità di togliere il processore ad un task per assegnarlo ad un altro. Questo può accadere per esempio se un task arriva nella ready list avendo priorità maggiore, e lo scheduler lavora con politiche a priorità.

7.1.1 FIFO

Forse l’algoritmo di scheduling più semplice è il first in first out (FIFO): esegue i task in ordine di arrivo. Quando un task è in esecuzione, questo terrà il processore fin quando non ha terminato la sua esecuzione. FIFO minimizza l’overhead, cambiando task solo quando uno ha completato la sua esecuzione. Poiché minimizza l’overhead, se avessimo un numero fissato di task, e questi task hanno bisogno del processore, FIFO avrà il migliore throughput: completerà la maggior parte dei task più velocemente. E come menzionato, FIFO appare la migliore politica di scheduling per la fairness – ogni task pazientemente aspetta il proprio turno.

Risulta che SJF è pessima nella variazione del tempo di risposta. Ma eseguendo i task più corti il più velocemente possibile, SJF necessariamente esegue i task più lunghi il più lentamente possibile. In altre parole, c’è un tradeoff fondamentale tra migliorare il tempo medio di risposta e la variazione del tempo medio di risposta.

Peggio, SJF può soffrire di starvation e di frequenti cambi di contesto. Se arrivassero abbastanza task corti, quelli lunghi non verrebbero mai completati. In ogni caso il task è messo nella coda dei ready, ed essendo che il tempo di completamento è minore del task corrente, lo scheduler effettuerà un cambio di contesto. Se questo accadesse in modo indefinito, il task lungo non verrà mai eseguito.

7.1.3 Round Robin (RR)

Una politica che affronta la starvation è la Round Robin (RR). Con essa, i task utilizzano il processore per un periodo di tempo definito. Lo scheduler assegna il processore al primo task presente nella coda dei pronti, impostando il timer con qualche ritardo, chiamo time quantum (QdT – Quanto di tempo). Alla fine del quanto di tempo, se il task non è stato completato, quest’ultimo è obbligato a rilasciare il processore e a darlo al primo task nella coda dei pronti. Il rilascio del processore da parte del task causa lo spostamento di quest’ultimo nella coda dei pronti in attesa del suo turno. Con la RR non c’è possibilità che un task non verrà eseguito – Eventualmente raggiungerà la testa della coda ottenendo il suo QdT.

Certamente dobbiamo scegliere il QdT accuratamente. Una considerazione è l’overhead: se avessimo un QdT corto, il processore perderebbe tanto tempo nello switching e si farebbe poco lavoro. Ma se prendessimo il QdT troppo grande, i task dovranno aspettare per molto tempo fin quando un task lasci il processore.

Figura 7.

Nella figura 7.2 viene illustrato lo stesso workload della figura 7.1 con QdT differenti.

Un modo di vedere la RR è un buon compromesso tra la FIFO e la SJF. Da una parte, se il quanto di tempo fosse infinito, la RR sarebbe come la FIFO. Ogni task starà in esecuzione fin quando non verrà completato il suo lavoro. Dall’altra parte invece, supponendo che lo switching di task abbia zero overhead, potremmo scegliere un QdT per una sola istruzione a volta. Con questa divisione, i task finirebbero in ordine della loro lunghezza, come la SJF, ma un po’ più lentamente. La coda di A ci impiegherà lo stesso tempo della SJF, più un fattore proporzionale dato dal numero degli altri task di A * la lunghezza di A.

Sfortunatamente, la RR ha qualche difetto. La figura 7.3 mostra cosa succede per FIFO, SJF e RR quando molti task cominciano approssimativamente nello stesso tempo ed abbiano la stessa lunghezza. RR ruoterà attorno i task, eseguendoli un po’ per ciascuno, finendo però i task tutti allo stesso tempo (sempre

approssimativamente). Questa è probabilmente la peggior politica di scheduling per questo tipo di workload. Fifo sarebbe molto meglio, prendendo un task ed eseguendolo fin quando questo non abbia finito. Non solo FIFO riduce il tempo medio di risposta per questo workload relativo alla RR, ma nessun task è degradato sotto FIFO.

La suddivisione del tempo aggiunge overhead senza alcun beneficio. Infine, consideriamo cosa fa la SJF in questo workload. SJF schedula i task allo stesso modo della fifo. Il primo task che arriva sarà assegnato al processore, e presto eseguirà una singola istruzione, avrà dunque meno tempo di completamento rispetto agli altri task, e così continuerà ad essere eseguito fino al suo completamento. Sappiamo che la SJF è ottima, questo significa che sia la FIFO che la RR sono ottime per qualche workload e pessime per altri, con qualche differenza.

Dipendendo dal quanto di tempo, RR può anche risultare scadente se vi sono mix di task di tipo I/O o computer-bound. I task I/O-bound spesso hanno bisogno di brevi periodi nel processore per poter completare le altre operazioni di I/O. Ogni ritardo che avviene nel processore, porta il sistema ad un rallentamento complessivo.

Figura 7.

Supponiamo di avere un task che esegue operazioni per 1ms e usa il disco per 10ms, in un loop. Se fosse in esecuzione da solo, il disco sarebbe completamente occupato. Nella figura 7.4 è illustrato un I/O bound e due CPU-bound che vengono eseguiti alternativamente su di un processore.

7.1.4 Max-Min Fairness

  • Background Task. Ritardare il sistema di gestione dei task, come in una frammentazione del disco, così questo non interferisce con il lavoro dell’utente.
  • Equità. Assegnare processi (non-background) approssimativamente alla loro massima quota minima (min-max fairness) del processore

MFQ è un’estensione di RR. Invece di avere una singola coda, MFQ ha code multiple RR, ognuna delle quali ha differenti priorità e QdT. I task con maggiore priorità precedono i task di priorità minore, i task dello stesso livello sono schedulati con lo scheduling Round Robin. Inoltre, i task di alta priorità hanno un QdT minore rispetto a quelli di bassa priorità.

I Task sono spostati da livelli di priorità per favorire i task più piccoli rispetto a quelli più lunghi. Un nuovo task entra al top di priorità. Ogni volta che il task utilizza il suo QdT, scende di 1 livello; Ogni volta che un task rilascia il processore perché sta aspettando un operazione di I/O, rimane nello stesso livello; e se il task ha finito lascia il sistema.

Figura 7.

La figura 7.5 illustra le operazioni di una MFQ con 3 livelli. Un nuovo task compute-bound inizierà dalla priorità 0, ma quando avrà finito il suo QdT scenderà di un livello, e così per il successivo. Così, un I/O bound task ha bisogno di solo una piccola modesta quantità di computazione e sarà sempre schedulato velocemente, mantenendo il disco occupato. I task compute-bound verranno eseguiti con un Qdt lungo per minimizzare l’overhead di switch mentre ancora è condiviso il processore.

Da notare, che l’algoritmo descritto non raggiunge la completa libertà dalla starvation o l’equità di max-min fairness. Se ci sono molti task I/O bound, i task compute-bound non dovrebbero avere tempo nel processore. Per affrontare questo, lo scheduler MFQ, monitora ogni processo per assicurare che si riceva l’equità per la condivisione della risorsa. Ad ogni livello, Linux attualmente mantiene 2 code – Task i cui processi hanno già raggiunto la loro giusta quota sono schedulati solo se anche tutti gli altri processi a quel livello hanno ricevuto la loro giusta quota. Periodicamente, ogni processo riceve meno della sua quota condivisa, e avrà il task incrementato di priorità. Equamente, i task che ricevono più della loro quota condivisa possono scendere di priorità.

Aggiustare la priorità è una strategia di comportamento. Un task può mantenere la sua priorità alta facendo un richiesta I/O bound prima che il suo QdT finisca. Eventualmente il sistema dovrà individuarlo e ridurre la sua priorità alla sua quota di livello.