In che modo i programmi che possono riprendere i trasferimenti di file non riusciti sanno dove iniziare ad aggiungere dati?


23

Ad alcuni programmi di copia dei file piace rsynce curlhanno la possibilità di riprendere trasferimenti / copie non riusciti.

Notando che ci possono essere molte cause di questi errori, in alcuni casi il programma può "ripulire", in alcuni casi il programma no.

Quando riprendono questi programmi, sembrano semplicemente calcolare la dimensione del file / dei dati che sono stati trasferiti correttamente e iniziare a leggere il byte successivo dalla sorgente e accodarsi al frammento di file.

ad esempio, la dimensione del frammento di file che "l'ha fatto" alla destinazione è 1378 byte, quindi iniziano a leggere dal byte 1379 sull'originale e si aggiungono al frammento.

La mia domanda è, sapendo che i byte sono costituiti da bit e non tutti i file hanno i loro dati segmentati in blocchi di dimensioni di byte puliti, come fanno questi programmi a sapere se il punto in cui hanno scelto di iniziare ad aggiungere i dati è corretto?

Quando si scrive il file di destinazione si verifica una sorta di buffering o "transazioni" simili ai database SQL, a livello di programma, kernel o filesystem per garantire che solo byte puliti e ben formati arrivino al dispositivo a blocchi sottostante?
Oppure i programmi presumono che l'ultimo byte sia potenzialmente incompleto, quindi lo eliminano supponendo che sia danneggiato, ricopiano il byte e iniziano a aggiungere da lì?

sapendo che non tutti i dati sono rappresentati come byte, queste ipotesi sembrano errate.

Quando questi programmi "riprendono" come fanno a sapere che stanno iniziando nel posto giusto?


21
"non tutti i file hanno i loro dati segmentati in blocchi puliti di dimensioni in byte", non è vero? Come si scrive qualcosa di meno di un byte in un file?
muru,

17
Non conosco chiamate di sistema in grado di scrivere qualcosa di meno di un byte e, per quanto riguarda il disco stesso, penso che nessun disco oggi scriva meno di 512 byte (o 4096 byte).
muru,

8
No, sto dicendo che il minimo è un byte. Le applicazioni sane utilizzerebbero blocchi da 4KB o 8KB: head -c 20480 /dev/zero | strace -e write tee foo >/dev/nulle quindi il sistema operativo li bufferizzerà e li invierà sul disco in blocchi ancora più grandi.
muru,

9
@the_velour_fog: come scrivi solo un po ' fwrite()?
psmears,

9
Per tutti gli scopi pratici, i dati sono costituiti da byte e tutto funziona con essi come la più piccola unità. Alcuni sistemi (principalmente relativi alla compressione, ad esempio gzip, h264) decomprimono i singoli bit dai byte, ma il sistema operativo e il funzionamento della memoria sono a livello di byte.
pjc50,

Risposte:


40

Per motivi di chiarezza - la vera meccanica è più complicata per offrire una sicurezza ancora migliore - puoi immaginare l'operazione di scrittura su disco in questo modo:

  • l'applicazione scrive byte (1)
  • il kernel (e / o il file system IOSS) li buffer
  • una volta che il buffer è pieno, viene scaricato nel file system:
    • il blocco è assegnato (2)
    • il blocco è scritto (3)
    • le informazioni sul file e sul blocco vengono aggiornate (4)

Se il processo viene interrotto in (1), non si ottiene nulla sul disco, il file è intatto e troncato nel blocco precedente. Hai inviato 5000 byte, solo 4096 sono sul disco, riavvia il trasferimento all'offset 4096.

Se in (2), non succede nulla se non in memoria. Come per (1). Se in (3), i dati vengono scritti ma nessuno se ne ricorda . Hai inviato 9000 byte, 4096 scritti, 4096 scritti e persi , il resto è andato perso. Il trasferimento riprende all'offset 4096.

Se in (4), i dati ora avrebbero dovuto essere impegnati su disco. I byte successivi nello stream potrebbero andare persi. Hai inviato 9000 byte, 8192 vengono scritti, il resto viene perso, il trasferimento riprende all'offset 8192.

Questo è un approccio semplificato . Ad esempio, ogni scrittura "logica" nelle fasi 3-4 non è "atomica", ma dà origine a un'altra sequenza (numeriamola n. 5) in base alla quale il blocco, suddiviso in sottoblocchi adatti al dispositivo di destinazione (ad es. Disco rigido ) viene inviato al controller host del dispositivo, che ha anche un meccanismo di memorizzazione nella cache , e infine memorizzato sul piatto magnetico. Questa sottosequenza non è sempre completamente sotto il controllo del sistema, quindi l'invio di dati sul disco rigido non è garanzia che siano stati effettivamente scritti e saranno nuovamente leggibili.

Diversi file system implementano il journaling , per assicurarsi che il punto più vulnerabile, (4), non sia effettivamente vulnerabile, scrivendo i metadati, avete indovinato, transazioni che funzioneranno in modo coerente qualunque cosa accada nella fase (5).

Se il sistema viene ripristinato nel mezzo di una transazione, può riprendere il cammino verso il checkpoint intatto più vicino. I dati scritti sono ancora persi, come nel caso (1), ma la ripresa se ne occuperà. Nessuna informazione viene effettivamente persa.


1
Ottima spiegazione che tutto ha molto senso. quindi se un processo arriva fino a (4) informazioni sul blocco file aggiornate, sai che tutti quei byte sono buoni. quindi tutti i byte che si trovavano in una fase precedente o non sono riusciti a raggiungerlo su disco o - se lo facessero - sarebbero "non ricordati" (nessun riferimento ad essi)
the_velour_fog

4
@the_velour_fog E solo per integrare il penultimo paragrafo - se stai usando un file system che non implementa il journaling, puoi effettivamente ottenere dati "rotti", causando il fallimento del curriculum e producendo un file confuso senza darti un errore. In passato ciò accadeva sempre, in particolare con i file system progettati per dispositivi ad alta latenza (come i floppy). C'erano ancora alcuni trucchi per evitarlo anche se il file system non era affidabile in questo modo, ma aveva bisogno di un'applicazione più intelligente per compensare e alcune ipotesi che potrebbero essere state sbagliate su alcuni sistemi.
Luaan,

Questa risposta sopravvaluta l'utilità del journaling nei file system. Non funziona in modo affidabile a meno che tutto non implementi la semantica transazionale, comprese le applicazioni dello spazio utente (tramite fsync) e il controller del disco rigido (spesso rotto, anche in unità presumibilmente "enterprise"). Senza fsyncmolte operazioni sui file, ordinate intuitivamente e atomiche non sono garantite da POSIX: i file aperti con O_APPENDpotrebbero comportarsi in modo diverso da quelli senza ecc. In pratica, le chiavi più importanti per la coerenza dei file sono il sistema VFS del kernel e la cache del disco. Tutto il resto è principalmente lanugine.
user1643723

11

Nota: non ho esaminato le fonti rsynco altre utilità di trasferimento file.

È banale scrivere un programma C che salta la fine di un file e ottiene la posizione di quella posizione in byte.

Entrambe le operazioni vengono eseguite con una singola chiamata alla funzione di libreria C standard lseek()( lseek(fd, 0, SEEK_END)restituisce la lunghezza del file aperto per il descrittore di file fd, misurata in byte).

Una volta che è fatto per il file di destinazione, una chiamata simile a lseek()può essere fatto sul file sorgente per passare alla posizione appropriata: lseek(fd, pos, SEEK_SET). Il trasferimento può quindi continuare a quel punto, supponendo che la parte precedente del file di origine sia stata identificata come invariata (diverse utility potrebbero farlo in modi diversi).

Un file può essere frammentato sul disco, ma il filesystem assicurerà che un'applicazione percepisca il file come una sequenza sequenziale di byte.


Riguardo alla discussione nei commenti su bit e byte: la più piccola unità di dati che può essere scritta su disco è un byte . Un singolo byte richiede che almeno un blocco di dati sia allocato su disco. La dimensione di un blocco dipende dal tipo di filesystem e probabilmente anche dai parametri utilizzati dall'amministratore durante l'inizializzazione del filesystem, ma di solito è compreso tra 512 byte e 4 KiB. Le operazioni di scrittura possono essere memorizzate nel buffer dal kernel, dalla libreria C sottostante o dall'applicazione stessa e l'effettiva scrittura su disco può avvenire in multipli della dimensione del blocco appropriata come ottimizzazione.

Non è possibile scrivere singoli bit in un file e se un'operazione di scrittura fallisce, non lascerà "byte scritti a metà" nel file.


grazie, quindi cosa garantisce se un'operazione di scrittura fallisce - non lascerà metà byte scritti? è il descrittore del buffering del kernel che stava descrivendo? - vale a dire se un processo viene interrotto nel mezzo dell'invio di un blocco da 8 KB al kernel e viene interrotto inaspettatamente - quel blocco da 8 KB non raggiungerebbe mai il kernel - ma si potrebbe presumere che quelli precedenti che hanno raggiunto il kernel e il filesystem siano buoni?
the_velour_fog

6
@the_velour_fog quel tipo di terminazione imprevista non può avvenire, perché il processo sarebbe ininterrotto nel mezzo di una chiamata di sistema I / O (ecco perché non è inusuale vedere un processo invendibile bloccato sulle chiamate di accesso al filesystem per un file NFS). Vedi anche: unix.stackexchange.com/q/62697/70524
muru

2
Potrebbero esserci problemi se il sistema perde potenza esattamente nel momento sbagliato. Ciò può occasionalmente causare immondizia nell'ultimo punto di scrittura di un file. È un problema molto complicato nella progettazione del database. Tuttavia, la normale unità più piccola che è "valida" o "non valida" è un blocco del disco.
pjc50,

1
@the_velour_fog Non è tanto quanto non si possono ottenere " byte scritti a metà " (o, più precisamente, un blocco di byte scritto a metà) poiché un blocco scritto a metà non verrebbe registrato come se fosse stato scritto (nella sua interezza ) - vedere i passaggi (3) e (4) della risposta di LSerni .
TripeHound,

5

Queste sono fondamentalmente due domande, perché programmi come curl e rsync sono molto diversi.

Per i client HTTP come curl, controllano le dimensioni del file corrente e quindi inviano Content-Rangeun'intestazione con la loro richiesta. Il server riprende a inviare l'intervallo del file utilizzando il codice di stato 206(contenuto parziale) invece di 200(esito positivo) e il download viene ripreso oppure ignora l'intestazione e inizia dall'inizio e il client HTTP non ha altra scelta che scaricare di nuovo tutto ancora.

Inoltre, il server può o meno inviare Content-Lengthun'intestazione. Potresti aver notato che alcuni download non mostrano una percentuale e una dimensione file. Si tratta di download in cui il server non indica al client la lunghezza, quindi il client conosce solo la quantità scaricata ma non quanti byte seguiranno.

L'utilizzo di Content-Rangeun'intestazione con posizione di inizio e di fine viene utilizzato da alcuni download manager per scaricare contemporaneamente un file da origini diverse, il che accelera il trasferimento se ogni mirror da solo è più lento della connessione di rete.

D'altra parte, rsync è un protocollo avanzato per i trasferimenti di file incrementali. Genera checksum di parti del file sul lato server e client per rilevare quali byte sono uguali. Quindi invia solo le differenze. Ciò significa che non può solo riprendere un download, ma può anche scaricare i byte modificati se sono stati modificati pochi byte nel mezzo di un file molto grande senza scaricare di nuovo il file.

Un altro protocollo creato per riprendere i trasferimenti è bittorrent, in cui il .torrentfile contiene un elenco di checksum per blocchi dal file, quindi i blocchi possono essere scaricati e verificati in ordine arbitrario e in parallelo da fonti diverse.

Nota che rsync e bittorent verificheranno i dati parziali sul tuo disco, mentre la ripresa di un download HTTP no. Pertanto, se si sospetta che i dati parziali siano danneggiati, è necessario verificare l'integrità in caso contrario, ad esempio utilizzando un checksum del file finale. Ma interrompere semplicemente il download o perdere la connessione di rete di solito non corrompe il file parziale mentre può verificarsi un'interruzione di corrente durante il trasferimento.


4

TL; DR: Non possono, a meno che il protocollo che usano lo consenta.

I programmi non possono sempre riprendere da una posizione arbitraria: ad esempio, le richieste HTTP sono riavviabili solo se il server lo supporta e il client lo implementa: questo non è universale, quindi controlla la documentazione del tuo programma. Se il server lo supporta, i programmi possono riprendere il trasferimento semplicemente chiedendo come parte del protocollo. Di solito vedrai trasferimenti parziali nella tua directory di download (sono comunemente contrassegnati con un'estensione ".partial" o qualcosa di simile.)

Se il download di un file viene messo in pausa o comunque interrotto, il client può scrivere il file su disco e avere un'idea precisa di dove riprendere. Se, d'altra parte, il client si arresta in modo anomalo o si verifica un errore durante la scrittura nel file, il client deve presumere che il file sia danneggiato e ricominciare da capo. BitTorrent in qualche modo lo mitiga suddividendo i file in "blocchi" e tenendo traccia di quali sono stati scaricati correttamente; il massimo che dovrà mai ripetere è qualche pezzo. Rsync fa qualcosa di simile.

Come fanno i programmi a sapere che il contenuto è lo stesso? Un metodo consiste nel verificare che alcuni identificatori siano gli stessi tra client e server. Alcuni esempi di questo potrebbero essere il timestamp e la dimensione, ma ci sono meccanismi che possono essere specifici di un protocollo. Se gli identificativi corrispondono, il client può presumere che la ripresa funzionerà.

Se desideri una verifica più definita, HTTP e i tuoi amici non dovrebbero essere la tua prima scelta. Dovrai utilizzare un protocollo che ha anche un checksum o hash per l'intero file e ogni blocco trasferito in modo da poter confrontare il checksum del download con il checksum del computer del server: tutto ciò che non corrisponde verrà quindi riscaricato. Ancora una volta, BitTorrent è un esempio di questo tipo di protocollo; rsync può facoltativamente fare anche questo.


per l'esempio di rsync, sarà semplice perché esiste un solo protocollo rsync. per i download http, esiste l'intervallo di richiesta standard. Sono curioso di sapere cosa fa effettivamente il ricciolo sul curriculum-upload, perché la semantica standard del caricamento è multipart / form-data (per wget e curl), ma non credo che la semantica del curriculum di caricamento sia universalmente concordata. YouTube e Nginx potrebbero farlo diversamente, ad esempio.
Rob,

1

Dipende dal protocollo utilizzato per il trasferimento. Ma curl usa http e trasferisce i dati in sequenza nell'ordine in cui appaiono nel file. Quindi l'arricciatura può riprendere in base alla dimensione del file di un trasferimento parzialmente completato. In effetti, puoi ingannarlo per saltare i primi N byte creando un file di lunghezza N (di qualsiasi cosa) e chiedendogli di trattare quel file come un download parzialmente completato (e quindi scartando i primi N byte).

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.