(:title Sistemi Operativi - Lezione del 21 aprile 2008:)
:: Sistemi Operativi - Lezione del 21 aprile 2008 ::
%sottotitoloTecniche di allocazione dei frame
Come decidere di allocare i frame? Senza fare riferimenti ai segmenti, ma parlando solo di pagine, vediamo un po' quante pagine dare ad un processo.
- Poche pagine = tanti probabili page fault
- Tante pagine = meno pagefault, e tempo di accesso medio inferiore
Da notare, che anche se do TUTTA la memoria fisica ad un processo, non è detto che non avrò pagefault, per il semplice motivo che se lo spazio di indirizzamento del processo è superiore alla memoria installata, ovviamente non ci starà:)
Quindi, al solito occorre trovare la risposta giusta ad una domanda: quanti frame allocare ad un processo?
Vincoli
Dobbiamo tenere presente alcuni vincoli, prima di decidere i numeri. Uno di questi deriva dall'architettura del computer, che riguarda anche le dimensioni dele pagine.
Poi, devo tenere conto anche della condivisione
fi = numero di frames allocati al processo Pi
fj = numero di frame utlizzati dal gruppo di processi Gj
d = frame disponibili
Σifi - Σjfj <= d
Il gruppo di processi è composto da processi che condividono dei frame, per i motivi che abbiamo visto nelle pagine precedenti.
Ho due strategie per andare a prendere i frame da allocare:
- globale = tutti i frame liberi sono a disposizione di qualsiasi processo
- locale = un processo ha a disposizione un tot di frame e basta
Allocazione omogenea
Ogni processo ha la stessa quantità di frame di ogni altro processo correntemente in memoria
m = numero totale di frame
n = numero di processi
fi = frame del processo Pi
fi = m /n
In questo modello, anche il SO ha lo stesso numero di frame dei processi normali. Al massimo pretenderà un trattamento migliore.
Più ho processi in memoria, meno pagine posso dare al singolo processo. Ciò vuol dire che c'è un limite alla multiprogrammazione. Allo stesso tempo, se ho pochi processi in memoria, ne ho al converso tanti nello spazio di swap che non avanzano pur magari potendolo fare.
L'idea sarebbe allora di tenere in memoria i processi ready e quelli che presumo lo saranno a breve. Come fare ciò, è un mistero...
Allocazione proporzionale alla dimensione
Consideriamo questi due casi:
- programmi con tanti dati = accedono a strutture dati ciccione, e quindi spantegate in diverse pagine
- programmi con codice ampio = hanno routine qua e là per tutto lo spazio di indirizzamento
L'allocazione omogenea tratterebbe molto male questi processi. Per migliorarla, si è pensato di allocare tanti frame quanto più un processo è grosso:
si = dimensione dello spazio virtuale occupato dal processo Pi
fi = m * Si / Σisi
Allocazione proporzionale alla priorità
Finora, ho trattato tutti i processi come se fossero uguali dal punto di vista del processore. Però, ho scenari in cui i processi hanno priorità diverse, e sembra allora giusto poter dar loro più frame così che perdano meno tempo a caricare roba dal disco.
Ecco perché parla di allocazione proporzionale alla priorità.
fi = m * pi / Σipi
Ocio: questa formula è scritta con il presupposto che p alto = priorità alta. Non sempre è così (eg Unix).
Generalizzando, posso decidere di allocare in modo proporzionale a qualsiasi parametro che più mi garba.
Lezione 4 - Thrashing
Se un processo ha poche pagine in memoria, passa più tempo a caricare e scambiarle col disco fisso che nemmeno a progredire. Questo è ciò che si dice thrashing, ovvero paginazione della spazzatura.
Le cause del thrashing sono l'elevata multiprogrammazione, e il fatto che lo scheduler a lungo termine introduca in memoria molti processi per tenere occupato il processore.
Per evitare il thrashing, devo avere:
- una politica di schedulazione che eviti il thrashing, cioè che limiti la multiprogrammazione arrivati ad una certa soglia
- una politica di allocazione dei frame che riduca il numero dei frame che possono essere tolti dalla memoria
Per ottenere il secondo punto, serve una allocazione locale. Un processo ha un numero minimo di frame, sotto al quale non deve scendere. Quando devo scegliere la vittima, la scelgo tra i frame dello stesso processo che ne ha bisogno, così che i suoi bisogni non vadano a thrashare gli altri processi: al massimo ci smena soltanto lui, piuttosto che l'intero sistema.
Prevenzione del thrashing
I programmi hanno una piacevole caratteristica, detto principio di località di esecuzione.
Questa proprietà mi dice che, nell'intorno di un certo istante di tempo, un processo accederà tipicamente solo a certe zone del suo spazio di indirizzamento, e non ad altre. Poi, dopo un po' cambierà zone, poi ancora etc., ma in ognuna di queste diverse configurazioni ci passerà un po' di tempo.
Come è possibile sta magia? È possibile per via di come sono. scritti i programmi. Se uso tecniche di buona programmazione, e ancor meglio se uso linguaggi di alto livello, tendenzialmente scriverò codice raggruppato in moduli, che il compilatore ed il linker metteranno in zone attigue della memoria.
Se riesco a sfruttare questo principio, riduco il numero di page fault. Ma come, visto che non ho nessuna sfera di cristallo? Posso approssimare con 2 tecniche:
- il working set
- la page-fault frequency
Working Set
Nella mia stringa di riferimento, definisco una finestra di dimensione Δ, che contiene quindi un certo numero di pagine precedenti a quella in cui mi trovo in un dato istante. Questo insieme di pagine nella finestra Δ è detto appunto working set, cioè insieme di lavoro.
Man mano che procedo nella stringa di riferimento, il WS si sposta, e i frame che stanno prima della finestra sono passibili di scalzamento.
La formuletta magica è
fi = maxt (wsi(t))
Più Δ è grande, più frame un processo ha in memoria, meno pagefault ho.
Se invece capita che
Σk fk > m
dove m è il numero di pagine disponibili, allora scelgo una vittima e la scarico.
Il brutto è che devo avere una stringa di riferimento per poter usare il WS, e la stringa non ce l'ho finché non è un po' che eseguo il programma...
Page fault frequency

Round-Robin Hood
Se ho poche miss, sto usando troppe pagine per il processo.
Se ho poche pagine, posso rischiare il thrashing.
Allora, in medio stat virtus, e cerco di far sì che ogni processo si mantenga ad un certo livello di frequenza di page fault, così da evitare le due situazioni estreme.
I processi ricchi di pagine vengono depauperati, quelli poveri arricchiti. Esattamente come faceva Robin Hood.
Lezione 5 - Ottimizzazione delle prestazioni
Prepaginazione
Sceglo prima quali pagine caricare, prevedendo magicamente quali pagine il processo utilizzerà. Il problema è che non ho in anticipo la stringa di riferimento.
Un processo, comunque, ha la sua frequenza massima di page fault quando viene avviato (perché non ha ancora caricato niente) e quando viene riattivato, perché deve riguadagnarsi lo spazio.
Almeno nel secondo caso, quando riattivo un processo, carico anche tutto il WS precedente, così da cercare di ottimizzare un po' l'uso delle pagine.
Dimensione della pagina
La dimensione della pagina ideale dipende da alcuni fattori.
Se ho pagine grandi, ho queste conseguenze:
- uso meno pagine => tabella piccola
- frammentazione interna (pagine poco sfruttate) e spreco di memoria
- tempi di s/caricamento minore
- minore risoluzione (il programma è spezzettato in un minor numero di pagine)
- possibili più page fault, perché carico roba per meno processi in memoria, avendo le pagine grosse.
Se ho invece pagine piccole:
- più pagine
- tabella più grossa
- minore frammentazione interna e meno spreco
- più tempo per s/caricamento (tempo totale, perché lo faccio più volte)
- più località
- più risoluzione
- possibile minor numero di page fault
TLB
Vedi le lezioni precedenti per sapere che cosa è.
Tabella invertita delle pagine
Anche per questa, vedi le lezioni precedenti
Strutturazione del programma
È quello che dicevamo prima, con l'aggiunta che le strutture dati usate influenzano la "resa" del programma.
Infatti, se devo accedere ad una matrice per righe, è meglio salvare per righe, così è più facile che ci stia una riga in una pagina. Se salvassi per colonne, potrei invece dover saltare di pagina in pagina per ogni colonna.
Pagine residenti per dispositivi di I/O
I buffer dei dispositivi I/O li lascio in memoria, marcandoli come residenti, così le I/O durano meno.
Pagine residenti per processi in tempo reale
Così non perdono tempo a caricare dal disco. Per maggiori spiegazioni vedi lo scheduling su sistemi a tempo reale.
:: I/O ::