Como os programas que podem retomar transferências de arquivos com falha sabem por onde começar a acrescentar dados?


23

Alguns programas de cópia de arquivos gostam rsynce curltêm a capacidade de retomar transferências / cópias com falha.

Observando que pode haver muitas causas dessas falhas, em alguns casos o programa pode "limpar" alguns casos que o programa não pode.

Quando esses programas são retomados, eles parecem apenas calcular o tamanho do arquivo / dados que foram transferidos com sucesso e apenas começar a ler o próximo byte da fonte e anexá-lo ao fragmento do arquivo.

por exemplo, o tamanho do fragmento de arquivo que "chegou" ao destino é 1378 bytes; portanto, eles começam a ler o byte 1379 no original e a adicioná-lo ao fragmento.

Minha pergunta é: sabendo que os bytes são compostos de bits e nem todos os arquivos têm seus dados segmentados em blocos de tamanho de bytes limpos, como esses programas sabem se o ponto que eles escolheram para começar a adicionar dados está correto?

Ao gravar o arquivo de destino, ocorre algum tipo de buffer ou "transações" semelhantes aos bancos de dados SQL, no nível do programa, do kernel ou do sistema de arquivos, para garantir que apenas bytes limpos e bem formados cheguem ao dispositivo de bloco subjacente?
Ou os programas assumem que o byte mais recente seria potencialmente incompleto; portanto, eles o excluem por suposição ruim, copiam novamente o byte e iniciam o anexo a partir daí?

sabendo que nem todos os dados são representados como bytes, essas suposições parecem incorretas.

Quando esses programas são retomados, como eles sabem que estão começando no lugar certo?


21
"nem todos os arquivos têm seus dados segmentados em blocos limpos do tamanho de bytes", eles não? Como você escreve nada menos que um byte em um arquivo?
muru

17
Não conheço nenhuma chamada de sistema que possa escrever nada menos que um byte e, quanto ao próprio disco, acho que hoje não há discos que gravem menos que blocos de 512 bytes (ou blocos de 4096 bytes).
muru

8
Não, estou dizendo que o mínimo é um byte. Os aplicativos sãos usariam pedaços de 4KB ou 8 KB: head -c 20480 /dev/zero | strace -e write tee foo >/dev/nulle, em seguida, o sistema operacional os armazenará em buffer e os enviará para o disco em pedaços ainda maiores.
muru

9
@the_velour_fog: Como você escreve apenas um pouco fwrite()?
Psmears

9
Para todos os fins práticos, os dados são compostos de bytes e tudo funciona com eles como a menor unidade. Alguns sistemas (principalmente relacionados à compactação, por exemplo, gzip, h264) descompactam bits individuais dos bytes, mas o sistema operacional e a operação da memória estão no nível de bytes.
Pjc50

Respostas:


40

Por uma questão de clareza - a mecânica real é mais complicada para oferecer segurança ainda melhor - você pode imaginar a operação de gravação em disco como esta:

  • aplicativo escreve bytes (1)
  • o kernel (e / ou o IOSS do sistema de arquivos) os armazena em buffer
  • quando o buffer estiver cheio, ele será liberado para o sistema de arquivos:
    • o bloco está alocado (2)
    • o bloco está escrito (3)
    • as informações de arquivo e bloco são atualizadas (4)

Se o processo for interrompido em (1), você não terá nada no disco, o arquivo está intacto e truncado no bloco anterior. Você enviou 5000 bytes, apenas 4096 estão no disco e reiniciou a transferência no deslocamento 4096.

Se em (2), nada acontece, exceto na memória. O mesmo que (1). Se em (3), os dados são gravados, mas ninguém se lembra . Você enviou 9000 bytes, 4096 foi gravado, 4096 foi gravado e perdido , o resto foi perdido. A transferência é retomada no deslocamento 4096.

Se em (4), os dados agora devem ter sido confirmados no disco. Os próximos bytes no fluxo podem ser perdidos. Você enviou 9000 bytes, 8192 foi gravado, o restante foi perdido, a transferência é retomada no deslocamento 8192.

Esta é uma tomada simplificada . Por exemplo, cada gravação "lógica" nos estágios 3-4 não é "atômica", mas dá origem a outra sequência (vamos número 5) em que o bloco subdividido em sub-blocos adequados ao dispositivo de destino (por exemplo, disco rígido) ) é enviado para o controlador host do dispositivo, que também possui um mecanismo de armazenamento em cache , e finalmente armazenado no prato magnético. Essa subseqüência nem sempre está completamente sob o controle do sistema, portanto, enviar dados para o disco rígido não é garantia de que foram realmente gravados e serão legíveis de volta.

Vários sistemas de arquivos implementam o registro no diário , para garantir que o ponto mais vulnerável (4) não seja realmente vulnerável, escrevendo metadados em, você adivinhou, transações que funcionarão consistentemente, independentemente do que acontecer no estágio (5).

Se o sistema for redefinido no meio de uma transação, ele poderá retomar o caminho para o ponto de verificação intacto mais próximo. Os dados gravados ainda são perdidos, como no caso (1), mas a retomada cuidará disso. Nenhuma informação realmente se perde.


1
Ótima explicação. tudo isso faz muito sentido. portanto, se um processo chegar até (4) as informações do bloco de arquivos atualizadas, você saberá que todos esses bytes são bons. então qualquer bytes que estavam em qualquer fase anterior ou não ter feito isso para disco ou - se eles fizeram - eles seriam "não-lembrado" (há referências a eles)
the_velour_fog

4
@the_velour_fog E apenas para complementar o penúltimo parágrafo - se você estiver usando um sistema de arquivos que não implementa o registro no diário, é possível obter dados "quebrados", causando falha no currículo e produzindo um arquivo ilegível, sem causar nenhum erro. Isso costumava acontecer o tempo todo, especialmente com sistemas de arquivos projetados para dispositivos de alta latência (como disquetes). Ainda havia alguns truques para evitar isso, mesmo que o sistema de arquivos não fosse confiável dessa maneira, mas precisava de um aplicativo mais inteligente para compensar e algumas suposições que poderiam estar erradas em alguns sistemas.
Luaan

Esta resposta exagera a utilidade do registro no diário em sistemas de arquivos. Ele não funciona de maneira confiável, a menos que tudo implemente a semântica transacional, incluindo aplicativos de espaço do usuário (via fsync) e controlador de disco rígido (geralmente quebrado, mesmo em unidades supostamente "corporativas"). Sem fsyncmuitas operações de arquivo, que são intuitivamente ordenadas e atômicas, não é garantido que o POSIX: os arquivos abertos com O_APPENDpossam se comportar de maneira diferente daqueles sem etc. Na prática, as chaves mais importantes para a consistência dos arquivos são o sistema VFS do kernel e o cache de disco. Tudo o resto é principalmente fofinho.
user1643723

11

Nota: Eu não procurei as fontes rsyncou qualquer outro utilitário de transferência de arquivos.

É trivial escrever um programa C que salte o final de um arquivo e obtenha a posição desse local em bytes.

As duas operações são feitas com uma única chamada para a função da biblioteca C padrão lseek()( lseek(fd, 0, SEEK_END)retorna o tamanho do arquivo aberto para o descritor de arquivo fd, medido em bytes).

Uma vez que é feito para o arquivo de destino, uma chamada semelhante à lseek()pode ser feito no arquivo de origem para saltar para a posição adequada: lseek(fd, pos, SEEK_SET). A transferência pode continuar nesse ponto, supondo que a parte anterior do arquivo de origem tenha sido identificada como inalterada (utilitários diferentes podem fazer isso de maneiras diferentes).

Um arquivo pode estar fragmentado no disco, mas o sistema de arquivos garantirá que um aplicativo perceba o arquivo como uma sequência seqüencial de bytes.


Em relação à discussão nos comentários sobre bits e bytes: A menor unidade de dados que pode ser gravada no disco é um byte . Um único byte requer que pelo menos um bloco de dados seja alocado no disco. O tamanho de um bloco depende do tipo de sistema de arquivos e possivelmente também dos parâmetros usados ​​pelo administrador ao inicializar o sistema de arquivos, mas geralmente está entre 512 bytes e 4 KiB. As operações de gravação podem ser armazenadas em buffer pelo kernel, pela biblioteca C subjacente ou pelo próprio aplicativo, e a gravação no disco pode ocorrer em múltiplos do tamanho de bloco apropriado como uma otimização.

Não é possível gravar bits únicos em um arquivo e, se uma operação de gravação falhar, ela não deixará "bytes semi-gravados" no arquivo.


obrigado, então o que é que garante se uma operação de gravação falhar - não deixará meio bytes escritos? é o muru de buffer do kernel que estava descrevendo? - ou seja, se um processo for interrompido no meio do envio de um pedaço de 8 KB para o kernel e for encerrado inesperadamente - esse pedaço de 8 KB nunca chegaria ao kernel - mas qualquer um dos anteriores que chegasse ao kernel e ao sistema de arquivos poderia ser considerado bom?
the_velour_fog

6
@the_velour_fog esse tipo de encerramento inesperado não pode acontecer, porque o processo seria ininterrupto no meio de uma chamada do sistema de E / S (é por isso que não é incomum ver um processo inábil preso nas chamadas de acesso ao sistema de arquivos para um arquivo NFS). Veja também: unix.stackexchange.com/q/62697/70524
muru

2
Pode haver problemas se o sistema perder energia exatamente na hora errada. Ocasionalmente, isso pode resultar em lixo no último ponto de gravação de um arquivo. É um problema muito complicado no design de banco de dados. Mas ainda a menor unidade normal que é "válida" ou "inválida" é um bloco de disco.
Pjc50

1
@the_velour_fog Não é tanto quanto você não pode obter " bytes meio gravados " (ou, mais precisamente, um bloco de bytes meio escrito) como um bloco meio escrito não seria gravado como tendo sido gravado (na sua totalidade ) - veja as etapas (3) e (4) da resposta da LSerni .
TripeHound 7/02

5

Essas são basicamente duas perguntas, porque programas como curl e rsync são muito diferentes.

Para clientes HTTP como curl, eles verificam o tamanho do arquivo atual e enviam um Content-Rangecabeçalho com sua solicitação. O servidor continua enviando o intervalo do arquivo usando o código de status 206(conteúdo parcial) em vez de 200(com êxito) e o download é retomado ou ignora o cabeçalho e inicia desde o início, e o cliente HTTP não tem outra opção a não ser baixar novamente tudo novamente.

Além disso, o servidor pode ou não enviar um Content-Lengthcabeçalho. Você deve ter notado que alguns downloads não estão mostrando uma porcentagem e tamanho do arquivo. São downloads em que o servidor não informa o comprimento ao cliente, portanto, o cliente sabe apenas a quantidade baixada, mas não quantos bytes seguirão.

O uso de um Content-Rangecabeçalho com a posição inicial e final é usado por algum gerenciador de download para baixar um arquivo de diferentes fontes ao mesmo tempo, o que acelera a transferência se cada espelho por si só for mais lento que a sua conexão de rede.

O rsync, por outro lado, é um protocolo avançado para transferências incrementais de arquivos. Ele gera somas de verificação de partes do arquivo no servidor e no cliente para detectar quais bytes são iguais. Então ele envia apenas as diferenças. Isso significa que ele não pode apenas retomar um download, mas também pode baixar os bytes alterados se você alterou alguns bytes no meio de um arquivo muito grande sem fazer o download novamente.

Outro protocolo feito para retomar as transferências é o bittorrent, onde o .torrentarquivo contém uma lista de somas de verificação para blocos do arquivo, para que os blocos possam ser baixados e verificados em ordem arbitrária e paralelamente de diferentes fontes.

Observe que o rsync e o bittorent verificarão os dados parciais no seu disco, enquanto a retomada de um download HTTP não. Portanto, se você suspeitar que os dados parciais estejam corrompidos, precisará verificar a integridade, caso contrário, usando uma soma de verificação do arquivo final. Mas apenas interromper o download ou perder a conexão de rede geralmente não corrompe o arquivo parcial, enquanto pode ocorrer uma falha de energia durante a transferência.


4

TL; DR: Eles não podem, a menos que o protocolo que eles usam o permita.

Os programas nem sempre podem ser reiniciados a partir de um local arbitrário: por exemplo, as solicitações HTTP são reiniciáveis ​​apenas se o servidor suportar e o cliente implementá-lo: isso não é universal, portanto verifique a documentação do programa. Se o servidor o suportar, os programas podem retomar a transferência simplesmente pedindo como parte do protocolo. Você geralmente verá transferências parciais no diretório de download (geralmente são marcadas com uma extensão ".partial" ou algo semelhante.)

Se um download de arquivo for pausado ou interrompido, o cliente poderá gravar o arquivo no disco e ter uma idéia definitiva de onde retomar. Se, por outro lado, o cliente travar ou ocorrer um erro ao gravar no arquivo, o cliente deverá assumir que o arquivo está corrompido e reiniciado. O BitTorrent atenua um pouco isso, dividindo os arquivos em "pedaços" e mantendo o controle de quais foram baixados com sucesso; o máximo que ele precisará refazer é alguns pedaços. Rsync faz algo semelhante.

Como os programas sabem que o conteúdo é o mesmo? Um método é verificar se algum identificador é o mesmo entre o cliente e o servidor. Alguns exemplos disso seriam o carimbo de data e hora, mas existem mecanismos que podem ser específicos a um protocolo. Se os identificadores corresponderem, o cliente poderá assumir que a retomada funcionará.

Se você deseja uma verificação mais definitiva, HTTP e amigos não devem ser sua primeira escolha. Você desejará usar um protocolo que também tenha uma soma de verificação ou hash para o arquivo inteiro e cada parte transferida, para poder comparar a soma de verificação do download com a soma de verificação do computador do servidor: qualquer coisa que não corresponda será novamente baixada. Novamente, o BitTorrent é um exemplo desse tipo de protocolo; O rsync também pode fazer isso opcionalmente.


para o exemplo do rsync, será simples, porque existe apenas um protocolo rsync. para downloads http, há solicitações de intervalo como padrão. Estou curioso para saber o que o curl realmente faz no upload de currículo, porque a semântica padrão do upload é multipart / form-data (para wget e curl), mas não acredito que a semântica do currículo de upload seja universalmente aceita. O YouTube e o Nginx podem fazer isso de maneira diferente, por exemplo.
Rob

1

Depende do protocolo usado para transferir. Mas o curl usa http e transfere dados sequencialmente na ordem em que aparecem no arquivo. Assim, a ondulação pode continuar com base no tamanho do arquivo de uma transferência parcialmente concluída. Na verdade, você pode enganá-lo para ignorar os primeiros N bytes, criando um arquivo de tamanho N (de qualquer coisa) e solicitando que trate esse arquivo como um download parcialmente concluído (e depois descartando os primeiros N bytes).

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.