5df509eaa2
always work and is no use anyway
1045 lines
56 KiB
XML
1045 lines
56 KiB
XML
<?xml version="1.0" encoding="iso-8859-15"?>
|
|
<!DOCTYPE article PUBLIC "-//FreeBSD//DTD DocBook XML V4.5-Based Extension//EN"
|
|
"../../../share/xml/freebsd45.dtd">
|
|
|
|
<!--
|
|
The FreeBSD Italian Documentation Project
|
|
|
|
$FreeBSD$
|
|
Original revision: 1.16
|
|
-->
|
|
|
|
<article lang="it">
|
|
<articleinfo>
|
|
<title>Elementi di progettazione del sistema di VM di FreeBSD</title>
|
|
|
|
<authorgroup>
|
|
<author>
|
|
<firstname>Matthew</firstname>
|
|
|
|
<surname>Dillon</surname>
|
|
|
|
<affiliation>
|
|
<address>
|
|
<email>dillon@apollo.backplane.com</email>
|
|
</address>
|
|
</affiliation>
|
|
</author>
|
|
</authorgroup>
|
|
|
|
<legalnotice id="trademarks" role="trademarks">
|
|
&tm-attrib.freebsd;
|
|
&tm-attrib.linux;
|
|
&tm-attrib.microsoft;
|
|
&tm-attrib.opengroup;
|
|
&tm-attrib.general;
|
|
</legalnotice>
|
|
|
|
<pubdate>$FreeBSD$</pubdate>
|
|
|
|
<releaseinfo>$FreeBSD$</releaseinfo>
|
|
|
|
<abstract>
|
|
<para>Il titolo è in realtà solo un modo complicato per dire
|
|
che cercherò di descrivere l'intera enchilada della memoria
|
|
virtuale (VM), sperando di farlo in una maniera che chiunque possa
|
|
seguire.
|
|
Nell'ultimo anno mi sono concentrato su un certo numero di sottosistemi
|
|
principali del kernel in FreeBSD, trovando quelli della VM (la memoria
|
|
virtuale) e dello Swap i più interessanti, e considerando quello
|
|
di NFS <quote>un lavoretto necessario</quote>.
|
|
Ho riscritto solo piccole porzioni di quel codice. Nell'arena
|
|
della VM la sola grossa riscrittura che ho affrontato è stata
|
|
quella del sottosistema di swap.
|
|
La maggior parte del mio lavoro è stato di pulizia e
|
|
mantenimento, con solo alcune moderate riscritture di codice e
|
|
nessuna correzione rilevante a livello algoritmico nel sottosistema
|
|
della VM. Il nocciolo della base teorica del sottosistema
|
|
rimane immutato ed un bel po' del merito per gli sforzi di
|
|
modernizzazione negli ultimi anni appartiene a John Dyson e David
|
|
Greenman. Poiché non sono uno storico come Kirk non
|
|
tenterò di marcare tutte le varie caratteristiche con i nomi
|
|
delle relative persone, perché sbaglierei
|
|
invariabilmente.</para>
|
|
|
|
&trans.it.surrender;
|
|
</abstract>
|
|
|
|
<legalnotice>
|
|
<para>Questo articolo è stato pubblicato in origine nel numero di
|
|
gennaio 2000 di <ulink
|
|
url="http://www.daemonnews.org/">DaemonNews</ulink>.
|
|
Questa versione dell'articolo può includere aggiornamenti da
|
|
parte di Matt e di altri autori per riflettere i cambiamenti
|
|
nell'implementazione della VM di FreeBSD.</para>
|
|
</legalnotice>
|
|
</articleinfo>
|
|
|
|
<sect1 id="introduction">
|
|
<title>Introduzione</title>
|
|
|
|
<para>Prima di andare avanti con la descrizione del progetto effettivo
|
|
della VM spendiamo un po' di tempo sulla necessità di mantenere
|
|
e modernizzare una qualunque base di codice longeva.
|
|
Nel mondo della programmazione, gli algoritmi tendono ad essere più
|
|
importanti del codice ed è dovuto alle radici accademiche
|
|
di BSD che si è prestata grande attenzione alla progettazione
|
|
algoritmica sin dal principio.
|
|
Una maggiore attenzione al design in genere conduce ad una base di codice
|
|
flessibile e pulita che può essere modificata abbastanza
|
|
semplicemente, estesa, o rimpiazzata nel tempo.
|
|
Mentre BSD viene considerato un sistema operativo <quote>vecchio</quote>
|
|
da alcune persone, quelli di noi che lavorano su di esso tendono
|
|
a considerarlo come una base di codice <quote>matura</quote>
|
|
che ha vari componenti modificati, estesi, o rimpiazzati con codice
|
|
moderno. Questa si è evoluta, e FreeBSD è all'avanguardia,
|
|
non importa quanto possa essere vecchio qualche pezzo di codice.
|
|
Questa è una distinzione importante da fare ed una di quelle che
|
|
sfortunatamente sfuggono alla maggior parte delle persone. Il più
|
|
grande errore che un programmatore possa fare è non imparare
|
|
dalla storia, e questo è precisamente l'errore che molti sistemi
|
|
operativi moderni hanno commesso. &windowsnt; è il miglior esempio
|
|
di questo, e le conseguenze sono state disastrose. Anche Linux commette
|
|
questo errore a un certo livello—abbastanza perché noi
|
|
appassionati di BSD possiamo scherzarci su ogni tanto, comunque.
|
|
Il problema di Linux è semplicemente la mancanza di esperienza e
|
|
di una storia con la quale confrontare le idee, un problema che sta
|
|
venendo affrontato rapidamente dalla comunità Linux nello stesso
|
|
modo in cui è stato affrontato da quella BSD—con il continuo
|
|
sviluppo di codice. La gente di &windowsnt;, d'altro canto, fa
|
|
ripetutamente gli stessi errori risolti da &unix; decadi fa e poi impiega
|
|
anni nel risolverli. E poi li rifanno, ancora, e ancora.
|
|
Soffrono di un preoccupante caso di <quote>non è stato progettato
|
|
qui</quote> e di <quote>abbiamo sempre ragione perché il nostro
|
|
dipartimento marketing dice così</quote>. Io ho pochissima
|
|
tolleranza per chiunque non impari dalla storia.</para>
|
|
|
|
<para>La maggior parte dell'apparente complessità di progettazione di
|
|
FreeBSD, specialmente nel sottosistema VM/Swap, è una conseguenza
|
|
diretta dell'aver dovuto risolvere importanti problemi di prestazioni
|
|
legati a varie condizioni. Questi problemi non sono dovuti a cattivi
|
|
progetti algoritmici ma sorgono invece da fattori ambientali.
|
|
In ogni paragone diretto tra piattaforme, questi problemi
|
|
diventano più evidenti quando le risorse di sistema cominciano ad
|
|
essere stressate.
|
|
Mentre descrivo il sottosistema VM/Swap di FreeBSD il lettore
|
|
dovrebbe sempre tenere a mente almeno due punti. Primo, l'aspetto
|
|
più importante nel design prestazionale è ciò che
|
|
è noto come <quote>Ottimizzazione del Percorso Critico</quote>.
|
|
Accade spesso che le ottimizzazioni prestazionali aggiungano
|
|
un po di impurità al codice per far migliorare il percorso critico.
|
|
Secondo, un progetto solido e generalizzato, funziona meglio di
|
|
un progetto pesantemente ottimizzato, alla lunga. Mentre un progetto
|
|
generale può alla fin fine essere più lento di un sistema
|
|
pesantemente ottimizzato quando vengono implementati inizialmente, il
|
|
progetto generalizzato tende ad essere più semplice da adattare
|
|
alle condizioni variabili mentre quello pesantemente ottimizzato finisce
|
|
per dover essere gettato via. Ogni base di codice che dovrà
|
|
sopravvivere ed essere mantenibile per anni deve dunque essere progettata
|
|
con attenzione fin dall'inizio anche se questo può portare a
|
|
piccoli peggioramenti nelle prestazioni.
|
|
Vent'anni fa c'era ancora gente che sosteneva che programmare in assembly
|
|
era meglio che programmare in linguaggi di alto livello, perché
|
|
si poteva produrre codice che era dieci volte più veloce. Oggi,
|
|
la fallacia di tale argomento è ovvia—così come i
|
|
paralleli con il design algoritmico e la generalizzazione del
|
|
codice.</para>
|
|
</sect1>
|
|
|
|
<sect1 id="vm-objects">
|
|
<title>Oggetti VM</title>
|
|
|
|
<para>Il modo migliore per iniziare a descrivere il sistema di VM di FreeBSD
|
|
è guardandolo dalla prospettiva di un processo a livello
|
|
utente. Ogni processo utente vede uno spazio di indirizzamento della VM
|
|
singolo, privato e contiguo, contenente molti tipi di oggetti di memoria.
|
|
Questi oggetti hanno varie caratteristiche.
|
|
Il codice del programma e i dati del programma sono effettivamente
|
|
un singolo file mappato in memoria (il file binario che è stato
|
|
eseguito), ma il codice di programma è di sola lettura mentre i
|
|
dati del programma sono copy-on-write <footnote>
|
|
<para>I dati copy on write sono dati che vengono copiati solo al momento
|
|
della loro effettiva modifica</para>
|
|
</footnote>. Il BSS del programma è solamente una zona di memoria
|
|
allocata e riempita con degli zero su richiesta, detta in inglese
|
|
<quote>demand zero page fill</quote>.
|
|
Nello spazio di indirizzamento possono essere mappati anche file
|
|
arbitrari, che è in effetti il meccanismo con il quale funzionano
|
|
le librerie condivise. Tali mappature possono richiedere modifiche per
|
|
rimanere private rispetto al processo che le ha effettuate.
|
|
La chiamata di sistema fork aggiunge una dimensione completamente nuova
|
|
al problema della gestione della VM in cima alla complessità
|
|
già data.</para>
|
|
|
|
<para>Una pagina di dati di un programma (che è una basilare pagina
|
|
copy-on-write) illustra questa complessità. Un programma binario
|
|
contiene una sezione di dati preinizializzati che viene inizialmente
|
|
mappata direttamente in memoria dal file del programma.
|
|
Quando un programma viene caricato nello spazio di memoria virtuale di un
|
|
processo, questa area viene inizialmente copiata e mappata in memoria dal
|
|
binario del programma stesso, permettendo al sistema della VM di
|
|
liberare/riusare la pagina in seguito e poi ricaricarla dal binario.
|
|
Nel momento in cui un processo modifica questi dati, comunque, il
|
|
sistema della VM deve mantenere una copia privata della pagina per quel
|
|
processo. Poiché la copia privata è stata modificata, il
|
|
sistema della VM non può più liberarlo, poiché non ci
|
|
sarebbe più nessuna possibilità di recuperarlo in
|
|
seguito.</para>
|
|
|
|
<para>Noterai immediatamente che quella che in origine era soltanto
|
|
una semplice mappatura di un file è diventata qualcosa di
|
|
più complesso.
|
|
I dati possono essere modificati pagina per pagina
|
|
mentre una mappatura di file coinvolge molte pagine alla volta.
|
|
La complessità aumenta ancora quando un processo esegue una fork.
|
|
Quando un processo esegue una fork, il risultato sono due
|
|
processi—ognuno con il proprio spazio di indirizzamento privato,
|
|
inclusa ogni modifica fatta dal processo originale prima della chiamata a
|
|
<function>fork()</function>. Sarebbe stupido per un sistema di VM creare
|
|
una copia completa dei dati al momento della <function>fork()</function>
|
|
perché è abbastanza probabile che almeno uno dei due
|
|
processi avrà bisogno soltanto di leggere da una certa pagina da
|
|
quel momento in poi, permettendo di continuare ad usare la
|
|
pagina originale. Quella che era una pagina privata viene di nuovo
|
|
resa una copy-on-write, poiché ogni processo (padre e figlio) si
|
|
aspetta che i propri cambiamenti rimangano privati per loro e non abbiano
|
|
effetti sugli altri.</para>
|
|
|
|
<para>FreeBSD gestisce tutto ciò con un modello a strati di oggetti
|
|
VM. Il file binario originale del programma risulta come lo strato di
|
|
Oggetti VM più basso.
|
|
Un livello copy-on-write viene messo sopra questo per mantenere quelle
|
|
pagine che sono state copiate dal file originale.
|
|
Se il programma modifica una pagina di dati appartenente al file originale
|
|
il sistema dell VM prende un page fault <footnote>
|
|
<para>Un page fault, o <quote>mancanza di pagina</quote>,
|
|
corrisponde ad una mancanza di una determinata pagina di memoria a un
|
|
certo livello, ed alla necessità di copiarla da un livello
|
|
più lento. Ad esempio se una pagina di memoria è stata
|
|
spostata dalla memoria fisica allo spazio di swap su disco, e viene
|
|
richiamata, si genera un page fault e la pagina viene di nuovo copiata
|
|
in ram.</para>
|
|
</footnote> e fa una copia della pagina nel livello più alto.
|
|
Quando un processo effettua una fork, vengono aggiunti altri livelli di
|
|
Oggetti VM. Tutto questo potrebbe avere un po' più senso con un
|
|
semplice esempio.
|
|
Una <function>fork()</function> è un'operazione comune per ogni
|
|
sistema *BSD, dunque questo esempio prenderà in considerazione un
|
|
programma che viene avviato ed esegue una fork. Quando il processo viene
|
|
avviato, il sistema della VM crea uno starto di oggetti, chiamiamolo
|
|
A:</para>
|
|
|
|
<mediaobject>
|
|
<imageobject>
|
|
<imagedata fileref="fig1" format="EPS"/>
|
|
</imageobject>
|
|
|
|
<textobject>
|
|
<literallayout class="monospaced">+---------------+
|
|
| A |
|
|
+---------------+</literallayout>
|
|
</textobject>
|
|
|
|
<textobject>
|
|
<phrase>Un'immagine</phrase>
|
|
</textobject>
|
|
</mediaobject>
|
|
|
|
<para>A rappresenta il file—le pagine possono essere
|
|
spostate dentro e fuori dal mezzo fisico del file se necessario.
|
|
Copiare il file dal disco è sensato per un programma,
|
|
ma di certo non vogliamo effettuare il page out <footnote>
|
|
<para>La copia dalla memoria al disco, l'opposto del page in, la
|
|
mappatura in memoria.</para>
|
|
</footnote> e sovrascrivere l'eseguibile.
|
|
Il sistema della VM crea dunque un secondo livello, B, che verrà
|
|
copiato fisicamente dallo spazio di swap:</para>
|
|
|
|
<mediaobject>
|
|
<imageobject>
|
|
<imagedata fileref="fig2" format="EPS"/>
|
|
</imageobject>
|
|
|
|
<textobject>
|
|
<literallayout class="monospaced">+---------------+
|
|
| B |
|
|
+---------------+
|
|
| A |
|
|
+---------------+</literallayout>
|
|
</textobject>
|
|
</mediaobject>
|
|
|
|
<para>Dopo questo, nella prima scrittura verso una pagina, viene creata una
|
|
nuova pagina in B, ed il suo contenuto viene inizializzato con i dati di
|
|
A. Tutte le pagine in B possono essere spostate da e verso un dispositivo
|
|
di swap. Quando il programma esegue la fork, il sistema della VM crea
|
|
due nuovi livelli di oggetti—C1 per il padre e C2 per il
|
|
figlio—che restano sopra a B:</para>
|
|
|
|
<mediaobject>
|
|
<imageobject>
|
|
<imagedata fileref="fig3" format="EPS"/>
|
|
</imageobject>
|
|
|
|
<textobject>
|
|
<literallayout class="monospaced">+-------+-------+
|
|
| C1 | C2 |
|
|
+-------+-------+
|
|
| B |
|
|
+---------------+
|
|
| A |
|
|
+---------------+</literallayout>
|
|
</textobject>
|
|
</mediaobject>
|
|
|
|
<para>In questo caso, supponiamo che una pagina in B venga modificata dal
|
|
processo genitore. Il processo subirà un fault di copy-on-write e
|
|
duplicherà la pagina in C1, lasciando la pagina originale in B
|
|
intatta.
|
|
Ora, supponiamo che la stessa pagina in B venga modificata dal processo
|
|
figlio. Il processo subirà un fault di copy-on-write e
|
|
duplicherà la pagina in C2.
|
|
La pagina originale in B è ora completamente nascosta poiché
|
|
sia C1 che C2 hanno una copia e B potrebbe teoricamente essere distrutta
|
|
(se non rappresenta un <quote>vero</quote> file);
|
|
comunque, questo tipo di ottimizzazione non è triviale da
|
|
realizzare perché è di grana molto fine.
|
|
FreeBSD non effettua questa ottimizzazione.
|
|
Ora, supponiamo (come è spesso il caso) che
|
|
il processo figlio effettui una <function>exec()</function>. Il suo
|
|
attuale spazio di indirizzamento è in genere rimpiazzato da un
|
|
nuovo spazio di indirizzamento rappresentante il nuovo file.
|
|
In questo caso il livello C2 viene distrutto:</para>
|
|
|
|
<mediaobject>
|
|
<imageobject>
|
|
<imagedata fileref="fig4" format="EPS"/>
|
|
</imageobject>
|
|
|
|
<textobject>
|
|
<literallayout class="monospaced">+-------+
|
|
| C1 |
|
|
+-------+-------+
|
|
| B |
|
|
+---------------+
|
|
| A |
|
|
+---------------+</literallayout>
|
|
</textobject>
|
|
</mediaobject>
|
|
|
|
<para>In questo caso, il numero di figli di B scende a uno, e tutti gli
|
|
accessi a B avvengono attraverso C1. Ciò significa che B e C1
|
|
possono collassare insieme in un singolo strato.
|
|
Ogni pagina in B che esista anche in C1 viene cancellata da
|
|
B durante il crollo. Dunque, anche se l'ottimizzazione nel passo
|
|
precedente non era stata effettuata, possiamo recuperare le pagine morte
|
|
quando il processo esce o esegue una <function>exec()</function>.</para>
|
|
|
|
<para>Questo modello crea un bel po' di problemi potenziali. Il primo
|
|
è che ci si potrebbe ritrovare con una pila abbastanza profonda di
|
|
Oggetti VM incolonnati che costerebbe memoria e tempo per la ricerca
|
|
quando accadesse un fault. Può verificarsi un ingrandimento della
|
|
pila quando un processo esegue una fork dopo l'altra (che sia il padre o
|
|
il figlio). Il secondo problema è che potremmo ritrovarci con
|
|
pagine morte, inaccessibili nella profondità della pila degli
|
|
Oggetti VM. Nel nostro ultimo esempio se sia il padre che il figlio
|
|
modificano la stessa pagina, entrambi hanno una loro copia della pagina e
|
|
la pagina originale in B non è più accessibile
|
|
da nessuno. Quella pagina in B può essere liberata.</para>
|
|
|
|
<para>FreeBSD risolve il problema della profondità dei livelli con
|
|
un'ottimizzazione speciale detta <quote>All Shadowed Case</quote> (caso
|
|
dell'oscuramento totale).
|
|
Questo caso accade se C1 o C2 subiscono sufficienti COW fault (COW
|
|
è l'acronimo che sta per copy on write) da oscurare completamente
|
|
tutte le pagine in B.
|
|
Ponimo che C1 abbia raggiunto questo livello. C1 può ora
|
|
scavalcare B del tutto, dunque invece di avere C1->B->A e C2->B->A adesso
|
|
abbiamo C1->A e C2->B->A.
|
|
ma si noti cos'altro è accaduto—ora B ha solo un riferimento
|
|
(C2), dunque possiamo far collassare B e C2 insieme.
|
|
Il risultato finale è che B viene cancellato
|
|
interamente e abbiamo C1->A e C2->A. Spesso accade che B contenga un
|
|
grosso numero di pagine e ne' C1 ne' C2 riescano a oscurarlo
|
|
completamente. Se eseguiamo una nuova fork e creiamo un insieme di
|
|
livelli D, comunque, è molto più probabile che uno dei
|
|
livelli D sia eventualmente in grado di oscurare completamente l'insieme
|
|
di dati più piccolo rappresentato da C1 o C2. La stessa
|
|
ottimizzazione funzionerà in ogni punto nel grafico ed il
|
|
risultato di ciò è che anche su una macchina con
|
|
moltissime fork le pile degli Oggetti VM tendono a non superare una
|
|
profondità di 4. Ciò è vero sia per il padre che per
|
|
il figlio ed è vero nel caso sia il padre a eseguire la fork ma
|
|
anche se è il figlio a eseguire fork in cascata.</para>
|
|
|
|
<para>Il problema della pagina morta esiste ancora nel caso C1 o C2 non
|
|
oscurino completamente B. A causa delle altre ottimizzazioni questa
|
|
eventualità
|
|
non rappresenta un grosso problema e quindi permettiamo semplicemente
|
|
alle pagine di essere morte. Se il sistema si trovasse con poca memoria
|
|
le manderebbe in swap, consumando un po' di swap, ma così
|
|
è.</para>
|
|
|
|
<para>Il vantaggio del modello ad Oggetti VM è che
|
|
<function>fork()</function> è estremamente veloce, poiché
|
|
non deve aver luogo nessuna copia di dati effettiva. Lo svantaggio
|
|
è che è possibile costruire un meccanismo a livelli di
|
|
Oggetti VM relativamente complesso che rallenterebbe la gestione dei page
|
|
fault, e consumerebbe memoria gestendo le strutture degli Oggetti VM.
|
|
Le ottimizazioni realizzate da FreeBSD danno prova di ridurre
|
|
i problemi abbastanza da poter essere ignorati, non lasciando
|
|
nessuno svantaggio reale.</para>
|
|
</sect1>
|
|
|
|
<sect1 id="swap-layers">
|
|
<title>Livelli di SWAP</title>
|
|
|
|
<para>Le pagine di dati private sono inizialmente o pagine
|
|
copy-on-write o pagine zero-fill.
|
|
Quando avviene un cambiamento, e dunque una copia, l'oggetto di copia
|
|
originale (in genere un file) non può più essere utilizzato
|
|
per salvare la copia quando il sistema della VM ha bisogno di
|
|
riutilizzarla per altri scopi. A questo punto entra in gioco lo SWAP. Lo
|
|
SWAP viene allocato per creare spazio dove salvare memoria che altrimenti
|
|
non sarebbe disponibile. FreeBSD alloca la struttura di gestione di
|
|
un Oggetto VM solo quando è veramente necessario.
|
|
Ad ogni modo, la struttura di gestione dello swap ha avuto storicamente
|
|
dei problemi.</para>
|
|
|
|
<para>Su FreeBSD 3.X la gestione della struttura di swap prealloca un
|
|
array che contiene l'intero oggetto che necessita di subire
|
|
swap—anche se solo poche pagine di quell'oggetto sono effettivamente
|
|
swappate questo crea una frammentazione della memoria del kernel quando
|
|
vengono mappati oggetti grandi, o processi con grandi dimensioni
|
|
all'esecuzione (large runsizes, RSS). Inoltre, per poter tenere traccia
|
|
dello spazio di swap, viene mantenuta una <quote>lista dei buchi</quote>
|
|
nella memoria del kernel, ed anche questa tende ad essere pesantemente
|
|
frammentata. Poiché la <quote>lista dei buchi</quote> è una
|
|
lista lineare, l'allocazione di swap e la liberazione hanno prestazioni
|
|
non ottimali O(n) per ogni pagina.
|
|
Questo richiede anche che avvengano allocazioni di memoria
|
|
durante il processo di liberazione dello swap, e questo crea
|
|
problemi di deadlock, blocchi senza uscita, dovuti a scarsa memoria.
|
|
Il problema è ancor più esacerbato dai buchi creati a causa
|
|
dell'algoritmo di interleaving.
|
|
Inoltre il blocco di swap può divenire frammentato molto facilmente
|
|
causando un'allocazione non contigua. Anche la memoria del Kernel deve
|
|
essere allocata al volo per le strutture aggiuntive di gestione dello
|
|
swap quando avviene uno swapout. È evidente che c'era molto spazio
|
|
per dei miglioramenti.</para>
|
|
|
|
<para>Per FreeBSD 4.X, ho completamente riscritto il sottosistema di swap.
|
|
Con questa riscrittura, le strutture di gestione dello swap vengono
|
|
allocate attraverso una tabella di hash invece che con un array lineare
|
|
fornendo una dimensione di allocazione fissata e una granularità
|
|
molto maggiore.
|
|
Invece di usare una lista lineare collegata per tenere traccia delle
|
|
riserve di spazio di swap, essa usa una mappa di bit di blocchi di swap
|
|
organizzata in una struttura ad albero radicato con riferimenti allo
|
|
spazio libero nelle strutture nei nodi dell'albero. Ciò rende in
|
|
effetti l'operazione di allocazione e liberazione delle risorse
|
|
un'operazione O(1).
|
|
L'intera mappa di bit dell'albero radicato viene anche preallocata in modo
|
|
da evitare l'allocazione di memoria kernel durante le operazioni di swap
|
|
critiche nei momenti in cui la memoria disponibile è ridotta.
|
|
Dopo tutto, il sistema tende a fare uso dello swap quando ha poca memoria
|
|
quindi dovremmo evitare di allocare memoria per il kernel in quei momenti
|
|
per poter evitare potenziali deadlock. Infine, per ridurre la
|
|
frammentazione l'albero radicato è in grado di allocare grandi
|
|
spezzoni contigui in una volta, saltando i pezzetti frammentati.
|
|
Non ho ancora compiuto il passo finale di avere un <quote>puntatore di
|
|
supportoall'allocazione</quote> che scorra su una porzione di swap nel
|
|
momento in cui vengano effettuate delle allocazioni, in modo da garantire
|
|
ancor di più le allocazioni contigue o almeno una località
|
|
nel riferimento, ma ho assicurato che un'aggiunta simile possa essere
|
|
effettuata.</para>
|
|
</sect1>
|
|
|
|
<sect1 id="freeing-pages">
|
|
<title>Quando liberare una pagina</title>
|
|
|
|
<para>Poiché il sistema della VM usa tutta la memoria disponibile
|
|
per il caching del disco, in genere ci sono pochissime pagine veramente
|
|
libere. Il sistema della VM dipende dalla possibilità di
|
|
scegliere in maniera appropriata le pagine che non sono in uso per
|
|
riusarle in nuove allocazioni. Selezionare le pagine ottimali da liberare
|
|
è forse la funzione singola più importante che possa essere
|
|
eseguita da una VM perché se si effettua una selezione non
|
|
accurata, il sistema della VM può essere forzato a recuperare
|
|
pagine dal disco in modo non necessari, degradando seriamente le
|
|
prestazioni del sistema.</para>
|
|
|
|
<para>Quanto sovraccarico siamo disposti a sopportare nel percorso critico
|
|
per evitare di liberare la pagina sbagliata? Ogni scelta sbagliata che
|
|
facciamo ci costerà centinaia di migliaia di cicli di CPU ed uno
|
|
stallo percettibile nei processi coinvolti, dunque permettiamo un
|
|
sovraccarico significativo in modo da poter avere la certezza che la
|
|
pagina scelta sia quella giusta.
|
|
Questo è il motivo per cui FreeBSD tende ad avere prestazioni
|
|
migliori di altri sistemi quando le risorse di memoria vengono
|
|
stressate.</para>
|
|
|
|
<para>L'algoritmo di determinazione della pagina da liberare
|
|
è costruito su una storia di uso delle pagine di memoria.
|
|
Per acquisire tale storia, il sistema si avvantaggia di una
|
|
caratteristica della maggior parte dell'hardware moderno, il bit che
|
|
indica l'attività di una pagina (page-used bit).</para>
|
|
|
|
<para>In qualsiasi caso, il page-used bit viene azzerato e in un momento
|
|
seguente il sistema della VM passa di nuovo sulla pagina e vede che il
|
|
page-used bit è stato di nuovo attivato. Questo indica che la
|
|
pagina viene ancora usata attivamente.
|
|
Il bit ancora disattivato è un indice che quella pagina non viene
|
|
usata attivamente.
|
|
Controllando questo bit periodicamente, viene sviluppata una storia
|
|
d'uso (in forma di contatore) per la pagina fisica. Quando il sistema
|
|
della VM avrà bisogno di liberare delle pagine, controllare questa
|
|
storia diventa la pietra angolare nella determinazione del candidato
|
|
migliore come pagina da riutilizzare.</para>
|
|
|
|
<sidebar>
|
|
<title>E se l'hardware non ha un page-used bit?</title>
|
|
|
|
<para>Per quelle piattaforme che non hanno questa caratteristica, il
|
|
sistema in effetti emula un page-used bit. Esso elimina la mappatura di
|
|
una pagina, o la protegge, forzando un page fault se c'è un
|
|
accesso successivo alla pagina.
|
|
Quando avviene il page fault, il sistema segnala semplicemente
|
|
la pagina come usata e la sprotegge in maniera che possa essere usata.
|
|
Mentre prendere tale page fault solo per determinare se una pagina
|
|
è in uso può apparire una scelta costosa, in realtà
|
|
essa lo è molto meno che riusare la pagina per altri scopi, per
|
|
dover poi scoprire che un processo ne aveva ancora bisogno e dovere
|
|
andare a cercarla di nuovo su disco.</para>
|
|
</sidebar>
|
|
|
|
<para>FreeBSD fa uso di parecchie code per le pagine per raffinare
|
|
ulteriormente la selezione delle pagine da riutilizzare, come anche per
|
|
determinare quando le pagine sporche devono essere spostate dalla memoria
|
|
e immagazzinate da qualche parte. Poiché le tabelle delle pagine
|
|
sono entità dinamiche in FreeBSD, non costa praticamente nulla
|
|
eliminare la mappatura di una pagina dallo spazio di indirizzamento di un
|
|
qualsiasi processo che la stia usando. Quando una pagina candidata
|
|
è stata scelta sulla base del contatore d'uso, questo è
|
|
esattamente quello che viene fatto.
|
|
Il sistema deve effettuare una distinzione tra pagine pulite che
|
|
possono essere teoricamente liberate in qualsiasi momento, e pagine
|
|
sporche che devono prima essere scritte (salvate) per poter essere
|
|
riutilizzabili.
|
|
Quando una pagina candidata viene trovata viene spostata nella coda
|
|
delle pagine inattive, se è una pagina sporca, o nella coda di
|
|
cache se è pulita.
|
|
Un algoritmo separato basato su un rapporto sporche/pulite
|
|
determina quando le pagine sporche nella coda inattiva devono essere
|
|
scritte su disco. Una volta che è stato fatto questo, le pagine
|
|
ormai salvate vengono spostate dalla coda delle inattive alla coda di
|
|
cache. A questo punto, le pagine nella coda di cache possono ancora
|
|
essere riattivate da un VM fault ad un costo relativamente basso.
|
|
Ad ogni modo, le pagine nella coda di cache vengono considerate
|
|
<quote>immediatamente liberabili</quote> e verranno riutilizzate con un
|
|
metodo LRU (least-recently used <footnote>
|
|
<para>Usate meno recentemente. Le pagine che non vengono usate da molto
|
|
tempo probabilmente non saranno necessarie a breve, e possono essere
|
|
liberate.</para>
|
|
</footnote>) quando il sistema avrà bisogno di allocare nuova
|
|
memoria.</para>
|
|
|
|
<para>È importante notare che il sistema della VM di FreeBSD tenta
|
|
di separare pagine pulite e sporche per l'espressa ragione di evitare
|
|
scritture non necessarie di pagine sporche (che divorano banda di I/O), e
|
|
non sposta le pagine tra le varie code gratuitamente quando il
|
|
sottosistema non viene stressato. Questo è il motivo per cui
|
|
dando un <command>systat -vm</command> vedrai sistemi con contatori della
|
|
coda di cache bassi e contatori della coda delle pagine attive molto alti.
|
|
Quando il sistema della VM diviene maggiormente stressato, esso fa un
|
|
grande sforzo per mantenere le varie code delle pagine ai livelli
|
|
determinati come più efficenti.
|
|
Per anni è circolata la leggenda urbana che Linux facesse un lavoro
|
|
migliore di FreeBSD nell'evitare gli swapout, ma in pratica questo non
|
|
è vero. Quello che stava effettivamente accadendo era che FreeBSD
|
|
stava salvando le pagine inutilizzate proattivamente per fare spazio
|
|
mentre Linux stava mantendendo le pagine inutilizzate lasciando meno
|
|
memoria disponibile per la cache e le pagine dei processi.
|
|
Non so se questo sia vero ancora oggi.</para>
|
|
</sect1>
|
|
|
|
<sect1 id="prefault-optimizations">
|
|
<title>Pre-Faulting e Ottimizzazioni di Azzeramento</title>
|
|
|
|
<para>Subire un VM fault non è costoso se la pagina sottostante
|
|
è già nella memoria fisica e deve solo essere mappata di
|
|
nuovo nel processo, ma può divenire costoso nel caso se ne
|
|
subiscano un bel po' su base regolare. Un buon esempio di ciò si
|
|
ha eseguendo un programma come &man.ls.1; o &man.ps.1; ripetutamente.
|
|
Se il binario del programma è mappato in memoria ma non nella
|
|
tabella delle pagine, allora tutte le pagine che verranno accedute dal
|
|
programmma dovranno generare un page fault ogni volta che il programma
|
|
viene eseguito.
|
|
Ciò non è necessario quando le pagine in questione sono
|
|
già nella cache della VM, quindi FreeBSD tenterà di
|
|
pre-popolare le tabelle delle pagine di un processo con quelle pagine che
|
|
sono già nella VM Cache. Una cosa che FreeBSD non fa ancora
|
|
è effettuare il pre-copy-on-write di alcune pagine nel caso di una
|
|
chiamata a exec.
|
|
Ad esempio, se esegui il programma &man.ls.1; mentre stai eseguendo
|
|
<command>vmstat 1</command> noterai che subisce sempre un certo numero
|
|
di page fault, anche eseguendolo ancora e ancora. Questi sono
|
|
zero-fill fault, legati alla necessità di azzerare memoria,
|
|
non program code fault, legati alla copia dell'eseguibile in memoria
|
|
(che erano già stati gestiti come pre-fault).
|
|
Pre-copiare le pagine all'exec o alla fork è un'area che potrebbe
|
|
essere soggetta a maggior studio.</para>
|
|
|
|
<para>Una larga percentuale dei page fault che accadono è composta di
|
|
zero-fill fault. In genere è possibile notare questo fatto
|
|
osservando l'output di <command>vmstat -s</command>.
|
|
Questi accadono quando un processo accede a pagine nell'area del BSS.
|
|
Ci si aspetta che l'area del BSS sia composta inizialmente da zeri
|
|
ma il sistema della VM non si preoccupa di allocare nessuna memoria
|
|
finché il processo non ne ha effettivamente bisogno.
|
|
Quindi nel momento in cui accade un fault il sistema della VM non
|
|
deve solo allocare una nuova pagina, ma deve anche azzerarla.
|
|
Per ottimizzare l'operazione di azzeramento, il sistema della VM
|
|
ha la capacità di pre-azzerare le pagine e segnalarle come tali,
|
|
e di richiedere pagine pre-azzerate quando avvengono zero-fill fault.
|
|
Il pre-azzeramento avviene quando la CPU è inutilizzata ma il
|
|
numero di pagine che vengono pre-azzerate dal sistema è limitato
|
|
per evitare di spazzare via la cache della memoria. Questo è un
|
|
eccellente esempio di complessità aggiunta al sistema della VM per
|
|
ottimizare il percorso critico.</para>
|
|
</sect1>
|
|
|
|
<sect1 id="page-table-optimizations">
|
|
<title>Ottimizzazioni della Tabella delle Pagine </title>
|
|
|
|
<para>Le ottimizzazioni alla tabella delle pagine costituiscono
|
|
La parte più controversa nel design della VM di FreeBSD ed ha
|
|
mostrato un po' di affanno con l'avvento di un uso pesante di
|
|
<function>mmap()</function>.
|
|
Penso che questa sia una caratteristiche della maggior parte dei
|
|
BSD anche se non sono sicuro di quando è stata introdotta
|
|
la prima volta. Ci sono due ottimizzazioni maggiori. La prima è
|
|
che le tabelle della pagine hardware non contengono uno stato persistente
|
|
ma possono essere gettate via in qualsiasi momento con un sovraccarico di
|
|
gestione minimo.
|
|
La seconda è che ogni pagina attiva nel sistema ha una struttura di
|
|
controllo <literal>pv_entry</literal> che è integrata con la
|
|
struttura <literal>vm_page</literal>. FreeBSD può semplicemente
|
|
operare attraverso quelle mappature di cui è certa l'esistenza,
|
|
mentre Linux deve controllare tutte le tabelle delle pagine che
|
|
<emphasis>potrebbero</emphasis> contenere una mappatura specifica per
|
|
vedere se lo stanno effettivamente facendo, il che può portare ad
|
|
un sovraccarico computazionale O(n^2) in alcune situazioni.
|
|
È per questo che FreeBSD tende a fare scelte migliori su quale
|
|
pagina riutilizzare o mandare in swap quando la memoria è messa
|
|
sotto sforzo, fornendo una miglior performance sotto carico. Comunque,
|
|
FreeBSD richiede una messa a punto del kernel per accomodare situazioni
|
|
che richiedano grandi spazi di indirizzamento condivisi, come quelli che
|
|
possono essere necessari in un sistema di news perché potrebbe
|
|
esaurire il numero di struct <literal>pv_entry</literal>.</para>
|
|
|
|
<para>Sia Linux che FreeBSD necessitano di lavoro in quest'area.
|
|
FreeBSD sta cercando di massimizzare il vantaggio di avere un modello di
|
|
mappatura attiva potenzialmente poco denso (non tutti i processi hanno
|
|
bisogno di mappare tutte le pagine di una libreria condivisa, ad esempio),
|
|
mentre linux sta cercando di semplificare i suoi algoritmi. FreeBSD
|
|
generalmente ha dei vantaggi prestazionali al costo di un piccolo spreco
|
|
di memoria in più, ma FreeBSD crolla nel caso in cui un grosso file
|
|
sia condiviso massivamente da centinaia di processi.
|
|
Linux, d'altro canto, crolla nel caso in cui molti processi mappino a
|
|
macchia di leopardo la stessa libreria condivisa e gira in maniera non
|
|
ottimale anche quando cerca di determinare se una pagina deve essere
|
|
riutilizzata o no.</para>
|
|
</sect1>
|
|
|
|
<sect1 id="page-coloring-optimizations">
|
|
<title>Colorazione delle Pagine</title>
|
|
|
|
<para>Concluderemo con le ottimizzazioni di colorazione delle pagine.
|
|
La colorazione delle pagine è un'ottimizzazione prestazionale
|
|
progettata per assicurare che gli accessi a pagine contigue nella memoria
|
|
virtuale facciano il miglior uso della cache del processore. Nei
|
|
tempi antichi (cioè più di 10 anni fa) le cache dei
|
|
processori tendevano a mapparela memoria virtuale invece della memoria
|
|
fisica. Questo conduceva ad un numero enorme di problemi inclusa la
|
|
necessità di ripulire la cache ad ogni cambio di contesto, in
|
|
alcuni casi, e problemi con l'aliasing dei dati nella cache.
|
|
Le cache dei processori moderni mappano la memoria fisica proprio per
|
|
risolvere questi problemi.
|
|
Questo significa che due pagine vicine nello spazio di indirizzamento
|
|
dei processi possono non corrispondere a due pagine vicine nella cache.
|
|
In effetti, se non si è attenti pagine affiancate nella memoria
|
|
virtuale possono finire con l'occupare la stessa pagina nella cache del
|
|
processore—portando all'eliminazione prematura di dati
|
|
immagazzinabili in cache e riducendo le prestazioni della cache.
|
|
Ciò è vero anche con cache set-associative <footnote>
|
|
<para>set-associative sta per associative all'interno di un insieme, in
|
|
quanto c'è un insieme di blocchi della cache nei quale puo
|
|
essere mappato un elemento della memoria fisica.</para>
|
|
</footnote> a molte vie (anche se l'effetto viene in qualche maniera
|
|
mitigato).</para>
|
|
|
|
<para>Il codice di allocazione della memoria di FreeBSD implementa
|
|
le ottimizizzazioni di colorazione delle pagine, ciò significa che
|
|
il codice di allocazione della memoria cercherà di trovare delle
|
|
pagine libere che siano vicine dal punto di vista della cache.
|
|
Ad esempio, se la pagina 16 della memoria fisica è assegnata
|
|
alla pagina 0 della memoria virtuale di un processo e la cache può
|
|
contenere 4 pagine, il codice di colorazione delle pagine non
|
|
assegnerà la pagina 20 di memoria fisica alla pagina 1 di
|
|
quella virtuale.
|
|
Invece, gli assegnerà la pagina 21 della memoria fisica.
|
|
Il codice di colorazione delle pagine cerca di evitare l'assegnazione
|
|
della pagina 20 perché questa verrebbe mappata sopra lo stesso
|
|
blocco di memoria cache della pagina 16 e ciò causerrebbe un uso
|
|
non ottimale della cache.
|
|
Questo codice aggiunge una complessità significativa
|
|
al sottosistema di allocazione memoria della VM, come si può ben
|
|
immaginare, ma il gioco vale ben più della candela. La colorazione
|
|
delle pagine rende la memoria virtuale deterministica quanto la memoria
|
|
fisica per quel che riguarda le prestazioni della cache.</para>
|
|
</sect1>
|
|
|
|
<sect1 id="conclusion">
|
|
<title>Conclusione</title>
|
|
|
|
<para>La memoria virtuale nei sistemi operativi moderni deve affrontare
|
|
molti problemi differenti efficientemente e per molti diversi tipi di uso.
|
|
L'approccio modulare ed algoritmico che BSD ha storicamente seguito ci
|
|
permette di studiare e comprendere l'implementazione attuale cosi come di
|
|
poter rimpiazzare in maniera relativamente pulita grosse sezioni di
|
|
codice. Ci sono stati un gran numero di miglioramenti al sistema della
|
|
VM di FreeBSD negli ultimi anni, ed il lavoro prosegue.</para>
|
|
</sect1>
|
|
|
|
<sect1 id="allen-briggs-qa">
|
|
<title>Sessione Bonus di Domande e Risposte di Allen Briggs
|
|
<email>briggs@ninthwonder.com</email></title>
|
|
|
|
<qandaset>
|
|
<qandaentry>
|
|
<question>
|
|
<para>Cos'è <quote>l'algoritmo di interleaving</quote> a cui
|
|
fai riferimento nell'elenco delle debolezze della gestione dello
|
|
swap in FreeBSD 3.X ?</para>
|
|
</question>
|
|
|
|
<answer>
|
|
<para>FreeBSD usa un intervallo tra zone di swap fissato, con un
|
|
valore predefinito di 4. Questo significa che FreeBSD riserva
|
|
spazio per quattro aree di swap anche se ne hai una sola o due o
|
|
tre. Poiché lo swap è intervallato lo spazio di
|
|
indirizzamento lineare che rappresenta le <quote>quattro aree di
|
|
swap</quote> verrà frammentato se non si possiedono
|
|
veramente quattro aree di swap. Ad esempio, se hai due aree di
|
|
swap A e B la rappresentazione dello spazio di FreeBSD per
|
|
quell'area di swap verrà interrotta in blocchi di 16
|
|
pagine:</para>
|
|
|
|
<literallayout>A B C D A B C D A B C D A B C D</literallayout>
|
|
|
|
<para>FreeBSD 3.X usa una <quote>lista sequenziale delle
|
|
regioni libere </quote> per registrare le aree di swap libere.
|
|
L'idea è che grandi blocchi di spazio libero e lineare
|
|
possano essere rappresentati con un nodo singolo
|
|
(<filename>kern/subr_rlist.c</filename>).
|
|
Ma a causa della frammentazione la lista sequenziale risulta
|
|
assurdamente frammentata.
|
|
Nell'esempio precedente, uno spazio di swap completamente non
|
|
allocato farà si che A e B siano mostrati come
|
|
<quote>liberi</quote> e C e D come <quote>totalmente
|
|
allocati</quote>. Ogni sequenza A-B richiede un nodo per essere
|
|
registrato perché C e D sono buchi, dunquei nodi di lista non
|
|
possono essere combinati con la sequenza A-B seguente.</para>
|
|
|
|
<para>Perché organizziamo lo spazio in intervalli invece di
|
|
appiccicare semplicemente le area di swap e facciamo qualcosa di
|
|
più carino? Perché è molto più semplice
|
|
allocare strisce lineari di uno spazio di indirizzamento ed ottenere
|
|
il risultato già ripartito tra dischi multipli piuttosto che
|
|
cercare di spostare questa complicazione altrove.</para>
|
|
|
|
<para>La frammentazione causa altri problemi. Essendoci una lista
|
|
lineare nella serie 3.X, ed avendo una tale quantità di
|
|
frammentazione implicita, l'allocazione e la liberazione dello swap
|
|
finisce per essere un algoritmo O(N) invece di uno O(1).
|
|
Combinalo con altri fattori (attività di swap pesante)
|
|
e comincerai a trovarti con livelli di overhead come O(N^2) e
|
|
O(N^3), e ciò è male. Il sistema dela serie 3.X
|
|
può anche avere necessità di allocare KVM durante
|
|
un'operazione di swap per creare un nuovo nodo lista, il che
|
|
può portare ad un deadlock se il sistema sta cercando di
|
|
liberare pagine nella memoria fisica in un momento di
|
|
scarsità di memoria.</para>
|
|
|
|
<para>Nella serie 4.X non usiamo una lista sequenziale. Invece usiamo
|
|
un albero radicato e mappe di bit di blocchi di swap piuttosto che
|
|
nodi lista.
|
|
Ci prendiamo il peso di preallocare tutte le mappe di bit richieste
|
|
per l'intera area di swap ma ciò finisce per consumare meno
|
|
memoria grazie all'uso di una mappa di bit (un bit per blocco)
|
|
invece di una lista collegata di nodi. L'uso di un albero radicato
|
|
invece di una lista sequenziale ci fornisce una performance quasi
|
|
O(1) qualunque sia il livello di frammentazione dell'albero.</para>
|
|
</answer>
|
|
</qandaentry>
|
|
|
|
<qandaentry>
|
|
<question>
|
|
<para>Non ho capito questo:</para>
|
|
|
|
<blockquote>
|
|
<para>È importante notare che il sistema della VM di FreeBSD
|
|
tenta di separare pagine pulite e sporche per l'espressa ragione di
|
|
evitare scritture non necessarie di pagine sporche (che divorano
|
|
banda di I/O), e non sposta le pagine tra le varie code
|
|
gratuitamente se il sottosistema non viene stressato. Questo
|
|
è il motivo per cui dando un <command>systat -vm</command>
|
|
vedrai sistemi con contatori della coda di cache bassi e contatori
|
|
della coda delle pagine attive molto alti.</para>
|
|
</blockquote>
|
|
|
|
<para>Come entra in relazione la separazione delle pagine pulite e
|
|
sporche (inattive) con la situazione nella quale vediamo contatori
|
|
bassi per la coda di cache e valori alti per la coda delle pagine
|
|
attive in <command>systat -vm</command>? I dati di systat derivano
|
|
da una fusione delle pagine attive e sporche per la coda delle
|
|
pagine attive?</para>
|
|
</question>
|
|
|
|
<answer>
|
|
<para>Si, questo può confondere. La relazione è
|
|
<quote>obiettivo</quote> contro <quote>realtà</quote>. Il
|
|
nostro obiettivo è separare le pagine ma la realtà
|
|
è che se non siamo in crisi di memoria, non abbiamo bisogno
|
|
di farlo.</para>
|
|
|
|
<para>Questo significa che FreeBSD non cercherà troppo di
|
|
separare le pagine sporche (coda inattiva) da quelle pulite
|
|
(code della cache), ne cercherà di disattivare le pagine
|
|
(coda pagine attive -> coda pagine inattive) quando il sistema non
|
|
è sotto sforzo, anche se non vengono effettivamente
|
|
usate.</para>
|
|
</answer>
|
|
</qandaentry>
|
|
|
|
<qandaentry>
|
|
<question>
|
|
<para> Nell'esempio di &man.ls.1; / <command>vmstat 1</command>,
|
|
alcuni dei page fault non potrebbero essere data page faults
|
|
(COW da file eseguibili a pagine private)? Cioè, io mi
|
|
aspetterei che i page fault fossero degli zero-fill e dei dati di
|
|
programma. O si implica che FreeBSD effettui il pre-COW per i dati
|
|
di programma?</para>
|
|
</question>
|
|
|
|
<answer>
|
|
<para>Un fault COW può essere o legato a uno zero-fill o a dati
|
|
di programma.
|
|
Il meccanismo è lo stesso in entrambi i casi poiché
|
|
i dati di programma da copiare sono quasi certamente già
|
|
presenti nella cache. E infatti li tratto insieme. FreeBSD non
|
|
effettua preventivamentela copia dei dati di programma o lo
|
|
zero-fill, <emphasis>effettua</emphasis> la mappatura preventiva
|
|
delle pagine che sono presenti nella sua cache.</para>
|
|
</answer>
|
|
</qandaentry>
|
|
|
|
<qandaentry>
|
|
<question>
|
|
<para>Nella sezione sull'ottimizzazione della tabella delle pagine,
|
|
potresti fornire maggiori dettagli su <literal>pv_entry</literal> e
|
|
<literal>vm_page</literal> (forse vm_page dovrebbe essere
|
|
<literal>vm_pmap</literal>—come in 4.4, cf. pp. 180-181 di
|
|
McKusick, Bostic, Karel, Quarterman)? Specificamente, che tipo di
|
|
operazioni/reazioni richiederebbero la scansione delle
|
|
mappature?</para>
|
|
|
|
<para>Come funziona Linux nel caso in cui FreeBSD fallisce
|
|
(la condivisione di un grosso file mappato tra molti
|
|
processi)?</para>
|
|
</question>
|
|
|
|
<answer>
|
|
<para>Una <literal>vm_page</literal> rappresenta una tupla
|
|
(oggetto,indice#).
|
|
Una <literal>pv_entry</literal> rappresenta una voce nella tabella
|
|
delle pagine hardware (pte). Se hai cinque processi che condividono
|
|
la stessa pagina fisica, e tre delle tabelle delle pagine di questi
|
|
processi mappano effettivamente la pagina, questa pagina
|
|
verrà rappresentata da una struttura
|
|
<literal>vm_page</literal> singola e da tre strutture
|
|
<literal>pv_entry</literal>.</para>
|
|
|
|
<para>Le strutture <literal>pv_entry</literal> rappresentano solo
|
|
le pagine mappate dalla MMU (una <literal>pv_entry</literal>
|
|
rappresenta un pte). Ciò significa che è necessario
|
|
rimuovere tutti i riferimenti hardware a <literal>vm_page</literal>
|
|
(in modo da poter riutilizzare la pagina per qualcos'altro,
|
|
effettuare il page out, ripulirla, sporcarla, e così via)
|
|
possiamo semplicemente scansionare la lista collegata di
|
|
<literal>pv_entry</literal> associate con quella
|
|
<literal>vm_page</literal> per rimuovere o modificare i pte
|
|
dalla loro tabella delle pagine.</para>
|
|
|
|
<para>Sotto Linux non c'è una lista collegata del genere. Per
|
|
poter rimuovere tutte le mappature della tabella delle pagine
|
|
hardware per una <literal>vm_page</literal> linux deve indicizzare
|
|
ogni oggetto VM che <emphasis>potrebbe</emphasis> aver mappato la
|
|
pagina. Ad esempio, se si hanno 50 processi che mappano la stessa
|
|
libreria condivisa e si vuole liberarsi della pagina X in quella
|
|
libreria, sarà necessario cercare nella tabella delle pagine
|
|
per ognuno dei 50 processi anche se solo 10 di essi ha
|
|
effettivamente mappato la pagina. Così Linux sta barattando
|
|
la semplicità del design con le prestazioni. Molti algoritmi
|
|
per la VM che sono O(1) o (piccolo N) in FreeBSD finiscono per
|
|
diventare O(N), O(N^2), o anche peggio in Linux.
|
|
Poiché i pte che rappresentano una particolare pagina in un
|
|
oggetto tendono ad essere allo stesso offset in tutte le tabelle
|
|
delle pagine nelle quali sono mappati, la riduzione del numero di
|
|
accessi alla tabela delle pagine allo stesso offset eviterà
|
|
che la la linea di cache L1 per quell'offset venga cancellata,
|
|
portando ad una performance migliore.</para>
|
|
|
|
<para>FreeBSD ha aggiunto complessità (lo schema
|
|
<literal>pv_entry</literal>) in modo da incrementare le prestazioni
|
|
(per limitare gli accessi alla tabella delle pagine
|
|
<emphasis>solo</emphasis> a quelle pte che necessitino di essere
|
|
modificate).</para>
|
|
|
|
<para>Ma FreeBSD ha un problema di scalabilità che linux non ha
|
|
nell'avere un numero limitato di strutture
|
|
<literal>pv_entry</literal> e questo provoca problemi quando si
|
|
hanno condivisioni massicce di dati. In questo caso c'è la
|
|
possibilità che finiscano le strutture
|
|
<literal>pv_entry</literal> anche se c'è ancora una grande
|
|
quantità di memoria disponibile.
|
|
Questo può essere risolto abbastanza facilmente
|
|
aumentando il numero di struttre <literal>pv_entry</literal> nella
|
|
configurazione del kernel, ma c'è veramente bisogno di
|
|
trovare un modo migliore di farlo.</para>
|
|
|
|
<para>Riguardo il sovrapprezzo in memoria di una tabella delle pagine
|
|
rispetto allo schema delle <literal>pv_entry</literal>: Linux usa
|
|
tabelle delle pagine <quote>permanenti</quote> che non vengono
|
|
liberate, ma non necessita una <literal>pv_entry</literal> per ogni
|
|
pte potenzialmente mappato.
|
|
FreeBSD usa tabelle delle pagine <quote>throw away</quote>,
|
|
eliminabili, ma aggiunge una struttura <literal>pv_entry</literal>
|
|
per ogni pte effettivamente mappato. Credo che l'utilizzo della
|
|
memoria finisca per essere più o meno lo stesso, fornendo a
|
|
FreeBSD un vantaggio algoritmico con la capacità di
|
|
eliminare completamente le tabelle delle pagine con un
|
|
sovraccarico prestazionale minimo.</para>
|
|
</answer>
|
|
</qandaentry>
|
|
|
|
<qandaentry>
|
|
<question>
|
|
<para>Infine, nella sezione sulla colorazione delle pagine, potrebbe
|
|
esser d'aiuto avere qualche descrizione in più di quello che
|
|
intendi. Non sono riuscito a seguire molto bene.</para>
|
|
</question>
|
|
|
|
<answer>
|
|
<para>Sai come funziona una memoria cache hardware L1? Spiego:
|
|
Considera una macchina con 16MB di memoria principale ma solo 128K
|
|
di cache L1. In genere il modo in cui funziona la cache è
|
|
che ogni blocco da 128K di memoria principale usa gli
|
|
<emphasis>stessi</emphasis> 128K di cache.
|
|
Se si accede all'offset 0 della memoria principale e poi al 128K si
|
|
può finire per cancellare i dati che si erano messi nella
|
|
cache dall'offset 0!</para>
|
|
|
|
<para>Ora, sto semplificando di molto. Ciò che ho appena
|
|
descritto è quella che viene detta memoria cache a
|
|
<quote>corrispondenza diretta</quote>, o direct mapped.
|
|
La maggior parte delle cache moderne sono quelle che
|
|
vengono dette set-associative a 2 o 4 vie.
|
|
L'associatività di questo tipo permette di accedere fino ad N
|
|
regioni di memoria differenti che si sovrappongano sulla stessa
|
|
cache senza distruggere i dati preventivamente immagazzinati.
|
|
Ma solo N.</para>
|
|
|
|
<para>Dunque se ho una cache set associativa a 4 vie posso accedere
|
|
agli offset 0, 128K, 256K 384K ed essere ancora in grado di
|
|
accedere all'offset 0 ritrovandolo nella cache L1. Se poi accedessi
|
|
all'offset 512K, ad ogni modo, uno degli oggetti dato immagazzinati
|
|
precedentemente verrebbero cancellati dalla cache.</para>
|
|
|
|
<para>È estremamente importante …
|
|
<emphasis>estremamente</emphasis> importante che la maggior parte
|
|
degli accessi del processore alla memoria vengano dalla cache L1,
|
|
poiché la cache L1 opera alla stessa frequenza del
|
|
processore. Nel momento in cui si ha un miss <footnote>
|
|
<para>Un miss nella cache è equivalente a un page fault per
|
|
la memoria fisica, ed allo stesso modo implica un accesso a
|
|
dispositivi molto più lenti, da L1 a L2 come da RAM a
|
|
disco.</para>
|
|
</footnote> nella cache L1 si deveandare a cercare nella cache L2 o
|
|
nella memoria principale, il processore andrà in stallo, e
|
|
potenzialmente potrà sedersi a girarsi i pollici per un tempo
|
|
equivalente a <emphasis>centinaia</emphasis> di istruzioni
|
|
attendendo che la lettura dalla memoria principale venga
|
|
completata. La memoria principale (la RAM che metti nel tuo
|
|
computer) è <emphasis>lenta</emphasis>, se comparata alla
|
|
velocità del nucleo di un moderno processore.</para>
|
|
|
|
<para>Ok, ora parliamo della colorazione dele pagine:
|
|
tutte le moderne cache sono del tipo noto come cache
|
|
<emphasis>fisiche</emphasis>. Esse memorizzano indirizzi di memoria
|
|
fisica, non indirizzi di memoria virtual. Ciò permette alla
|
|
cache di rimanere anche nel momento in cui ci sia un cambio di
|
|
contesto tra processi, e ciò è molto
|
|
importante.</para>
|
|
|
|
<para>Ma nel mondo &unix; devi lavorare con spazi di indirizzamento
|
|
virtuali, non con spazi di indirizzamento fisici. Ogni programma
|
|
che scrivi vedrà lo spazio di indirizzamento virtuale
|
|
assegnatogli. Le effettive pagine <emphasis>fisiche</emphasis>
|
|
nascoste sotto quello spazio di indirizzi virtuali
|
|
non saranno necessariamente contigue fisicamente! In effetti,
|
|
potresti avere due pagine affiancate nello spazio di
|
|
indirizzamento del processo cge finiscono per trovarsi agli
|
|
offset 0 e 128K nella memoria <emphasis>fisica</emphasis>.</para>
|
|
|
|
<para>Un programma normalmente assume che due pagine
|
|
affiancate verranno poste in cache in maniera ottimale.
|
|
Cioè, che possa accedere agli oggetti dato in
|
|
entrambe le pagine senza che esse si cancellino a vicenda le
|
|
rispettiva informazioni in cache.
|
|
Ma ciò è vero solo se le pagine fisiche sottostanti lo
|
|
spazio di indirizzo virtuale sono contigue (per quel che riguarda
|
|
la cache).</para>
|
|
|
|
<para>Questo è ciò che viene fatto dalla colorazione
|
|
delle pagine.
|
|
Invece di assegnare pagine fisiche <emphasis>casuali</emphasis> agli
|
|
indirizzi virtuali, che potrebbe causare prestazioni non ottimali
|
|
della cache, la colorazione dele pagine assegna pagine fisiche
|
|
<emphasis>ragionevolmente contigue</emphasis>.
|
|
Dunque i programmi possono essere scritti assumendo che
|
|
le caratteristiche per lo spazio di indirizzamento virtuale del
|
|
programma della cache hardware sottostante siano uguali a come
|
|
sarebbero state se avessero usato lo spazio di indirizzamento
|
|
fisico.</para>
|
|
|
|
<para>Si note ho detto <quote>ragionevolmente</quote> contigue invece
|
|
che semplicemente <quote>contigue</quote>. Dal punto di vista di
|
|
una cache di 128K a corrispondenza diretta, l'indirizzo fisico 0
|
|
è lo stesso che l'indirizzo fisico 128K.
|
|
Dunque due agine affiancate nello spzio di indirizzamento virtuale
|
|
potrebbero finire per essere all'offset 128K e al 132K nella memoria
|
|
fisica, ma potrebbero trovarsi tranquillamente anche agli offset
|
|
128K e 4K della memoria fisica e mantenera comunque le stesse
|
|
caratteristiche prestazionali nei riguardi della cache. Dunque la
|
|
colorazione delle pagine <emphasis>non</emphasis> deveassegnare
|
|
pagine di memoria fisica veramente contigue a pagine di memoria
|
|
virtuale contigue, deve solo assicurarsi che siano assegnate pagine
|
|
contigue dal punto di vista delle prestazioni/operazioni della
|
|
cache.</para>
|
|
</answer>
|
|
</qandaentry>
|
|
</qandaset>
|
|
</sect1>
|
|
</article>
|