"Named pipe" é na verdade um nome muito preciso para o que é - é como um pipe regular, exceto pelo fato de ter um nome (em um sistema de arquivos).
Um canal - o nome comum, não nomeado ("anônimo") usado em some-command | grep pattern
é um tipo especial de arquivo. E eu quero dizer arquivo, você lê e escreve para ele assim como você faz todos os outros arquivos. O Grep realmente não se importa se está lendo um pipe em vez de um terminal³ ou um arquivo comum.
Tecnicamente, o que acontece nos bastidores é que stdin, stdout e stderr são três arquivos abertos (descritores de arquivo) passados para cada execução de comando. Os descritores de arquivos (que são usados em todos os arquivos syscall para leitura / gravação / etc.) São apenas números; stdin, stdout e stderr são descritores de arquivos 0, 1 e 2. Assim, quando seu shell configura some-command | grep
, o que ele faz é algo assim:
-
Pergunta ao kernel por um canal anônimo. Não há nome, portanto, isso não pode ser feito com
open
, como em um arquivo normal. Em vez disso, ele é feito compipe
oupipe2
, que retorna dois descritores de arquivo.⁴ -
Garante um processo filho (
fork()
cria uma cópia do processo pai; ambos os lados do canal estão abertos aqui), copia o lado de gravação do canal para fd 1 (stdout). O kernel tem um syscall para copiar em torno de números de descritores de arquivos; édup2()
oudup3()
. Em seguida, fecha o lado de leitura e outra cópia do lado de gravação. Finalmente, usaexecve
para executarsome-command
. Como o pipe é fd 1, stdout desome-command
é o pipe. -
Garfos de outro processo filho. Desta vez, duplica o lado de leitura do pipe para fd 0 (stdin) e executa
grep
. Então o grep irá ler o pipe como stdin. -
Em seguida, ele espera que ambas as crianças saiam.
-
Neste ponto, o kernel percebe que o pipe não está mais aberto e o lixo o coleta. Isso é o que realmente destrói o tubo.
Um pipe nomeado apenas dá um nome ao pipe anônimo colocando-o no sistema de arquivos. Então agora qualquer qualquer processo, a qualquer momento no futuro, pode obter um descritor de arquivo para o pipe usando um open
syscall comum. Conceitualmente, o pipe não será destruído até que todos os leitores / escritores o tenham fechado e seja unlink
ed do sistema de arquivos.²
Isto, a propósito, é como os arquivos em geral funcionam no Unix. unlink
(o syscall atrás de rm
) apenas remove um dos nomes para o arquivo; somente quando todos os nomes forem removidos e nada tiver o arquivo aberto, ele será realmente excluído. Algumas respostas aqui exploram isso:
- Por que os links físicos parecem ter o mesmo espaço que os originais?
- Como um programa de log pode continuar registrando em um arquivo excluído?
- O que o Linux está fazendo de maneira diferente que me permite remover / substituir arquivos em que o Windows iria reclamar que o arquivo está em uso atualmente?
NOTAS
- Tecnicamente, isso provavelmente não é verdade - provavelmente é possível fazer algumas otimizações sabendo, e implementações reais do grep têm sido altamente otimizadas. Mas, conceitualmente, não se importa (e de fato uma implementação direta do grep).
- É claro que o kernel na verdade não mantém todas as estruturas de dados na memória para sempre, mas as recria, transparentemente, sempre que o primeiro programa abre o pipe nomeado (e as mantém contanto que estejam abertas). Então é como se eles existissem desde que o nome existisse.
- O terminal não é um lugar comum para o grep ler, mas é o padrão stdin quando você não especifica outro. Então, se você digitar apenas
grep pattern
no seu shell,grep
estará lendo no terminal. O único uso para isso que vem à mente é se você está prestes a colar algo no terminal. - No Linux, os pipes anônimos são criados em um sistema de arquivos especial, pipefs. Veja Como os canos funcionam no Linux para detalhes. Note que este é um detalhe de implementação interna do Linux.