Scheduling della CPU - FCFS, SJF, priorità e round robin
In questa pagina 11
In un sistema multiprogrammato più processi sono pronti nello stesso momento ma c'è una sola CPU (o poche): lo scheduling decide chi la usa. Questa nota presenta i criteri e gli algoritmi classici, ognuno con un esempio svolto passo per passo (in diagrammi di Gantt scritti come tabelle). Il caso di più core e dei sistemi con scadenze (real-time) è in Scheduling multiprocessore e real-timeNei sistemi real-time conta la scadenza: si misurano latenza di interrupt e latenza di dispatch; i sistemi soft real-time danno priorità ai compiti critici senza garanzie, quelli hard devono rispettare le scadenze; per compiti periodici (burst t, scadenza d, periodo p, con 0 <= t <= d <= p) l'utilizzo è la somma di t/p: Rate Monotonic (priorità fissa, minore il periodo maggiore la priorità) garantisce le scadenze se l'utilizzo non supera N(2^(1/N)-1) (83% per due compiti), Earliest Deadline First (priorità dinamica, scadenza più vicina) le garantisce se l'utilizzo è sotto il 100%; la ripartizione proporzionale assegna T quote con controllo di ammissione; con più processori si hanno code comuni o private (SMP), core con più thread hardware, bilanciamento del carico (push e pull migration), affinità al processore (soft e hard), scheduling NUMA-aware; Linux usa CFS/EEVDF con domini di scheduling, Windows 32 livelli di priorità.Scheduling multiprocessore e real-time →. Gli stati e le code dei processi sono in Processi - stati, PCB, creazione con fork ed execUn processo è un programma in esecuzione (entità attiva) con codice (text), dati globali, heap, stack e lo stato dell'esecuzione; il kernel lo descrive con il PCB (Process Control Block: stato, program counter, registri, informazioni di scheduling, memoria, accounting, file aperti, in Linux la struct task_struct) e passa da un processo all'altro con il context switch (puro overhead); gli stati sono new, ready, running, waiting, terminated, con una ready queue e varie wait queue; in UNIX fork() crea un figlio copia del padre (restituisce 0 al figlio e il pid del figlio al padre), exec() sostituisce il programma del figlio, wait()/waitpid() raccoglie lo stato di uscita: senza di essa un figlio terminato è uno zombie, un figlio il cui padre è morto è un orfano adottato da init/systemd; i processi cooperanti comunicano con memoria condivisa (POSIX shm) o scambio di messaggi (diretto o indiretto, sincrono o asincrono, buffer a capacità zero, limitata, illimitata) e con pipe ordinarie (padre-figlio, unidirezionali) e named pipe (FIFO).Processi - stati, PCB, creazione con fork ed exec →; i problemi d'esame con numeri sono raccolti negli esercizi elencati in fondo.
Il ciclo CPU-I/O
L'esecuzione di un processo alterna burst di CPU (periodi di calcolo) e burst di I/O (periodi di attesa del dispositivo). L'istogramma dei burst di CPU ha tanti burst brevi e pochi lunghi: i processi I/O-bound hanno la curva spostata verso i burst brevi, quelli CPU-bound verso i lunghi. Con la multiprogrammazione, quando un processo entra in un burst di I/O la CPU può servire un altro: è questa sovrapposizione che dà la massima utilizzazione della CPU.
Scheduler e dispatcher
Lo scheduler della CPU sceglie, tra i processi nella ready queue, quello a cui assegnare un core. Le decisioni si prendono in quattro situazioni:
| # | situazione del processo | decisione |
|---|---|---|
| 1 | passa da running a waiting (chiede I/O) | obbligata: serve un altro processo |
| 2 | passa da running a ready (interrupt del timer) | si può scegliere |
| 3 | passa da waiting a ready (fine dell'I/O) | si può scegliere |
| 4 | termina | obbligata |
Se le decisioni sono prese solo nei casi 1 e 4 lo scheduling è non preemptive (senza prelazione, non-preemptive): una volta assegnata la CPU, il processo la tiene finché la rilascia terminando o passando in attesa. Se si decide anche nei casi 2 e 3 è preemptive (con prelazione): il sistema può togliere la CPU a un processo in esecuzione. Praticamente tutti i sistemi attuali (Windows, macOS, Linux, UNIX) sono preemptive.
La prelazione ha un costo: se due processi modificano dati comuni e uno viene interrotto a metà, l'altro vede i dati in uno stato incoerente (race condition, v. Sincronizzazione - sezione critica, race condition e mutexQuando più processi o thread accedono agli stessi dati e il risultato dipende dall'ordine degli accessi si ha una race condition (esempio: counter++ e counter-- sono tre istruzioni macchina ciascuna, e interleaving diversi danno 4, 5 o 6 partendo da 5); il problema della sezione critica chiede un protocollo (entry section, sezione critica, exit section, remainder section) che garantisca mutua esclusione, progresso e attesa limitata; disabilitare gli interrupt non scala sui multiprocessori, l'alternanza stretta con turn viola il progresso, la soluzione di Peterson (flag[2] e turn) soddisfa i tre requisiti ma può fallire su hardware moderno per il riordino delle istruzioni, per cui servono barriere di memoria; l'hardware offre test_and_set e compare_and_swap (atomiche) e variabili atomiche; il mutex lock (acquire/release, in POSIX pthread_mutex_t) è lo strumento di alto livello, con attesa attiva se è uno spinlock oppure con sospensione del thread se è un mutex di sistema; bloccare due volte lo stesso mutex senza sbloccarlo causa deadlock.Sincronizzazione - sezione critica, race condition e mutex →); inoltre preempire troppo spesso aumenta la latenza totale di dispatch.
Il dispatcher è il modulo che dà il controllo della CPU al processo scelto: cambia il contesto, passa in modalità utente e salta al punto giusto del programma. La latenza di dispatch è il tempo che passa tra l'arresto di un processo e l'avvio dell'altro.
Criteri di scheduling
| criterio | definizione | obiettivo |
|---|---|---|
| utilizzo della CPU | quota di tempo in cui la CPU lavora | massimizzare |
| throughput | processi completati per unità di tempo | massimizzare |
| tempo di turnaround (turnaround time) | dalla richiesta (arrivo) al completamento: | minimizzare |
| tempo di attesa (waiting time) | somma dei tempi passati nella ready queue: turnaround burst | minimizzare |
| tempo di risposta (response time) | dalla richiesta alla prima esecuzione | minimizzare |
Per processi con un solo burst di CPU: attesa turnaround durata del burst. Il turnaround normalizzato è turnaround / burst.
FCFS - First Come, First Served
Il primo processo arrivato nella ready queue è il primo a ricevere la CPU. È non preemptive e si realizza con una coda FIFO.
Esempio: arrivano a , nell'ordine con burst 24, 3, 3.
| t | 0 .. 24 | 24 .. 27 | 27 .. 30 |
|---|---|---|---|
| CPU |
Attese: , , ; media . Se arrivano nell'ordine :
| t | 0 .. 3 | 3 .. 6 | 6 .. 30 |
|---|---|---|---|
| CPU |
Attese: , , ; media . La differenza si chiama effetto convoglio (convoy effect): i processi brevi restano bloccati dietro uno lungo (per esempio un processo CPU-bound davanti a molti I/O-bound).
SJF - Shortest Job First
Si dà la CPU al processo con il burst di CPU successivo più breve. Se due hanno lo stesso burst si usa FCFS. È non preemptive. È ottimo: dà il minimo tempo di attesa medio per un insieme di processi con burst noti (portare avanti un processo breve riduce l'attesa dei brevi più di quanto aumenti quella del lungo).
Esempio con arrivi diversi: (arrivo 0, burst 8), (1, 4), (2, 9), (3, 5). A c'è solo , che parte e non viene interrotto. A sono pronti (4), (9), (5): si sceglie .
| t | 0 .. 8 | 8 .. 12 | 12 .. 17 | 17 .. 26 |
|---|---|---|---|---|
| CPU |
Attese inizio arrivo: , , , ; media .
Il problema: il prossimo burst non si conosce. Si può far indicare all'utente, oppure stimarlo dai burst precedenti con una media esponenziale.
Stima del prossimo burst
Definizione (media esponenziale). Siano la lunghezza effettiva dell'-esimo burst e la stima. La stima successiva è
Espandendo, ogni burst passato pesa meno del precedente: . Casi limite: dà (la storia recente non conta); dà (conta solo l'ultimo burst). Di solito , cioè .
Esempio con e :
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|---|
| burst effettivo | 6 | 4 | 6 | 4 | 13 | 13 | 13 | |
| stima | 10 | 8 | 6 | 6 | 5 | 9 | 11 | 12 |
Per esempio e : la stima segue i burst con un certo ritardo.
SRTF - Shortest Remaining Time First
È la versione preemptive di SJF: ogni volta che un nuovo processo entra nella ready queue si riconfronta il suo burst con il tempo rimanente del processo in esecuzione; se è minore, lo sostituisce. Stesso esempio di prima (: 0, 8; : 1, 4; : 2, 9; : 3, 5):
| t | 0 .. 1 | 1 .. 5 | 5 .. 10 | 10 .. 17 | 17 .. 26 |
|---|---|---|---|---|---|
| CPU |
- a arriva con burst 4 < rimanente di (7): prelazione;
- a arriva (9) e a arriva (5): nessuno batte il rimanente di (2 e 1);
- a finisce; tra (rimanente 7), (9), (5) vince ; poi e .
Attese turnaround burst: , , , ; media , meglio dei 7,75 di SJF non preemptive.
Round Robin
Ogni processo riceve la CPU per un piccolo intervallo, il quanto di tempo (time quantum) , di solito 10-100 ms; scaduto, viene prelazionato e messo in fondo alla ready queue. Si usa un interrupt del timer per far partire il processo successivo. Con processi pronti nessuno attende più di unità di tempo prima del turno. Se è molto grande RR si riduce a FCFS; se è molto piccolo il cambio di contesto (meno di 10 s, contro di 10-100 ms) pesa troppo: deve essere grande rispetto al tempo di cambio di contesto.
Esempio con , tutti arrivati a : burst 24, burst 3, burst 3.
| t | 0 .. 4 | 4 .. 7 | 7 .. 10 | 10 .. 30 |
|---|---|---|---|---|
| CPU |
finisce a 7 (turnaround 7), a 10, a 30. Attese: , , ; media (in FCFS sarebbe 17, in SJF 3). Il turnaround medio è .
Rispetto a SJF, RR ha di solito un turnaround medio maggiore ma una risposta migliore.
Convenzione sugli arrivi simultanei. Quando un processo finisce il quanto nello stesso istante in cui ne arriva un altro, bisogna dire chi entra prima in coda. Le soluzioni del corso non usano sempre la stessa regola: nell'esercizio 31 il processo prelazionato entra prima del nuovo arrivato (Esercizio - Round robin con arrivi e conteggio dei processi in coda (eserciziario, esercizio 31), dove le due convenzioni danno risultati diversi), nell'esercizio 61 il nuovo arrivato entra prima (Esercizio - Round robin con due quanti di tempo (eserciziario, esercizio 61)). In un compito si segue la regola dichiarata dal testo, o si dichiara la propria.
Scheduling a priorità
A ogni processo è associata una priorità (un intero); la CPU va al processo con la priorità più alta (nelle slide: il numero più piccolo, ma alcuni esercizi usano il numero più grande: si controlla sempre la convenzione). Può essere preemptive o no. SJF è uno scheduling a priorità in cui la priorità è l'inverso del burst.
Problema: starvation (starvation, attesa indefinita): i processi a bassa priorità possono non eseguire mai. Soluzione: aging (invecchiamento): col passare del tempo si aumenta la priorità del processo in attesa.
Esempio (priorità più piccola = più alta, tutti arrivati a 0): (burst 10, priorità 3), (1, 1), (2, 4), (1, 5), (5, 2).
| t | 0 .. 1 | 1 .. 6 | 6 .. 16 | 16 .. 18 | 18 .. 19 |
|---|---|---|---|---|---|
| CPU |
Attese: , , , , ; media . Turnaround: 1, 6, 16, 18, 19; media .
Priorità con round robin: si esegue il processo di priorità più alta; quelli con la stessa priorità girano a RR. Con e tre livelli, per esempio, quelli del livello migliore si alternano a quanti di 2 finché non finiscono, poi tocca al livello successivo. Un processo che arriva con priorità più alta del processo in esecuzione lo prelaziona (v. Esercizio - Round robin con priorità e task idle (eserciziario, esercizio 83)).
Code multilivello e a feedback
Una coda multilivello (multilevel queue) divide la ready queue in più code, ciascuna con il suo algoritmo (per esempio una coda per i processi interattivi con RR e una per i batch con FCFS). Parametri: numero di code, algoritmo di ogni coda, regola per decidere in quale coda entra un processo, scheduling tra le code (di solito a priorità: si serve la coda più alta non vuota). È di solito un criterio basato sul tipo di processo.
Nel feedback multilivello (multilevel feedback queue, MLFQ) i processi si spostano tra le code: un processo che usa troppa CPU viene declassato, uno che aspetta troppo viene promosso (così si realizza anche l'aging). Parametri: numero di code, algoritmo di ciascuna, regole per scegliere la coda d'ingresso, per promuovere e per declassare. Esempio (slide): tre code, RR con quanto 8 ms, RR con quanto 16 ms, FCFS. Un nuovo processo entra in e riceve 8 ms; se non finisce passa a , dove riceve altri 16 ms; se ancora non finisce va in , dove aspetta tutti i processi presenti in e quelli in e . I processi brevi finiscono presto, i lunghi scendono.
Esercizi collegati
- Esercizio - Scheduling FCFS, SJF e priorità con sei processi (eserciziario, esercizio 24)
- Esercizio - Scheduling FCFS e SJF con tempo di turnaround e processi in coda (eserciziario, esercizi 29 e 30)
- Esercizio - Round robin con arrivi e conteggio dei processi in coda (eserciziario, esercizio 31)
- Esercizio - Round robin con due quanti di tempo (eserciziario, esercizio 61)
- Esercizio - Lettura di un diagramma di Gantt e ordine di priorità (eserciziario, esercizi 25 e 28)
- Esercizio - Scheduling con I-O, effetto convoglio ed EDF (eserciziario, esercizio 27)
- Esercizio - Code a priorità con aging e feedback (eserciziario, esercizio 23)
- Esercizio - Code multiple con SRTF, round robin e FCFS (eserciziario, esercizio 26)
- Esercizio - Round robin con priorità e task idle (eserciziario, esercizio 83)
Domande d'esame
1. Perché SJF è ottimo per il tempo di attesa medio e perché non si usa in pratica così com'è? Traccia. Scambiare due processi adiacenti, il più lungo prima del più breve, aumenta l'attesa totale: mettere avanti i brevi la minimizza. Non si usa tal quale perché il prossimo burst non è noto: si stima con la media esponenziale e c'è rischio di starvation per i lunghi.
2. Che cos'è l'effetto convoglio? Fare un esempio con numeri. Traccia. Processi brevi in coda dietro uno lungo. Esempio: burst 24, 3, 3 in FCFS: attese 0, 24, 27, media 17; con l'ordine 3, 3, 24 la media è 3.
3. Come si sceglie il quanto in round robin? Che cosa succede se è troppo grande o troppo piccolo? Traccia. Deve essere grande rispetto al tempo di cambio di contesto (q tra 10 e 100 ms, contro meno di 10 s). Troppo grande: RR degenera in FCFS e la risposta peggiora. Troppo piccolo: l'overhead del cambio di contesto domina. Nessun processo attende più di prima del suo turno.
Versione ripasso
- Burst: CPU burst / I/O burst. Dispatcher: cambio di contesto, modalità utente, salto; latenza di dispatch. Non preemptive = decide solo quando il processo termina o si blocca; preemptive = può togliere la CPU (rischio race condition su dati condivisi).
- Criteri: massimizzare utilizzo CPU e throughput; minimizzare turnaround (), attesa (turnaround burst), risposta (alla prima esecuzione).
- FCFS: FIFO, non preemptive, effetto convoglio: 24, 3, 3 -> attesa media 17; ordine 3, 3, 24 -> 3.
- SJF: burst più breve, ottimo per l'attesa media, non preemptive. Stima: , tipico; : solo ; : solo l'ultimo burst. Esempio : 10, 8, 6, 6, 5, 9, 11, 12.
- SRTF: SJF preemptive (si confronta con il tempo rimanente): esempio (0,8), (1,4), (2,9), (3,5) -> 6,5 contro 7,75 di SJF.
- Round robin: quanto (10-100 ms), prelazione con timer, in fondo alla coda; nessuno attende più di ; grande -> FCFS, piccolo -> troppo overhead. Esempio , burst 24, 3, 3: attese 6, 4, 7 (media 5,67). Convenzione sugli arrivi simultanei: dichiararla (il corso non è coerente: esercizio 31 prelazionato prima, esercizio 61 nuovo arrivato prima).
- Priorità: numero più piccolo = più alta (controllare la convenzione); starvation -> aging. Stessa priorità: RR. Esempio (10,3), (1,1), (2,4), (1,5), (5,2): ordine , attesa media 8,2.
- Code multilivello: una coda per tipo di processo, priorità tra le code. MLFQ: i processi si spostano (declassamento se usano troppa CPU, promozione contro l'aging); esempio RR 8 ms, RR 16 ms, FCFS.
- Rimandi: Scheduling multiprocessore e real-timeNei sistemi real-time conta la scadenza: si misurano latenza di interrupt e latenza di dispatch; i sistemi soft real-time danno priorità ai compiti critici senza garanzie, quelli hard devono rispettare le scadenze; per compiti periodici (burst t, scadenza d, periodo p, con 0 <= t <= d <= p) l'utilizzo è la somma di t/p: Rate Monotonic (priorità fissa, minore il periodo maggiore la priorità) garantisce le scadenze se l'utilizzo non supera N(2^(1/N)-1) (83% per due compiti), Earliest Deadline First (priorità dinamica, scadenza più vicina) le garantisce se l'utilizzo è sotto il 100%; la ripartizione proporzionale assegna T quote con controllo di ammissione; con più processori si hanno code comuni o private (SMP), core con più thread hardware, bilanciamento del carico (push e pull migration), affinità al processore (soft e hard), scheduling NUMA-aware; Linux usa CFS/EEVDF con domini di scheduling, Windows 32 livelli di priorità.Scheduling multiprocessore e real-time →, Processi - stati, PCB, creazione con fork ed execUn processo è un programma in esecuzione (entità attiva) con codice (text), dati globali, heap, stack e lo stato dell'esecuzione; il kernel lo descrive con il PCB (Process Control Block: stato, program counter, registri, informazioni di scheduling, memoria, accounting, file aperti, in Linux la struct task_struct) e passa da un processo all'altro con il context switch (puro overhead); gli stati sono new, ready, running, waiting, terminated, con una ready queue e varie wait queue; in UNIX fork() crea un figlio copia del padre (restituisce 0 al figlio e il pid del figlio al padre), exec() sostituisce il programma del figlio, wait()/waitpid() raccoglie lo stato di uscita: senza di essa un figlio terminato è uno zombie, un figlio il cui padre è morto è un orfano adottato da init/systemd; i processi cooperanti comunicano con memoria condivisa (POSIX shm) o scambio di messaggi (diretto o indiretto, sincrono o asincrono, buffer a capacità zero, limitata, illimitata) e con pipe ordinarie (padre-figlio, unidirezionali) e named pipe (FIFO).Processi - stati, PCB, creazione con fork ed exec →.
- Decisioni di scheduling: running waiting (I/O) e terminazione: obbligate; running ready (timer) e waiting ready: scelta (solo qui c'è prelazione). Ready queue + wait queue; dispatcher = cambio di contesto + modalità utente + salto; latenza di dispatch.
- Esempio completo SJF / SRTF , , , : SJF 0-8, 8-12, 12-17, 17-26, attese 0, 7, 15, 9 7,75. SRTF 0-1, 1-5, 5-10, 10-17, 17-26, attese 9, 0, 15, 2 6,5.
- Stima del burst: espande in : ogni burst passato pesa meno del precedente. Esempio , , : .
- FCFS, convoglio: burst 24, 3, 3: attese 0, 24, 27 (media 17); ordine 3, 3, 24: 0, 3, 6 (media 3). RR , burst 24, 3, 3: Gantt 0-4, 4-7, 7-10, 10-30; attese 6, 4, 7 (media 5,67).
- Priorità, tutti a 0, burst 10, 1, 2, 1, 5 e priorità 3, 1, 4, 5, 2: ordine , attese 0, 1, 6, 16, 18 (media 8,2), turnaround medio 12. Aging (priorità ogni tot di attesa) contro la starvation.
- Code a feedback (MLFQ): i processi si spostano tra le code (declassamento se usano troppa CPU, promozione se attendono troppo); esempio RR 8 ms RR 16 ms FCFS.
- RR e cambio di contesto: grande rispetto al cambio (10-100 ms contro meno di 10 s); con processi nessuno attende più di ; turnaround medio di RR SJF, risposta migliore.
- Errori tipici: attesa calcolata senza sottrarre l'arrivo; SRTF confrontato con il burst intero invece del rimanente; dimenticare i tempi morti (CPU idle) nel Gantt; mettere in coda nell'ordine sbagliato in RR agli arrivi simultanei; confondere la convenzione di priorità; calcolare il turnaround come tempo di fine.
Esercizi su questo argomento
- Esercizio - Code a priorità con aging e feedback (eserciziario, esercizio 23)
- Esercizio - Code multiple con SRTF, round robin e FCFS (eserciziario, esercizio 26)
- Esercizio - Domande di teoria su sistemi operativi, scheduling e multiprocessore (eserciziario, esercizi 1, 2, 64 e 121)
- Esercizio - Lettura di un diagramma di Gantt e ordine di priorità (eserciziario, esercizi 25 e 28)
- Esercizio - Priorità, semaforo FIFO e code (eserciziario, esercizi 13 e 44)
- Esercizio - Round robin con arrivi e conteggio dei processi in coda (eserciziario, esercizio 31)
- Esercizio - Round robin con due quanti di tempo (eserciziario, esercizio 61)
- Esercizio - Round robin con priorità e task idle (eserciziario, esercizio 83)
- Esercizio - Scheduling con I-O, effetto convoglio ed EDF (eserciziario, esercizio 27)
- Esercizio - Scheduling FCFS e SJF con tempo di turnaround e processi in coda (eserciziario, esercizi 29 e 30)
- Esercizio - Scheduling FCFS, SJF e priorità con sei processi (eserciziario, esercizio 24)
Teoria collegata
- Memoria centrale - allocazione contigua, paginazione e segmentazione
- Memoria di massa e scheduling del disco
- Processi - stati, PCB, creazione con fork ed exec
- Scheduling multiprocessore e real-time
- Sicurezza e protezione nei sistemi operativi
- Sistema operativo - ruolo, struttura e system call
- Sistemi operativi embedded e real-time per l'IoT