Lezione 33 Il Block Layer - WEB Lab
←
→
Trascrizione del contenuto della pagina
Se il tuo browser non visualizza correttamente la pagina, ti preghiamo di leggere il contenuto della pagina quaggiù
Lezione 33 Il Block Layer 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...) “I think the major good idea of UNIX was its clean and simple interface: open, close, read and write.” Ken Thompson (1943-) Ingegnere, Programmatore Ideatore del sistema operativo UNIX Creatore di Belle 2
GESTIONE STRATIFICATA I/O BLOCCHI 3
Eterogeneità dei dispositivi (Elevata, purtroppo) La gestione di un dispositivo a caratteri prevede di mantenere una sola posizione, quella attuale del cursore di lettura/scrittura. La gestione di un dispositivo a blocchi (che può effettuare seek) è molto più complessa. La complessità è incrementata dalla presenza di dispositivi a blocchi con caratteristiche molto diverse. Rotazionali (a disco) e non rotazionali (SSD). Logici (LVM) e non. Provvisti di meccanismi per la lettura concorrente (ad esempio, Native Command Queueing). 4
Componenti del Block Layer (Generic Block Layer) Generic block layer. Fornisce una interfaccia di accesso comune a tutti i dispositivi a blocchi nascondendo i dettagli tipici del singolo dispositivo. Nasconde i dettagli tipici del singolo dispositivo a blocchi al file system. Consente il disaccoppiamento tra organizzazione logica del file (file system) e posizione fisica del dato sottostante. Utilizza politiche efficienti di servizio delle richieste di I/O. 5
Componenti del Block Layer (Scheduler di I/O) Scheduler del disco. Decide l'ordine di servizio delle richieste di I/O per uno specifico dispositivo a blocchi. L'utente può selezionare, tra quelli compilati nel kernel, lo scheduler utilizzato per ciascun dispositivo a blocchi. 6
Componenti del Block Layer (Diagramma semplificato) Descrittore File system File system del disco Block layer Generic block layer Scheduler del disco Scheduler del disco Driver del dispositivo Driver del dispositivo 7
Blocchi e settori (Unità di trasferimento del block layer e del dispositivo) L'unità di indirizzamento minima di un dispositivo a blocchi è il settore. Dimensione: è una caratteristica fisica del dispositivo. Dimensione tipica: 512 byte. Il block layer utilizza, come unità di memorizzazione, il blocco. Struttura dati “di collegamento” tra file system, mapping layer e block layer. Dimensione: multiplo di un settore. Dimensioni comuni: 512 byte, 1kB, 4 kB, ... 8
Blocchi e settori (Uno schema semplificato) Un blocco è un insieme di settori contigui su disco. Blocchi Settore ... più settori contigui = 1 blocco 9
Blocchi e pagine (Una pagina → Uno o più blocchi) Una pagina di memoria può contenere uno o più blocchi. La dimensione del blocco non può eccedere quella della pagina. Quando un blocco è mappato in memoria si dice “contenuto in un buffer”. Un buffer è la porzione di una pagina di memoria corrispondente ad un blocco. 10
La struttura buffer_head (Già incontrata nel contesto della page cache) La struct buffer_head ha il compito di mappare un buffer di una pagina di memoria nel blocco corrispondente. http://lxr.linux.no/linux+v3.6.5/include/linux/buffer_head.h#L59 La struttura dati contiene: informazioni sulla pagina. indirizzo della pagina di memoria. indirizzo del buffer all'interno della pagina. informazioni sul blocco. dispositivo fisico al quale il blocco si riferisce. numero di blocco. 11
La struttura buffer_head (Uno schema semplificato) buffer_head b_bdev b_page Dispositivo b_blocknr page b_size buffer b_data buffer Blocco ... 2 settori buffer 12
Problemi delle buffer_head (Prestazionali) Nelle versioni del kernel Linux precedenti alla 2.6, la struct buffer_head non descriveva solo la mappatura tra blocchi a pagine. Era utilizzata per tutte le operazioni di I/O. Ciascuna operazione di I/O era effettuata sul singolo blocco. Operazioni di I/O potenzialmente grandi risultavano frammentate. Aggravio computazionale enorme. Alto consumo di memoria (tante strutture piccole in caso di letture consistenti). 13
La struct bio (Risolve i problemi delle buffer_head) La struct bio (introdotta nel kernel v2.6 come rimpiazzo dei buffer) rappresenta le operazioni di I/O in corso come una lista di segmenti. Un segmento è una porzione di buffer contigua in memoria. Può essere più grande di un blocco! I singoli buffer non devono essere contigui in memoria. 14
Definizione della struct bio (Una lista di segmenti) La struct bio rappresenta le operazioni di I/O in corso come una lista di segmenti. http://lxr.linux.no/#linux+v3.6.6/include/linux/blk_types.h#L35 La struttura contiene: una lista di struct bio_vec, ciascuna delle quali rappresenta un segmento, ovvero una porzione di memoria ad un certo offset all'interno di una certa pagina. dimensioni dell'I/O da effettuare. caratteristiche del dispositivo coinvolto dall'operazione di I/O. 15
I/O vettorizzato (Scatter-gather I/O) La struct bio consente al kernel di effettuare I/O a blocchi da locazioni multiple di memoria. I/O vettorizzato (scatter-gather I/O): sfrutta il parallelismo del dispositivo per scrivere in memoria più buffer contemporaneamente. Dischi veloci, schede di rete. 16
La struct iovec (Contiene i dati letti in maniera vettorizzata) struct iovec: contiene un campo “base” ed uno “lunghezza” → identifica un “segmento” da leggere in memoria. La chiamata di sistema readv() legge una lista di 17 segmenti (anche non contigui).
La struct bio_vec (Il vettore di segmenti) struct bio_vec { pagina /* puntatore alla pagina */ struct page *bv_page; segmento /* lunghezza del segmento */ unsigned int bv_len; /* posizione del segmento */ unsigned int bv_offset; }; 18
Relazione tra bio, bio_vec e pagine (Come è implementata la lista di segmenti) struct bio bi_io_vec bi_idx (posizione attuale dell'I/O) bio_vec bio_vec bio_vec bio_vec Pagine coinvolte pagina nell'operazione di I/O pagina pagina pagina 19
PROGRAMMAZIONE ASINCRONA I/O 20
Accesso a dispositivi a blocchi (Sincrono o asincrono) Non è detto che una richiesta di I/O debba risultare bloccante per il flusso di esecuzione di un processo. Se è una lettura, c'è la possibilità che lo sia. Il processo generalmente ha bisogno dei dati che legge per proseguire con l'elaborazione. Questo non è sempre vero. Se è una scrittura, è stata delegata dal processo al flusher. Così come il processo, il flusher non ha bisogno di bloccarsi in attesa del suo completamento. 21
Accesso a dispositivi a blocchi (Sincrono o asincrono) Tuttavia, l'accesso al dispositivo fisico avviene in modo concorrente. C'è sempre qualcuno che deve aspettare il proprio turno... Il block layer deve interagire con il device driver del singolo dispositivo a blocchi. Bisogna rispettare i tempi del dispositivo fisico, che può essere impegnato in altre operazioni. 22
La struct request_queue (Contiene le operazioni di I/O in attesa di essere elaborate dal driver) La struct request_queue è utilizzata per memorizzare le operazioni di I/O in attesa di servizio da parte del device driver. http://lxr.linux.no/#linux+v3.6.6/include/linux/blkdev.h#L286 23
La struct request_queue (Contiene le operazioni di I/O in attesa di essere elaborate dal driver) Ciascun dispositivo a blocchi ha la propria request_queue. Rappresentata dal sottoalbero di sysfs /sys/block//queue. Contiene una lista doppiamente concatenata di struct request (viste in seguito) rappresentanti richieste di I/O. La coda è mantenuta ordinata per settori crescenti (in modo tale da ridurre i seek). 24
Accesso a dispositivi via request queue (È il driver a pescare richieste dalla request_queue) Nel momento in cui il block layer vuole mettere a disposizione del device driver di un dispositivo una richiesta, la accoda nella request_queue del dispositivo. Quando il driver è pronto a servire una richiesta, controlla se la request_queue del dispositivo è vuota. Se non lo è, estrae una richiesta dalla request_queue e la serve. Se lo è, attende che il block layer lo solleciti ad estrarre una richiesta. Tipicamente, ciò avviene ogni volta che un processo viene messo in attesa. 25
Uso di callback (Asincronia delle operazioni) Il completamento di una operazione di I/O viene segnalato dal device driver al block layer con l'uso di un callback (elv_completed_request()). L'invocazione del callback: avviene nella bottom half del dispositivo a blocchi. provoca la cancellazione delle strutture dati non più utili ai fini dell'operazione. provoca la schedulazione di altre richieste di I/O. 26
RIORGANIZZAZIONE RICHIESTE I/O 27
Modalità di I/O (I/O sequenziale) Un processo può effettuare richieste di I/O secondo due modalità. I/O sequenziale: una richiesta riguarda settori contigui rispetto a quelli della richiesta precedente. Esempi: lettura di un file grande, file system check, ... 28
Modalità di I/O (I/O casuale) Un processo può effettuare richieste di I/O secondo due modalità. I/O casuale: una richiesta non riguarda settori contigui rispetto a quelli della richiesta precedente. Esempio: il caricamento in memoria di librerie preliminare all'avviamento di un'applicazione segue un pattern casuale. 29
Modalità di I/O (Un problema) Ciascuna richiesta di I/O è rappresentata dalla struct bio, che mantiene la mappatura tra i blocchi e le pagine interessati dall'operazione. Si consideri il caso di un processo che effettua accesso sequenziale ad un disco. Il fatto di avere multiple richieste di I/O per blocchi i cui settori sottostanti sono adiacenti introduce: aggravio computazionale inutile. consumo non necessario di memoria. 30
La struct request (Identifica la singola richiesta da fare al dispositivo fisico) La struct request rappresenta un'operazione di I/O in corso su un insieme di blocchi i cui settori sottostanti sono adiacenti. Può coinvolgere diverse bio. Consente al block layer di comunicare con il device driver (struct request è la struttura accodata nella request_queue del dispositivo). http://lxr.linux.no/#linux+v3.6.5/include/linux/blkdev.h#L95 La struct request contiene il puntatore ad una lista di struct bio. 31
Fusione di richieste (Merging: una forma di batching) Nel momento in cui una struct bio viene costruita nel block layer: si controlla se esistono, all'interno del block layer, una o più struct request che riguardano settori adiacenti. In caso affermativo, le strutture dati bio sono accorpate in una unica struct request. → Operazione di merging: fusione di una bio in una o più richieste già esistenti. In questo modo, si riduce l'aggravio legato alla gestione di multiple struct bio. 32
Fusione di richieste (Front merge, back merge) Front merge: inserimento della nuova richiesta prima di una richiesta già presente. Back merge: inserimento della nuova richiesta dopo una richiesta già presente. Coalesce: raggruppamento di tre richieste in seguito al fatto che la nuova sia successiva alla prima e precedente alla seconda. Back merge richiesta nuova richiesta nuova già presente bio già presente bio Front merge Coalesce 33
Plugging (Un'altra forma di batching) Se le richieste fossero date immediatamente al driver, la testina subirebbe molti seek. Ciò va evitato come la peste. Plugging: il block layer attende per un po' di tempo, nella speranza che arrivino tanti richieste fondibili. Obiettivo: creare poche richieste grandi e vicine. Al verificarsi di un evento, viene attivato il device driver. Scadenza di un timer. 34 Accumulo eccessivo di richieste.
Plugging per processo (Abbassa le latenze dovute al plugging “globale”) Il plugging è un ottimo meccanismo che, tuttavia, soffre di un problema. Se diversi processi fanno richieste, tutti provano a prendere lo stesso lock sulla request_queue. Le attese possono diventare lunghe. Nelle ultime versioni del kernel, il lock è stato reso a grana più fine. Una struttura dati di plug per processo. Un lock per struttura dati di plug. Esempi successivi: plugging classico. 35
Tempi di esecuzione della richiesta (Seek, latency, transfer) In generale, il tempo impiegato da un disco magnetico per trasferire una certa quantità di dati può essere rappresentato come somma di: tempo di seek → tempo di posizionamento della testina sulla traccia corretta. latenza rotazionale → tempo impiegato per posizionare il settore giusto al di sotto della testina (tipicamente, la metà del tempo di rotazione indicato dal costruttore) tempo di trasferimento dei dati 36
Accesso ad un dispositivo rotazionale (Un diagramma semplificato) Disco magnetico Tempo di seek traccia Ultima posizione della testina Latenza rotazionale Tempo di trasferimento 37 Settori da trasferire
Sorting (Si riordina la sequenza di richieste casuale per ridurre i seek) Il tempo di trasferimento è costante. Si conosce il valor medio della latenza (è fornito dal costruttore). Il tempo di seek dipende dalla posizione della richiesta servita rispetto alla posizione precedente della testina. Anche in questo caso si conosce il valor medio. Conoscendo i settori interessati dalle richieste di I/O, però, è possibile ridurre il numero di seek effettuati. Sorting: riordinamento delle strutture dati request per settori crescenti. 38
Sorting (Un esempio) Si consideri un semplice scenario di accesso casuale a un dispositivo a blocchi. Disco rotazionale avente tempo medio di seek di 15 millisecondi. 10 richieste effettuate per settori collocati su tracce diverse nel seguente ordine: 10, 2, 5, 8, 4, 7, 1, 9, 3, 6. 39
Accesso senza riordinamento (La testina si sposta di continuo) 10 9 10 seek Settore 8 10 * (15 ms) 7 = 150 ms 0,15 secondi sono persi 6 nelle operazioni di seek 5 Il tasso di servizio ne 4 risente 3 2 1 40 Tempo
Accesso con riordinamento (La testina non si sposta mai) 10 9 1 seek (per raggiungere Settore 8 il primo settore, se necessario) 7 Attesa di 15 ms 6 In seguito, il 5 trasferimento avviene in modo continuato con 4 alto tasso di servizio 3 2 1 41 Tempo
Scheduler del disco (Riordina le richieste di I/O) Scheduler del disco: componente il cui compito è decidere in che ordine le strutture request debbano essere messe a disposizione del device driver. Tipicamente, persegue due obiettivi: il tasso di servizio deve essere alto. le richieste devono essere servite il prima possibile. Implementa politiche di gestione delle richieste. Deve rispettare prototipi generici definiti in un descrittore. 42 Può implementare operazioni specifiche.
Scheduler del disco (Riordina le richieste di I/O) La politica implementata da uno scheduler solitamente prevede che partecipi alle operazioni di merge e sort operate dal generic block layer. Dà il consenso ad operazioni di merge su request già presenti nelle proprie strutture dati private. Tipicamente, gestisce le richieste secondo l'algoritmo dell'ascensore (elevator): non salta follemente di piano in piano, ma serve le richieste in ordine di settore. 43
UNA VISIONE D'INSIEME 44
Un esempio concreto di I/O (Interazione fra i componenti del block layer) Pagina User Processo memoria mode Kernel Block layer mode Richiesta lettura blocco Il processo richiede un dato via read(). Il VFS individua il giusto dispositivo ed invoca la do_sync_read(), che attiva il block layer. Hardware 45
Un esempio concreto di I/O (Interazione fra i componenti del block layer) Pagina ... Pagina User Processo memoria memoria mode Kernel Block layer mode Read ahead Il block layer decide se è il caso di effettuare una anticipazione (read ahead) di 128KB, per migliorare le prestazioni delle read() successive (nel caso di accesso sequenziale). Hardware 46
Un esempio concreto di I/O (Interazione fra i componenti del block layer) Pagina ... Pagina User Processo memoria memoria mode Kernel Block layer mode Preparazione struct bio Il block layer prepara la struct bio descrivente il segmento di bio memoria coinvolto nella lettura. bio_vec ... bio_vec Hardware 47
Un esempio concreto di I/O (Interazione fra i componenti del block layer) Pagina ... Pagina User Processo memoria memoria mode Kernel Block layer mode Inserimento della bio nello scheduler Il block layer trasforma la bio in una request e la inserisce nella coda di bio request richieste dello scheduler di I/O. Hardware 48
Un esempio concreto di I/O (Interazione fra i componenti del block layer) Pagina ... Pagina User Processo memoria memoria mode Kernel Block layer mode Inserimento della bio nello Il block layer trasforma la bio in una scheduler request e la inserisce nella coda di richieste dello scheduler di I/O. La bio request coda è ordinata per settori crescenti. Hardware 49
Un esempio concreto di I/O (Interazione fra i componenti del block layer) Pagina ... Pagina User Processo memoria memoria mode Kernel Block layer mode Inserimento della bio nello scheduler Lo scheduler opera una fusione delle richieste, se contigue. bio request Hardware 50
Un esempio concreto di I/O (Interazione fra i componenti del block layer) Pagina ... Pagina User Processo memoria memoria mode Kernel Block layer mode Inserimento della bio nello Il block layer NON invia subito la scheduler richiesta al device driver, bensì attende per uno specifico intervallo bio request (unplug timeout). L'idea è quella di operare più fusioni possibili, ottenendo poche, grandi richieste. Hardware 51
Un esempio concreto di I/O (Interazione fra i componenti del block layer) Pagina ... Pagina User Processo memoria memoria mode Kernel Block layer mode Device driver Allo scadere del timer di unplug, il device driver è attivato. Esso chiede una richiesta allo scheduler di I/O e la request Coda inserisce nella coda delle richieste del scheduler dispositivo in modo tale da mantenere ordinati i settori. Coda dispositivo Hardware 52
Un esempio concreto di I/O (Interazione fra i componenti del block layer) Pagina ... Pagina User Processo memoria memoria mode Kernel Block layer mode Device driver Successivamente, il device driver DMA pesca la prima richiesta dalla coda request Coda del dispositivo e la programma in scheduler DMA. Coda dispositivo Hardware 53
Un esempio concreto di I/O (Interazione fra i componenti del block layer) Pagina ... Pagina User Processo memoria memoria mode Kernel Block layer mode Device driver La richiesta è programmata. Non schedule() rimane che attendere l'interruzione del dispositivo. Il device driver rischedula il processo: schedule(). Hardware 54
Un esempio concreto di I/O (Interazione fra i componenti del block layer) Pagina ... Pagina User Processo memoria memoria mode Kernel Block layer mode Quando arriva l'interruzione del Bottom half dispositivo, viene eseguita la sua top half, che controlla lo stato del dispositivo, riabilita le interruzioni e schedula la bottom half. Attenzione: i Top half dati richiesti sono già in memoria centrale (il trasferimento è avvenuto in DMA). Hardware 55
Un esempio concreto di I/O (Interazione fra i componenti del block layer) Pagina ... Pagina User Processo memoria memoria mode Kernel Block layer mode Device driver Bottom half Quando viene eseguita la bottom half, si dichiara completata la Coda richiesta. Il device driver distrugge, scheduler per conto della bottom half, tutte le strutture dati coinvolte per la gestione della stessa. Coda dispositivo Hardware 56
Un esempio concreto di I/O (Interazione fra i componenti del block layer) Pagina ... Pagina User Processo memoria memoria mode Kernel Block layer mode Device driver Bottom half Il device driver chiede allo scheduler la prossima richiesta da request Coda programmare in DMA. La procedura scheduler di prelievo si ripete fino a quando non sono portati in memoria centrale tutti i dati. Coda dispositivo Hardware 57
Un esempio concreto di I/O (Interazione fra i componenti del block layer) Pagina ... Pagina User Processo memoria memoria mode Kernel Block layer mode Device driver Il device driver risveglia il processo. Hardware 58
Puoi anche leggere