Qualcuno può spiegare questo diagramma sull'allocazione delle lastre?


10

Sto cercando di capire come funziona l'allocazione delle lastre e perché è diversa o migliore del paging ordinario.

Ho trovato questo diagramma che credo sarebbe utile se avesse più spiegazioni.

Alcune domande:

  • Cosa rappresentano gli articoli da 3KB e 7KB? Devono essere collegati in qualche modo? Perché sono confezionati in quel modo?
  • Nella colonna delle cache, le cache sono le caselle grigie o le caselle bianche / blu all'interno delle caselle grigie? Le scatole grigie sono un pacchetto di cache?
  • Le lastre sono solo le caselle blu o l'intera "Pagine contigue fisiche" è una lastra?

Gradirei davvero un aiuto. Grazie!

Allocazione delle lastre

Risposte:


15

Vedo perché sei confuso. Il diagramma è un po 'confuso e potrebbe non essere corretto.

Prima di tutto, pensiamo al motivo per cui un kernel ha bisogno di un allocatore di memoria al di sotto del livello delle pagine. Probabilmente sono già cose che conosci per lo più, ma le esaminerò per completezza.

Le pagine sono la tipica "unità" delle operazioni di memoria. Quando un'applicazione per lo spazio utente alloca memoria o mappa la memoria di un file o qualcosa del genere, in genere ottiene un multiplo delle dimensioni della pagina della macchina. Vi sono notevoli eccezioni; Windows utilizza 64k come unità di allocazione della memoria virtuale, indipendentemente dalle dimensioni della pagina della CPU. Tuttavia, pensiamolo in questo modo.

Su una CPU moderna, per quanto riguarda il codice dello spazio utente, ha uno spazio di indirizzamento piatto. Questa è in realtà un'illusione fornita dal sistema di memoria virtuale. Il sistema operativo fornisce pagine da qualsiasi punto della RAM (o forse non nella RAM, nel caso di memoria scambiata o file mappati in memoria) e li mappa in uno spazio di indirizzi virtuale contiguo.

Il punto di tutto ciò è che, a parte alcuni casi speciali per il sistema operativo stesso (forse buffer DMA, forse alcune strutture dati speciali impostate all'avvio, oh e l'immagine del kernel stessa), probabilmente il kernel del sistema operativo non deve mai gestire qualsiasi blocco di RAM più grande di una pagina. Questo semplifica enormemente le cose, perché significa che per quanto riguarda le pagine, ogni allocazione e deallocazione ha le stesse dimensioni. Elimina inoltre efficacemente la frammentazione esterna a livello macro.

Tuttavia, i kernel devono anche implementare alcune strutture di dati proprie e per questo hanno bisogno di un diverso tipo di allocatore di memoria. Queste strutture di dati di solito possono essere pensate come una raccolta di singoli oggetti (ad esempio un oggetto può essere un "thread" o un "mutex"). Le dimensioni di questi oggetti sono in genere molto più piccole di quelle di una pagina.

Quindi, ad esempio, un oggetto che rappresenta le credenziali di sicurezza di un processo (si pensi all'id utente e all'ID gruppo in POSIX, diciamo) potrebbe essere di soli 16 byte o giù di lì, mentre un "processo" o "thread" potrebbe essere fino a 1kb di dimensioni. Chiaramente non si desidera utilizzare un'intera pagina per questi piccoli record, quindi l'idea è quella di implementare un allocatore in cima alle pagine.

Il sistema di allocazione di livello inferiore deve soddisfare molti degli stessi problemi dell'allocatore a livello di pagina: deve essere ragionevolmente veloce (anche su sistemi multicore), si desidera ridurre al minimo la frammentazione e così via. Ma soprattutto, dovrebbe essere sintonizzabile e configurabile a seconda del tipo di struttura di dati che stai memorizzando.

Alcune strutture di dati sono intrinsecamente "cache-like". Ad esempio, molti sistemi operativi mantengono una cache di nomi di percorso per gli oggetti del filesystem per evitare lunghe catene di ricerca di directory (chiamate "cache dei nomi" o "cache dei nomi" in Unix-speak). Questi oggetti sono necessari solo per le prestazioni, non per la correttezza, quindi potresti (in teoria) dimenticare una intera pagina piena di voci se la memoria è ridotta e devi liberare rapidamente un frame di pagina.

Altre strutture di dati potrebbero essere scambiate su disco se la memoria è stretta e non ne hai bisogno presto. Ma non vuoi farlo con le strutture di dati che controllano lo scambio o il sistema di memoria virtuale!

Alcune strutture di dati possono essere spostate in memoria senza penalità (ad esempio se nessuno si riferisce a esse con un puntatore), quindi potrebbero "compattarsi" per evitare la frammentazione se necessario.

Quindi l'idea principale dell'allocatore di lastre è che una pagina dovrebbe memorizzare solo strutture di dati dello stesso "tipo". Questo spunta tutte le caselle: ogni oggetto in una pagina ha le stesse dimensioni, quindi non c'è frammentazione esterna. Gli oggetti dello stesso "tipo" hanno gli stessi requisiti di prestazione e la stessa semantica.

Per inciso, è una storia simile con allocazione. Per alcuni tipi di oggetti, probabilmente va bene aspettare se non c'è memoria immediatamente disponibile per allocare quell'oggetto. Un oggetto che rappresenta un file aperto potrebbe essere un esempio; l'apertura di un file è un'operazione costosa nel migliore dei casi, quindi attendere un po 'di più non farà molto male.

Per altri tipi di oggetti (ad esempio un oggetto che rappresenta un evento in tempo reale che deve accadere da un certo momento a oggi), non si vuole davvero aspettare. Quindi ha senso che alcuni tipi di oggetti si allocino eccessivamente (diciamo, abbiano alcune pagine libere riservate) in modo che le richieste possano essere soddisfatte senza aspettare.

Quello che stai sostanzialmente facendo è consentire ad ogni tipo di oggetto di avere un proprio allocatore, che può essere configurato per le esigenze di quell'oggetto. Questi allocatori per oggetto sono chiamati confusamente "cache". Allocare una cache per tipo di oggetto. (Sì, in genere implementeresti anche una "cache di cache".) Ogni cache memorizza solo oggetti dello stesso tipo (ad esempio solo strutture di thread o solo strutture di spazi di indirizzi).

Ogni cache, a sua volta, gestisce "lastre". Una lastra è una cornice di pagina che contiene una matrice di oggetti dello stesso tipo. Le lastre possono essere "piene" (tutti gli oggetti in uso), "vuote" (nessun oggetto in uso) o "parziali" (alcuni oggetti in uso).

Le lastre parziali sono probabilmente le più interessanti, poiché l'allocatore di lastre mantiene un elenco gratuito per ogni lastra parziale. (Le lastre complete e le lastre vuote non hanno bisogno di un elenco gratuito.) Gli oggetti vengono prima allocati dalle lastre parziali (e probabilmente dalle lastre parziali "più complete") per cercare di evitare l'allocazione di pagine non necessarie.

La cosa bella dell'allocazione delle lastre è che tutte queste opzioni della politica di allocazione (così come la semantica della memoria) possono essere ottimizzate per ogni tipo di oggetto. Alcune cache potrebbero conservare un pool di lastre vuote e altre no. Alcuni potrebbero essere in grado di essere scambiati con memoria secondaria e altri no.

Linux ha tre diversi tipi di allocatore di lastre, a seconda che tu abbia bisogno di compattezza, compatibilità con la cache o velocità raw. C'è stata una buona presentazione su questo un paio d'anni fa che spiega bene i compromessi.

L'allocatore di lastre Solaris (vedere il documento per i dettagli ) ha alcuni dettagli in più per ottenere prestazioni ancora maggiori. Per cominciare, in Solaris, tutto viene eseguito con l'allocazione delle lastre, inclusa l'allocazione dei frame di pagina. (Questa è la soluzione di Solaris per l'allocazione di oggetti di dimensioni superiori a mezza pagina.) Gestisce oggetti più piccoli annidando gli allocatori delle lastre nello spazio allocato sulle lastre.

Alcuni oggetti in Solaris richiedono costruzioni e distruzioni complesse e costose (ad esempio oggetti che hanno un blocco del kernel), e quindi potrebbero essere "parzialmente liberi" (ovvero costruiti ma non allocati). Solaris ottimizza inoltre l'allocazione gratuita delle lastre mantenendo elenchi gratuiti in base alla CPU, garantendo che alcune operazioni siano completamente prive di attesa.

Per supportare l'allocazione per scopi generici (ad es. Per array le cui dimensioni non sono note in fase di compilazione), la maggior parte dei sistemi operativi di tipo macrokernel ha anche cache che rappresentano le dimensioni degli oggetti anziché i tipi di oggetti . FreeBSD, ad esempio, mantiene le cache per oggetti sconosciuti le cui dimensioni sono potenze di 2 byte, da 4 a 256.

Ciò che spero che possiate vedere è che l'allocazione delle lastre è un framework molto flessibile che può essere ottimizzato per le esigenze di diversi tipi di dati. Non compete con il paging, ma lo integra (sebbene in Solaris, i frame di pagina siano allocati con lastre).

Spero che questo possa essere d'aiuto. Fammi sapere se qualcosa necessita di chiarimenti.


6

L'idea alla base dell'allocatore di lastre è che il sistema operativo necessita di strutture dati specifiche ma piuttosto standard (ad esempio PCB di processo, semafori, metadati di file, ecc.) Che suggeriscano l'utilizzo della memoria necessario per loro.

L'algoritmo dell'allocatore di lastre fornisce stock di aree di memoria le cui dimensioni e inizializzazione sono ottimizzate per queste strutture di dati standard. Ad esempio, come puoi vedere dall'immagine, possono esistere oggetti da 3kb e oggetti da 7kb. Tuttavia, sappiamo che il kernel alloca memoria solo in multipli della dimensione della pagina.

Al fine di evitare perdite di tempo e di memoria, il sistema operativo mantiene in cache diversi pool di aree di memoria che possono essere rapidamente allocati su richiesta e le dimensioni della cache sono diverse per ogni tipo di oggetto e struttura. Tuttavia, le cache non contengono e gestiscono direttamente queste aree di memoria e non garantiscono nemmeno che risiedano nella memoria contigua. Sono invece divisi in lastre, pagine contigue di memoria, il cui numero è generalmente scelto per ridurre la frammentazione della memoria. È nel solaio che risiedono le diverse istanze di oggetti, allocate o libere. Se una cache è piena, il nuovo solaio viene allocato da qualche parte nella memoria primaria e viene aggiunto alla cache.

Per riassumere, le cose che puoi vedere nella figura sono esempi di oggetti che il sistema operativo sa essere 3-7-qualunque sia il kB di grandi dimensioni. Il kernel non alloca una memoria di pagina appositamente per loro, poiché ciò aumenterebbe esponenzialmente la frammentazione della memoria, ma li "reindirizza" a una cache (la casella grigia). Nella cache dovrebbero essere presenti indirizzi che indicano aree di memoria fisicamente contigue, le lastre (i set di scatole bianchi / blu) e gli oggetti vengono infine allocati in un'area della lastra (una parte che è colorata in blu, tra le aree di memoria completamente analoghe della stessa lastra).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.