Qual è la filosofia alla base del ritardo nella scrittura dei dati su disco?


72

In Linux, un'esecuzione finita di un comando come cpo ddnon significa che i dati siano stati scritti sul dispositivo. È necessario, ad esempio, chiamare synco richiamare la funzione "Rimozione sicura" o "Espulsione" sull'unità.

Qual è la filosofia alla base di tale approccio? Perché i dati non vengono scritti contemporaneamente? Non c'è pericolo che la scrittura fallisca a causa di un errore I / O?


16
Ricordare che le chiamate di sistema di lettura e scrittura possono funzionare con un byte alla volta, ma le unità disco possono solo leggere o scrivere blocchi di dimensioni fisse. L'overhead per byte alla volta I / O sarebbe intollerabile senza buffering. Con buffering, è sopportabile.
Jonathan Leffler,

Risposte:


47

Qual è la filosofia alla base di tale approccio?

Efficienza (migliore utilizzo delle caratteristiche del disco) e prestazioni (consente all'applicazione di continuare immediatamente dopo una scrittura).

Perché i dati non vengono scritti contemporaneamente?

Il vantaggio principale è che il sistema operativo è libero di riordinare e unire le operazioni di scrittura contigue per migliorare l'utilizzo della larghezza di banda (meno operazioni e meno ricerche). I dischi rigidi hanno prestazioni migliori quando sono richieste poche operazioni di grandi dimensioni, mentre le applicazioni tendono invece a richiedere un numero elevato di piccole operazioni. Un'altra chiara ottimizzazione è che il sistema operativo può anche rimuovere tutto tranne l'ultima scrittura quando lo stesso blocco viene scritto più volte in un breve periodo di tempo, o addirittura rimuovere alcune scritture tutte insieme se il file interessato è stato rimosso nel frattempo.

Queste scritture asincrone vengono eseguite dopo la writerestituzione della chiamata di sistema. Questo è il secondo vantaggio più visibile per l'utente. Le scritture asincrone velocizzano le applicazioni in quanto sono libere di continuare il loro lavoro senza attendere che i dati siano effettivamente sul disco. Lo stesso tipo di buffering / caching è implementato anche per le operazioni di lettura in cui blocchi di lettura recenti o spesso vengono conservati in memoria anziché essere letti nuovamente dal disco.

Non c'è pericolo che la scrittura fallisca a causa di un errore IO?

Non necessariamente. Ciò dipende dal file system utilizzato e dalla ridondanza in atto. Un errore I / O potrebbe essere innocuo se i dati possono essere salvati altrove. I file system moderni come ZFS fanno auto-curare blocchi di dischi danneggiati. Si noti inoltre che gli errori I / O non bloccano i sistemi operativi moderni. Se si verificano durante l'accesso ai dati, vengono semplicemente segnalati all'applicazione interessata. Se si verificano durante l'accesso ai metadati strutturali e mettono a rischio il file system, potrebbe essere rimontato in sola lettura o reso inaccessibile.

Esiste anche un leggero rischio di perdita di dati in caso di arresto anomalo del sistema operativo, interruzione di corrente o guasto hardware. Questo è il motivo per cui le applicazioni che devono essere sicure al 100% dei dati su disco (ad es. Database / app finanziarie) eseguono scritture sincrone meno efficienti ma più sicure. Per mitigare l'impatto sulle prestazioni, molte applicazioni usano ancora scritture asincrone ma alla fine le sincronizzano quando l'utente salva esplicitamente un file (ad es. Vim, elaboratori di testi).

D'altra parte, la stragrande maggioranza degli utenti e delle applicazioni non necessita né si preoccupa della sicurezza fornita dalle scritture sincrone. Se si verifica un arresto anomalo o un'interruzione dell'alimentazione, l'unico rischio è spesso quello di perdere nel peggiore dei casi gli ultimi 30 secondi di dati. A meno che non sia coinvolta una transazione finanziaria o qualcosa di simile che implicherebbe un costo molto più grande di 30 secondi del loro tempo, l'enorme guadagno nelle prestazioni (che non è un'illusione ma molto reale) le scritture asincrone sta permettendo in gran parte di superare il rischio.

Infine, le scritture sincrone non sono sufficienti per proteggere i dati scritti comunque. Se la tua applicazione deve davvero essere certa che i suoi dati non possano andare persi, qualunque cosa accada, la replica dei dati su più dischi e su più aree geografiche deve essere messa in atto per resistere a disastri come incendi, inondazioni, ecc.


Oltre al costo, considera se è stato fatto qualcosa che si basa sul salvataggio dei dati. Se sto scrivendo il mio romanzo, risparmiando in sequenza e una interruzione di corrente significa che perdo 30 secondi di lavoro, quindi indipendentemente dal valore di quei 30 secondi almeno mi riprendo a uno stato che si è effettivamente verificato durante il processo di digitazione e posso ricominciare da lì. D'altra parte, se premo "salva" e poi incrocio qualcosa dalla mia lista di todo di carta sulla mia scrivania, allora quando mi riprendo ho un'incoerenza tra il mio disco rigido e la mia carta. In genere è più difficile riprendere da ...
Steve Jessop,

1
... quindi, come utente normale, potrei voler sincronizzare il filesystem prima di cancellare "finish writing my novel" dalla mia lista di cose da fare, per essere sicuro di non pensare di aver fatto qualcosa che in realtà non funziona. Ed è per questo che database e simili necessitano di scritture sincrone: anche se perdono dati, devono assolutamente mantenere la coerenza.
Steve Jessop,

1
@SteveJessop Sono d'accordo con il tuo esempio ma non mi aspetto che un utente occasionale si sincronizzi manualmente. Se l'editor utilizzato per scrivere il prezioso romanzo non chiama fsync o simili quando il documento viene salvato, questo è un bug da correggere , ad esempio bugs.launchpad.net/ubuntu/+source/libreoffice/+bug/817326 . Vorrei usare vi (vim) per scrivere il mio, vim chiama fsync al salvataggio di default.
jlliagre,

59

Dà semplicemente un'illusione di velocità ai programmi che in realtà non devono attendere fino al completamento di una scrittura. Monta i tuoi filesystem in modalità sync (che ti dà le tue scritture istantanee) e vedi quanto è lento tutto.

A volte i file esistono solo temporaneamente ... un programma fa un po 'di lavoro ed elimina il file subito dopo il lavoro. Se hai ritardato quelle scritture, potresti evitare di non averle scritte in primo luogo.

Non c'è pericolo che la scrittura fallisca a causa di un errore IO?

Oh assolutamente. In tal caso, di solito l'intero filesystem passa in modalità di sola lettura e tutto è orribile. Ma ciò accade raramente, inutile perdere i vantaggi prestazionali in generale.


Alcuni controller HDD hanno il backup della batteria, quindi in caso di interruzione dell'alimentazione i dati non impegnati vengono conservati sul controller fino al ripristino dell'alimentazione. Ciò consente l'utilizzo in applicazioni di database in cui la perdita di dati non è un'opzione.
Strattonn,

Linux memorizza i dati non ancora scritti nella RAM, non sull'HDD. Anche l'HDD ha la sua cache.
Barafu Albino,

Sarebbe abbastanza conveniente se un file aperto da un processo fosse sincronizzato alla chiusura del processo. Ciò non influirebbe sul processo stesso, ma semplificherebbe gli script di shell e simili (che ora devono sincronizzare un intero file system)
MSalters,

14
È più di un'illusione. Le scritture asincrone migliorano le prestazioni complessive delle applicazioni.
jlliagre,

4
@frostschutz: Oltre ai file esistenti solo temporaneamente, c'è anche il fatto che alcune aree dei file vengono riscritte più volte.
Matthieu M.,

26

L'I / O asincrono e bufferizzato era in uso prima di Linux e persino prima di Unix. Unix ce l'aveva e anche tutti i suoi derivati.

Ecco cosa hanno scritto Ritchie e Thompson nel loro documento CACM The UNIX Time-Sharing System :

Per l'utente, sia la lettura che la scrittura dei file sembrano essere sincrone e senza buffer. Questo è immediatamente dopo il ritorno da una chiamata di lettura, i dati sono disponibili e, al contrario, dopo una scrittura, l'area di lavoro dell'utente può essere riutilizzata. In effetti il ​​sistema mantiene un meccanismo di buffering piuttosto complicato che riduce notevolmente il numero di operazioni I / O richieste per accedere a un file.


Nella tua domanda, hai anche scritto:

Non c'è pericolo che la scrittura fallisca a causa di un errore IO?

Sì, la scrittura può fallire e il programma potrebbe non saperlo mai. Sebbene non sia mai una buona cosa, gli effetti di questo possono essere ridotti al minimo nei casi in cui un errore I / O genera un panico di sistema (su alcuni SO è configurabile - invece di panico, il sistema può continuare a funzionare ma il filesystem interessato è non montato o montato in sola lettura). Gli utenti possono quindi essere informati che i dati su quel file system sono sospetti. E un'unità disco può essere monitorata in modo proattivo per vedere se l' elenco dei difetti cresciuto sta rapidamente aumentando, il che indica che l'unità non funziona.

BSD ha aggiunto la fsyncchiamata di sistema in modo che un programma potesse essere certo che i suoi dati di file fossero stati completamente scritti su disco prima di procedere, e i successivi sistemi Unix hanno fornito opzioni per eseguire scritture sincrone. GNU dd ha un'opzione conv=fsyncper assicurarsi che tutti i dati siano stati scritti prima della chiusura del comando. È utile quando si scrive su unità flash rimovibili lente, in cui i dati bufferizzati possono richiedere diversi minuti per la scrittura.

Un'altra fonte di corruzione dei file è un arresto improvviso del sistema, ad esempio a causa di una perdita di potenza. Praticamente tutti i sistemi attuali supportano un flag clean / dirty nei loro filesystem. Il flag è impostato su clean quando non ci sono più dati da scrivere e il filesystem sta per essere smontato, in genere durante l'arresto del sistema o chiamando manualmente umount. Di solito i sistemi funzioneranno fsckal riavvio se rilevano che i filesystem non sono stati chiusi in modo pulito.


Supponiamo di copiare la musica dall'HDD su un'unità esterna. È possibile che l'unità esterna sia danneggiata e che la scrittura non riesca. Ciò non provocherebbe l'esecuzione di un programma con dati errati. E sembra eccessivo andare nel panico su un IO non riuscito su un dispositivo esterno.
Marmistrz,

Buon punto. Modificherò la mia risposta.
Mark Plotnick,

15

Molte buone risposte, ma lasciami aggiungere un'altra cosa ... Ricorda che Unix è un sistema multi-processo e multi-utente, quindi potenzialmente molti utenti potrebbero provare a fare operazioni sui file (specialmente le scritture) in (quasi) il contemporaneamente. Con i vecchi dischi rigidi lenti - magari montati sulla rete - questo non solo richiederebbe tempo (per cui i programmi si bloccherebbero sostanzialmente e gli utenti dovrebbero aspettare), ma causerebbero molti spostamenti del read / write-head del disco avanti e indietro.

Quindi, invece, i file in attesa di essere scritti sono stati conservati per un po 'in memoria e ordinati dopo dove dovevano finire sul disco ... e quando il buffer era pieno - o il demone di sincronizzazione del disco aveva atteso il numero di secondi richiesto (penso che in genere fosse di circa 30 secondi) - l'intero buffer è stato scritto sul disco "in ordine", con la testina di scrittura che deve solo fare un movimento continuo, scrivendo i file sul disco come è andato ... invece di saltare dappertutto.

Di certo con i dischi veloci di oggi - per non parlare dei dispositivi a stato solido - il guadagno è molto meno ... espeicamente su un sistema Linux domestico, dove c'è un solo utente alla volta, e solo con pochi programmi.

Ad ogni modo, la combinazione di anticipare le letture leggendo (nella cache / buffer) più di quanto richiesto - e ordinando i dati in attesa di essere scritti, in modo da poter essere scritti in "un movimento" - è stata in realtà un'ottima idea tempo, soprattutto su sistemi con molta lettura e scrittura da parte di molti utenti.


2
XFS non decide nemmeno dove inserire i dati fino alla scrittura. L'allocazione ritardata fornisce all'allocatore molte più informazioni su cui basare le sue decisioni. Quando un file viene scritto per la prima volta, non c'è modo di sapere se sarà un file 4k o un file 1G e in continua crescita. Se c'è 10G di spazio libero contiguo da qualche parte, mettere il file 4k all'inizio non serve. Mettere il file di grandi dimensioni all'inizio di un grande spazio libero riduce la frammentazione.
Peter Cordes,

13

Non è specifico di Linux e si chiama cache della pagina (cosa che Linux fa abbastanza bene). Vedi anche http://linuxatemyram.com/ ; quindi se un file viene scritto, quindi riletto qualche secondo dopo, molto spesso non è necessario l'I / O del disco.

Il vantaggio principale è che su molti sistemi c'è molta RAM, e alcuni possono essere usati come cache dal kernel. Quindi alcune operazioni sui file possono trarre profitto da questa memorizzazione nella cache. Inoltre, il tempo di I / O del disco è molto più lento (in genere molte migliaia di volte per SDD e quasi un milione di volte più lento per i dischi rigidi meccanici) rispetto alla RAM.

Il codice dell'applicazione può fornire suggerimenti in merito a questa memorizzazione nella cache: vedere ad esempio posix_fadvise (2) e madvise (2)


8

I piatti rotanti sono più lenti della RAM. Usiamo la memorizzazione nella cache di letture / scritture per "nascondere" questo fatto.

La cosa utile di scrivere IO è che non richiede che l'IO del disco avvenga immediatamente, a differenza di una lettura, in cui non è possibile restituire i dati all'utente fino a quando la lettura non viene completata sul disco.

In questo modo le scritture funzionano con un limite di tempo morbido - fintanto che la nostra velocità effettiva non supera quella del nostro disco, possiamo nascondere molte delle penalità di prestazione in una cache di scrittura.

E abbiamo bisogno di scrivere cache - i dischi rotanti sono relativamente lenti relativamente. Ma anche i moderni tipi di RAID comportano una penalità significativa per il funzionamento.

Un RAID 6, ad esempio, per completare un IO di scrittura deve:

  • Leggi il blocco di aggiornamento
  • leggi la parità1
  • leggi la parità 2
  • scrivi nuovo blocco
  • scrivere la parità 1
  • scrivi parità 2

Quindi ogni scrittura è in realtà 6 operazioni IO - e in particolare quando si hanno dischi lenti come grandi unità SATA, questo diventa estremamente costoso.

Ma c'è una bella soluzione facile: scrivere coalescing. Se riesci a creare una scrittura a "banda intera" in un buffer, non è necessario leggere la parità dal disco: puoi calcolarla in base a ciò che hai in memoria.

È molto desiderabile farlo, perché allora non hai più l'amplificazione in scrittura. In effetti, puoi finire con una penalità di scrittura inferiore a RAID 1 + 0.

Tener conto di:

Mandrini RAID 6, 8 + 2 - 10.

8 blocchi di dati consecutivi da scrivere: calcolare la parità nella cache e scrivere un blocco su ciascun disco. 10 scritture per 8, significa una penalità di scrittura di 1,25. 10 dischi di RAID 1 + 0 hanno ancora una penalità di scrittura di 2 (perché è necessario scrivere su ciascun submirror). Quindi, in questo scenario, puoi effettivamente fare in modo che RAID 6 funzioni meglio di RAID1 + 0. Nell'uso nel mondo reale, si ottiene un po 'più di un profilo IO misto.

Quindi la cache di scrittura fa un'enorme differenza per le prestazioni percepite dei set RAID - puoi scrivere a velocità RAM e avere una penalità di scrittura bassa - migliorando la tua produttività sostenuta se lo fai.

E se non lo fai, subirai le dolorose prestazioni lente di SATA, ma moltiplicalo per 6 e aggiungi qualche contesa. Il tuo SATA RAID-6 a 10 vie senza cache di scrittura sarebbe un po 'più veloce di una singola unità senza RAID ... ma non di molto.

Si corre un rischio, tuttavia - come si nota - la perdita di potenza significa perdita di dati. È possibile mitigare questo problema mediante cicli di svuotamento della cache, backup della batteria della cache o utilizzo di SSD o di altre cache non volatili.


7

Nessuna delle altre risposte menzionava l'allocazione ritardata . XFS, ext4, BTRFS e ZFS lo usano tutti. XFS lo utilizza da prima che esistesse ext4, quindi lo userò come esempio:

XFS non decide nemmeno dove inserire i dati fino alla scrittura. L'allocazione ritardata fornisce all'allocatore molte più informazioni su cui basare le sue decisioni. Quando un file viene scritto per la prima volta, non c'è modo di sapere se sarà un file 4k o un file 1G e in continua crescita. Se c'è 10G di spazio libero contiguo da qualche parte, mettere il file 4k all'inizio non serve. Mettere il file di grandi dimensioni all'inizio di un grande spazio libero riduce la frammentazione.


4

Tutte le altre risposte qui sono almeno per lo più corrette per il caso normale, e consiglierei di leggerne una prima della mia, ma hai menzionato dd e dd ha un tipico caso d'uso che potrebbe non comportare la memorizzazione nella cache. La cache di scrittura è implementata principalmente a livello di filesystem. I dispositivi raw normalmente non scrivono nella cache (più driver di dispositivo come raid o lvm sono un'altra palla di cera). Poiché dd viene spesso utilizzato con dispositivi a blocchi non elaborati, fornisce bs e le opzioni correlate per consentire scritture di grandi dimensioni per prestazioni migliori su dispositivi non elaborati. Ciò non è utile quando entrambi gli endpoint sono file regolari (anche se le scritture di grandi dimensioni utilizzano meno chiamate di sistema in questo caso). L'altro luogo comune in cui ciò è particolarmente visibile è con il pacchetto mtools, che è un'implementazione del file system fat dello spazio utente. l'utilizzo di mtools con un'unità floppy è sempre incredibilmente lento poiché gli strumenti sono completamente sincroni e le unità floppy sono incredibilmente lente. Montare il floppy e usare il file system fat del kernel è molto più reattivo ad eccezione di umount che è sincrono (e molto importante perché sia ​​in questo modo per prevenire la perdita di dati, specialmente per i dispositivi rimovibili come i floppy). Ci sono solo pochi altri programmi di cui sono a conoscenza che vengono regolarmente utilizzati con dispositivi non elaborati come database appositamente configurati (che implementano la propria cache di scrittura), tar e dispositivi speciali e strumenti di filesystem come chdsk, mkfs e mt. Montare il floppy e usare il file system fat del kernel è molto più reattivo ad eccezione di umount che è sincrono (e molto importante perché sia ​​in questo modo per prevenire la perdita di dati, specialmente per i dispositivi rimovibili come i floppy). Ci sono solo pochi altri programmi di cui sono a conoscenza che vengono regolarmente utilizzati con dispositivi non elaborati come database appositamente configurati (che implementano la propria cache di scrittura), tar e dispositivi speciali e strumenti di filesystem come chdsk, mkfs e mt. Montare il floppy e usare il file system fat del kernel è molto più reattivo ad eccezione di umount che è sincrono (e molto importante perché sia ​​in questo modo per prevenire la perdita di dati, specialmente per i dispositivi rimovibili come i floppy). Ci sono solo pochi altri programmi di cui sono a conoscenza che vengono regolarmente utilizzati con dispositivi non elaborati come database appositamente configurati (che implementano la propria cache di scrittura), tar e dispositivi speciali e strumenti di filesystem come chdsk, mkfs e mt.


4
I dispositivi a blocchi Linux leggono / scrivono la cache della pagina per impostazione predefinita. Devi usare O_DIRECTse si vuole bypassare la cache. dd oflag=direct. IIRC, alcuni unices predefiniti dirigono l'I / O sui dispositivi a blocchi. (E richiede la lettura / scrittura di blocchi allineati, cosa che Linux non fa perché sta solo scrivendo la pagecache.)
Peter Cordes,

3

La filosofia non è sicura per impostazione predefinita.

Esistono due strategie ragionevoli e ovvie possibili: flush scrive immediatamente sul disco o ritarda la scrittura. UNIX ha scelto storicamente il secondo. Quindi ottieni sicurezza, devi chiamare in fsyncseguito.

Tuttavia, è possibile specificare la sicurezza in anticipo montando un dispositivo con opzione synco per file aprendoli con O_SYNC.

Ricorda che UNIX è stato progettato per esperti di computer. "Sicuro di default" non è stato considerato. Sicurezza significa I / O più lento e quei primi sistemi avevano davvero un I / O lento che aumentava il prezzo. Sfortunatamente, né UNIX né Linux sono passati alla modalità di sicurezza, anche se si tratta di una modifica continua.


6
La stragrande maggioranza delle applicazioni e degli utenti non ha bisogno o cura della sicurezza fornita dalle scritture sincrone. Se si verifica un arresto anomalo o un'interruzione dell'alimentazione, si rischia di perdere fino agli ultimi 30 secondi di dati. Va bene per la maggior parte delle persone a meno che non ci sia una transazione finanziaria o qualcosa di simile che costerà più di 30 secondi del nostro tempo. L'impostazione predefinita degli I / O sincroni avrebbe implicato la definizione di O_NOSYNC in tutte le applicazioni che hanno come target l'usabilità.
jlliagre,

2

Scambia una piccola quantità di affidabilità per un grande aumento della produttività.

Supponiamo, ad esempio, un programma di compressione video. Con scrittura ritardata ("riscrivi"):

  1. spendere 10ms per comprimere il frame
  2. problema scrivere frame su disco
  3. attendere 10 ms affinché il disco riconosca la scrittura completata
  4. GOTO 1

Contro

  1. spendere 10ms per comprimere il frame
  2. problema scrivere frame su disco (completa in background)
  3. GOTO 1

La seconda versione appare due volte più veloce perché può utilizzare la CPU e il disco contemporaneamente, mentre la prima versione è sempre in attesa dell'una o dell'altra.

In genere si desidera riscrivere per operazioni di streaming e operazioni di file di massa e scrivere per database e applicazioni simili a database.


1

In molte applicazioni, i dispositivi di archiviazione saranno occupati a intermittenza nella lettura dei dati. Se un sistema è sempre in grado di rinviare le scritture fino a quando il dispositivo di archiviazione non è occupato a leggere i dati, dal punto di vista di un'applicazione le scritture impiegheranno zero tempo per completarsi. Le uniche situazioni in cui le scritture non sarebbero istantanee sarebbero quando:

  1. I buffer di scrittura si riempiono fino al punto che non è più possibile accettare richieste di scrittura differite fino a quando le scritture non vengono effettivamente completate.

  2. È necessario arrestare o rimuovere il dispositivo per il quale sono in corso le scritture.

  3. Un'applicazione richiede specificamente la conferma che una scrittura è stata effettivamente completata.

In effetti, è solo a causa dei requisiti di cui sopra che le scritture devono mai aver effettivamente luogo. D'altra parte, in genere non c'è motivo di non eseguire scritture in sospeso in momenti in cui un dispositivo sarebbe altrimenti inattivo, quindi molti sistemi li eseguono in quel momento.


0

C'è anche questo:

Scrivi "Ciao, Joe Moe"
è più veloce di:
Scrivi "Ciao,"
Scrivi "Joe"
Scrivi "Moe"

E anche:

Scrivi "Ciao, come stai?"
è più veloce di:
Scrivi "Ciao, come va?"
Elimina quella
scritta "Salve, come stai?"
Elimina quel messaggio
"Ciao, come stai?"

È meglio che si verifichino modifiche e aggregazioni nella RAM che sul disco. Scrivere in batch su disco libera gli sviluppatori di applicazioni da tali problemi.

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.