2 pontos por GN⁺ 2024-11-30 | 1 comentários | Compartilhar no WhatsApp

Por que um pipe “trava”: buffering

  • Descrição do problema: ao executar o comando tail -f /some/log/file | grep thing1 | grep thing2 para encontrar uma saída específica em um arquivo de log, pode acontecer de nada aparecer quando novas linhas são adicionadas lentamente ao log. Parece que o pipe travou, mas na prática o programa não está escrevendo os dados no pipe.

A causa do buffering

  • Por que existe buffering: é comum que programas façam buffering antes de escrever dados em um pipe ou arquivo. Isso melhora o desempenho, porque em vez de gravar cada saída imediatamente, o programa junta uma certa quantidade de dados e escreve tudo de uma vez.
  • Exemplo: grep thing1 pode guardar os dados correspondentes até acumular 8KB, e por isso a saída pode não aparecer.

Ao escrever no terminal, não faz buffering

  • Diferença entre terminal e pipe: o grep usa line buffering quando a saída vai para o terminal, mas usa block buffering quando a saída vai para um pipe. Isso é decidido pela função isatty.

Comandos que fazem buffering e os que não fazem

  • Comandos que não fazem buffering: tail, cat, tee etc. não fazem buffering.
  • Comandos que fazem buffering: grep, sed, awk, tcpdump, jq, tr, cut etc. fazem buffering, e alguns permitem desativá-lo com flags específicas.

Buffering padrão de saída em linguagens de programação

  • Linguagens que fazem buffering: C, Python, Ruby, Perl etc. fazem buffering na saída por padrão, mas isso pode ser desativado de maneiras específicas.

Perda do conteúdo do buffer ao pressionar Ctrl-C

  • Descrição do problema: ao pressionar Ctrl-C, o conteúdo que está no buffer se perde. Isso acontece porque o sinal SIGINT é entregue primeiro.
  • Solução: encontre o PID do tcpdump e execute kill -TERM $PID para forçar o flush do buffer.

Também há buffering ao redirecionar para arquivo

  • Redirecionamento para arquivo: ao redirecionar para um arquivo também ocorre buffering, mas o problema de perda do buffer causado por Ctrl-C não acontece.

Várias maneiras de evitar buffering

  • Solução 1: executar um programa que termine rapidamente.
  • Solução 2: usar a flag --line-buffered do grep.
  • Solução 3: usar awk.
  • Solução 4: usar stdbuf.
  • Solução 5: usar unbuffer.

Variáveis de ambiente para desativar buffering

  • Ideia: seria bom existir uma variável de ambiente padrão como PYTHON_UNBUFFERED. É sugerida uma variável como NO_BUFFER.

Conteúdo omitido

  • Tópicos omitidos: a diferença entre line buffering e buffering totalmente desativado, a diferença de buffering entre stderr e stdout, o buffering do driver de TTY do sistema operacional etc.

1 comentários

 
GN⁺ 2024-11-30
Comentários do Hacker News
  • O acesso com buffer precisa fazer flush após um certo número de bytes ou após um certo tempo. É uma forma comum de resolver problemas parecidos em interfaces de hardware

    • Bibliotecas que fazem buffering no espaço do usuário devem configurar um temporizador apropriado quando começarem a armazenar dados em buffer
    • É melhor que o parâmetro de timeout seja passado como argumento, ou fique um pouco abaixo da escala de tempo humana, ou seja proporcional à largura de banda/limiar, ou ao overhead do flush
    • Isso se aplica tanto à escrita quanto à leitura, e pode variar conforme o canal de dados
  • Quando a CPU do sistema inteiro fica ociosa, eu gostaria de descarregar todos os buffers

    • Buffering normalmente é uma técnica para economizar CPU
    • Quando a CPU fica ociosa, deveria ser enviado um sinal para todos os processos dizendo "façam flush dos buffers"
  • Trabalho com sistemas NIX há mais de 20 anos, mas sempre esqueço dos problemas de buffering

  • Uso Unix há mais de 35 anos, mas nunca entendi completamente como o buffering funciona. Essa explicação foi útil

  • As pessoas estão confundindo "sem buffer" com "buffer por linha"

    • Sem buffer pode prejudicar o desempenho e, quando várias fontes escrevem no mesmo pipe, pode gerar saída incorreta
    • Buffer por linha é o padrão no terminal e funciona bem para pipes
  • Buffers existem porque escrever no buffer é relativamente muito mais lento do que imprimir saída na tela

    • Isso é um problema frequente ao trabalhar com UART, e há várias soluções
    • Existem várias abordagens, como uso de caracteres especiais, abordagem baseada em comprimento e abordagem baseada em tempo
  • Ao pressionar Ctrl-C, o conteúdo do buffer pode ser perdido

    • Eu achava que a maioria dos programas faria flush do buffer em SIGINT
  • Já tive problemas de buffering no Unix, e nem todas as implementações de awk se comportam da mesma forma

  • Sinto que deixei passar a piada do pipe congelado