Lendo um pipe nomeado: tail ou cat?

6

Eu fiz um descritor de arquivo usando

mkfifo fifo

Assim que algo é escrito neste pipe, quero reutilizá-lo imediatamente . Devo usar

tail -f fifo

ou

while true; do cat fifo; done

?

Eles parecem fazer a mesma coisa e eu não consegui medir a diferença de desempenho. No entanto, quando um sistema não suporta inotify (Busybox, por exemplo), o primeiro precisa ser

tail -f -s 0 fifo

Mas isso consome a CPU com 100% de uso (teste: mkfifo fifo && busybox tail -f -s 0 fifo & echo hi>fifo / cancel com fg 1 e Ctrl C ). Então o gato while-true é a solução mais confiável?

    
por Blauhirn 17.09.2017 / 02:25

2 respostas

5

Quando você faz:

cat fifo

Supondo que nenhum outro processo tenha aberto o fifo para gravação ainda, cat bloqueará a chamada do sistema open() . Quando outro processo abrir o arquivo para gravação, um canal será instanciado e open() retornará. cat chamará read() em um loop e read() bloqueará até que algum outro processo grave dados no pipe.

cat verá o final do arquivo (eof) quando todos os outros processos de escrita tiverem fechado seu descritor de arquivo para o fifo . Em que pontos cat termina e o tubo é destruído¹.

Você precisaria executar cat novamente para ler o que será gravado depois disso no fifo (mas por meio de uma instância de pipe diferente).

Em:

tail -f file

Como cat , tail aguardará um processo para abrir um arquivo para gravação. Mas aqui, como você não especificou -n +1 para copiar desde o início, tail precisará aguardar até eof para descobrir quais foram as últimas 10 linhas, para que você não veja nada até que o fim da escrita seja fechado.

Depois disso, tail não irá fechar seu fd para o pipe, o que significa que a instância do pipe não será destruída, e ainda tentará ler o pipe a cada segundo (no Linux, essa pesquisa pode ser evitada via uso de inotify e algumas versões do GNU tail fazem isso lá. Esse read() retornará com eof (imediatamente, e é por isso que você vê 100% da CPU com -s 0 (que com GNU tail significa não esperar entre read() s em vez de esperar por um segundo)) até que outro processo abre o arquivo novamente para gravação.

Aqui, em vez disso, você pode querer usar cat , mas certifique-se de que a instância do pipe permaneça sempre após ser instanciada. Para isso, na maioria dos sistemas, você poderia fazer:

cat 0<> fifo # the 0 is needed for recent versions of ksh93 where the
             # default fd changed from 0 to 1 for the <> operator
O stdin de

cat estará aberto para leitura e escrita, o que significa que cat nunca verá eof sobre ele (também instancia o pipe imediatamente mesmo se não houver outro processo abrindo fifo para escrita) .

Em sistemas onde isso não funciona, você pode fazer isso:

cat < fifo 3> fifo

Dessa forma, assim que algum outro processo abrir o fifo para gravação, o primeiro percentual somente de leituraopen() retornará, ponto no qual o shell fará o% somente gravaçãoopen() antes de iniciar cat , o que impedirá que o cano seja novamente destruído.

Então, para resumir:

  • comparado a cat file , não pararia após a primeira rodada.
  • comparado a tail -n +1 -f file : ele não usaria read() inútil a cada segundo após a primeira rodada, nunca haveria eof na única instância da tubulação, não haveria esse atraso de até um segundo quando segundo processo abre o pipe para escrever depois que o primeiro fechou.
  • comparado a tail -f file . Além do acima, ele não teria que esperar que a primeira rodada terminasse antes de produzir algo (apenas as últimas 10 linhas).
  • comparado a cat file em um loop, haveria apenas uma instância de pipe. As janelas de corrida mencionadas em ¹ seriam evitadas.

¹ neste ponto, entre o último read() que indica eof e cat terminando e fechando o final de leitura do pipe, há realmente uma pequena janela durante a qual um processo poderia abrir o fifo para gravação novamente (e não ser bloqueado, pois ainda há um final de leitura). Então, se ele escrever algo depois que cat tenha saído e antes que outro processo abra o fifo para leitura, ele será morto com um SIGPIPE.

    
por 17.09.2017 / 14:56
2

Deixe-me propor outra solução. O tubo estará disponível para leitura, contanto que algum processo seja gravado na segunda extremidade. Então você pode criar alguns cat falsos em segundo plano (ou em outro terminal), por exemplo:

mkfifo fifo
cat >fifo &
cat fifo

Agora você pode escrever para fifo o quanto quiser, e quando terminar, basta matar a corrente cat com Cc , e então fg trazer o primeiro cat do fundo e finalmente Cd para pará-lo.

    
por 17.09.2017 / 03:20

Tags