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
© Copyright 2024 Paperzz