Lezione 10 Scheduling e dispatching

Lezione 10
Scheduling e dispatching
Sistemi Operativi (9 CFU), CdL Informatica, A. A. 2014/2015
Dipartimento di Scienze Fisiche, Informatiche e Matematiche
Università di Modena e Reggio Emilia
http://weblab.ing.unimo.it/people/andreolini/didattica/sistemi-operativi
1
Quote of the day
(Meditate, gente, meditate...)
“If you spend too much time
thinking about a thing, you'll never
get it done. Make at least one
definite move daily toward your
goal.”
Lee Yun Fan (1940-1973)
Filosofo, attore, esperto di arti marziali
2
INTRODUZIONE
3
Modello di comportamento
(Una visione diversa rispetto all'alternanza Calcolo - Servizio)
Durante la sua esecuzione, un processo si alterna
in due fasi:
fase di elaborazione user o kernel (CPU burst).
attesa di I/O o evento (Wait).
User
Kernel
CPU burst
CPU
burst
CPU burst
CPU burst
CPU
burst
CPU burst
Wait
4
Processi vincolati alle risorse
(Resource boundedness)
Un processo si dice vincolato (bound) ad una
risorsa (resource) oppure ad un evento (event) se
la sua prestazione è correlata direttamente alla
disponibilità della risorsa o al verificarsi
dell'evento.
Se la risorsa scarseggia o l'evento è raro, il processo va a
singhiozzo.
Più e performante la risorsa, tanto più performante è il
processo.
5
Alcuni esempi di programmi “bound”
(CPU, disco, rete)
CPU. Il programma factor che fattorizza un
numero intero è vincolato alla CPU.
Disco. Il programma dd che trasferisce dati a
blocchi da e verso periferiche è vincolato al disco.
Rete. Il programma wget che scarica documenti
dal Web è vincolato alla capacità del
collegamento di rete.
6
Processi CPU-bound e I/O-bound
(I più comuni)
Il comportamento di un processo si posiziona in
genere fra questi due estremi.
Processo CPU-bound.
Tende a produrre poche sequenze di CPU burst lunghe.
Tende a fare poche richieste di I/O.
Processo I/O-bound.
Tende a produrre tante sequenze di CPU burst brevi.
Tende a fare molte richieste di I/O.
7
Processi CPU-bound e I/O-bound
(Diagrammi temporali di esecuzione tipici)
Processo CPU-bound
CPU burst
CPU burst
CPU burst
U
K
CPU burst
CPU burst
Processo I/O-bound
CB
CB
CB
CB
U
CB
CB
Wait
CB
CB
Wait
CB
K
CB
Wait
8
Quali processi sono più importanti?
(Bella domanda; dipende dall'uso che si fa del SO)
Sistema desktop. È caratterizzato da elevata
interattività con l'utente. Tale interattività si
estrinseca in richieste di I/O a periferiche
(terminale, disco, rete). Si dovrebbero favorire i
processi I/O-bound rispetto a quelli CPU-bound.
Sistema di calcolo batch. Duale del sistema
desktop: interattività pressoché inesistente,
necessità di completare quanti più processi di
calcolo possibile. Si preferiscono i processi CPU9
bound.
Problemi da risolvere 1/4
(Troppi processi per poche CPU)
Le CPU a disposizione in un calcolatore sono, in
linea generale, poche.
Una potenza di due piccola (1, 2, 4, 8) su macchine
low-medium end.
Una potenza di due un po' più grande (16, 32, 64, 128)
su macchine high end.
Le applicazioni lanciate su un sistema sono, in
linea generale, tante.
Su una macchina Debian ragionevolmente scarica,
dopo l'avvio dell'ambiente grafico: ps faxu | wc -l10
→ 137 processi!
Problemi da risolvere 2/4
(Indisponibilità delle periferiche)
Le periferiche non sono sempre disponibili.
Se il disco è occupato con un numero eccessivo di
richieste, non può servirne altre.
Se la stampante sta stampando un documento, non
può stamparne un altro.
11
Problemi da risolvere 3/4
(Sotto-utilizzazione della CPU)
Se:
tutti i processi richiedono servizio alle periferiche;
le periferiche sono occupate;
le CPU non possono eseguire alcun processo!
Si rischia una pesante sotto-utilizzazione delle
stesse in scenari di lavoro orientati all'I/O.
12
Problemi da risolvere 4/4
(Riduzione dell'interattività)
Un processo CPU-bound può impegnare la CPU
per un tempo sufficientemente lungo da
impedire ad altri processi I/O-bound di avanzare
rapidamente.
→ Non c'è più alcuna garanzia di interattività
(disastro in un sistema desktop con poche CPU).
Il terminale stampa i tasti premuti “dopo un po'”.
Le applicazioni partono “dopo un po'” .
Il SO sembra, in generale, lento come un bradipo
13
(sluggish).
Il gestore dei processi
(Affronta e risolve i problemi ora esposti)
Il gestore dei processi è un sottosistema del
kernel preposto a risolvere i problemi esposti in
precedenza. Esso consta di tre grandi aree.
Gestione delle attese.
Algoritmo di scheduling.
Dispatching.
14
Grado di multiprogrammazione
(Quanti processi son stati fatti partire?)
Il grado di multiprogrammazione è la somma di
due contributi:
il numero di processi in esecuzione.
il numero di processi pronti per l'esecuzione.
Tale numero dipende da diversi fattori:
la frequenza con cui un utente fa partire processi.
la frequenza con cui i processi si bloccano.
la frequenza con cui i processi terminano.
15
Obiettivi del gestore dei processi
(Se non sono rigorosamente mantenuti, la macchina ha prestazioni basse)
Reggere, nei limiti del possibile, il grado di
multiprogrammazione imposto dall'utente senza
degradare le prestazioni proprie e dei processi.
Favorire i processi “giusti” per il tipo di SO
considerato:
Desktop → I/O-bound.
Server → CPU-bound.
Non lasciare mai inutilmente inoperosa la CPU.
16
“Scheduling”? “Dispatching”?
(Eh?)
17
Scheduling
(http://www.youtube.com/watch?v=AN7M4ULqz8Y)
In parecchie situazioni, il
kernel deve assegnare una
risorsa ad n entità che la
vogliono utilizzare.
Vincolo fondamentale: le
entità richiedenti eccedono
le istanze di risorsa presenti.
Come assegnare le richieste
alle risorse?
Utente
Utente
Utente
Utente
Utente
Utente
Kernel
Risorsa
Risorsa
18
Lo scheduler
(http://www.youtube.com/watch?v=ptuo3e3u6T8)
Uno
scheduler
l'accesso alle risorse.
arbitra
Accodamento: le richieste
sono ricevute e “parcheggiate”.
Algoritmo: una richiesta è
scelta dalla coda.
Dispatching: la richiesta
scelta è assegnata alla risorsa.
Utente
Utente
Utente
Utente
Utente
Utente
Scheduler
Risorsa
Risorsa
19
Un semplice modello
(Scheduling = coda attesa + algoritmo selezione + meccanismo dispatching)
Algoritmo: pesca una
Algoritmo
richiesta dalla coda.
Utente
Utente
2
Richiesta
3
Utente
1
Utente
Dispatcher
...
Utente
Utente
4
Coda di attesa: parcheggia
le richieste delle applicazioni.
Gli utenti inviano le richieste
simultaneamente.
Dispatcher: assegna
la richiesta prescelta
alla risorsa.
5
Risorsa
20
GESTIONE DELLE ATTESE
21
Code di attesa
(Wait queue)
Nei moderni SO la gestione delle attese è
effettuata tramite code di attesa (wait queue).
Lista normalissima con disciplina di inserimento e
cancellazione tipicamente FIFO.
Funzioni di gestione della lista.
I membri della coda di attesa sono le strutture
dati che rappresentano le richieste degli utenti
della risorsa. Nel caso di attese di processi, le
strutture sono i PCB. In Linux: task struct. 22
Quante code di attesa esistono?
(Tante; una per ogni possibile ragione di attesa)
Esiste una coda di attesa per ogni possibile
evento che, non ancora verificatosi, provoca il
blocco di un processo.
Attesa periferica I/O.
Attesa di un segnale da parte di un altro processo.
Attesa di un timeout.
…
Ogni CPU ha anche una coda di pronto (ready
queue) che rappresenta tutti i processi sbloccati
dal verificarsi di un qualche evento e pronti per
23
l'esecuzione.
Uno schema concettuale
(Non dettagliato; serve solo a far capire l'idea)
Gestore processi Elemento
sentinella
CPU #2
Coda di
head
pronto
tail
User
Kernel
PCB7
PCB2
registri
registri
...
...
PCB3
PCB14
registri
registri
...
...
...
Elemento
Gestore disco
sentinella
Disco #1
Coda di
head
attesa I/O
tail
24
Uso di una risorsa occupata
(Richiesta → attesa → liberazione risorsa → risveglio → uso)
Un processo vuole usare una risorsa gestita dal kernel.
Pertanto, effettua una richiesta al relativo gestore
(tramite una chiamata di sistema).
Processo A
Richiesta
uso risorsa
User
Kernel
Gestore processi
CPU
Gestore risorsa
PCB
A
Risorsa
25
Uso di una risorsa occupata
(Richiesta → attesa → liberazione risorsa → risveglio → uso)
Processo A
La risorsa è bloccata. Il gestore della risorsa:
blocca il processo perché non può avanzare.
inserisce il PCB del processo A nella coda di attesa.
cambia lo stato del processo a “bloccato”.
invoca la schedulazione di un nuovo processo B ed il
suo ripristino (stay tuned per tutti i dettagli sullo
scheduling).
User
Kernel
Gestore processi
CPU
Gestore risorsa
PCB
A
Risorsa
26
Uso di una risorsa occupata
(Richiesta → attesa → liberazione risorsa → risveglio → uso)
Ora è in esecuzione il processo B, che non usa la risorsa.
Il processo A non può più essere schedulato per
l'esecuzione fintantoché non si libera la risorsa.
Processo B
User
Kernel
Gestore processi
CPU
Gestore risorsa
PCB
B
Risorsa
27
Uso di una risorsa occupata
(Richiesta → attesa → liberazione risorsa → risveglio → uso)
Successivamente un altro processo C (in esecuzione)
finisce di usare la risorsa e la libera (ad esempio tramite
una chiamata di sistema).
Processo B
Rilascio
uso risorsa
Processo C
User
Kernel
Gestore processi
CPU
Gestore risorsa
PCB
B
Risorsa
PCB
C
28
Uso di una risorsa occupata
(Richiesta → attesa → liberazione risorsa → risveglio → uso)
Processo A
Tale chiamata di sistema invoca il gestore della risorsa e
provoca il risveglio di un processo in attesa (A).
Si disassocia il PCB di C dalla risorsa.
Si seleziona il primo PCB in coda di attesa e lo si
inserisce in coda di pronto.
Si imposta lo stato del Processo C
PCBA a “pronto”.
User
Kernel
Gestore processi
CPU
Gestore risorsa
PCB
A
Risorsa
PCB
C
29
Uso di una risorsa occupata
(Richiesta → attesa → liberazione risorsa → risveglio → uso)
Dopo un po' il processo A sarà selezionato per
l'esecuzione.
Processo A
Processo C
User
Kernel
Gestore processi
CPU
Gestore risorsa
PCB
A
Risorsa
PCB
C
30
Un esempio concreto
(Guys, meet /dev/{,u}random)
Il kernel fornisce un meccanismo di generazione
di numeri casuali.
Utile per la generazione di chiavi GPG e one-time pad.
Esistono due modalità di generazione.
Dispositivo /dev/urandom: generazione non
bloccante di numeri casuali “non forti” (facili da
indovinare).
Dispositivo /dev/random: generazione bloccante
di numeri casuali “forti” (difficili da indovinare).
31
Provare per credere!
(cat < /dev/urandom, cat < /dev/random)
Si digiti: cat /dev/urandom.
L'output casuale non si blocca.
Si digiti: cat /dev/random.
L'output casuale dopo un po' si blocca.
Muovete freneticamente il mouse.
L'output casuale riprende.
32
“These things never work as you expect”
(“That's the beauty of science”)
33
Come generare numeri casuali buoni?
(Entropy, entropy, entropy, ...)
Il kernel legge valori casuali da diverse fonti e
stabilisce l'entropia (entropy) della sequenza
risultante.
Se l'entropia non è sufficiente, la funzione di
lettura da /dev/random blocca il processo e ne
rischedula un altro.
Quando c'è sufficiente entropia, il processo viene
risvegliato e la lettura continua.
Implementazione:
$LINUX/drivers/char/random.c
34
Le operazioni I/O di /dev/random
(Individuabili tramite la struct file_operations)
Operazioni di /dev/random: contenute nella struct
file_operations random_fops.
→ Lettura bloccante di /dev/random: implementata
tramite la funzione random_read(), che a sua volta
invoca _random_read().
35
Attesa e schedulazione in /dev/random
(Entropy, entropy, entropy, I need ,more entropy!)
_random_read() calcola il minimo numero di byte
casuali forti erogabili (almeno 512 byte).
nbytes = min_t(...);
Poi eroga i byte.
n = extract_entropy_user(...);
Se l'entropia è bassa (il kernel non ha abbastanza dati
casuali da ritornare), n = 0 ed il processo si deve bloccare.
wait_event_interruptible(...);
36
wait_event_interruptible()
(Accoda il processo e gli permette di ricevere segnali)
La funzione wait_event_interruptible():
accoda il processo in una coda di attesa (primo
parametro).
schedula un nuovo processo.
$LINUX/include/linux/wait.h
D'ora in avanti, il processo che sta leggendo da
/dev/random è bloccato!
37
Blocco e risveglio di /dev/urandom
(Entropy, entropy, entropy, ...)
La funzione credit_entropy_bits() è eseguita
ogni volta che è disponibile entropia (dati casuali) dalle
varie sorgenti.
Se sono presenti sufficienti byte di entropia, viene
svegliato un processo lettore.
if (entropy_bits >= random_read_wakeup_bits) {
wake_up_interruptible(&random_read_wait);
…
}
38
wake_up_interruptible()
(Risveglia il processo e lo inserisce in coda di pronto)
La funzione wake_up_interruptible():
estrae il primo processo dalla coda di attesa (nello
specifico random_read_wait).
inserisce il processo in coda di pronto.
imposta lo stato del processo ad “esecuzione”
(TASK_RUNNING).
$LINUX/include/linux/wait.h
Il processo è sbloccato e può nuovamente eseguire!
39
SCHEDULAZIONE
40
Algoritmo di scheduling
(Sceglie il prossimo processo da eseguire)
Una volta in coda di pronto, un processo può
essere selezionato per l'esecuzione.
L'algoritmo di schedulazione sceglie, fra i vari
processi pronti per l'esecuzione, quello la cui
traccia sarà eseguita prossimamente.
Come è invocato l'algoritmo di scheduling?
In Linux, viene invocata la funzione seguente:
schedule() in diversi punti strategici.
$LINUX/kernel/sched/core.c
41
Quando schedulare?
(In che punti del ciclo di vita di un processo è invocata schedule()?)
La
funzione
schedule()
è
invocata
sicuramente nelle seguenti due occasioni.
Un processo si blocca in attesa di un evento.
Un processo termina l'esecuzione.
Altrimenti, il processore rimane inattivo (e ciò non
è bene).
Esistono altre occasioni in cui è conveniente (o
addirittura necessario) schedulare?
42
Ovvio che sì
(Il problema ora è capire quali sono queste occasioni!)
43
Quando schedulare?
(Eccole, le altre occasioni!)
In Linux, la funzione schedule() è invocata
anche nelle seguenti occasioni.
È stato creato un nuovo processo.
Un processo è interrotto da una interruzione (inclusa
la scadenza del quanto di tempo).
Un processo passa dallo stato bloccato allo stato
pronto.
PERCHÉ?
44
Prelazione
(Il kernel toglie la CPU ad un processo e la assegna ad un altro più importante)
Prelazione (preemption): è l'atto di sottrarre la
CPU ad un processo per assegnarla ad un altro
ritenuto più importante.
“Importante” (è bene ricordarlo ancora):
desktop
→ processo I/O-bound.
sistema batch → processo CPU-bound.
45
Scheduling senza prelazione
(Il kernel schedula un processo quando non può farne a meno)
Un algoritmo di scheduling senza prelazione
sceglie il prossimo processo nelle occasioni in cui
proprio non può farne a meno (pena l'inattività
della CPU).
Un processo si blocca in attesa di un evento.
Un processo termina l'esecuzione.
Tuttavia, in tale scenario, un processo CPU-bound
è in grado di occupare perennemente la CPU,
impedendo agli altri processi (inclusi gli I/O46
bound) di eseguire (starvation).
Scheduling con prelazione
(Il kernel schedula anche quando ciò può favorire un processo più importante)
Un algoritmo di scheduling con prelazione
sceglie il prossimo processo anche nelle occasioni
in cui può favorire un processo importante.
Un processo si blocca in attesa di un evento.
Un processo termina l'esecuzione.
È stato creato un nuovo processo (si può scegliere a
quale CPU assegnarlo).
Un processo è interrotto da una interruzione (inclusa
la scadenza del quanto di tempo → impedisce la
starvation dei processi CPU-bound).
Un processo passa dallo stato bloccato allo stato
47
pronto (se è più importante → può eseguire).
Priorità di un processo
(AKA “Come fa il kernel a determinare l'importanza di un processo”)
Ad ogni processo è associato un numero intero
non negativo detto priorità. La priorità stabilisce
l'importanza del processo nel SO.
Tra due processi con priorità differenti, un
algoritmo di schedulazione tende sempre a
pescare quello con la priorità più alta.
48
“Quando un uomo con la pistola...”
(“...incontra un uomo col fucile, quello con la pistola è un uomo morto!”)
49
Priorità, starvation ed aging
(Altrimenti il SO stallerebbe in presenza di diversi processi CPU-bound)
Se
uno
scheduler
decidesse
sempre
esclusivamente in base alla priorità, un processo a
bassa priorità sarebbe spacciato.
Non riceverebbe mai il cibo “CPU”.
Morirebbe di fame (starvation).
Per evitare ciò, il gestore dei processi individua i
processi in starvation ed alza loro gradualmente
la priorità (aging o priority boost).
→ Prima o dopo (molto dopo) esegue anche il
50
processo a priorità più bassa.
Priorità in GNU/Linux
(Sono ben 140!)
GNU/Linux usa 140 livelli distinti di priorità,
mappati nell'intervallo [0, 139].
Occhio: le priorità sono invertite numericamente!
139 è la priorità più bassa.
0 è la priorità più alta.
Le priorità sono divise in due grandi intervallo.
Priorità statiche nell'intervallo [100, 139]. Dedicate ai
processi normali.
Priorità real time nell'intervallo [0, 99]. Dedicate ai
processi che devono eseguire con vincoli51
temporali più stringenti (soft real time).
Priorità statiche
(Nice value, the old UNIX way)
Ogni processo eredita dal padre una priorità
statica (nice value) che può essere reimpostata
dall'utente.
Storicamente, tale priorità assume valori
nell'intervallo [-20, 19]. Quest'ultimo è mappato
dal kernel Linux nell'intervallo [100, 139].
-20 → 100 (la priorità più alta fra le statiche).
+19 → 139 (la priorità più bassa fra le statiche).
La priorità statica di default di un processo è:
0 (considerando l'intervallo [-20, 19]).
120 (considerando l'intervallo [100, 139]).
52
Gestione delle priorità statiche
(Il comando nice)
Il comando esterno nice permette di lanciare un
programma con una priorità statica modificata.
È utile per lanciare comandi che:
durano molto.
non hanno alcun requisito di interattività.
non devono rubare troppe risorse ad altri processi.
Classico esempio: backup del root file system.
nice -n +19 tar zcvf backup.tar.gz /
53
Lettura del valore di priorità
(Comoda comoda :-) OK, just kidding...)
Il valore di priorità di un processo può essere letto
tramite i comandi ps e top.
ps faxl e si guarda la colonna PRI.
top e si guardano i campi PR e NI.
PRI, PR: priorità centrata sul valore 100. “20” si legge “120”.
NI: priorità statica (NICE) nell'intervallo [-20, +19].
54
Esercizi (2 min.)
1. Verificare che la priorità del processo di
archiviazione appena lanciato sia quella
impostata dall'utente.
55
Innalzamento della priorità statica
(Permission denied!)
Si scopre che il comando di archiviazione gira con
priorità 139 (o nice +19).
È possibile eseguire da utente normale lo stesso
comando con priorità più alte di quella di
default?
Una cosa del genere, per capirsi:
nice -n -20 tar zcvf backup.tar.gz /
57
Problema!
(Non si può alzare la priorità di un processo se non si è amministratori)
Il comando nice notifica l'impossibilità di alzare la
priorità con il seguente messaggio:
nice: cannot set niceness: Permission denied
Tuttavia, il comando viene eseguito lo stesso (con
la priorità di default).
Servono i diritti di amministratore per poter
elevare la priorità di un processo.
PS: si scopre ora perché il comando si chiama
nice...
58
Be nice to others!
(Lowering your process priority)
59
Modifica delle priorità statiche
(Il comando renice)
Il comando esterno renice permette di
modificare la priorità statica di un processo in
esecuzione.
Per il resto, le modalità di uso sono identiche a
quelle di nice.
tar zcf backup.tar.gz / & PID=$!
renice -n +19 -p $PID
60
Esercizi (5 min.)
2. Spiegare i due comandi appena dati.
Suggerimento: $! è una variabile interna di
BASH.
61
Una doverosa avvertenza
(Caveat emptor!)
È naturale fare la seguente associazione di idee:
lancio un comando con priorità bassa.
→ il processo relativo deve per forza eseguire meno
frequentemente.
Ciò non è sempre vero. Il processo esegue meno
frequentemente se tutte le CPU sono occupate!
Se esistono CPU disponibili, il processo viene
allocato ad una di esse ed esegue regolarmente!
63
Un piccolo esperimento
(Che dovrebbe dimostrare il punto precedente)
Si lanci il comando:
nice -n +19 ls -lR /
Il processo ls non sembra particolarmente
rallentato. È stato verosimilmente piazzato su una
CPU libera e può eseguire indisturbato.
IDEA: che succede se si occupano tutte le CPU
con processi CPU-bound, in modo tale da non
trovare una CPU libera?
Il processo lanciato a priorità bassa rallenta
64
veramente?
Occupazione delle CPU
(Quante CPU ha la macchina? Come occupare le CPU con processi CPU-bound?)
Si lanci il comando:
nproc
che stampa il numero di CPU logiche disponibili.
Si aprano tanti terminali quante sono le CPU e, su
ognuno di essi, si lanci un processo CPU-bound:
while true; do :; done
Ora nessuna CPU è libera.
65
Lancio del processo a priorità bassa
(Dovreste sperimentare un rallentamento)
Si lanci il comando:
nice -n +19 ls -lR /
Dovreste sperimentare un rallentamento
dell'output. Il processo ls sta combattendo
contro il processo CPU-bound che avete lanciato
prima.
66
Esercizi (2 min.)
3. Come si può rallentare ulteriormente il
processo ls?
67
Recap e domandone
(Domandone da 1M USD)
Linux schedula i processi tenendo conto di:
priorità assegnata.
boost di priorità anti-starvation.
DOMANDA: come fa il kernel a scegliere fra due
processi che hanno la stessa priorità?
→ Un algoritmo di scheduling deve gestire anche
questo caso (rognoso).
Perchè rognoso? Perché normalmente i processi
utente sono tutti alla stessa priorità!
Si sbaglia la scelta → il sistema rischia di perdere69
interattività.
Complete Fair Scheduler
(Descrizione fortemente semplificata)
I PCB dei processi sono ordinati in
un albero rosso-nero (red-black
tree).
La chiave di ordinamento è il tempo
di esecuzione già assegnato Δ.
Il boost di priorità viene applicato
abbassando Δ.
Il PCB più in basso a sinistra è il
candidato ideale alla esecuzione
(ha ricevuto meno tempo di CPU).
Esso viene eseguito per un tempo
tale da non rallentare troppo gli
altri processi.
Δ = 300
Δ = 100
Δ=0
Δ = 400
Δ = 150
Δ = 410
70
SCHEDULING AVANZATO
71
Coda di pronto globale
(Semplice da implementare, non scala con il numero di processori)
Coda globale: unica coda di pronto condivisa da
tutti i processori.
Lo scheduler pesca dalla coda di pronto e sceglie un
processore secondo un dato criterio.
Non scala benissimo con il numero di processori
(aumentano le contese dovute agli accessi
concorrenti).
72
Coda di pronto per CPU
(Più complicata da implementare, ma più CPU cache friendly)
Coda di pronto distribuita per CPU: ne esiste
una per ogni processore della macchina.
Lo scheduler può operare su più code allo stesso
istante.
Non esistono ritardi dovuti a misuso delle cache del
processore.
Soluzione preferita dai SO moderni su architetture
SMP.
Problema: come bilanciare i processi sulle
73
diverse code?
Predilezione
(Se il processo esegue sempre sulla stessa CPU, è CPU cache friendly)
Predilezione (CPU affinity): lo scheduler dei processi
cerca di eseguire il processo sempre sullo stesso
processore.
Stesso processore → dati in CPU cache → prestazione.
Predilezione debole (soft affinity): lo scheduler
rischedula il processo sempre sulla stessa CPU, a meno di
un bilanciamento delle code di scheduling.
Implementata nello scheduler di Linux.
Predilezione forte (hard affinity): l'utente impone che il
processo esegua sempre su una data CPU.
74
Bilanciamento del carico
(Tutte le code dovrebbero avere lo stesso numero di processi)
In uno scheduler con code di pronto locali, la tecnica di
predilezione rischia di sbilanciare fortemente il carico fra
processori. Si rende necessario il bilanciamento del
carico fra i diversi processori.
I processi sono spostati (migrati) da un processore
all'altro, in modo tale che il carico sui processori sia
grossomodo lo stesso.
Attenzione a bilanciare troppo spesso, però:
il bilanciamento annulla i benefici della predilezione!
75
Migrazione del carico
(Ovvero come spostare processi da una coda ad un'altra)
Migrazione guidata (push migration): un processo
dedicato controlla periodicamente la lunghezza delle
code. In caso di sbilanciamento, sposta i processi in
modo da bilanciare il carico.
Linux: bilanciamento controllato ed eseguito ogni 200ms.
Migrazione spontanea (pull migration): lo scheduler
sottrae un PCB ad una coda sovraccarica.
Linux: invocata quando la coda di pronto si svuota.
76
Gestione della predilezione
(Il comando taskset)
Il comando esterno taskset recupera e imposta
la maschera di affinità di un processo.
Maschera di affinità: maschera variabile di n bit
che imposta le CPU su cui il processo può
eseguire.
01010000 00000000 01000000 01010001
MSB: identifica Bit=0: il processo
la CPU #31
non può eseguire
su CPU #18
Bit=1: il processo
può eseguire su
CPU #6
LSB: identifica
la CPU #0
77
Esercizi (2 min.)
4. Si identifichi il pacchetto software contenente
l'eseguibile taskset e lo si installi.
78
Recupero dell'affinità
(Opzione -p)
L'opzione -p PID di taskset stampa il valore
esadecimale della maschera di affinità del
processo di PID pari a PID.
taskset -p 1
Il valore ritornato è, solitamente, 0xff (o 0xf su
macchine con poche CPU).
→ Il processo può essere schedulato su tutte le
CPU.
80
Impostazione dell'affinità
(Il processo esegue solo su CPU specifiche)
Il modo più semplice di impostare la maschera di
affinità è mediante l'opzione -c ID1, ID2,
…, IDn di taskset. Qui, gli ID sono numeri interi
nel range [0, #CPU – 1] che rappresentano le CPU
in maniera univoca.
taskset -c 0,1 → CPU #0, #1
In alternativa, si può non usare l'opzione -c e
passare il valore esadecimale della maschera
come primo argomento.
taskset 03
→ CPU #0, #1
81
Selezione di un processo esistente
(Uso perverso delle opzioni -p e -c)
Il processo può essere uno fra quelli già esistenti,
selezionabile tramite l'opzione -p e specificando
il suo PID come argomento.
Attenzione: le opzioni -p e -c vanno date in
questo ordine (pena un errore).
taskset -p -c 0,1 PID
82
Orrore!
(“First principles, Clarice. Simplicity. Read Marcus Aurelius.”)
83
Esecuzione di un nuovo processo
(Uso un po' meno perverso dell'opzione -c)
In alternativa è possibile omettere l'opzione -p e
specificare un comando come argomento. Tale
comando eseguirà sulle CPU prescelte.
taskset -c 0,1 top
Ora è chiaro il significato della semantica
perversa
vista
nella
slide
precedente.
L'argomento è un PID (opzione -p) oppure un
comando.
84
Su quale CPU esegue il processo?
(Comando pidstat, opzione -u)
L'output dell'opzione -u del comando pidstat
fornisce informazioni sul consumo di CPU.
È presente il campo CPU che stampa
l'identificatore della CPU su cui il processo sta
eseguendo attualmente.
pidstat -u 1
85
Esercizi (5 min.)
5. Si lancino cinque programmi CPU-bound su
una qualsiasi CPU della macchina. Si recuperi la
loro affinità. Si visualizzino le CPU su cui
eseguono i processi.
86
Classi di scheduling
(Perché, cari figlioli, dovete sapere che non esiste solo CFS!)
I processi sono suddivisi in classi disgiunte, dette
classi di scheduling.
SCHED_NORMAL: processi normali gestiti con
l'algoritmo CFS.
SCHED_FIFO: processi “real time” gestiti con
l'algoritmo FIFO.
FIFO: First In First Out senza quanto di tempo.
SCHED_RR: processi “real time” gestiti con
l'algoritmo Round Robin.
Round Robin: assegnazione circolare con quanto di88
tempo.
Gestione di classi di scheduling e priorità
(Il comando schedtool)
Il comando esterno schedtool gestisce alcuni
degli aspetti avanzati dello scheduling visti finora:
recupero informazioni di scheduling sui processi.
impostazione priorità (statiche e real time).
89
Esercizi (2 min.)
6. Si identifichi il pacchetto software contenente
l'eseguibile schedtool e lo si installi.
90
Recupero informazioni sui processi
(Nessuna opzione, elenco dei PID negli argomenti)
Se lanciato
senza opzioni;
con tanti argomenti numerici rappresentanti PID;
schedtool stampa le informazioni di
scheduling di tutti i processi associati a tali PID.
Ad esempio:
schedtool 1 10 $$
Il processo corrente
92
Recupero informazioni sui processi
(PID, priorità, classe e algoritmo di scheduling, priorità statiche e real time)
Sulla mia macchina l'output è il seguente.
PID
PID
PID
1: PRIO
10: PRIO
5912: PRIO
PID
0, POLICY N: SCHED_NORMAL
99, POLICY F: SCHED_FIFO
0, POLICY N: SCHED_NORMAL
Priorità Classi di scheduling
real
N → CFS
time
F → FIFO
, NICE
, NICE
, NICE
0, AFFINITY 0xff
0, AFFINITY 0x1
0, AFFINITY 0xff
Priorità
statica
Affinità
93
Impostazione delle priorità real time
(Opzioni -R, -F, -p)
È possibile impostare una qualsiasi delle classi di
scheduling a disposizione usando la relativa
opzione.
In questo corso introduttivo si considerano le
classi real time:
-F:
-R:
algoritmo real time FIFO.
algoritmo real time Round Robin.
Queste classi richiedono la priorità real time:
-p prio: imposta la priorità real time prio per gli
94
algoritmi Round Robin o FIFO.
Selezione di un processo esistente
(l PID del processo è passato come argomento)
Il processo può essere uno fra quelli già esistenti,
selezionabile specificando il suo PID come
argomento.
schedtool -R -p 40 $$
95
Esecuzione di un nuovo processo
(Si usa l'opzione -e e si passa il comando come argomento)
In alternativa è possibile aggiungere l'opzione -e
e specificare un comando come argomento. Tale
comando eseguirà con la priorità scelta.
schedtool -R -p 40 -e top
96
Qual è la priorità del processo?
(Comando pidstat, opzione -R)
L'output dell'opzione -R del comando pidstat
fornisce informazioni sulle priorità dei processi in
esecuzione (o selezionati tramite -p PID).
Sono presenti i campi seguenti:
PID: PID del processo
prio: priorità statica/real time
policy: classe di scheduling
pidstat -R -p $$ 1
97
Impostazione delle priorità real time
(Il processo mette le ali)
Si esegua un programma CPU-bound, ad
esempio:
while true; do :; done
Si ottenga il PID di tale processo.
pgrep -n bash
Si provi ad impostare:
una priorità real time (ad es., 70).
una classe di scheduling (ad es., Round Robin).
→ schedtool -R -p 70 PID
(Sostituire a PID l'output di pgrep).
98
Esercizi (5 min.)
7. Si lancino due comandi qualunque con le
seguenti priorità:
SCHED_NORMAL, nice 10
SCHED RR, prio 50
Si misurino le priorità durante l'esecuzione.
99
DISPATCHING
101
Cambio di contesto
(Come si salta di palo in frasca)
L'operazione di dispatching è nota anche con il
nome di cambio di contesto (context switch).
“Contesto” = risorse riferite dal PCB.
Durata di un cambio di contesto: qualche μs.
La funzione context_switch(), invocata da
schedule(), effettua il cambio di contesto vero
e proprio.
$LINUX/kernel/sched/core.c
102
Le operazioni di context_switch()
(Pure assembly, sigh)
Cambio aree di memoria: switch_mm().
Cambio registri e stack kernel: switch_to().
Queste funzioni:
salvano lo stato dei registri nel PCB del processo
uscente.
caricano i registri con i valori contenuti nel PCB del
processo entrante.
103
Il salto alla nuova traccia
(Avviene sovrascrivendo IP)
Uno dei registri è l'Instruction Pointer. Una volta
modificato tale registro, si salta direttamente al
prossimo processo.
→ la schedule() invocata dal processo uscente
rimane eseguita a metà. Essa terminerà quando il
processo uscente sarà rischedulato.
104
Un diagramma di esempio
(Salto alla nuova traccia)
Processo
A
User
Kernel
schedule()
PCB
context_switch()
switch_to()
Sovrascrittura IP
105
Un diagramma di esempio
(Salto alla nuova traccia)
Processo
B
Esecuzione...
User
Kernel
PCB
106
Un diagramma di esempio
(Salto alla nuova traccia)
Processo
B
User
Kernel
schedule()
PCB
context_switch()
switch_to()
Sovrascrittura IP
107
Un diagramma di esempio
(Salto alla nuova traccia)
Processo
A
User
Kernel
schedule()
PCB
Parte finale di
schedule()
108
Visione dei cambi di contesto
(Comando pidstat, opzione -w)
L'output dell'opzione -w del comando pidstat
fornisce informazioni sui cambi di contesto.
Sono presenti i campi seguenti:
cswtch/s:numero di cambi di contesto volontari al
secondo (il processo chiede una risorsa
non disponibile al momento).
nvcswtch/s: numero di cambi di contesto non
volontari al secondo (una interruzione
blocca il processo).
pidstat -w 1
109
Il significato di cswtch/s
(Il processo tende ad essere CPU-bound o I/O-bound?)
cswtch/s basso: il processo
non richiede quasi mai risorse bloccanti.
tende a consumare tutto il suo quanto di tempo.
è probabilmente CPU-bound.
cswtch/s alto: il processo
richiede molto spesso risorse bloccanti.
tende a consumare poco tempo di CPU.
è probabilmente I/O-bound.
Processo
CPU-bound
Processo
I/O-bound
cswtch/s 110
Esercizi (3 min.)
8. Si lancino due comandi qualunque con le
seguenti proprietà:
uno CPU-bound.
uno che utilizza il disco.
Si misurino i cambi di contesto volontari durante
l'esecuzione.
111