Una MMU svolge due ruoli principali: traduce gli indirizzi virtuali in indirizzi fisici e consente la possibilità di intrappolare (noto anche come errore e vari altri nomi), cioè eseguire codice (nel contesto del kernel) quando un processo dereferenzia certe indirizzi virtuali.
Esiste una terza funzionalità correlata, quella di avere mappature diverse e trap diverse per processi diversi. Questa è una conseguenza dell'esecuzione del codice per modificare le impostazioni della MMU durante un cambio di contesto. Questo non richiede nulla di speciale sulla MMU (ma la maggior parte dei design del processore ha caratteristiche nella MMU per renderla più efficiente).
Alcuni processori (tipicamente microcontrollori di fascia alta, come l'ARM Cortex-M3) non supportano le traduzioni ma supportano comunque il trapping: hanno una unità di protezione della memoria (MPU). Una MPU consente al kernel di impedire l'accesso a determinati indirizzi. In questo modo puoi applicare la separazione della memoria tra i processi in fase di esecuzione con solo una MPU, non hai bisogno di una MMU.
Se hai una MMU, puoi usarla come MPU. Sta a te progettare il tuo kernel in modo tale che ogni indirizzo virtuale sia sempre mappato all'indirizzo fisico corrispondente o non mappato. (Un indirizzo non mappato attiva una trappola.)
Esiste una versione del kernel Linux che supporta i processori con una MPU ma nessuna MMU: μClinux . Il codice è integrato nell'albero dei sorgenti di Linux sotto nommu
architectures. La mancanza di una MMU comporta alcune limitazioni , in particolare l'impossibilità di implementare fork .
Una cosa importante che ti arrendi se non hai memoria virtuale è la possibilità di caricare ogni programma nello stesso spazio degli indirizzi. Ciò semplifica notevolmente la compilazione dei programmi. Ad esempio, con la memoria virtuale, è possibile scegliere di caricare sempre il codice di un programma su un determinato indirizzo predefinito, memorizzare le variabili globali a un indirizzo predefinito, ecc. Se ogni processo è memorizzato in un indirizzo diverso, è necessario generare la posizione- codice indipendente tutto il tempo.
Un altro limite di non avere memoria virtuale è che la gestione della memoria diventa più difficile a causa della frammentazione. Supponiamo di avere quattro pagine di memoria fisica consecutiva di 4kB ciascuna, numerate 0,1,2,3, e in primo luogo si assegnano quattro oggetti di dimensioni di pagina, uno in ogni pagina; quindi si liberano gli oggetti nelle pagine 1 e 3. Con la memoria virtuale, è possibile allocare un oggetto da 8kB composto dalle pagine 1 e 3 mappandole a indirizzi consecutivi. Con una mappatura fisica diretta, la pagina 1 viene persa fino a quando non è necessario un oggetto a pagina singola o gli oggetti circostanti vengono liberati.
Se lo desideri, puoi utilizzare il processore senza attivare la sua MMU. Tutti i processori si avviano con la MMU disattivata, perché non è possibile fare nulla di utile finché le tabelle MMU non sono state inizializzate con qualcosa di sensato. Senza MMU, non è possibile applicare alcun isolamento tra i processi. Non puoi anche scambiare o avere file mappati in memoria.
Con l'indirizzo 0 mappato, fai attenzione se programmi in C o nella maggior parte delle altre lingue. Esistono molti ambienti di runtime e codice C non completamente portatile che presuppongono che un indirizzo di tutti i bit zero sia il puntatore NULL
, che non punta a nessun oggetto.
A seconda dell'architettura, potrebbe non esserci modo di accedere ad alcuni componenti hardware senza mapparli in memoria. Ciò richiede che la MMU sia attiva e funzionante. Puoi ancora ignorare la MMU mappando la RAM fisica dall'indirizzo 0, mappando i dispositivi sopra l'ultimo indirizzo RAM e non modificando mai il mapping MMU.
Tutto sommato, evitando la memoria virtuale, stai semplificando alcune parti del kernel, ma rendendo molto più difficile scrivere programmi che fanno qualcosa di utile. C'è una ragione per cui tutti i processori high-end hanno una MMU: è difficile fare qualcosa di sofisticato senza di essa. La gestione della MMU non è la parte più difficile della scrittura di un kernel se la si mantiene semplice; se la trovi "da capogiro", hai molto da imparare sulla programmazione di basso livello. Scrivere un kernel con la gestione della MMU sarebbe un ottimo esercizio di apprendimento.