Explorando Transferência De Arquivos Para Dispositivos Externos

por isso precisariamos realmente testar da forma que mencionei, para ver se você tem o setup que pode levar a esse problema. O ideal seria fazer com rsync e depois se quiser mostrar o comportamento do programa que você usa para transferir em GUI, tranquilo. Mas entenda que diferente do Windows, Linux possui diversos programas que tratam a transferência de forma diferente, alguns gráficos outros não, o mais universal que sofre dos mesmo problemas é o cp.

Entendi. Mas eu não tenho esse problema. Eu realmente acredito q vc e outros usuários tenham. Meu ponto aqui é q isso não é uma característica. Como podemos testar isso? Simples!
Vc pega um arquivo aí que vc tem problemas para copiar para um pendrive, e que vc tmb tenha problemas de ter q esperar um tempo após a copia para remover o pendrive e aí eu gravo fazendo exatamente a mesma coisa, pego esse mesmo arquivo copio para um pendrive com o mesmo sistema de arquivos e demonstro que aqui assim que a barra completar eu já posso remover o pendrive. Que tal?

Amigo, o tipo de arquivo não importa, apenas precisa ser algo grande, de ~ 3 GB ou mais, e o dispositivo de origem precisa ser rápido ~ 200mps ou mais, e o de destino lento ~ 40mb ou menos. Se estas condições não forem as do usuário ele provavelmente não vai experiências este problema e se ele tunar bem para baixo os limites de dirty ratio, também (excluindo-se alguns efeitos colaterais como drops nos frames no record do OBS, etc). A questão é que em outros sistemas como Mac OS X, sob hipótese alguma, independente de ser usado a versão nativa do cpdeles, isso ocorre. entende? Por isso é uma caracteristica dos sistemas Linux :–) ligado a como o sistema gerencia paginas sujas.

Bom uso tudo no padrão nunca nem mexi em “dirty ratio” nem sei onde fica isso kk. Mas se necessitar eu gravo um vídeo passando um filme de mais de 3GB para um pendrive ruim da multileaser que tenho aqui que com certeza é bem menos q 4MB de leitura e escrita.

Quanto você tem de RAM, que como mencionei é um fator importante.

Apenas 4GB. Só uso Notebooks fracos da positivo kkk

Isso já explica, você raramente vai ter o problema, porque simplesmente seu sistema não oferece RAM o bastante para que o kernel permita a alocação de valores absurdos de páginas suja como + de 2GB.

Ah então se tipo eu tivesse mais de 4GB sofreria é isso?

com 8 GB, se aproximando de 2 GB de alocação destinada para páginas sujas você já poderia experienciar esse problema, pois o arquivo seria movido rapidamente para a paginação dando a ilusão de que o arquivo já foi movido para o disco. E você perceberia mais esse tempo faltante, já que demoraria consideravelmente para mover estes ~ 2 GB para seu pendrive.

1 curtida

Okay kk. Se vc diz rs. Espero que seu projeto ajude então quem tiver o problema. Desculpe se eu possa ter dado a impressão de ser agressivo, o q jamais quero ser. Apenas quando tenho um ponto o defendo afincadamente.

Por exemplo o meu caso em particular é insano, com 32 GB o kernel permite mais de 6 GB de páginas suja.

Ainda assim o meu projeto tem alguns benefícios para quem não sofre de forma perceptível do problema, pode consumir menos RAM na transferência, usar menos CPU e faz menos pressão sob os dispositivos envolvidos. E em alguns casos reduz o tempo de transferência.

De resto sem problema, reproduzir o problema de alguém nem sempre é fácil, se fosse assim tão simples aparentemente já estaria resolvido nas perguntas das exchanges lá em cima.

2 curtidas

Pois parabéns pela iniciativa. Se algum dia eu passar por este problema com certeza vou verificar a sua solução.

De resto, POR FAVOR, leiam o README, antes de surgir com dúvidas, tudo que foi discutido aqui está explicado lá assim como diversas outras nuances do problema! :–)

2 curtidas

Interessante. Eu tinha notado o que você descreve há um tempo mas não sabia explicar.

Notei esses dois casos várias vezes na vida:

The transference reaching 100% only some seconds after it start, generating confusion;

The transference “ending” before data really reach the external device, generating confusion when you try to umount;

No segundo caso notei isso numa das primeiras vezes que fui usar Linux e disse “nossa, como transfere rápido, não é possível…”

@romulopb Poderia dar uma explicação mais “nível básico” ou sugerir algum link para entendermos como se procede a transferência de uma arquivo em disco para um dispositivo externo?

1 curtida

Não é uma ofensa, é só um comentário, mas suas respostas parecem a personificação do meme “e nada mais importa a não ser a minha opinião” :rofl::rofl:

Não me ofendo não. Na verdade sou bem contra essa geração mais “sensível”. Mas realmente eu poderia ter abordado esse post de forma mais “suave” rs.

1 curtida

Vou tentar resumir como funciona uma transferência no geral. Espero que fique inteligível. :–)

Na prática a transferência se da mesma forma que qualquer outra, o que muda e nos leva a algumas confusões é que, com mais frequência:

  • A diferença de velocidade entre estes dispositivos é maior que entre discos SATA internos, ou ainda com alguma frequência o destino é muito lento (pendrive, vale também para sistemas de arquivo na rede onde isso gera outros problemas);
  • O usuário às vezes quer que o dispositivo seja desconectado assim que terminada a transferência.

Nestas condições você tem mais chance de notar este problema, mas eles podem ser observados entre qualquer dispositivo, dadas as seguintes condições:

  • O dispositivo de origem é rápido o bastante para transferir em segundos todo o arquivo para o sistema de paginação do sistema operacional.
  • O sistema tem RAM o bastante para alocar praticamente todo o arquivo em páginas sujas na RAM (não podem ser desalocadas, estão pendentes por serem escrita).
  • O dispositivo de destino é lento o bastante para demorar para receber todo o arquivo.
  • O programa não leva estas questões em consideração, apenas lê da origem na velocidade máxima e manda o kernel escrever tudo no dispositivo de destino sem esperar até que o sistema realmente termine de escrever as páginas realmente no destino.
  • O programa até espera que as páginas sejam escritas, mas está apenas analisando com que velocidade enche a fila de paginação do sistema, o resto do tempo fica esperando o sistema avisar que terminou de enviar tudo para o dispositivo de destino (neste meio tempo ele mostra 100% já tem algum tempo).

No geral de forma simplificada a maioria dos programas (nautilus, dolphin, rsync, etc). Seguem um algoritmo assim:

tamanhoPedaço = x bytes
Loop enquanto totalGravado diferente de 
tamanhoOrigem {
    lê tamanhoPedaço de origem
    grava tamanhoPedaço em destino
    contabiliza e mostra o progresso 
    # o que acontece neste loop vai na velocidade do dispositivo mais rápido, para a RAM
}
dependendo do programa, espera terminar de gravar no destino
# ou termina antes e o kernel segue gravando em background ou espera o kernel gravar em background e termina, isso é feito na velocidade do dispositivo mais lento

Além disso, existem muitas formas de se implementar a transferência em userspace (no seu programa), por exemplo, em C você tem de tudo, desde alta abstração até nuances do sistema operacional, sistema de arquivos, caching, etc. Pode por exemplo:

Usar as funções mais alto nível fopen(), fread() e fwrite(). Estas versões possuem buffers interno implementado diretamente na biblioteca glibc que expõe estas funções. O problema é que como qualquer outra abstração, você está a mercê de quão bem seu problema se adapta a este algoritmo. Hoje glibc é mais restrito ao Linux, mas poderia ser implementações para outros sistemas UNIX.

Você pode usar as funções mais baixo nível open(), read() e write() estas versões não possuem buffers internos (fica a seu critério implementar um se preciso) e permitem a utilização de flags especiais para permitir escrita síncrona (O_SYNC) e assíncrona, ou passando por cima do sistema de paginação (O_DIRECT), dentre outras coisas. Por exemplo, se você não usar O_SYNC, as chamadas write() não bloquearão até que o kernel realmente grave o conteúdo no disco, outra possibilidade para esperar é chamar fdsync() toda vez que chamar write(). Estas funções lidam com nuances específicas da plataforma e não são portáveis para Windows por exemplo.

Pode usar sendfile(), ou splice() a vantagem destas funções é que até o momento estávamos falando de funções que copiam o arquivo da origem, no kernel space para o userspace e depois novamente copiam para o kernel space, para ser escrito no arquivo destino, estas funções são comuns para realizar transferências zero copy o que é mais eficiente. Também são específicas do Linux.

Existem muitas outras questões e nuances na transferência de dados, como cópias esparsas, reflinks (cópia apenas de endereço), etc (sockets, pipes, arquivos, dispositivos), mas a questão neste caso em particular é que o processo não tem informações sobre quantas páginas do arquivo destino ainda estão esperando para serem escritas e quantas já foram de fato lá do outro lado, no kernel space, você pode tentar truques como observar /proc/self/io e /proc/self/smaps mas mesmo assim nem sempre as informações nestes arquivos é suficientemente precisa, pelo menos não sem abusar da leitura destes arquivos, afetando a performance do programa.

1 curtida

Poderia ter lido o README de uma forma mais ‘suave’ também, estas questões estavam descritas lá. :joy:

Deveria mam ter lido kk. Mas é q como n passo por o problema meio q deu uma preguiça. Mas vou ler dps por curiosidade.

Pergunta, porque rodar numa sub shell e não em bloco?

1 curtida