LEZIONE 29 PAGE CACHE - SISTEMI OPERATIVI (9 CFU), CDL INFORMATICA, A. A. 2020/2021 DIPARTIMENTO DI SCIENZE FISICHE, INFORMATICHE E MATEMATICHE ...

Pagina creata da Caterina Albano
 
CONTINUA A LEGGERE
LEZIONE 29 PAGE CACHE - SISTEMI OPERATIVI (9 CFU), CDL INFORMATICA, A. A. 2020/2021 DIPARTIMENTO DI SCIENZE FISICHE, INFORMATICHE E MATEMATICHE ...
Lezione 29
Page cache
Sistemi Operativi (9 CFU), CdL Informatica, A. A. 2020/2021
Dipartimento di Scienze Fisiche, Informatiche e Matematiche
Università di Modena e Reggio Emilia
http://weblab.ing.unimo.it/people/andreolini/didattica/sistemi-operativi
                                                                           1
Quote of the day
                     (Meditate, gente, meditate ...)
“There are two ways of constructing a
software design One way is to make it
so simple that there are no obvious
deficiencies, and the other way is to
make it so complicated that there are
no obvious deficiencies. The first
method is far more difficult.”
Sir Charles Anthony Richard Hoare (1943-)
Inventore dell'algoritmo di ordinamento QuickSort
                                                       2
Vincitore del Turing Award (1980)
INTRODUZIONE

               3
Problema: costo delle letture da disco
          (Costo elevatissimo, da abbattere a tutti i costi)

Le letture da disco sono notoriamente costose. Il
principale responsabile è il movimento della
testina del disco.
→ Leggere nuovamente un dato già letto e
presente in memoria è inefficiente.
Come si fa a ritornare un blocco dati già presente
in memoria centrale?
                                                               4
Problema: le scritture su disco
    (Costo elevatissimo, da abbattere; problema delle sovrascritture)

Le scritture su disco sono ancora più costose; il
kernel cerca di raggrupparle e farle tutte insieme.
→ Scritture asincrone: una scrittura marca una
pagina come sporca. Il flusher scrive su disco
tante pagine sporche.
Come riconoscere le pagine sporche da scrivere?

                                                                        5
Problema: le scritture su disco
    (Costo elevatissimo, da abbattere; problema delle sovrascritture)

Un'applicazione può voler scrivere lo stesso
blocco con frequenza elevata (si ricordi il
principio di località dei riferimenti).
Il blocco su disco va riscritto ogni volta (anche
centinaia di volte al secondo)?
  Pessima soluzione. Si distrugge un SSD, si rovina un
  disco rotazionale, le prestazioni calano vistosamente.
Si aggiorna il buffer periodicamente?
  Va molto meglio. Quanto “periodicamente”, però?
                                                                        6
Soluzione: caching dei blocchi
   (I blocchi già letti sono accessibili velocemente dalla memoria centrale)

Si deve mantenere in memoria centrale una cache
dei dati già letti da e scritti su disco, ed usarla per
servire richieste le cui risposte sono già in
memoria centrale.
Qual è l'unità ideale della cache?
  Settori di disco?
  Blocchi di file system?
  Pagine?
Bella domanda...
                                                                               7
Buffer cache
                 (Cache di blocchi letti da disco)

Alcuni SO dedicano una porzione di memoria
all'implementazione di una buffer cache.
La buffer cache è una cache dei blocchi letti dal e
e scritti sul disco nell'ultimo periodo.
  Un blocco letto e scritto viene inserito in buffer cache.
  Prima di ogni lettura, si controlla la presenza del
  blocco in buffer cache. Se il blocco esiste, la lettura è
  soddisfatta dalla cache.
  Le scritture sono ritardate al momento opportuno.
                                                          8
Buffer cache in Linux (≤ v2.4.10)
             (Una tabella hash di struct buffer_head)

Linux ha avuto una buffer cache fino alla versione
2.6 (esclusa).
Implementazione: file $LINUX/fs/buffer.c.
La buffer cache è una tabella hash di strutture dati
rappresentanti buffer caricati da disco.
La chiave di accesso alla tabella hash è la coppia
(dev, block) dove:
  dev: major number del dispositivo.
  block: numero identificativo del blocco sul           9
Rappresentazione del buffer letto
                   (struct buffer_head)

La struct buffer_head rappresenta il
blocco letto dal disco e salvato in memoria
centrale.
  File $LINUX/include/linux/fs.h.
Contenuto della struttura.
  b_dev: major number del dispositivo.
  b_blocknr: identificatore del blocco su disco.
  b_size: dimensione del buffer.
  b_next, b_prev: puntatori per il collegamento.
  b_data: puntatore al contenuto del blocco.       10
Tabella hash dei buffer
                (Per l'accesso rapido ai buffer)

 I buffer sono memorizzati in una tabella hash
 indicizzata sulla coppia (b_dev, b_blocknr).
hash_table                b_dev       0x0301       b_dev       0x0801
                          b_blocknr   42           b_blocknr   17
                          b_size      1024         b_size      2048

                         b_next                    b_next
                         b_prev                    b_prev
                         b_data                    b_data

                          b_dev       0x0301
                          b_blocknr   39
                          b_size      1024

                         b_next
                         b_prev                                         11
                         b_data
Rappresentazione del buffer letto
                (struct buffer_head)

La struct buffer_head rappresenta il
blocco letto dal disco e salvato in memoria
centrale.
  File $LINUX/include/linux/fs.h.

                                         12
Stati del buffer
 (Non utilizzato, in attesa di essere scritto, sporco, condiviso, non condivioso)
La buffer cache è suddivisa in cinque tabelle hash,
ciascuna per blocchi in uno degli stati seguenti.
   Clean:    blocchi non usati e liberi.
   Locked:   blocchi in attesa di essere sincronizzati su
             disco. Non possono essere usati da
             nessuno.
   Dirty:       blocchi modificati rispetto alla
versione                                               su
             disco (dovranno essere sincronizzati).
   Shared:   blocchi condivisi fra più applicazioni.
   Unshared: blocchi precedentemente condivisi (ora     13
             non lo sono più).
Lettura tramite cache
                       (bread())
                                   bread()            Trova blocco
                                             getblk()   in cache
Le operazioni di lettura                buffer_uptodate()
(solitamente        effettuate     Lettura blocco   NO      Esiste il
                                                            blocco?
tramite VFS, ma anche per via             ll_rw_block()
                                                                SÌ
diretta    al     dispositivo)
provocano l'esecuzione della         Attesa I/O

funzione bread().                         wait_on_buffer()
                                                         Ritorna blocco

                                                                     14
Scrittura tramite cache
                      (bdflush)

La buffer cache implementa un meccanismo di
writeback-cache (sincronizzazione ritardata) dei
blocchi sporchi (dirty).
Periodicamente, si attiva un servizio del kernel
(bdflush) che va a sincronizzare tutti i blocchi
sporchi.
In alternativa, l'utente può comandare l'immediata
(e bloccante!) sincronizzazione a mano con il
comando sync.                                    15
Limitazioni della buffer cache
                      (Tante, purtroppo)

La buffer cache opera a livello di blocchi di disco e
non di pagine.
  I blocchi sono più piccoli e di dimensioni diverse.
La cache soffre di collisioni.
  La scansione lineare delle liste ammazza le prestazioni.
Difficoltà implementative con il memory mapping.
  Il memory mapping richiede l'associazione esplicita fra
  pagina ed una porzione di disco pari a 4KB.
  La buffer cache non fornisce questa associazione.
                                                         16
PAGE CACHE

             17
La cache delle pagine
       (Una buffer cache più performante che opera per pagine)

Per eliminare le limitazioni della buffer cache i SO
moderni adottano una cache delle pagine (page
cache). La page cache memorizza in maniera
efficiente le pagine contenenti i blocchi fisici del
disco acceduti più recentemente.
È molto più performante della buffer cache:
  opera a pezzi di 4KB al posto di 512B o 1KB.
  implementazione più efficiente rispetto alla tabella
  hash (di solito, un radix tree).
                                                                 18
Unified Virtual Memory in SunOS
                    (Presenta un problemino)

Il meccanismo di memoria virtuale unificata
(Unified Virtual Memory) è stato introdotto in
SunOS 4 (antenato di Solaris) nel 1985.
Una page cache dedicata ai dati del filesystem.
  Pagine contenenti pezzi di file (dati, eseguibili).
Una buffer cache dedicata ai blocchi di disco.
  Buffer contenenti metadati e dati.
read() and write() usano la buffer cache.
mmap() usa la page cache.                               19
Double caching
                      (Il problemino)

Nel caso del I/O via mmap() lo stesso buffer è
conservato sia in buffer cache sia in page cache.
Si presenta il problema del double caching.
  È necessario propagare gli aggiornamenti del dato
  sulle due cache per mantenerle coerenti.
  È necessario ripetere due volte le stesse operazioni di
  gestione di una cache.
→ Si hanno problemi prestazionali all'aumentare
del volume di I/O.                            20
Il problema del double caching
               (Un diagramma vale più di 1000 parole)
                                          Applicazione
                                           mmap()      read()
                                                       write()
                                                                 User
                                                                 Kernel
La lettura della memoria del file
in seguito ad mmap() provoca la           Page cache
lettura del dato e la relativa
conservazione sia in buffer cache
sia in page cache.                              Buffer cache

                                                                 Hardware

                                                                        21
Page cache unificata
                (La soluzione al problemino)

Il double caching si elimina solo eliminando una
delle due cache (quella meno efficiente, ossia la
buffer cache). La page cache unificata (unified
page     cache)    gestisce    i   blocchi   letti
  tramite mmap().
  read() e write().
Usato da Linux (≥v2.4.10).
                                                22
Obiettivi di progetto della page cache
                        (In Linux)

Lookup rapido: dato un file, trovare velocemente
le pagine che ne mappano le porzioni in memoria
centrale.
Indipendenza dall'I/O: fornire i metodi di lettura
e scrittura della pagina (da e verso file system, da e
verso un dispositivo a blocchi).
                                                    23
La struct address_space
        (Il collante fra file system, dispositivi a blocchi e pagine)

La struct address_space, definita nel file
$LINUX/include/fs.h, rappresenta una
entità mappabile in memoria centrale.
  Un file, un dispositivo a blocchi, un segmento di
  memoria condivisa.
La struttura funge da vero e proprio collante fra
file system, dispositivi a blocchi, aree di memoria
virtuale e pagine.
                                                                        24
I campi principali della struttura
   (Implementano “la colla” fra i diversi sottosistemi di memoria virtuale)

struct inode *host: L'inode del file o del
dispositivo a blocchi mappato in memoria.
struct radix_tree_root page_tree: La
radice del radix tree rappresentante la page cache.
struct           address_space_operations
*a_ops: un array di puntatori a funzioni per le
operazioni di sincronizzazione delle pagine fra
dispositivo e memoria.
                                                                              25
La struct address_space_operations
 (AKA “ome sincronizzare pagine su disco indipendentemente dal dispositivo”)

La struct address_space_operations,
definita in $LINUX/include/fs.h, introduce
un insieme di puntatori alle operazioni per la
sincronizzazione delle pagine fra dispositivo, file
system e memoria.
Il contenuto di tale struttura è inizializzato ad ogni
apertura di file regolare o speciale di dispositivo.
                                                                           26
Alcune operazioni
        (Lettura, sincronizzazione su disco, invalidazione)

readpage()                      Trasferisce 4KB dal dispositivo
                                alla pagina.
writepage()                     Programma la scrittura di 4KB
                                dalla pagina al dispositivo.
sync_page()                     Forza     immediatamente       la
                                scrittura di una pagina sporca.
set_page_dirty()                Marca una pagina come sporca.

invalidatepage()                Marca una pagina come non
                                più valida (troncamento di un
                                file).                        27
Una osservazione importante
   (Uno stesso blocco di 4KB può essere presente più volte in page cache)

Un blocco di 4KB può essere presente più volte in page
cache (una volta per ogni metodo usato per leggerlo).
Esempi:
  si legge un file che contiene quel blocco
  → sarà presente un elemento nella page cache la cui
  struct address_operations punta alle operazioni
  di sincronizzazione tramite file system.
  si legge il blocco aprendo il file speciale del disco
  → sarà presente un elemento nella page cache la cui
  struct address_operations punta alle operazioni
  di sincronizzazione tramite dispositivo a blocchi.  28
Il radix tree
            (Usato per implementare la page cache)

In Linux, i file possono avere dimensioni enormi
(anche di alcuni TB). Accedendo ad un file grande
la page cache può contenere un numero molto
elevato di pagine.
→ Ogni forma di scansione lineare (lista) delle
pagine di un file è destinata a fallire.
Per tale motivo, la page cache è organizzata nella
forma di un radix tree di struct page *.
                                                     29
I tipi di nodo
               (radix_tree_root, radix_tree_node)
Il nodo radice (radix_tree_root)                    radix_tree_root

è    un      cosiddetto “nodo
sentinella”.
  Memorizza l'altezza dell'albero.
                                          radix_tree_node
  Punta al primo nodo dell'albero.                                    h=1

Il nodo generico dell'albero
(radix_tree_node) può puntare                               struct
ad altri nodi oppure a                                      page *

descrittori di memoria.
L'altezza h dell'albero è contata                                     30
dal primo nodo non sentinella.
La chiave di accesso
           (L'offset del file in termini di numero di pagine)
                                                 Indice file radix_tree_root
La chiave di accesso al radix                    in pagine

tree è l'offset del file, espresso
in termini di numero di pagina.
                                                   radix_tree_node
Si vuole accedere al quinto KB
del file → index = 1.
L'indice è usato per attraversare                                    struct
in maniera efficiente l'albero                                       page *

dalla radice alla foglia associata
alla struct page.                                                         31
Fanout dei nodi
                  (radix_tree_node)
                                      radix_tree_root

Ciascun nodo ha un fanout di
64 → al più 64 nodi figli.
Nell'esempio, un albero di
altezza 1 gestisce i primi 64                  ...
blocchi da 4KB del file.
                                             64 nodi
                                               figli
                                          struct
                                          page *     32
Aumento dell'altezza dell'albero
   (Il radix tree si espande a contenere l'indice più grande di pagina cachata)
                                                                radix_tree_root

Se si vuole inserire in cache il 131mo
blocco da 4KB, l'albero aumenta la
sua altezza a 2. In tal modo è
possibile gestire le prime 4096                                                        h=2
pagine del file (64 * 64).
In generale, per un albero di altezza
n è possibile contenere le pagine
dalla 0 alla 26n-1.                                                  struct
                                                                     page *       33
Altezza dell'albero e numero di pagine
           (Il radix tree è adatto per rappresentazioni sparse)
                                                            radix_tree_root

Il radix tree è ideale per la
rappresentazione   sparse delle
pagine di un file.                                                           1
Esempio:                                         h=3                       struct
  una pagina di indice 1 in cache.                                         page *
  una pagina di indice 100000 in
  cache.
L'albero si espande fino a contenere
l'indice 100000 → altezza h=3.                                    100000
                                                            struct           34
                                                            page *
Tag dei nodi
                (Per capire velocemente se una pagina è )
                                                            radix_tree_root
Il kernel deve capire velocemente, senza
scandire l'intero radix tree, se un gruppo
di pagine è sporco (PG_Dirty) oppure in                                 D
fase di sincronizzazione (PG_Writeback).                Pagina              1
                                                        sporca
Per far ciò, ogni nodo nel percorso dalla                                struct
radice alla pagina è marcato in maniera                           D      page *
corrispondente (PG_Writeback oppure                                     Sottoalbero
PG_Dirty).                                                    D
                                                                        con pagine
                                                                        sporche
Ogni nodo ha due vettori di 64 bit (uno                        100000
per PG_Writeback ed uno per PG_Dirty).
                                                            structD
   Bit = 1: il figlio è marcato dirty/writeback.            page *
                                                                            35
Integrazione della buffer cache
              (Per gestire blocchi di dimensione diversa da 4KB)

La buffer cache è integrata              struct page
nella page cache. I metadati
del file system ed i blocchi letti         Frame fisico
direttamente dal disco sono                   Buffer           buffer_head
memorizzati in pagine dette
buffer page che contengono                    Buffer           buffer_head
più istanze di buffer_head.                   Buffer           buffer_head
       b_data
                                              Buffer           buffer_head
       private
       b_this_page
                                                                             36
       b_page
Invalidazione di frame condivisi
                (Un grosso problema prestazionale)

I frame fisici possono essere condivisi fra più
applicazioni.
  Codice di libreria, blocco di file, area di memoria.
Che succede se, sotto pressione di memoria, un
frame condiviso viene invalidato (e/o swappato)?
→ Devono prima essere aggiornate in maniera
efficiente tutte le tabelle delle pagine che
contengono un riferimento al frame condiviso.
Come fa il kernel a trovare velocemente tutte le
tabelle delle pagine coinvolte nell'invalidazione? 37
Approcci precedenti
                        (Inefficienti)
Il kernel 2.4 scandiva linearmente l'intera lista dei
processi alla ricerca di PTE specifiche da invalidare.
  Lentissimo ed inefficiente; O(n) nel numero di processi.
Nel kernel 2.5 ad ogni frame fisico veniva associata
una lista di puntatori alle tabelle delle pagine
riferenti tale frame.
  Aggravio consistente nell'uso di memoria, dovuto alla
  gestione delle liste.
  La chiamata di sistema fork() veniva rallentata dalla
  creazione di una lista per ogni nuovo frame fisico 38
  allocato.
Mappatura inversa
       (Da frame fisici direttamente alle aree di memoria virtuale)

Ad ogni frame fisico delle applicazioni è associato
un puntatore alla lista di “oggetti” che condividono
tale frame.
  Aree di memoria virtuale (vm_area_struct).
Si evita la scansione delle tabelle delle pagine (che
possono essere tante e grandi).
                                                                      39
Mappatura inversa per aree anonime
             (Una semplice lista di struct anon_vma)

Ciascuna struct page punta ad una struct
anon_vma. Tale struttura non è altro che una lista
di tutte le struct vm_area_struct riferenti il
frame fisico.
Ciascuna vm_area_struct punta (campo
vm_mm) al descrittore di memoria (struct
mm_struct) e di lì alla tabella delle pagine.
Perché una lista e non un albero? Perché le mappe
anonime non sono condivise frequentemente. 40
Mappatura inversa per aree anonime
                    (Un diagramma, da NON imparare a memoria)
                           anon_vma     struct      anon_vma
                                       anon_vma
                  struct                                    struct
              vm_area_struct          anon_             vm_area_struct
                                      vma_                vm_mm
                   vm_mm              node
  struct                                        struct
mm_struct                                      mm_struct
             pgd                                            pgd
                                              mapping

vm_start                                                     vm_start
                                      struct index
                                 index page

                     Frame                                         Frame
                    condiviso                                     condiviso
                                                                              41
   Mappa di memoria anonima                      Mappa di memoria anonima
Mappatura inversa per mappe su file
                   (Un priority search tree)

I frame mappati da file e condivisi possono essere
molto       popolari.    L'implementazione     della
mappatura inversa tramite lista presenta limiti
evidenti di scalabilità.
Si usa un priority search tree, che permette di
individuare velocemente le aree di memoria che
riferiscono un intervallo di frame fisici.
I dettagli sulla struttura dati sono omessi (si
consulti il link di approfondimento sul sito).     42
Collegamento delle strutture dati
    (Obiettivo: raggiungere efficientemente le pagine a partire da indici)

La struct address_space è intimamente
collegata con le altre strutture principali del VFS e
del gestore della memoria virtuale.
In particolare, devono essere possibili diversi
lookup veloci.

                                                                             43
Un diagramma riassuntivo
                       (Da NON imparare a memoria)
                 mapping
                                                                              Radix
    struct       page_tree             struct        struct         struct     tree
address_space                          page          page           page
            host                           index
                                                                              RAM
          struct Immagine file         Frame       Frame            Frame
          inode                         fisico      fisico           fisico

        i_mapping
                                        vm_pgoff
                                     struct         struct
                                                                  Radix
                                 vm_area_struct vm_area_struct priority
                                            vm_file          search tree
                 f_mapping                                                     44
                                     struct file              struct file
API di gestione della page cache
              (Usata dalle funzioni di lettura e scrittura)

        Funzione                             Funzionamento
find_get_page()              Data una struct address_space ed un
                             offset, ritorna la pagina corrispondente dalla
                             page cache (se esiste).
add_to_page_cache()          Inserisce una struct page nella page cache.
delete_from_page_cache()     Rimuove una struct page dalla page cache.
read_cache_page()            Data una struct address_space ed un
                             offset, legge la pagina in page cache o la
                             aggiorna se esiste già.

                                                                              45
RECLAMO DEI FRAME

                    46
Problema: improvvisa scarsità di frame
    (Va gestita tramite il recupero di frame fisici attualmente non usati)

Il kernel non è in grado, a priori, di determinare
quanta memoria fisica sarà usata.
(Quasi) tutte le pagine:
  possono essere richieste ad un qualunque istante, da
  una qualunque applicazione.
  possono essere usate per qualunque scopo.
→ Il kernel può trovarsi improvvisamente a corto
di frame liberi disponibili.
Cosa fare se, nel frattempo, arrivano altre richieste
di frame fisici?                                    47
Algoritmo di reclamo dei frame
    (Recupera frame fisici per le applicazioni che ne hanno bisogno)

L'algoritmo di reclamo dei frame associati alle
pagine (page frame reclaiming algorithm)
risolve tale problema.
  Garantisce che, in un dato istante, siano sempre
  presenti frame liberi.
  Può comandare operazioni di swap-out per liberare la
  memoria fisica.
                                                                       48
Tipologie di frame e azioni possibili
                     (Uno schema riassuntivo, tanto per dare un'idea)

        Tipo                        Descrizione                         Azione

                          Pagine bloccate, stack in Ignorare; non si possono
Non reclamabili           kernel mode, codice kernel, usare
                          pagine libere

Scrivibili su swap    Pagine      anonime             delle Scrivere su area di swap
                      applicazioni
Aggiornabili su disco Porzioni   di file;
                      mappate su file
                                                    pagine Sincronizzare su file e
                                                           scrivere su area di swap

Reclamabili               Pagine non usate                  Non fare nulla
                                                                                 49
Criteri di scelta dei frame
              (Definiscono una “importanza” dei frame)
Si ignorano le pagine non reclamabili.
Si scelgono per prime le pagine non associate ad alcun
processo.
    → Non servono modifiche alla tabella delle pagine
       (reclamo più efficiente).
Se non ve ne sono, si scelgono le pagine associate a
processi, ma non usate nell'ultimo periodo.
    → Il kernel tiene traccia delle pagine “usate” e non
       “usate”.
Se la pagina scelta è condivisa, si aggiornano le tabelle
delle pagine di tutti i processi che ne referenziano il
                                                        50
frame.
Quando reclamare la memoria?
                  (In diverse circostanze)

Quando la memoria libera è scarsa.
Quando il calcolatore entra in uno stato di “sleep”.
  Ibernazione, sospensione.
Su base periodica.
  Si “forza” una certa porzione della memoria ad essere
  libera.
  Comportamento “swappy” del sistema, regolato dal
  parametro “swappiness”.
                                                     51
Le liste “active” e “inactive”
   (Divide i frame acceduti di recente da quelli non acceduti di recente)

Problema: come fa l'algoritmo a discernere quali
pagine sono usate recentemente (e quali no) per
ciascun processo?
Per ciascun processo, il kernel mantiene due liste
collegate di pagine, gestite in modalità LRU.
  Lista attiva (active list): contiene le pagine accedute
  recentemente.
  Lista inattiva (inactive list): contiene le pagine non
  accedute recentemente.
                                                                            52
Come si marcano i frame?
            (AKA “Come passano da una lista all'altra?”)
Primo accesso alla pagina: si vuole evitare di
dichiararla subito attiva. La pagina viene marcata
riferita (referenced) con il flag PG_reference.
Se viene di nuovo acceduta nell'immediato
futuro, viene marcata attiva con il flag
PG_active e non riferita.
  Spostamento nella active list.
Altrimenti, non era poi così popolare → viene
marcata come non riferita e inattiva.
  Spostamento nella inactive list.                         53
Come si marcano i frame?
            (AKA “Come passano da una lista all'altra?”)

Quando la pagina attiva è riferita di nuovo, è
marcata come riferita.
Se la pagina non è stata usata per un certo
periodo di tempo, è sempre attiva, ma viene
marcata come non riferita.
Se scade il timeout di attivazione del refill
periodico e la pagina non è ancora stata usata,
viene marcata come inattiva.
  Spostamento nella inactive list.
                                                           54
Diagramma di stato
                     (Illustra il passaggio fra liste active e inactive)

  PG_active == 0                      PG_active == 1
PG_referenced == 0                  PG_referenced == 0

                                                                    lru_cache_add()
                                                                    lru_cache_add_active()
                                                                    mark_page_accessed()
                                                                    page_referenced()
                                                                    refill_inactive_zone()
  PG_active == 0                      PG_active == 1
PG_referenced == 1                  PG_referenced == 1

                                                                                    55
Refill?
                      (Che cosa è?)
Il reclamo periodico delle pagine prende anche il
nome di refill (ricarica).
Obiettivo: avere frame liberi a disposizione
periodicamente.
Problema: quante pagine deve reclamare il refill?
  Se il refill è troppo aggressivo, saranno reclamate
  troppe pagine.
  Se il refill è troppo timido, non sarà disponibile un
  quantitativo sufficiente di memoria.
Il refill calcola il numero di pagine in maniera
                                               56
adattativa.
Refill adattativo
                            (Che cosa è?)
Il refill inizia con una scansione (scan) di poche pagine,
per controllare se debbano essere reclamate.
   Pagine inattive di codice.
Se, nel frattempo, la memoria libera continua a
decrescere, al prossimo scan si aumenta il numero di
pagine da scandire.
Se invece, nel frattempo, la memoria libera continua ad
aumentare, al prossimo scan si riduce il numero di pagine
da scandire.
Se il refill non riesce a liberare abbastanza memoria, si
swappano le pagine di dati.
                                                        57
   Prima le inattive, poi le attive.
Un po' di matematica
                  (Scalatura logaritmica della priorità)
Il refill adattativo è controllato da un parametro interno
(priority) nell'intervallo [0, 12].
   0: scansione di tutte le pagine del processo.
   12: scansione del numero minimo di pagine.
Priority è trasformato in maniera esponenziale un altro
parametro (distress) nell'intervallo [0, 100].
Distress misura la crescente “pressione” sul kernel in
termini di richieste di memoria.
   0: nessuna criticità.
   100: criticità (memoria libera quasi assente).
    Priority   12..7    6     5     4     3      2     1    0
                                                                  58
    Distress   0        1     3     6     12     25    50   100
Un po' di matematica
         (Calcolo della percentuale di memoria fisica usata)

Durante il refill si calcola anche la percentuale di
memoria associata a frame fisici (mapped_ratio).
  mapped_ratio=(nr_mapped * 100) / total_mem;
Mapped_ratio alto → lo spazio di indirizzamento
virtuale è zeppo di page frame (i processi usano
tanta memoria fisica).
Mapped_ratio basso → lo spazio degli indirizzi
lineari fa riferimento a pochi frame molto
popolari.                                      59
Un po' di matematica
                (Stima della tendenza allo swap)
Infine, si calcola la “tendenza allo swap” (swap
tendency).
  swap_tendency = mapped_ratio/2 +
                  distress       +
                  vm_swappiness;
Più è alto swap_tendency, più pagine saranno
soggette a swap-out.
Influenza su swap_tendency:
  numero di pagine mappate su frame.
  pressione sul kernel in termini di richieste di memoria.
                                                         60
  “swappiness”.
Swappiness
  (Configura la solerzia con cui il kernel swappa processi inattivi da tempo)
Il parametro vm_swappiness indica se, in
condizioni operative normali, il kernel deve
swappare pagine oppure no.
Valore configurabile dall'utente tramite il file
/proc/sys/vm/swappiness.
Intervallo dei valori: [0, 100].
  0: il sistema non deve swappare mai in condizioni
  normali.
  100: il sistema deve swappare tutte le pagine di
  processi non attivi, anche quando non ce ne sarebbe
                                                    61
  bisogno.
Out Of Memory killer
                   (Il killer dei processi)

Se il kernel non trova:
  memoria libera
  memoria reclamabile
si adotta una misura drastica: uccide qualche
processo. È scelto un processo
  che consuma tanta memoria (nel complesso, fra lui ed
  i suoi figli).
  con un tempo di esecuzione basso.
  di bassa priorità (CPU).
  non appartenente a root.                           62
Scrittura delle pagine sporche
                    (Strategia generale)

In condizioni normali, il kernel cerca di non
scrivere tante pagine sporche su disco in un colpo
solo.
  Avete visto gli effetti devastanti di una scrittura
  continua sull'interattività.
Inoltre, una pagina può essere sporcata spesso.
  Non ha molto senso scriverla ogni volta su disco.
Tuttavia, se la memoria libera è poca, ossia se una
richiesta di allocazione dinamica ha ritornato un
errore, le pagine da scrivere diventano tante!    63
Implementazione del refill
            (Processo di servizio del kernel kswapd)

Il refill è eseguito da un un processo di servizio
del kernel, kswapd, che esegue periodicamente
e reclama la memoria con le tecniche viste finora.
Ad ogni giro, kswapd reclama ben 32 pagine
(128KB di memoria).
Al termine del reclamo (e dell'eventuale swap-
out), kswapd invoca lo scheduler, per consentire
ad altri processi di continuare la loro esecuzione.
                                                       64
Contrasto al thrashing
  (Un token magico permette ad un processo di non essere mai swappato)
Per contrastare il thrashing, in passato kswapd
ha introdotto un meccanismo di “elezione” dei
processi.
Un processo alla volta possiede un gettone di
swap (swap token). Le pagine di tale processo
non sono mai reclamate.
  Tale processo non può swappare, mentre gli altri sì.
  Rationale: il processo può continuare l' esecuzione
  nonostante la pressione di memoria.
Supporto rimosso nei kernel recenti, che hanno65
invece meccanismi di controllo delle risorse.
Puoi anche leggere