LEZIONE 29 PAGE CACHE - SISTEMI OPERATIVI (9 CFU), CDL INFORMATICA, A. A. 2020/2021 DIPARTIMENTO DI SCIENZE FISICHE, INFORMATICHE E MATEMATICHE ...
←
→
Trascrizione del contenuto della pagina
Se il tuo browser non visualizza correttamente la pagina, ti preghiamo di leggere il contenuto della pagina quaggiù
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