Dato che scelgo volontariamente di non implementare la memoria virtuale nel mio kernel, come funzionerebbe l'unità di gestione della memoria?

3

Per il mio kernel di base mi rifiuto di implementare la temuta confusione di schemi di memoria virtuale, quindi voglio solo veri indirizzi di memoria per tutto. Alcune persone hanno discusso con me sul fatto che la memoria virtuale è supportata da hardware e, a volte l'hardware MMU è integrato con la CPU, è una regola che la memoria virtuale è obbligatoria per la progettazione hardware?

Ho almeno un buon motivo per non preoccuparmi della memoria virtuale a questo punto, visto che la vera memoria che ho è abbondante (32 GB), e che non mi interessa passare attraverso problemi e soluzioni temporanee con l'indirizzamento virtuale e tale (e che onestamente non mi piace l'idea, indipendentemente dal fatto che sia d'aiuto in molte aree).

Quindi, se la mia domanda non era chiara, è possibile scrivere un kernel dedicato e non affatto implementare la memoria virtuale e utilizzare solo indirizzi reali per tutto, con una MMU?

(Alcuni di questi programmi di programmazione e hardware di basso livello sono sbalorditivi, anche dopo ore e ore di studio e implementazione).

PS: Sto indovinando che la risposta ad un certo punto è "sì", ma sentire qualcuno con più conoscenza sull'argomento sarebbe sicuramente un buon indicatore per me e per gli altri che lo leggono.

    
posta leslar Bonar 22.01.2013 - 23:01
fonte

5 risposte

17

Puoi certamente farlo, ma potrebbe non salvarti la complessità che pensi.

Uno dei principali vantaggi della memoria virtuale è che impedisce a diversi processi di sapere quali parti della memoria altri processi stanno utilizzando.

Su un sistema con memoria virtuale, uno dei compiti principali della MMU è dare a ogni processo l'illusione che l'intera macchina sia la loro. Ad esempio, se hai tre programmi che vuoi eseguire sul tuo sistema operativo, ognuno può essere compilato per iniziare all'indirizzo di memoria 0 - e nessuno di questi deve essere modificato se vuoi eseguirli tutti e tre allo stesso tempo. Hai ragione, tuttavia, che ciò aggiunge complessità al kernel.

Se decidi di salvare questa complessità facendo sì che il lavoro del programma non usi gli stessi indirizzi usati da altri programmi, ci sono due modi principali per andare:

  • Un'opzione è far sì che ogni programma usi un diverso intervallo di memoria - se program1 usa solo gli indirizzi 0 .. 8191 , e program2 usa solo gli indirizzi 8192 .. 16383 , poi (salvo un bug), non lo faranno interferire tra loro, ma - e questo è un grosso problema - è necessario pianificare in anticipo quale intervallo di memoria utilizzerà ogni programma, quindi è necessario conoscere in anticipo tutti i programmi che verranno eseguiti sul proprio sistema operativo. Questo può essere praticabile per un sistema embedded (e potrebbe essere l'unica scelta), ma è ovviamente uno stopper per un sistema operativo che speri che altre persone costruiscano software. Storicamente, un metodo come questo è stato usato anche per alcune delle prime implementazioni di librerie condivise, e ha dimostrato molto difficile da ottenere.

  • Un'altra opzione è compilare ogni programma usando quello che viene chiamato codice indipendente dalla posizione - codice in cui nessun indirizzo assoluto viene compilato nel programma e tutti gli accessi alle variabili e al codice all'interno del programma sono fatto prima controllando un registro (possibilmente il contatore del programma) per un valore che indica dove il programma è effettivamente in memoria. Ciò consente a più programmi di coesistere, ma richiede al compilatore e al linker di fare più lavoro per far funzionare il programma in questo modo, richiede che il sistema operativo faccia più lavoro durante il caricamento di ciascun programma e abbia un costo in termini di prestazioni ogni volta che accede a una variabile o la funzione deve essere calcolata usando questo metodo. Storicamente, alcuni sistemi operativi, incluso il classico MacOS prima della versione 7.1, hanno adottato questo approccio, e qualcosa di simile è ancora usato nelle moderne implementazioni di librerie condivise.

Quindi puoi spostare la complessità dal kernel a ciascun programma in esecuzione, o dal kernel alla tua registrazione di quale programma viene caricato dove - ma non puoi sbarazzarti di tutto sommato.

    
risposta data 22.01.2013 - 23:52
fonte
12

È abbastanza comune per i kernel incorporati non utilizzare la memoria virtuale, perché proteggere gli spazi di memoria dei processi gli uni dagli altri non è così importante, e in genere si dispone di una quantità relativamente piccola di memoria. In questo caso, la MMU viene principalmente utilizzata per proteggere le parti appropriate dello spazio indirizzo in sola lettura, in modo da ottenere segfault quando lo desideri.

Con 32 GB di RAM, tuttavia, è necessario un computer a 64 bit per indirizzare tutto in modo nativo, o qualche altro schema di paging. Inoltre, devi ancora capire la MMU per inizializzarla correttamente, quindi potrebbe non farti risparmiare molto. A meno che tu non stia costruendo un computer dedicato che esegue solo una manciata di processi, ti consiglio vivamente di mordere il proiettile e di implementare la memoria virtuale.

    
risposta data 22.01.2013 - 23:22
fonte
3

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.

    
risposta data 23.01.2013 - 00:57
fonte
3

Dato che non prendi la risposta di Karl, proverò a rispondere alla tua domanda: senza memoria virtuale, un'utile MMU sarà basata sulla segmentazione della memoria, più o meno come faceva il DOS nei vecchi (buoni) giorni - allocare memoria nei segmenti e gli indirizzi sono sempre locali ai segmenti corrispondenti. Un altro approccio è senza MMU (per sistemi embedded) - tutti i processi utilizzano uno schema di indirizzo pre-assegnato e scoprirai rapidamente che il 32G non è abbastanza grande.

Ancora, come suggerito da Karl, dovresti morderlo.

Aggiunti ulteriori dettagli al commento di Robert

Per un sistema che deve caricare e scaricare in modo dinamico i moduli, la memoria fisica libera è sempre dinamica. Per un modulo che presuppone l'accesso diretto alla memoria fisica, esiste un modo difficile per individuarne i dati e il codice: quando il modulo viene caricato, esegue la scansione del codice macchina e riscrive tutti i puntatori agli indirizzi allocati. Non ho letto nulla in dettaglio su questa tecnica, ma mi sembra un po 'fastidioso (mentre Windows lo fa per le DLL). Un modo più semplice per risolvere questo problema è utilizzare gli indirizzi locali ai segmenti: la posizione di un segmento può cambiare ma l'indirizzo relativo al suo inizio non cambia mai.

Se la segmentazione è supportata nel computer moderno non è relavent, è solo una soluzione di gestione della memoria facile da implementare.

    
risposta data 22.01.2013 - 23:37
fonte
1

Certamente non devi, molti piccoli sistemi no. Le prime versioni di Windows, PalmOS e sistemi pre-OS X di Apple utilizzavano "handle", che erano fondamentalmente indici in una tabella di puntatori a regioni di memoria. L'idea era che i programmi avrebbero fatto tutto il loro riferimento alla memoria tramite le maniglie, dando al sistema la possibilità di spostare le regioni di memoria secondo necessità (per evitare la frammentazione). È difficile prevenire il danneggiamento della memoria con un tale schema e può portare a strani bug se si crea un puntatore a una regione che viene spostata.

Credo che l'OS / MFT di IBM (tornando indietro) abbia semplicemente ripartito la memoria in regioni di dimensioni fisse (definite al momento della "generazione del sistema"), quindi ha caricato un singolo programma in ciascuna regione. Si supponeva che ogni programma usasse la memoria nella sua regione, quindi tutti i programmi dovevano conoscere i loro requisiti di memoria. A seconda delle attività che eseguirai, forse qualcosa di semplice come questo potrebbe funzionare.

    
risposta data 23.01.2013 - 18:40
fonte

Leggi altre domande sui tag