L'allocazione della memoria può essere suddivisa a
È importante tenere distinte l'allocazione di memoria virtuale da quella fisica: quando un processo esegue una brk
il suo spazio virtual viene incrementato ma non viene allocata memoria fisica. Solo quando il processo va a scrivere nel nuovo spazio virtuale la necessaria memoria fisica viene allocata.
Nei sistemi Linux una certa quantità di memoria viene allocata inizialmente al SO e non viene mai deallocata. Le eventuali richieste di memoria dinamica da parte del SO stesso vengono soddisfatte con la massima priorità. Quando invece un processo richiede memoria, questa gli viene allocata senza particolari limitazioni.
Tutti i dati letti dal disco vengono conservati indefinitamente in memoria per poter essere eventualmente riutilizzati, tuttavia, quando la memoria disponibile scende sotto una soglia predefinita viene eseguita un'operazione di riduzione delle pagine occupate (Page Reclaiming).
Se una pagina è stata letta da disco e non è mai stata modificata la pagina viene semplicemente resa disponibile. Se invece è stata modifica deve essere scritta su disco prima di essere resa disponibile.
Per quanto riguarda alla deallocazione, si scaricano molte pagine di cache non utilizzate dai processi, se non è sufficiente. Alcune pagine dei processi vengono scaricate, se non è sufficiente. E in certi casi un processo può venire eliminato completamente.
Il file /proc/meminfo
riporta maggiori informazioni sull'utilizzo della memoria. I buffers contengono informazioni associate a dispositivi di tipo "block device" e contengono informazioni relative al file system.
L'unità di base per l'allocazione di memoria è la pagina, ma Linux cerca di mantenere la memoria meno frammentata possibile. Nonostante questa scelta possa apparire inutile non lo è, questo perché la memoria è anche acceduta da DMA, è preferibile usare pagine contigue fisicamente per motivi legati sia alle cache che alla latenza di accesso alle RAM, e la rappresentazione della RAM libera risulta più compatta.
L'allocazione a blocchi di pagine contigue si basa sul seguente meccanismo.
La memoria è suddivisa in grandi blocchi di pagine contigue, la cui dimensione è una potenza di
Se un blocco richiesto è disponibile questo viene allocato, altrimenti è diviso in
Quando un blocco viene liberato il suo buddy viene analizzato e, se è libero, i due vengono riunificati, ricreando un blocco più grande.
Fino a quando la quantità di RAM libera è sufficientemente grande Linux alloca la memoria senza svolgere particolari controlli. La memoria destinata ai processi viene rilasciata quando un processo termina, e la memoria destinata alle disk cache cresce sempre perché non esiste un criterio per stabilire se un dato su un disco non verrà più utilizzato.
Quando la quantità di memoria scende sotto un livello critico interviene PFRA (Page Frame Reclaiming Algorithm), il cui obiettivo è riportare il numero di pagine libere ad un livello di dimensione al minimo maxFree
.
Il meccanismo utilizzato da PFRA per scegliere quali pagine liberare si basa su LRU, cioè sulla scelta delle pagine non utilizzate da più tempo.
Definiamo freePages
come il numero di pagine di memoria fisiche libere in un certo istante, requiredPages
come il numero di pagine che vengono richieste per una certa attività da parte di un processo, minFree
come il numero minimo di pagine libere sotto il quale non si vorrebbe scendere, e maxFree
come il numero di pagine libere al quale PFRA tenta di riportare freePages
.
Il PFRA può essere invocato direttamente da parte di un processo che richiede requiredPages
di memoria se
È inoltre invocato periodicamente tramite kswapd
, una funzione attivata periodicamente dal SO che invoca PFRA se
Il PFRA determina il numero di pagine da liberare con l'algoritmo:
Esistono due liste globali, dette Least Recently Used List (LRU List) che collegano tutte le pagine appartenenti ai processi:
Entrambe le liste tengono le pagine in ordine di invecchiamento, tramite spostamento delle pagine da una lista all'altra per tenere conto degli accessi a memoria.
Dato che l'x64
non tiene traccia del numero di accessi alla memoria, Linux realizza un'approssimazione basata sul bit di accesso A presente nella PTE della pagina, che viene posto a x64
ogni volta che la pagina viene acceduta, e viene azzerato periodicamente dal sistema operativo.
Ad ogni pagina viene inoltre associato un flag ref
, oltre al bit di accesso A settato dall'hardware. Questo serve a raddoppiare il numero di accessi necessari per spostare una pagina da una lista all'altra.
Definiamo un'algoritmo semplificato per vedere come si spostano le due pagine:
Per le pagine nell'active list, se
Per le pagine nell'inactive list (escluse le pagine precedentemente spostate), se
Per quanto riguarda lo scaricamento prima si scaricano le pagine appartenenti alla page cache non più utilizzate da nessun processo, in ordine di NPF. Successivamente si parte dalla coda della lista inactive, tenendo conto della eventuale condivisione delle pagine. Una pagina virtuale è ritenuta scaricabile solo se tutte le pagine condivise sono nella inactive list in posizioni successive.
È necessario che sia disponibile una Swap Area sul disco, e pertanto esiste lo Swap File, una sequenza di Page slots, ognuna di dimensioni pari ad una pagina, identificata da un numero (SWPN).
Ogni swap area è identificata da un descrittore, che contiene un contatore per ogni page slot (swap_map_counter
), che indica il numero di pagine virtuali condivise dalla pagina fisica copiata nel Page Slot.
Quando PFRA chiede di scaricare una pagina fisica (swap_out
) viene allocato un page slot. La pagina fisica viene copiata nel page slot e liberata, e il swap_map_counter
viene impostato al numero di pagine virtuali che condividono la pagina fisica. In ogni elemento della tabella delle pagine che condivideva la pagina fisica viene registrato il SWPN del page slot al posto di NPF e il bit di presenza viene azzerato.
Quando un processo poi accede ad una pagina virtuale swappata si verifica un page fault. Quindi il page fault handler attiva la procedura di caricamento (swap_in
) e viene allocata una pagina fisica in memoria.
Il page slot indicato nella tabella delle pagine in corrispondenza della pagina virtuale cercata viene copiato nella pagina fisica, e la tabella delle pagine viene aggiornata inserendo il NPF della pagina fisica allocata al posto di SWPN del page slot.
swap_out
Per ogni pagina da scaricare swap_out
si determina, in base al dirty bit della pagina se salvarla su disco. Se questa è mappata su file e shared si salva su file, se invece è anonima si salva nella swap area.
Una pagina fisica condivisa è dirty se il suo descrittore è marcato D a seguito di un TLB flush, oppure una delle pagine virtuali condivise sulla stessa pagina fisica è contenuta nel TLB ed è marcata dirty.
swap_in
Quando una pagina viene ricaricata con lo swap_in
in Linux non cancella la pagina dalla swap area, che rappresenta una backing store per quella pagina. L'informazione è mantenuta nella swap cache, cioè l'insieme delle pagine che sono state rilette dalla swap area e non sono state modificate, assieme ad alcune strutture ausiliarie che permettono di gestirla come se tali pagine fossero mappate sulla swap area.
Consideriamo le pagine anonime e private: quando si esegue uno swap_in
la pagina viene copiata in memoria fisica, la copia nella swap area non viene eliminata, la pagina letta in memoria è marcata in sola lettura. Nella swap cache index viene inserito un descrittore che contiene i riferimenti alla pagina fisica e al page slot. Se la pagina fisica viene solo letta, quando la pagina viene nuovamente scaricata non viene riscritta su disco, ma se viene scritta, si verifica un page fault e la pagina diventa private non appartiene più alla swap area.
Per quanto riguarda le liste LRU il comportamento è come segue:
swap_in
, viene inserita in testa alla active con L'allocazione/deallocazione della memoria interferisce con i meccanismi di scheduling. Supponiamo che un processo
Quando
In sistemi molto carichi il PFRA può non riuscire a risolvere la situazione. In tal caso come estrema ratio deve invocare il OOMK, che seleziona un processo e lo elimina.
La scelta del processo da eliminare è fatta in base ai seguenti criteri:
I meccanismi adottati da Linux sono complessi e il loro comportamento è difficile da predire in diverse condizioni di carico. Non è quindi escluso che in certe situazioni si verifichi un fenomeno detto Thrashing, cioè il sistema continua a deallocare pagine che vengono nuovamente richieste dai processi.